1// Copyright 2012 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include "v8.h" 29 30#include "api.h" 31#include "arguments.h" 32#include "bootstrapper.h" 33#include "codegen.h" 34#include "debug.h" 35#include "deoptimizer.h" 36#include "date.h" 37#include "elements.h" 38#include "execution.h" 39#include "full-codegen.h" 40#include "hydrogen.h" 41#include "objects-inl.h" 42#include "objects-visiting.h" 43#include "objects-visiting-inl.h" 44#include "macro-assembler.h" 45#include "mark-compact.h" 46#include "safepoint-table.h" 47#include "string-stream.h" 48#include "utils.h" 49#include "vm-state-inl.h" 50 51#ifdef ENABLE_DISASSEMBLER 52#include "disasm.h" 53#include "disassembler.h" 54#endif 55 56namespace v8 { 57namespace internal { 58 59void PrintElementsKind(FILE* out, ElementsKind kind) { 60 ElementsAccessor* accessor = ElementsAccessor::ForKind(kind); 61 PrintF(out, "%s", accessor->name()); 62} 63 64 65MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor, 66 Object* value) { 67 Object* result; 68 { MaybeObject* maybe_result = 69 constructor->GetHeap()->AllocateJSObject(constructor); 70 if (!maybe_result->ToObject(&result)) return maybe_result; 71 } 72 JSValue::cast(result)->set_value(value); 73 return result; 74} 75 76 77MaybeObject* Object::ToObject(Context* global_context) { 78 if (IsNumber()) { 79 return CreateJSValue(global_context->number_function(), this); 80 } else if (IsBoolean()) { 81 return CreateJSValue(global_context->boolean_function(), this); 82 } else if (IsString()) { 83 return CreateJSValue(global_context->string_function(), this); 84 } 85 ASSERT(IsJSObject()); 86 return this; 87} 88 89 90MaybeObject* Object::ToObject() { 91 if (IsJSReceiver()) { 92 return this; 93 } else if (IsNumber()) { 94 Isolate* isolate = Isolate::Current(); 95 Context* global_context = isolate->context()->global_context(); 96 return CreateJSValue(global_context->number_function(), this); 97 } else if (IsBoolean()) { 98 Isolate* isolate = HeapObject::cast(this)->GetIsolate(); 99 Context* global_context = isolate->context()->global_context(); 100 return CreateJSValue(global_context->boolean_function(), this); 101 } else if (IsString()) { 102 Isolate* isolate = HeapObject::cast(this)->GetIsolate(); 103 Context* global_context = isolate->context()->global_context(); 104 return CreateJSValue(global_context->string_function(), this); 105 } 106 107 // Throw a type error. 108 return Failure::InternalError(); 109} 110 111 112Object* Object::ToBoolean() { 113 if (IsTrue()) return this; 114 if (IsFalse()) return this; 115 if (IsSmi()) { 116 return Isolate::Current()->heap()->ToBoolean(Smi::cast(this)->value() != 0); 117 } 118 HeapObject* heap_object = HeapObject::cast(this); 119 if (heap_object->IsUndefined() || heap_object->IsNull()) { 120 return heap_object->GetHeap()->false_value(); 121 } 122 // Undetectable object is false 123 if (heap_object->IsUndetectableObject()) { 124 return heap_object->GetHeap()->false_value(); 125 } 126 if (heap_object->IsString()) { 127 return heap_object->GetHeap()->ToBoolean( 128 String::cast(this)->length() != 0); 129 } 130 if (heap_object->IsHeapNumber()) { 131 return HeapNumber::cast(this)->HeapNumberToBoolean(); 132 } 133 return heap_object->GetHeap()->true_value(); 134} 135 136 137void Object::Lookup(String* name, LookupResult* result) { 138 Object* holder = NULL; 139 if (IsJSReceiver()) { 140 holder = this; 141 } else { 142 Context* global_context = Isolate::Current()->context()->global_context(); 143 if (IsNumber()) { 144 holder = global_context->number_function()->instance_prototype(); 145 } else if (IsString()) { 146 holder = global_context->string_function()->instance_prototype(); 147 } else if (IsBoolean()) { 148 holder = global_context->boolean_function()->instance_prototype(); 149 } 150 } 151 ASSERT(holder != NULL); // Cannot handle null or undefined. 152 JSReceiver::cast(holder)->Lookup(name, result); 153} 154 155 156MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, 157 String* name, 158 PropertyAttributes* attributes) { 159 LookupResult result(name->GetIsolate()); 160 Lookup(name, &result); 161 MaybeObject* value = GetProperty(receiver, &result, name, attributes); 162 ASSERT(*attributes <= ABSENT); 163 return value; 164} 165 166 167MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, 168 Object* structure, 169 String* name) { 170 Isolate* isolate = name->GetIsolate(); 171 // To accommodate both the old and the new api we switch on the 172 // data structure used to store the callbacks. Eventually foreign 173 // callbacks should be phased out. 174 if (structure->IsForeign()) { 175 AccessorDescriptor* callback = 176 reinterpret_cast<AccessorDescriptor*>( 177 Foreign::cast(structure)->foreign_address()); 178 MaybeObject* value = (callback->getter)(receiver, callback->data); 179 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 180 return value; 181 } 182 183 // api style callbacks. 184 if (structure->IsAccessorInfo()) { 185 AccessorInfo* data = AccessorInfo::cast(structure); 186 Object* fun_obj = data->getter(); 187 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); 188 HandleScope scope(isolate); 189 JSObject* self = JSObject::cast(receiver); 190 Handle<String> key(name); 191 LOG(isolate, ApiNamedPropertyAccess("load", self, name)); 192 CustomArguments args(isolate, data->data(), self, this); 193 v8::AccessorInfo info(args.end()); 194 v8::Handle<v8::Value> result; 195 { 196 // Leaving JavaScript. 197 VMState state(isolate, EXTERNAL); 198 result = call_fun(v8::Utils::ToLocal(key), info); 199 } 200 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 201 if (result.IsEmpty()) { 202 return isolate->heap()->undefined_value(); 203 } 204 return *v8::Utils::OpenHandle(*result); 205 } 206 207 // __defineGetter__ callback 208 if (structure->IsAccessorPair()) { 209 Object* getter = AccessorPair::cast(structure)->getter(); 210 if (getter->IsSpecFunction()) { 211 // TODO(rossberg): nicer would be to cast to some JSCallable here... 212 return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); 213 } 214 // Getter is not a function. 215 return isolate->heap()->undefined_value(); 216 } 217 218 UNREACHABLE(); 219 return NULL; 220} 221 222 223MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, 224 String* name_raw) { 225 Isolate* isolate = GetIsolate(); 226 HandleScope scope(isolate); 227 Handle<Object> receiver(receiver_raw); 228 Handle<Object> name(name_raw); 229 230 Handle<Object> args[] = { receiver, name }; 231 Handle<Object> result = CallTrap( 232 "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args); 233 if (isolate->has_pending_exception()) return Failure::Exception(); 234 235 return *result; 236} 237 238 239Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) { 240 Isolate* isolate = object->IsHeapObject() 241 ? Handle<HeapObject>::cast(object)->GetIsolate() 242 : Isolate::Current(); 243 CALL_HEAP_FUNCTION(isolate, object->GetElement(index), Object); 244} 245 246 247MaybeObject* JSProxy::GetElementWithHandler(Object* receiver, 248 uint32_t index) { 249 String* name; 250 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 251 if (!maybe->To<String>(&name)) return maybe; 252 return GetPropertyWithHandler(receiver, name); 253} 254 255 256MaybeObject* JSProxy::SetElementWithHandler(uint32_t index, 257 Object* value, 258 StrictModeFlag strict_mode) { 259 String* name; 260 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 261 if (!maybe->To<String>(&name)) return maybe; 262 return SetPropertyWithHandler(name, value, NONE, strict_mode); 263} 264 265 266bool JSProxy::HasElementWithHandler(uint32_t index) { 267 String* name; 268 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 269 if (!maybe->To<String>(&name)) return maybe; 270 return HasPropertyWithHandler(name); 271} 272 273 274MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, 275 JSReceiver* getter) { 276 HandleScope scope; 277 Handle<JSReceiver> fun(getter); 278 Handle<Object> self(receiver); 279#ifdef ENABLE_DEBUGGER_SUPPORT 280 Debug* debug = fun->GetHeap()->isolate()->debug(); 281 // Handle stepping into a getter if step into is active. 282 // TODO(rossberg): should this apply to getters that are function proxies? 283 if (debug->StepInActive() && fun->IsJSFunction()) { 284 debug->HandleStepIn( 285 Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); 286 } 287#endif 288 289 bool has_pending_exception; 290 Handle<Object> result = 291 Execution::Call(fun, self, 0, NULL, &has_pending_exception, true); 292 // Check for pending exception and return the result. 293 if (has_pending_exception) return Failure::Exception(); 294 return *result; 295} 296 297 298// Only deal with CALLBACKS and INTERCEPTOR 299MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( 300 Object* receiver, 301 LookupResult* result, 302 String* name, 303 PropertyAttributes* attributes) { 304 if (result->IsProperty()) { 305 switch (result->type()) { 306 case CALLBACKS: { 307 // Only allow API accessors. 308 Object* obj = result->GetCallbackObject(); 309 if (obj->IsAccessorInfo()) { 310 AccessorInfo* info = AccessorInfo::cast(obj); 311 if (info->all_can_read()) { 312 *attributes = result->GetAttributes(); 313 return result->holder()->GetPropertyWithCallback( 314 receiver, result->GetCallbackObject(), name); 315 } 316 } 317 break; 318 } 319 case NORMAL: 320 case FIELD: 321 case CONSTANT_FUNCTION: { 322 // Search ALL_CAN_READ accessors in prototype chain. 323 LookupResult r(GetIsolate()); 324 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); 325 if (r.IsProperty()) { 326 return GetPropertyWithFailedAccessCheck(receiver, 327 &r, 328 name, 329 attributes); 330 } 331 break; 332 } 333 case INTERCEPTOR: { 334 // If the object has an interceptor, try real named properties. 335 // No access check in GetPropertyAttributeWithInterceptor. 336 LookupResult r(GetIsolate()); 337 result->holder()->LookupRealNamedProperty(name, &r); 338 if (r.IsProperty()) { 339 return GetPropertyWithFailedAccessCheck(receiver, 340 &r, 341 name, 342 attributes); 343 } 344 break; 345 } 346 default: 347 UNREACHABLE(); 348 } 349 } 350 351 // No accessible property found. 352 *attributes = ABSENT; 353 Heap* heap = name->GetHeap(); 354 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_GET); 355 return heap->undefined_value(); 356} 357 358 359PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( 360 Object* receiver, 361 LookupResult* result, 362 String* name, 363 bool continue_search) { 364 if (result->IsProperty()) { 365 switch (result->type()) { 366 case CALLBACKS: { 367 // Only allow API accessors. 368 Object* obj = result->GetCallbackObject(); 369 if (obj->IsAccessorInfo()) { 370 AccessorInfo* info = AccessorInfo::cast(obj); 371 if (info->all_can_read()) { 372 return result->GetAttributes(); 373 } 374 } 375 break; 376 } 377 378 case NORMAL: 379 case FIELD: 380 case CONSTANT_FUNCTION: { 381 if (!continue_search) break; 382 // Search ALL_CAN_READ accessors in prototype chain. 383 LookupResult r(GetIsolate()); 384 result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); 385 if (r.IsProperty()) { 386 return GetPropertyAttributeWithFailedAccessCheck(receiver, 387 &r, 388 name, 389 continue_search); 390 } 391 break; 392 } 393 394 case INTERCEPTOR: { 395 // If the object has an interceptor, try real named properties. 396 // No access check in GetPropertyAttributeWithInterceptor. 397 LookupResult r(GetIsolate()); 398 if (continue_search) { 399 result->holder()->LookupRealNamedProperty(name, &r); 400 } else { 401 result->holder()->LocalLookupRealNamedProperty(name, &r); 402 } 403 if (r.IsProperty()) { 404 return GetPropertyAttributeWithFailedAccessCheck(receiver, 405 &r, 406 name, 407 continue_search); 408 } 409 break; 410 } 411 412 default: 413 UNREACHABLE(); 414 } 415 } 416 417 GetIsolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 418 return ABSENT; 419} 420 421 422Object* JSObject::GetNormalizedProperty(LookupResult* result) { 423 ASSERT(!HasFastProperties()); 424 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); 425 if (IsGlobalObject()) { 426 value = JSGlobalPropertyCell::cast(value)->value(); 427 } 428 ASSERT(!value->IsJSGlobalPropertyCell()); 429 return value; 430} 431 432 433Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) { 434 ASSERT(!HasFastProperties()); 435 if (IsGlobalObject()) { 436 JSGlobalPropertyCell* cell = 437 JSGlobalPropertyCell::cast( 438 property_dictionary()->ValueAt(result->GetDictionaryEntry())); 439 cell->set_value(value); 440 } else { 441 property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value); 442 } 443 return value; 444} 445 446 447Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object, 448 Handle<String> key, 449 Handle<Object> value, 450 PropertyDetails details) { 451 CALL_HEAP_FUNCTION(object->GetIsolate(), 452 object->SetNormalizedProperty(*key, *value, details), 453 Object); 454} 455 456 457MaybeObject* JSObject::SetNormalizedProperty(String* name, 458 Object* value, 459 PropertyDetails details) { 460 ASSERT(!HasFastProperties()); 461 int entry = property_dictionary()->FindEntry(name); 462 if (entry == StringDictionary::kNotFound) { 463 Object* store_value = value; 464 if (IsGlobalObject()) { 465 Heap* heap = name->GetHeap(); 466 MaybeObject* maybe_store_value = 467 heap->AllocateJSGlobalPropertyCell(value); 468 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value; 469 } 470 Object* dict; 471 { MaybeObject* maybe_dict = 472 property_dictionary()->Add(name, store_value, details); 473 if (!maybe_dict->ToObject(&dict)) return maybe_dict; 474 } 475 set_properties(StringDictionary::cast(dict)); 476 return value; 477 } 478 // Preserve enumeration index. 479 details = PropertyDetails(details.attributes(), 480 details.type(), 481 property_dictionary()->DetailsAt(entry).index()); 482 if (IsGlobalObject()) { 483 JSGlobalPropertyCell* cell = 484 JSGlobalPropertyCell::cast(property_dictionary()->ValueAt(entry)); 485 cell->set_value(value); 486 // Please note we have to update the property details. 487 property_dictionary()->DetailsAtPut(entry, details); 488 } else { 489 property_dictionary()->SetEntry(entry, name, value, details); 490 } 491 return value; 492} 493 494 495MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) { 496 ASSERT(!HasFastProperties()); 497 StringDictionary* dictionary = property_dictionary(); 498 int entry = dictionary->FindEntry(name); 499 if (entry != StringDictionary::kNotFound) { 500 // If we have a global object set the cell to the hole. 501 if (IsGlobalObject()) { 502 PropertyDetails details = dictionary->DetailsAt(entry); 503 if (details.IsDontDelete()) { 504 if (mode != FORCE_DELETION) return GetHeap()->false_value(); 505 // When forced to delete global properties, we have to make a 506 // map change to invalidate any ICs that think they can load 507 // from the DontDelete cell without checking if it contains 508 // the hole value. 509 Object* new_map; 510 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors(); 511 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 512 } 513 set_map(Map::cast(new_map)); 514 } 515 JSGlobalPropertyCell* cell = 516 JSGlobalPropertyCell::cast(dictionary->ValueAt(entry)); 517 cell->set_value(cell->GetHeap()->the_hole_value()); 518 dictionary->DetailsAtPut(entry, details.AsDeleted()); 519 } else { 520 Object* deleted = dictionary->DeleteProperty(entry, mode); 521 if (deleted == GetHeap()->true_value()) { 522 FixedArray* new_properties = NULL; 523 MaybeObject* maybe_properties = dictionary->Shrink(name); 524 if (!maybe_properties->To(&new_properties)) { 525 return maybe_properties; 526 } 527 set_properties(new_properties); 528 } 529 return deleted; 530 } 531 } 532 return GetHeap()->true_value(); 533} 534 535 536bool JSObject::IsDirty() { 537 Object* cons_obj = map()->constructor(); 538 if (!cons_obj->IsJSFunction()) 539 return true; 540 JSFunction* fun = JSFunction::cast(cons_obj); 541 if (!fun->shared()->IsApiFunction()) 542 return true; 543 // If the object is fully fast case and has the same map it was 544 // created with then no changes can have been made to it. 545 return map() != fun->initial_map() 546 || !HasFastElements() 547 || !HasFastProperties(); 548} 549 550 551Handle<Object> Object::GetProperty(Handle<Object> object, 552 Handle<Object> receiver, 553 LookupResult* result, 554 Handle<String> key, 555 PropertyAttributes* attributes) { 556 Isolate* isolate = object->IsHeapObject() 557 ? Handle<HeapObject>::cast(object)->GetIsolate() 558 : Isolate::Current(); 559 CALL_HEAP_FUNCTION( 560 isolate, 561 object->GetProperty(*receiver, result, *key, attributes), 562 Object); 563} 564 565 566MaybeObject* Object::GetProperty(Object* receiver, 567 LookupResult* result, 568 String* name, 569 PropertyAttributes* attributes) { 570 // Make sure that the top context does not change when doing 571 // callbacks or interceptor calls. 572 AssertNoContextChange ncc; 573 Heap* heap = name->GetHeap(); 574 575 // Traverse the prototype chain from the current object (this) to 576 // the holder and check for access rights. This avoids traversing the 577 // objects more than once in case of interceptors, because the 578 // holder will always be the interceptor holder and the search may 579 // only continue with a current object just after the interceptor 580 // holder in the prototype chain. 581 // Proxy handlers do not use the proxy's prototype, so we can skip this. 582 if (!result->IsHandler()) { 583 Object* last = result->IsProperty() 584 ? result->holder() 585 : Object::cast(heap->null_value()); 586 ASSERT(this != this->GetPrototype()); 587 for (Object* current = this; true; current = current->GetPrototype()) { 588 if (current->IsAccessCheckNeeded()) { 589 // Check if we're allowed to read from the current object. Note 590 // that even though we may not actually end up loading the named 591 // property from the current object, we still check that we have 592 // access to it. 593 JSObject* checked = JSObject::cast(current); 594 if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) { 595 return checked->GetPropertyWithFailedAccessCheck(receiver, 596 result, 597 name, 598 attributes); 599 } 600 } 601 // Stop traversing the chain once we reach the last object in the 602 // chain; either the holder of the result or null in case of an 603 // absent property. 604 if (current == last) break; 605 } 606 } 607 608 if (!result->IsProperty()) { 609 *attributes = ABSENT; 610 return heap->undefined_value(); 611 } 612 *attributes = result->GetAttributes(); 613 Object* value; 614 switch (result->type()) { 615 case NORMAL: 616 value = result->holder()->GetNormalizedProperty(result); 617 ASSERT(!value->IsTheHole() || result->IsReadOnly()); 618 return value->IsTheHole() ? heap->undefined_value() : value; 619 case FIELD: 620 value = result->holder()->FastPropertyAt(result->GetFieldIndex()); 621 ASSERT(!value->IsTheHole() || result->IsReadOnly()); 622 return value->IsTheHole() ? heap->undefined_value() : value; 623 case CONSTANT_FUNCTION: 624 return result->GetConstantFunction(); 625 case CALLBACKS: 626 return result->holder()->GetPropertyWithCallback( 627 receiver, result->GetCallbackObject(), name); 628 case HANDLER: 629 return result->proxy()->GetPropertyWithHandler(receiver, name); 630 case INTERCEPTOR: { 631 JSObject* recvr = JSObject::cast(receiver); 632 return result->holder()->GetPropertyWithInterceptor( 633 recvr, name, attributes); 634 } 635 case MAP_TRANSITION: 636 case ELEMENTS_TRANSITION: 637 case CONSTANT_TRANSITION: 638 case NULL_DESCRIPTOR: 639 break; 640 } 641 UNREACHABLE(); 642 return NULL; 643} 644 645 646MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { 647 Heap* heap = IsSmi() 648 ? Isolate::Current()->heap() 649 : HeapObject::cast(this)->GetHeap(); 650 Object* holder = this; 651 652 // Iterate up the prototype chain until an element is found or the null 653 // prototype is encountered. 654 for (holder = this; 655 holder != heap->null_value(); 656 holder = holder->GetPrototype()) { 657 if (!holder->IsJSObject()) { 658 Isolate* isolate = heap->isolate(); 659 Context* global_context = isolate->context()->global_context(); 660 if (holder->IsNumber()) { 661 holder = global_context->number_function()->instance_prototype(); 662 } else if (holder->IsString()) { 663 holder = global_context->string_function()->instance_prototype(); 664 } else if (holder->IsBoolean()) { 665 holder = global_context->boolean_function()->instance_prototype(); 666 } else if (holder->IsJSProxy()) { 667 return JSProxy::cast(holder)->GetElementWithHandler(receiver, index); 668 } else { 669 // Undefined and null have no indexed properties. 670 ASSERT(holder->IsUndefined() || holder->IsNull()); 671 return heap->undefined_value(); 672 } 673 } 674 675 // Inline the case for JSObjects. Doing so significantly improves the 676 // performance of fetching elements where checking the prototype chain is 677 // necessary. 678 JSObject* js_object = JSObject::cast(holder); 679 680 // Check access rights if needed. 681 if (js_object->IsAccessCheckNeeded()) { 682 Isolate* isolate = heap->isolate(); 683 if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) { 684 isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET); 685 return heap->undefined_value(); 686 } 687 } 688 689 if (js_object->HasIndexedInterceptor()) { 690 return js_object->GetElementWithInterceptor(receiver, index); 691 } 692 693 if (js_object->elements() != heap->empty_fixed_array()) { 694 MaybeObject* result = js_object->GetElementsAccessor()->Get( 695 receiver, js_object, index); 696 if (result != heap->the_hole_value()) return result; 697 } 698 } 699 700 return heap->undefined_value(); 701} 702 703 704Object* Object::GetPrototype() { 705 if (IsSmi()) { 706 Heap* heap = Isolate::Current()->heap(); 707 Context* context = heap->isolate()->context()->global_context(); 708 return context->number_function()->instance_prototype(); 709 } 710 711 HeapObject* heap_object = HeapObject::cast(this); 712 713 // The object is either a number, a string, a boolean, 714 // a real JS object, or a Harmony proxy. 715 if (heap_object->IsJSReceiver()) { 716 return heap_object->map()->prototype(); 717 } 718 Heap* heap = heap_object->GetHeap(); 719 Context* context = heap->isolate()->context()->global_context(); 720 721 if (heap_object->IsHeapNumber()) { 722 return context->number_function()->instance_prototype(); 723 } 724 if (heap_object->IsString()) { 725 return context->string_function()->instance_prototype(); 726 } 727 if (heap_object->IsBoolean()) { 728 return context->boolean_function()->instance_prototype(); 729 } else { 730 return heap->null_value(); 731 } 732} 733 734 735MaybeObject* Object::GetHash(CreationFlag flag) { 736 // The object is either a number, a string, an odd-ball, 737 // a real JS object, or a Harmony proxy. 738 if (IsNumber()) { 739 uint32_t hash = ComputeLongHash(double_to_uint64(Number())); 740 return Smi::FromInt(hash & Smi::kMaxValue); 741 } 742 if (IsString()) { 743 uint32_t hash = String::cast(this)->Hash(); 744 return Smi::FromInt(hash); 745 } 746 if (IsOddball()) { 747 uint32_t hash = Oddball::cast(this)->to_string()->Hash(); 748 return Smi::FromInt(hash); 749 } 750 if (IsJSReceiver()) { 751 return JSReceiver::cast(this)->GetIdentityHash(flag); 752 } 753 754 UNREACHABLE(); 755 return Smi::FromInt(0); 756} 757 758 759bool Object::SameValue(Object* other) { 760 if (other == this) return true; 761 if (!IsHeapObject() || !other->IsHeapObject()) return false; 762 763 // The object is either a number, a string, an odd-ball, 764 // a real JS object, or a Harmony proxy. 765 if (IsNumber() && other->IsNumber()) { 766 double this_value = Number(); 767 double other_value = other->Number(); 768 return (this_value == other_value) || 769 (isnan(this_value) && isnan(other_value)); 770 } 771 if (IsString() && other->IsString()) { 772 return String::cast(this)->Equals(String::cast(other)); 773 } 774 return false; 775} 776 777 778void Object::ShortPrint(FILE* out) { 779 HeapStringAllocator allocator; 780 StringStream accumulator(&allocator); 781 ShortPrint(&accumulator); 782 accumulator.OutputToFile(out); 783} 784 785 786void Object::ShortPrint(StringStream* accumulator) { 787 if (IsSmi()) { 788 Smi::cast(this)->SmiPrint(accumulator); 789 } else if (IsFailure()) { 790 Failure::cast(this)->FailurePrint(accumulator); 791 } else { 792 HeapObject::cast(this)->HeapObjectShortPrint(accumulator); 793 } 794} 795 796 797void Smi::SmiPrint(FILE* out) { 798 PrintF(out, "%d", value()); 799} 800 801 802void Smi::SmiPrint(StringStream* accumulator) { 803 accumulator->Add("%d", value()); 804} 805 806 807void Failure::FailurePrint(StringStream* accumulator) { 808 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value())); 809} 810 811 812void Failure::FailurePrint(FILE* out) { 813 PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value())); 814} 815 816 817// Should a word be prefixed by 'a' or 'an' in order to read naturally in 818// English? Returns false for non-ASCII or words that don't start with 819// a capital letter. The a/an rule follows pronunciation in English. 820// We don't use the BBC's overcorrect "an historic occasion" though if 821// you speak a dialect you may well say "an 'istoric occasion". 822static bool AnWord(String* str) { 823 if (str->length() == 0) return false; // A nothing. 824 int c0 = str->Get(0); 825 int c1 = str->length() > 1 ? str->Get(1) : 0; 826 if (c0 == 'U') { 827 if (c1 > 'Z') { 828 return true; // An Umpire, but a UTF8String, a U. 829 } 830 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') { 831 return true; // An Ape, an ABCBook. 832 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) && 833 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' || 834 c0 == 'S' || c0 == 'X')) { 835 return true; // An MP3File, an M. 836 } 837 return false; 838} 839 840 841MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) { 842#ifdef DEBUG 843 // Do not attempt to flatten in debug mode when allocation is not 844 // allowed. This is to avoid an assertion failure when allocating. 845 // Flattening strings is the only case where we always allow 846 // allocation because no GC is performed if the allocation fails. 847 if (!HEAP->IsAllocationAllowed()) return this; 848#endif 849 850 Heap* heap = GetHeap(); 851 switch (StringShape(this).representation_tag()) { 852 case kConsStringTag: { 853 ConsString* cs = ConsString::cast(this); 854 if (cs->second()->length() == 0) { 855 return cs->first(); 856 } 857 // There's little point in putting the flat string in new space if the 858 // cons string is in old space. It can never get GCed until there is 859 // an old space GC. 860 PretenureFlag tenure = heap->InNewSpace(this) ? pretenure : TENURED; 861 int len = length(); 862 Object* object; 863 String* result; 864 if (IsAsciiRepresentation()) { 865 { MaybeObject* maybe_object = heap->AllocateRawAsciiString(len, tenure); 866 if (!maybe_object->ToObject(&object)) return maybe_object; 867 } 868 result = String::cast(object); 869 String* first = cs->first(); 870 int first_length = first->length(); 871 char* dest = SeqAsciiString::cast(result)->GetChars(); 872 WriteToFlat(first, dest, 0, first_length); 873 String* second = cs->second(); 874 WriteToFlat(second, 875 dest + first_length, 876 0, 877 len - first_length); 878 } else { 879 { MaybeObject* maybe_object = 880 heap->AllocateRawTwoByteString(len, tenure); 881 if (!maybe_object->ToObject(&object)) return maybe_object; 882 } 883 result = String::cast(object); 884 uc16* dest = SeqTwoByteString::cast(result)->GetChars(); 885 String* first = cs->first(); 886 int first_length = first->length(); 887 WriteToFlat(first, dest, 0, first_length); 888 String* second = cs->second(); 889 WriteToFlat(second, 890 dest + first_length, 891 0, 892 len - first_length); 893 } 894 cs->set_first(result); 895 cs->set_second(heap->empty_string(), SKIP_WRITE_BARRIER); 896 return result; 897 } 898 default: 899 return this; 900 } 901} 902 903 904bool String::MakeExternal(v8::String::ExternalStringResource* resource) { 905 // Externalizing twice leaks the external resource, so it's 906 // prohibited by the API. 907 ASSERT(!this->IsExternalString()); 908#ifdef DEBUG 909 if (FLAG_enable_slow_asserts) { 910 // Assert that the resource and the string are equivalent. 911 ASSERT(static_cast<size_t>(this->length()) == resource->length()); 912 ScopedVector<uc16> smart_chars(this->length()); 913 String::WriteToFlat(this, smart_chars.start(), 0, this->length()); 914 ASSERT(memcmp(smart_chars.start(), 915 resource->data(), 916 resource->length() * sizeof(smart_chars[0])) == 0); 917 } 918#endif // DEBUG 919 Heap* heap = GetHeap(); 920 int size = this->Size(); // Byte size of the original string. 921 if (size < ExternalString::kShortSize) { 922 return false; 923 } 924 bool is_ascii = this->IsAsciiRepresentation(); 925 bool is_symbol = this->IsSymbol(); 926 927 // Morph the object to an external string by adjusting the map and 928 // reinitializing the fields. 929 if (size >= ExternalString::kSize) { 930 this->set_map_no_write_barrier( 931 is_symbol 932 ? (is_ascii ? heap->external_symbol_with_ascii_data_map() 933 : heap->external_symbol_map()) 934 : (is_ascii ? heap->external_string_with_ascii_data_map() 935 : heap->external_string_map())); 936 } else { 937 this->set_map_no_write_barrier( 938 is_symbol 939 ? (is_ascii ? heap->short_external_symbol_with_ascii_data_map() 940 : heap->short_external_symbol_map()) 941 : (is_ascii ? heap->short_external_string_with_ascii_data_map() 942 : heap->short_external_string_map())); 943 } 944 ExternalTwoByteString* self = ExternalTwoByteString::cast(this); 945 self->set_resource(resource); 946 if (is_symbol) self->Hash(); // Force regeneration of the hash value. 947 948 // Fill the remainder of the string with dead wood. 949 int new_size = this->Size(); // Byte size of the external String object. 950 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); 951 if (Marking::IsBlack(Marking::MarkBitFrom(this))) { 952 MemoryChunk::IncrementLiveBytesFromMutator(this->address(), 953 new_size - size); 954 } 955 return true; 956} 957 958 959bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { 960#ifdef DEBUG 961 if (FLAG_enable_slow_asserts) { 962 // Assert that the resource and the string are equivalent. 963 ASSERT(static_cast<size_t>(this->length()) == resource->length()); 964 ScopedVector<char> smart_chars(this->length()); 965 String::WriteToFlat(this, smart_chars.start(), 0, this->length()); 966 ASSERT(memcmp(smart_chars.start(), 967 resource->data(), 968 resource->length() * sizeof(smart_chars[0])) == 0); 969 } 970#endif // DEBUG 971 Heap* heap = GetHeap(); 972 int size = this->Size(); // Byte size of the original string. 973 if (size < ExternalString::kShortSize) { 974 return false; 975 } 976 bool is_symbol = this->IsSymbol(); 977 978 // Morph the object to an external string by adjusting the map and 979 // reinitializing the fields. Use short version if space is limited. 980 if (size >= ExternalString::kSize) { 981 this->set_map_no_write_barrier( 982 is_symbol ? heap->external_ascii_symbol_map() 983 : heap->external_ascii_string_map()); 984 } else { 985 this->set_map_no_write_barrier( 986 is_symbol ? heap->short_external_ascii_symbol_map() 987 : heap->short_external_ascii_string_map()); 988 } 989 ExternalAsciiString* self = ExternalAsciiString::cast(this); 990 self->set_resource(resource); 991 if (is_symbol) self->Hash(); // Force regeneration of the hash value. 992 993 // Fill the remainder of the string with dead wood. 994 int new_size = this->Size(); // Byte size of the external String object. 995 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); 996 if (Marking::IsBlack(Marking::MarkBitFrom(this))) { 997 MemoryChunk::IncrementLiveBytesFromMutator(this->address(), 998 new_size - size); 999 } 1000 return true; 1001} 1002 1003 1004void String::StringShortPrint(StringStream* accumulator) { 1005 int len = length(); 1006 if (len > kMaxShortPrintLength) { 1007 accumulator->Add("<Very long string[%u]>", len); 1008 return; 1009 } 1010 1011 if (!LooksValid()) { 1012 accumulator->Add("<Invalid String>"); 1013 return; 1014 } 1015 1016 StringInputBuffer buf(this); 1017 1018 bool truncated = false; 1019 if (len > kMaxShortPrintLength) { 1020 len = kMaxShortPrintLength; 1021 truncated = true; 1022 } 1023 bool ascii = true; 1024 for (int i = 0; i < len; i++) { 1025 int c = buf.GetNext(); 1026 1027 if (c < 32 || c >= 127) { 1028 ascii = false; 1029 } 1030 } 1031 buf.Reset(this); 1032 if (ascii) { 1033 accumulator->Add("<String[%u]: ", length()); 1034 for (int i = 0; i < len; i++) { 1035 accumulator->Put(buf.GetNext()); 1036 } 1037 accumulator->Put('>'); 1038 } else { 1039 // Backslash indicates that the string contains control 1040 // characters and that backslashes are therefore escaped. 1041 accumulator->Add("<String[%u]\\: ", length()); 1042 for (int i = 0; i < len; i++) { 1043 int c = buf.GetNext(); 1044 if (c == '\n') { 1045 accumulator->Add("\\n"); 1046 } else if (c == '\r') { 1047 accumulator->Add("\\r"); 1048 } else if (c == '\\') { 1049 accumulator->Add("\\\\"); 1050 } else if (c < 32 || c > 126) { 1051 accumulator->Add("\\x%02x", c); 1052 } else { 1053 accumulator->Put(c); 1054 } 1055 } 1056 if (truncated) { 1057 accumulator->Put('.'); 1058 accumulator->Put('.'); 1059 accumulator->Put('.'); 1060 } 1061 accumulator->Put('>'); 1062 } 1063 return; 1064} 1065 1066 1067void JSObject::JSObjectShortPrint(StringStream* accumulator) { 1068 switch (map()->instance_type()) { 1069 case JS_ARRAY_TYPE: { 1070 double length = JSArray::cast(this)->length()->Number(); 1071 accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length)); 1072 break; 1073 } 1074 case JS_WEAK_MAP_TYPE: { 1075 accumulator->Add("<JS WeakMap>"); 1076 break; 1077 } 1078 case JS_REGEXP_TYPE: { 1079 accumulator->Add("<JS RegExp>"); 1080 break; 1081 } 1082 case JS_FUNCTION_TYPE: { 1083 Object* fun_name = JSFunction::cast(this)->shared()->name(); 1084 bool printed = false; 1085 if (fun_name->IsString()) { 1086 String* str = String::cast(fun_name); 1087 if (str->length() > 0) { 1088 accumulator->Add("<JS Function "); 1089 accumulator->Put(str); 1090 accumulator->Put('>'); 1091 printed = true; 1092 } 1093 } 1094 if (!printed) { 1095 accumulator->Add("<JS Function>"); 1096 } 1097 break; 1098 } 1099 // All other JSObjects are rather similar to each other (JSObject, 1100 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). 1101 default: { 1102 Map* map_of_this = map(); 1103 Heap* heap = GetHeap(); 1104 Object* constructor = map_of_this->constructor(); 1105 bool printed = false; 1106 if (constructor->IsHeapObject() && 1107 !heap->Contains(HeapObject::cast(constructor))) { 1108 accumulator->Add("!!!INVALID CONSTRUCTOR!!!"); 1109 } else { 1110 bool global_object = IsJSGlobalProxy(); 1111 if (constructor->IsJSFunction()) { 1112 if (!heap->Contains(JSFunction::cast(constructor)->shared())) { 1113 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!"); 1114 } else { 1115 Object* constructor_name = 1116 JSFunction::cast(constructor)->shared()->name(); 1117 if (constructor_name->IsString()) { 1118 String* str = String::cast(constructor_name); 1119 if (str->length() > 0) { 1120 bool vowel = AnWord(str); 1121 accumulator->Add("<%sa%s ", 1122 global_object ? "Global Object: " : "", 1123 vowel ? "n" : ""); 1124 accumulator->Put(str); 1125 printed = true; 1126 } 1127 } 1128 } 1129 } 1130 if (!printed) { 1131 accumulator->Add("<JS %sObject", global_object ? "Global " : ""); 1132 } 1133 } 1134 if (IsJSValue()) { 1135 accumulator->Add(" value = "); 1136 JSValue::cast(this)->value()->ShortPrint(accumulator); 1137 } 1138 accumulator->Put('>'); 1139 break; 1140 } 1141 } 1142} 1143 1144 1145void JSObject::PrintElementsTransition( 1146 FILE* file, ElementsKind from_kind, FixedArrayBase* from_elements, 1147 ElementsKind to_kind, FixedArrayBase* to_elements) { 1148 if (from_kind != to_kind) { 1149 PrintF(file, "elements transition ["); 1150 PrintElementsKind(file, from_kind); 1151 PrintF(file, " -> "); 1152 PrintElementsKind(file, to_kind); 1153 PrintF(file, "] in "); 1154 JavaScriptFrame::PrintTop(file, false, true); 1155 PrintF(file, " for "); 1156 ShortPrint(file); 1157 PrintF(file, " from "); 1158 from_elements->ShortPrint(file); 1159 PrintF(file, " to "); 1160 to_elements->ShortPrint(file); 1161 PrintF(file, "\n"); 1162 } 1163} 1164 1165 1166void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { 1167 Heap* heap = GetHeap(); 1168 if (!heap->Contains(this)) { 1169 accumulator->Add("!!!INVALID POINTER!!!"); 1170 return; 1171 } 1172 if (!heap->Contains(map())) { 1173 accumulator->Add("!!!INVALID MAP!!!"); 1174 return; 1175 } 1176 1177 accumulator->Add("%p ", this); 1178 1179 if (IsString()) { 1180 String::cast(this)->StringShortPrint(accumulator); 1181 return; 1182 } 1183 if (IsJSObject()) { 1184 JSObject::cast(this)->JSObjectShortPrint(accumulator); 1185 return; 1186 } 1187 switch (map()->instance_type()) { 1188 case MAP_TYPE: 1189 accumulator->Add("<Map(elements=%u)>", Map::cast(this)->elements_kind()); 1190 break; 1191 case FIXED_ARRAY_TYPE: 1192 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length()); 1193 break; 1194 case FIXED_DOUBLE_ARRAY_TYPE: 1195 accumulator->Add("<FixedDoubleArray[%u]>", 1196 FixedDoubleArray::cast(this)->length()); 1197 break; 1198 case BYTE_ARRAY_TYPE: 1199 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length()); 1200 break; 1201 case FREE_SPACE_TYPE: 1202 accumulator->Add("<FreeSpace[%u]>", FreeSpace::cast(this)->Size()); 1203 break; 1204 case EXTERNAL_PIXEL_ARRAY_TYPE: 1205 accumulator->Add("<ExternalPixelArray[%u]>", 1206 ExternalPixelArray::cast(this)->length()); 1207 break; 1208 case EXTERNAL_BYTE_ARRAY_TYPE: 1209 accumulator->Add("<ExternalByteArray[%u]>", 1210 ExternalByteArray::cast(this)->length()); 1211 break; 1212 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: 1213 accumulator->Add("<ExternalUnsignedByteArray[%u]>", 1214 ExternalUnsignedByteArray::cast(this)->length()); 1215 break; 1216 case EXTERNAL_SHORT_ARRAY_TYPE: 1217 accumulator->Add("<ExternalShortArray[%u]>", 1218 ExternalShortArray::cast(this)->length()); 1219 break; 1220 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: 1221 accumulator->Add("<ExternalUnsignedShortArray[%u]>", 1222 ExternalUnsignedShortArray::cast(this)->length()); 1223 break; 1224 case EXTERNAL_INT_ARRAY_TYPE: 1225 accumulator->Add("<ExternalIntArray[%u]>", 1226 ExternalIntArray::cast(this)->length()); 1227 break; 1228 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: 1229 accumulator->Add("<ExternalUnsignedIntArray[%u]>", 1230 ExternalUnsignedIntArray::cast(this)->length()); 1231 break; 1232 case EXTERNAL_FLOAT_ARRAY_TYPE: 1233 accumulator->Add("<ExternalFloatArray[%u]>", 1234 ExternalFloatArray::cast(this)->length()); 1235 break; 1236 case EXTERNAL_DOUBLE_ARRAY_TYPE: 1237 accumulator->Add("<ExternalDoubleArray[%u]>", 1238 ExternalDoubleArray::cast(this)->length()); 1239 break; 1240 case SHARED_FUNCTION_INFO_TYPE: 1241 accumulator->Add("<SharedFunctionInfo>"); 1242 break; 1243 case JS_MESSAGE_OBJECT_TYPE: 1244 accumulator->Add("<JSMessageObject>"); 1245 break; 1246#define MAKE_STRUCT_CASE(NAME, Name, name) \ 1247 case NAME##_TYPE: \ 1248 accumulator->Put('<'); \ 1249 accumulator->Add(#Name); \ 1250 accumulator->Put('>'); \ 1251 break; 1252 STRUCT_LIST(MAKE_STRUCT_CASE) 1253#undef MAKE_STRUCT_CASE 1254 case CODE_TYPE: 1255 accumulator->Add("<Code>"); 1256 break; 1257 case ODDBALL_TYPE: { 1258 if (IsUndefined()) 1259 accumulator->Add("<undefined>"); 1260 else if (IsTheHole()) 1261 accumulator->Add("<the hole>"); 1262 else if (IsNull()) 1263 accumulator->Add("<null>"); 1264 else if (IsTrue()) 1265 accumulator->Add("<true>"); 1266 else if (IsFalse()) 1267 accumulator->Add("<false>"); 1268 else 1269 accumulator->Add("<Odd Oddball>"); 1270 break; 1271 } 1272 case HEAP_NUMBER_TYPE: 1273 accumulator->Add("<Number: "); 1274 HeapNumber::cast(this)->HeapNumberPrint(accumulator); 1275 accumulator->Put('>'); 1276 break; 1277 case JS_PROXY_TYPE: 1278 accumulator->Add("<JSProxy>"); 1279 break; 1280 case JS_FUNCTION_PROXY_TYPE: 1281 accumulator->Add("<JSFunctionProxy>"); 1282 break; 1283 case FOREIGN_TYPE: 1284 accumulator->Add("<Foreign>"); 1285 break; 1286 case JS_GLOBAL_PROPERTY_CELL_TYPE: 1287 accumulator->Add("Cell for "); 1288 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator); 1289 break; 1290 default: 1291 accumulator->Add("<Other heap object (%d)>", map()->instance_type()); 1292 break; 1293 } 1294} 1295 1296 1297void HeapObject::Iterate(ObjectVisitor* v) { 1298 // Handle header 1299 IteratePointer(v, kMapOffset); 1300 // Handle object body 1301 Map* m = map(); 1302 IterateBody(m->instance_type(), SizeFromMap(m), v); 1303} 1304 1305 1306void HeapObject::IterateBody(InstanceType type, int object_size, 1307 ObjectVisitor* v) { 1308 // Avoiding <Type>::cast(this) because it accesses the map pointer field. 1309 // During GC, the map pointer field is encoded. 1310 if (type < FIRST_NONSTRING_TYPE) { 1311 switch (type & kStringRepresentationMask) { 1312 case kSeqStringTag: 1313 break; 1314 case kConsStringTag: 1315 ConsString::BodyDescriptor::IterateBody(this, v); 1316 break; 1317 case kSlicedStringTag: 1318 SlicedString::BodyDescriptor::IterateBody(this, v); 1319 break; 1320 case kExternalStringTag: 1321 if ((type & kStringEncodingMask) == kAsciiStringTag) { 1322 reinterpret_cast<ExternalAsciiString*>(this)-> 1323 ExternalAsciiStringIterateBody(v); 1324 } else { 1325 reinterpret_cast<ExternalTwoByteString*>(this)-> 1326 ExternalTwoByteStringIterateBody(v); 1327 } 1328 break; 1329 } 1330 return; 1331 } 1332 1333 switch (type) { 1334 case FIXED_ARRAY_TYPE: 1335 FixedArray::BodyDescriptor::IterateBody(this, object_size, v); 1336 break; 1337 case FIXED_DOUBLE_ARRAY_TYPE: 1338 break; 1339 case JS_OBJECT_TYPE: 1340 case JS_CONTEXT_EXTENSION_OBJECT_TYPE: 1341 case JS_VALUE_TYPE: 1342 case JS_DATE_TYPE: 1343 case JS_ARRAY_TYPE: 1344 case JS_SET_TYPE: 1345 case JS_MAP_TYPE: 1346 case JS_WEAK_MAP_TYPE: 1347 case JS_REGEXP_TYPE: 1348 case JS_GLOBAL_PROXY_TYPE: 1349 case JS_GLOBAL_OBJECT_TYPE: 1350 case JS_BUILTINS_OBJECT_TYPE: 1351 case JS_MESSAGE_OBJECT_TYPE: 1352 JSObject::BodyDescriptor::IterateBody(this, object_size, v); 1353 break; 1354 case JS_FUNCTION_TYPE: 1355 reinterpret_cast<JSFunction*>(this) 1356 ->JSFunctionIterateBody(object_size, v); 1357 break; 1358 case ODDBALL_TYPE: 1359 Oddball::BodyDescriptor::IterateBody(this, v); 1360 break; 1361 case JS_PROXY_TYPE: 1362 JSProxy::BodyDescriptor::IterateBody(this, v); 1363 break; 1364 case JS_FUNCTION_PROXY_TYPE: 1365 JSFunctionProxy::BodyDescriptor::IterateBody(this, v); 1366 break; 1367 case FOREIGN_TYPE: 1368 reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v); 1369 break; 1370 case MAP_TYPE: 1371 Map::BodyDescriptor::IterateBody(this, v); 1372 break; 1373 case CODE_TYPE: 1374 reinterpret_cast<Code*>(this)->CodeIterateBody(v); 1375 break; 1376 case JS_GLOBAL_PROPERTY_CELL_TYPE: 1377 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v); 1378 break; 1379 case HEAP_NUMBER_TYPE: 1380 case FILLER_TYPE: 1381 case BYTE_ARRAY_TYPE: 1382 case FREE_SPACE_TYPE: 1383 case EXTERNAL_PIXEL_ARRAY_TYPE: 1384 case EXTERNAL_BYTE_ARRAY_TYPE: 1385 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: 1386 case EXTERNAL_SHORT_ARRAY_TYPE: 1387 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: 1388 case EXTERNAL_INT_ARRAY_TYPE: 1389 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: 1390 case EXTERNAL_FLOAT_ARRAY_TYPE: 1391 case EXTERNAL_DOUBLE_ARRAY_TYPE: 1392 break; 1393 case SHARED_FUNCTION_INFO_TYPE: { 1394 SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this); 1395 shared->SharedFunctionInfoIterateBody(v); 1396 break; 1397 } 1398 1399#define MAKE_STRUCT_CASE(NAME, Name, name) \ 1400 case NAME##_TYPE: 1401 STRUCT_LIST(MAKE_STRUCT_CASE) 1402#undef MAKE_STRUCT_CASE 1403 StructBodyDescriptor::IterateBody(this, object_size, v); 1404 break; 1405 default: 1406 PrintF("Unknown type: %d\n", type); 1407 UNREACHABLE(); 1408 } 1409} 1410 1411 1412Object* HeapNumber::HeapNumberToBoolean() { 1413 // NaN, +0, and -0 should return the false object 1414#if __BYTE_ORDER == __LITTLE_ENDIAN 1415 union IeeeDoubleLittleEndianArchType u; 1416#elif __BYTE_ORDER == __BIG_ENDIAN 1417 union IeeeDoubleBigEndianArchType u; 1418#endif 1419 u.d = value(); 1420 if (u.bits.exp == 2047) { 1421 // Detect NaN for IEEE double precision floating point. 1422 if ((u.bits.man_low | u.bits.man_high) != 0) 1423 return GetHeap()->false_value(); 1424 } 1425 if (u.bits.exp == 0) { 1426 // Detect +0, and -0 for IEEE double precision floating point. 1427 if ((u.bits.man_low | u.bits.man_high) == 0) 1428 return GetHeap()->false_value(); 1429 } 1430 return GetHeap()->true_value(); 1431} 1432 1433 1434void HeapNumber::HeapNumberPrint(FILE* out) { 1435 PrintF(out, "%.16g", Number()); 1436} 1437 1438 1439void HeapNumber::HeapNumberPrint(StringStream* accumulator) { 1440 // The Windows version of vsnprintf can allocate when printing a %g string 1441 // into a buffer that may not be big enough. We don't want random memory 1442 // allocation when producing post-crash stack traces, so we print into a 1443 // buffer that is plenty big enough for any floating point number, then 1444 // print that using vsnprintf (which may truncate but never allocate if 1445 // there is no more space in the buffer). 1446 EmbeddedVector<char, 100> buffer; 1447 OS::SNPrintF(buffer, "%.16g", Number()); 1448 accumulator->Add("%s", buffer.start()); 1449} 1450 1451 1452String* JSReceiver::class_name() { 1453 if (IsJSFunction() && IsJSFunctionProxy()) { 1454 return GetHeap()->function_class_symbol(); 1455 } 1456 if (map()->constructor()->IsJSFunction()) { 1457 JSFunction* constructor = JSFunction::cast(map()->constructor()); 1458 return String::cast(constructor->shared()->instance_class_name()); 1459 } 1460 // If the constructor is not present, return "Object". 1461 return GetHeap()->Object_symbol(); 1462} 1463 1464 1465String* JSReceiver::constructor_name() { 1466 if (map()->constructor()->IsJSFunction()) { 1467 JSFunction* constructor = JSFunction::cast(map()->constructor()); 1468 String* name = String::cast(constructor->shared()->name()); 1469 if (name->length() > 0) return name; 1470 String* inferred_name = constructor->shared()->inferred_name(); 1471 if (inferred_name->length() > 0) return inferred_name; 1472 Object* proto = GetPrototype(); 1473 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name(); 1474 } 1475 // TODO(rossberg): what about proxies? 1476 // If the constructor is not present, return "Object". 1477 return GetHeap()->Object_symbol(); 1478} 1479 1480 1481MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map, 1482 String* name, 1483 Object* value) { 1484 int index = new_map->PropertyIndexFor(name); 1485 if (map()->unused_property_fields() == 0) { 1486 ASSERT(map()->unused_property_fields() == 0); 1487 int new_unused = new_map->unused_property_fields(); 1488 Object* values; 1489 { MaybeObject* maybe_values = 1490 properties()->CopySize(properties()->length() + new_unused + 1); 1491 if (!maybe_values->ToObject(&values)) return maybe_values; 1492 } 1493 set_properties(FixedArray::cast(values)); 1494 } 1495 set_map(new_map); 1496 return FastPropertyAtPut(index, value); 1497} 1498 1499 1500static bool IsIdentifier(UnicodeCache* cache, 1501 unibrow::CharacterStream* buffer) { 1502 // Checks whether the buffer contains an identifier (no escape). 1503 if (!buffer->has_more()) return false; 1504 if (!cache->IsIdentifierStart(buffer->GetNext())) { 1505 return false; 1506 } 1507 while (buffer->has_more()) { 1508 if (!cache->IsIdentifierPart(buffer->GetNext())) { 1509 return false; 1510 } 1511 } 1512 return true; 1513} 1514 1515 1516MaybeObject* JSObject::AddFastProperty(String* name, 1517 Object* value, 1518 PropertyAttributes attributes) { 1519 ASSERT(!IsJSGlobalProxy()); 1520 1521 // Normalize the object if the name is an actual string (not the 1522 // hidden symbols) and is not a real identifier. 1523 Isolate* isolate = GetHeap()->isolate(); 1524 StringInputBuffer buffer(name); 1525 if (!IsIdentifier(isolate->unicode_cache(), &buffer) 1526 && name != isolate->heap()->hidden_symbol()) { 1527 Object* obj; 1528 { MaybeObject* maybe_obj = 1529 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1530 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1531 } 1532 return AddSlowProperty(name, value, attributes); 1533 } 1534 1535 DescriptorArray* old_descriptors = map()->instance_descriptors(); 1536 // Compute the new index for new field. 1537 int index = map()->NextFreePropertyIndex(); 1538 1539 // Allocate new instance descriptors with (name, index) added 1540 FieldDescriptor new_field(name, index, attributes); 1541 Object* new_descriptors; 1542 { MaybeObject* maybe_new_descriptors = 1543 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS); 1544 if (!maybe_new_descriptors->ToObject(&new_descriptors)) { 1545 return maybe_new_descriptors; 1546 } 1547 } 1548 1549 // Only allow map transition if the object isn't the global object and there 1550 // is not a transition for the name, or there's a transition for the name but 1551 // it's unrelated to properties. 1552 int descriptor_index = old_descriptors->Search(name); 1553 1554 // Element transitions are stored in the descriptor for property "", which is 1555 // not a identifier and should have forced a switch to slow properties above. 1556 ASSERT(descriptor_index == DescriptorArray::kNotFound || 1557 old_descriptors->GetType(descriptor_index) != ELEMENTS_TRANSITION); 1558 bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound || 1559 old_descriptors->GetType(descriptor_index) == ELEMENTS_TRANSITION; 1560 bool allow_map_transition = 1561 can_insert_transition && 1562 (isolate->context()->global_context()->object_function()->map() != map()); 1563 1564 ASSERT(index < map()->inobject_properties() || 1565 (index - map()->inobject_properties()) < properties()->length() || 1566 map()->unused_property_fields() == 0); 1567 // Allocate a new map for the object. 1568 Object* r; 1569 { MaybeObject* maybe_r = map()->CopyDropDescriptors(); 1570 if (!maybe_r->ToObject(&r)) return maybe_r; 1571 } 1572 Map* new_map = Map::cast(r); 1573 if (allow_map_transition) { 1574 // Allocate new instance descriptors for the old map with map transition. 1575 MapTransitionDescriptor d(name, Map::cast(new_map), attributes); 1576 Object* r; 1577 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS); 1578 if (!maybe_r->ToObject(&r)) return maybe_r; 1579 } 1580 old_descriptors = DescriptorArray::cast(r); 1581 } 1582 1583 if (map()->unused_property_fields() == 0) { 1584 if (properties()->length() > MaxFastProperties()) { 1585 Object* obj; 1586 { MaybeObject* maybe_obj = 1587 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1588 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1589 } 1590 return AddSlowProperty(name, value, attributes); 1591 } 1592 // Make room for the new value 1593 Object* values; 1594 { MaybeObject* maybe_values = 1595 properties()->CopySize(properties()->length() + kFieldsAdded); 1596 if (!maybe_values->ToObject(&values)) return maybe_values; 1597 } 1598 set_properties(FixedArray::cast(values)); 1599 new_map->set_unused_property_fields(kFieldsAdded - 1); 1600 } else { 1601 new_map->set_unused_property_fields(map()->unused_property_fields() - 1); 1602 } 1603 // We have now allocated all the necessary objects. 1604 // All the changes can be applied at once, so they are atomic. 1605 map()->set_instance_descriptors(old_descriptors); 1606 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 1607 set_map(new_map); 1608 return FastPropertyAtPut(index, value); 1609} 1610 1611 1612MaybeObject* JSObject::AddConstantFunctionProperty( 1613 String* name, 1614 JSFunction* function, 1615 PropertyAttributes attributes) { 1616 // Allocate new instance descriptors with (name, function) added 1617 ConstantFunctionDescriptor d(name, function, attributes); 1618 Object* new_descriptors; 1619 { MaybeObject* maybe_new_descriptors = 1620 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS); 1621 if (!maybe_new_descriptors->ToObject(&new_descriptors)) { 1622 return maybe_new_descriptors; 1623 } 1624 } 1625 1626 // Allocate a new map for the object. 1627 Object* new_map; 1628 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors(); 1629 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 1630 } 1631 1632 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors); 1633 Map::cast(new_map)->set_instance_descriptors(descriptors); 1634 Map* old_map = map(); 1635 set_map(Map::cast(new_map)); 1636 1637 // If the old map is the global object map (from new Object()), 1638 // then transitions are not added to it, so we are done. 1639 Heap* heap = GetHeap(); 1640 if (old_map == heap->isolate()->context()->global_context()-> 1641 object_function()->map()) { 1642 return function; 1643 } 1644 1645 // Do not add CONSTANT_TRANSITIONS to global objects 1646 if (IsGlobalObject()) { 1647 return function; 1648 } 1649 1650 // Add a CONSTANT_TRANSITION descriptor to the old map, 1651 // so future assignments to this property on other objects 1652 // of the same type will create a normal field, not a constant function. 1653 // Don't do this for special properties, with non-trival attributes. 1654 if (attributes != NONE) { 1655 return function; 1656 } 1657 ConstTransitionDescriptor mark(name, Map::cast(new_map)); 1658 { MaybeObject* maybe_new_descriptors = 1659 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS); 1660 if (!maybe_new_descriptors->ToObject(&new_descriptors)) { 1661 // We have accomplished the main goal, so return success. 1662 return function; 1663 } 1664 } 1665 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 1666 1667 return function; 1668} 1669 1670 1671// Add property in slow mode 1672MaybeObject* JSObject::AddSlowProperty(String* name, 1673 Object* value, 1674 PropertyAttributes attributes) { 1675 ASSERT(!HasFastProperties()); 1676 StringDictionary* dict = property_dictionary(); 1677 Object* store_value = value; 1678 if (IsGlobalObject()) { 1679 // In case name is an orphaned property reuse the cell. 1680 int entry = dict->FindEntry(name); 1681 if (entry != StringDictionary::kNotFound) { 1682 store_value = dict->ValueAt(entry); 1683 JSGlobalPropertyCell::cast(store_value)->set_value(value); 1684 // Assign an enumeration index to the property and update 1685 // SetNextEnumerationIndex. 1686 int index = dict->NextEnumerationIndex(); 1687 PropertyDetails details = PropertyDetails(attributes, NORMAL, index); 1688 dict->SetNextEnumerationIndex(index + 1); 1689 dict->SetEntry(entry, name, store_value, details); 1690 return value; 1691 } 1692 Heap* heap = GetHeap(); 1693 { MaybeObject* maybe_store_value = 1694 heap->AllocateJSGlobalPropertyCell(value); 1695 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value; 1696 } 1697 JSGlobalPropertyCell::cast(store_value)->set_value(value); 1698 } 1699 PropertyDetails details = PropertyDetails(attributes, NORMAL); 1700 Object* result; 1701 { MaybeObject* maybe_result = dict->Add(name, store_value, details); 1702 if (!maybe_result->ToObject(&result)) return maybe_result; 1703 } 1704 if (dict != result) set_properties(StringDictionary::cast(result)); 1705 return value; 1706} 1707 1708 1709MaybeObject* JSObject::AddProperty(String* name, 1710 Object* value, 1711 PropertyAttributes attributes, 1712 StrictModeFlag strict_mode) { 1713 ASSERT(!IsJSGlobalProxy()); 1714 Map* map_of_this = map(); 1715 Heap* heap = GetHeap(); 1716 if (!map_of_this->is_extensible()) { 1717 if (strict_mode == kNonStrictMode) { 1718 return value; 1719 } else { 1720 Handle<Object> args[1] = {Handle<String>(name)}; 1721 return heap->isolate()->Throw( 1722 *FACTORY->NewTypeError("object_not_extensible", 1723 HandleVector(args, 1))); 1724 } 1725 } 1726 if (HasFastProperties()) { 1727 // Ensure the descriptor array does not get too big. 1728 if (map_of_this->instance_descriptors()->number_of_descriptors() < 1729 DescriptorArray::kMaxNumberOfDescriptors) { 1730 if (value->IsJSFunction()) { 1731 return AddConstantFunctionProperty(name, 1732 JSFunction::cast(value), 1733 attributes); 1734 } else { 1735 return AddFastProperty(name, value, attributes); 1736 } 1737 } else { 1738 // Normalize the object to prevent very large instance descriptors. 1739 // This eliminates unwanted N^2 allocation and lookup behavior. 1740 Object* obj; 1741 { MaybeObject* maybe_obj = 1742 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1743 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1744 } 1745 } 1746 } 1747 return AddSlowProperty(name, value, attributes); 1748} 1749 1750 1751MaybeObject* JSObject::SetPropertyPostInterceptor( 1752 String* name, 1753 Object* value, 1754 PropertyAttributes attributes, 1755 StrictModeFlag strict_mode) { 1756 // Check local property, ignore interceptor. 1757 LookupResult result(GetIsolate()); 1758 LocalLookupRealNamedProperty(name, &result); 1759 if (result.IsFound()) { 1760 // An existing property, a map transition or a null descriptor was 1761 // found. Use set property to handle all these cases. 1762 return SetProperty(&result, name, value, attributes, strict_mode); 1763 } 1764 bool found = false; 1765 MaybeObject* result_object; 1766 result_object = SetPropertyWithCallbackSetterInPrototypes(name, 1767 value, 1768 attributes, 1769 &found, 1770 strict_mode); 1771 if (found) return result_object; 1772 // Add a new real property. 1773 return AddProperty(name, value, attributes, strict_mode); 1774} 1775 1776 1777MaybeObject* JSObject::ReplaceSlowProperty(String* name, 1778 Object* value, 1779 PropertyAttributes attributes) { 1780 StringDictionary* dictionary = property_dictionary(); 1781 int old_index = dictionary->FindEntry(name); 1782 int new_enumeration_index = 0; // 0 means "Use the next available index." 1783 if (old_index != -1) { 1784 // All calls to ReplaceSlowProperty have had all transitions removed. 1785 ASSERT(!dictionary->ContainsTransition(old_index)); 1786 new_enumeration_index = dictionary->DetailsAt(old_index).index(); 1787 } 1788 1789 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index); 1790 return SetNormalizedProperty(name, value, new_details); 1791} 1792 1793 1794MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition( 1795 String* name, 1796 Object* new_value, 1797 PropertyAttributes attributes) { 1798 Map* old_map = map(); 1799 Object* result; 1800 { MaybeObject* maybe_result = 1801 ConvertDescriptorToField(name, new_value, attributes); 1802 if (!maybe_result->ToObject(&result)) return maybe_result; 1803 } 1804 // If we get to this point we have succeeded - do not return failure 1805 // after this point. Later stuff is optional. 1806 if (!HasFastProperties()) { 1807 return result; 1808 } 1809 // Do not add transitions to the map of "new Object()". 1810 if (map() == GetIsolate()->context()->global_context()-> 1811 object_function()->map()) { 1812 return result; 1813 } 1814 1815 MapTransitionDescriptor transition(name, 1816 map(), 1817 attributes); 1818 Object* new_descriptors; 1819 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()-> 1820 CopyInsert(&transition, KEEP_TRANSITIONS); 1821 if (!maybe_new_descriptors->ToObject(&new_descriptors)) { 1822 return result; // Yes, return _result_. 1823 } 1824 } 1825 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 1826 return result; 1827} 1828 1829 1830MaybeObject* JSObject::ConvertDescriptorToField(String* name, 1831 Object* new_value, 1832 PropertyAttributes attributes) { 1833 if (map()->unused_property_fields() == 0 && 1834 properties()->length() > MaxFastProperties()) { 1835 Object* obj; 1836 { MaybeObject* maybe_obj = 1837 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 1838 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1839 } 1840 return ReplaceSlowProperty(name, new_value, attributes); 1841 } 1842 1843 int index = map()->NextFreePropertyIndex(); 1844 FieldDescriptor new_field(name, index, attributes); 1845 // Make a new DescriptorArray replacing an entry with FieldDescriptor. 1846 Object* descriptors_unchecked; 1847 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()-> 1848 CopyInsert(&new_field, REMOVE_TRANSITIONS); 1849 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) { 1850 return maybe_descriptors_unchecked; 1851 } 1852 } 1853 DescriptorArray* new_descriptors = 1854 DescriptorArray::cast(descriptors_unchecked); 1855 1856 // Make a new map for the object. 1857 Object* new_map_unchecked; 1858 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors(); 1859 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) { 1860 return maybe_new_map_unchecked; 1861 } 1862 } 1863 Map* new_map = Map::cast(new_map_unchecked); 1864 new_map->set_instance_descriptors(new_descriptors); 1865 1866 // Make new properties array if necessary. 1867 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer. 1868 int new_unused_property_fields = map()->unused_property_fields() - 1; 1869 if (map()->unused_property_fields() == 0) { 1870 new_unused_property_fields = kFieldsAdded - 1; 1871 Object* new_properties_object; 1872 { MaybeObject* maybe_new_properties_object = 1873 properties()->CopySize(properties()->length() + kFieldsAdded); 1874 if (!maybe_new_properties_object->ToObject(&new_properties_object)) { 1875 return maybe_new_properties_object; 1876 } 1877 } 1878 new_properties = FixedArray::cast(new_properties_object); 1879 } 1880 1881 // Update pointers to commit changes. 1882 // Object points to the new map. 1883 new_map->set_unused_property_fields(new_unused_property_fields); 1884 set_map(new_map); 1885 if (new_properties) { 1886 set_properties(FixedArray::cast(new_properties)); 1887 } 1888 return FastPropertyAtPut(index, new_value); 1889} 1890 1891 1892 1893MaybeObject* JSObject::SetPropertyWithInterceptor( 1894 String* name, 1895 Object* value, 1896 PropertyAttributes attributes, 1897 StrictModeFlag strict_mode) { 1898 Isolate* isolate = GetIsolate(); 1899 HandleScope scope(isolate); 1900 Handle<JSObject> this_handle(this); 1901 Handle<String> name_handle(name); 1902 Handle<Object> value_handle(value, isolate); 1903 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 1904 if (!interceptor->setter()->IsUndefined()) { 1905 LOG(isolate, ApiNamedPropertyAccess("interceptor-named-set", this, name)); 1906 CustomArguments args(isolate, interceptor->data(), this, this); 1907 v8::AccessorInfo info(args.end()); 1908 v8::NamedPropertySetter setter = 1909 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter()); 1910 v8::Handle<v8::Value> result; 1911 { 1912 // Leaving JavaScript. 1913 VMState state(isolate, EXTERNAL); 1914 Handle<Object> value_unhole(value->IsTheHole() ? 1915 isolate->heap()->undefined_value() : 1916 value, 1917 isolate); 1918 result = setter(v8::Utils::ToLocal(name_handle), 1919 v8::Utils::ToLocal(value_unhole), 1920 info); 1921 } 1922 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 1923 if (!result.IsEmpty()) return *value_handle; 1924 } 1925 MaybeObject* raw_result = 1926 this_handle->SetPropertyPostInterceptor(*name_handle, 1927 *value_handle, 1928 attributes, 1929 strict_mode); 1930 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 1931 return raw_result; 1932} 1933 1934 1935Handle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object, 1936 Handle<String> key, 1937 Handle<Object> value, 1938 PropertyAttributes attributes, 1939 StrictModeFlag strict_mode) { 1940 CALL_HEAP_FUNCTION(object->GetIsolate(), 1941 object->SetProperty(*key, *value, attributes, strict_mode), 1942 Object); 1943} 1944 1945 1946MaybeObject* JSReceiver::SetProperty(String* name, 1947 Object* value, 1948 PropertyAttributes attributes, 1949 StrictModeFlag strict_mode) { 1950 LookupResult result(GetIsolate()); 1951 LocalLookup(name, &result); 1952 return SetProperty(&result, name, value, attributes, strict_mode); 1953} 1954 1955 1956MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, 1957 String* name, 1958 Object* value, 1959 JSObject* holder, 1960 StrictModeFlag strict_mode) { 1961 Isolate* isolate = GetIsolate(); 1962 HandleScope scope(isolate); 1963 1964 // We should never get here to initialize a const with the hole 1965 // value since a const declaration would conflict with the setter. 1966 ASSERT(!value->IsTheHole()); 1967 Handle<Object> value_handle(value, isolate); 1968 1969 // To accommodate both the old and the new api we switch on the 1970 // data structure used to store the callbacks. Eventually foreign 1971 // callbacks should be phased out. 1972 if (structure->IsForeign()) { 1973 AccessorDescriptor* callback = 1974 reinterpret_cast<AccessorDescriptor*>( 1975 Foreign::cast(structure)->foreign_address()); 1976 MaybeObject* obj = (callback->setter)(this, value, callback->data); 1977 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 1978 if (obj->IsFailure()) return obj; 1979 return *value_handle; 1980 } 1981 1982 if (structure->IsAccessorInfo()) { 1983 // api style callbacks 1984 AccessorInfo* data = AccessorInfo::cast(structure); 1985 Object* call_obj = data->setter(); 1986 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); 1987 if (call_fun == NULL) return value; 1988 Handle<String> key(name); 1989 LOG(isolate, ApiNamedPropertyAccess("store", this, name)); 1990 CustomArguments args(isolate, data->data(), this, JSObject::cast(holder)); 1991 v8::AccessorInfo info(args.end()); 1992 { 1993 // Leaving JavaScript. 1994 VMState state(isolate, EXTERNAL); 1995 call_fun(v8::Utils::ToLocal(key), 1996 v8::Utils::ToLocal(value_handle), 1997 info); 1998 } 1999 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 2000 return *value_handle; 2001 } 2002 2003 if (structure->IsAccessorPair()) { 2004 Object* setter = AccessorPair::cast(structure)->setter(); 2005 if (setter->IsSpecFunction()) { 2006 // TODO(rossberg): nicer would be to cast to some JSCallable here... 2007 return SetPropertyWithDefinedSetter(JSReceiver::cast(setter), value); 2008 } else { 2009 if (strict_mode == kNonStrictMode) { 2010 return value; 2011 } 2012 Handle<String> key(name); 2013 Handle<Object> holder_handle(holder, isolate); 2014 Handle<Object> args[2] = { key, holder_handle }; 2015 return isolate->Throw( 2016 *isolate->factory()->NewTypeError("no_setter_in_callback", 2017 HandleVector(args, 2))); 2018 } 2019 } 2020 2021 UNREACHABLE(); 2022 return NULL; 2023} 2024 2025 2026MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSReceiver* setter, 2027 Object* value) { 2028 Isolate* isolate = GetIsolate(); 2029 Handle<Object> value_handle(value, isolate); 2030 Handle<JSReceiver> fun(setter, isolate); 2031 Handle<JSReceiver> self(this, isolate); 2032#ifdef ENABLE_DEBUGGER_SUPPORT 2033 Debug* debug = isolate->debug(); 2034 // Handle stepping into a setter if step into is active. 2035 // TODO(rossberg): should this apply to getters that are function proxies? 2036 if (debug->StepInActive() && fun->IsJSFunction()) { 2037 debug->HandleStepIn( 2038 Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); 2039 } 2040#endif 2041 bool has_pending_exception; 2042 Handle<Object> argv[] = { value_handle }; 2043 Execution::Call(fun, self, ARRAY_SIZE(argv), argv, &has_pending_exception); 2044 // Check for pending exception and return the result. 2045 if (has_pending_exception) return Failure::Exception(); 2046 return *value_handle; 2047} 2048 2049 2050void JSObject::LookupCallbackSetterInPrototypes(String* name, 2051 LookupResult* result) { 2052 Heap* heap = GetHeap(); 2053 for (Object* pt = GetPrototype(); 2054 pt != heap->null_value(); 2055 pt = pt->GetPrototype()) { 2056 if (pt->IsJSProxy()) { 2057 return result->HandlerResult(JSProxy::cast(pt)); 2058 } 2059 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); 2060 if (result->IsProperty()) { 2061 if (result->type() == CALLBACKS && !result->IsReadOnly()) return; 2062 // Found non-callback or read-only callback, stop looking. 2063 break; 2064 } 2065 } 2066 result->NotFound(); 2067} 2068 2069 2070MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( 2071 uint32_t index, 2072 Object* value, 2073 bool* found, 2074 StrictModeFlag strict_mode) { 2075 Heap* heap = GetHeap(); 2076 for (Object* pt = GetPrototype(); 2077 pt != heap->null_value(); 2078 pt = pt->GetPrototype()) { 2079 if (pt->IsJSProxy()) { 2080 String* name; 2081 MaybeObject* maybe = GetHeap()->Uint32ToString(index); 2082 if (!maybe->To<String>(&name)) { 2083 *found = true; // Force abort 2084 return maybe; 2085 } 2086 return JSProxy::cast(pt)->SetPropertyWithHandlerIfDefiningSetter( 2087 name, value, NONE, strict_mode, found); 2088 } 2089 if (!JSObject::cast(pt)->HasDictionaryElements()) { 2090 continue; 2091 } 2092 SeededNumberDictionary* dictionary = 2093 JSObject::cast(pt)->element_dictionary(); 2094 int entry = dictionary->FindEntry(index); 2095 if (entry != SeededNumberDictionary::kNotFound) { 2096 PropertyDetails details = dictionary->DetailsAt(entry); 2097 if (details.type() == CALLBACKS) { 2098 *found = true; 2099 return SetElementWithCallback(dictionary->ValueAt(entry), 2100 index, 2101 value, 2102 JSObject::cast(pt), 2103 strict_mode); 2104 } 2105 } 2106 } 2107 *found = false; 2108 return heap->the_hole_value(); 2109} 2110 2111MaybeObject* JSObject::SetPropertyWithCallbackSetterInPrototypes( 2112 String* name, 2113 Object* value, 2114 PropertyAttributes attributes, 2115 bool* found, 2116 StrictModeFlag strict_mode) { 2117 Heap* heap = GetHeap(); 2118 // We could not find a local property so let's check whether there is an 2119 // accessor that wants to handle the property. 2120 LookupResult accessor_result(heap->isolate()); 2121 LookupCallbackSetterInPrototypes(name, &accessor_result); 2122 if (accessor_result.IsFound()) { 2123 *found = true; 2124 if (accessor_result.type() == CALLBACKS) { 2125 return SetPropertyWithCallback(accessor_result.GetCallbackObject(), 2126 name, 2127 value, 2128 accessor_result.holder(), 2129 strict_mode); 2130 } else if (accessor_result.type() == HANDLER) { 2131 // There is a proxy in the prototype chain. Invoke its 2132 // getPropertyDescriptor trap. 2133 bool found = false; 2134 // SetPropertyWithHandlerIfDefiningSetter can cause GC, 2135 // make sure to use the handlified references after calling 2136 // the function. 2137 Handle<JSObject> self(this); 2138 Handle<String> hname(name); 2139 Handle<Object> hvalue(value); 2140 MaybeObject* result = 2141 accessor_result.proxy()->SetPropertyWithHandlerIfDefiningSetter( 2142 name, value, attributes, strict_mode, &found); 2143 if (found) return result; 2144 // The proxy does not define the property as an accessor. 2145 // Consequently, it has no effect on setting the receiver. 2146 return self->AddProperty(*hname, *hvalue, attributes, strict_mode); 2147 } 2148 } 2149 *found = false; 2150 return heap->the_hole_value(); 2151} 2152 2153 2154void JSObject::LookupInDescriptor(String* name, LookupResult* result) { 2155 DescriptorArray* descriptors = map()->instance_descriptors(); 2156 int number = descriptors->SearchWithCache(name); 2157 if (number != DescriptorArray::kNotFound) { 2158 result->DescriptorResult(this, descriptors->GetDetails(number), number); 2159 } else { 2160 result->NotFound(); 2161 } 2162} 2163 2164 2165void Map::LookupInDescriptors(JSObject* holder, 2166 String* name, 2167 LookupResult* result) { 2168 DescriptorArray* descriptors = instance_descriptors(); 2169 DescriptorLookupCache* cache = 2170 GetHeap()->isolate()->descriptor_lookup_cache(); 2171 int number = cache->Lookup(descriptors, name); 2172 if (number == DescriptorLookupCache::kAbsent) { 2173 number = descriptors->Search(name); 2174 cache->Update(descriptors, name, number); 2175 } 2176 if (number != DescriptorArray::kNotFound) { 2177 result->DescriptorResult(holder, descriptors->GetDetails(number), number); 2178 } else { 2179 result->NotFound(); 2180 } 2181} 2182 2183 2184static bool ContainsMap(MapHandleList* maps, Handle<Map> map) { 2185 ASSERT(!map.is_null()); 2186 for (int i = 0; i < maps->length(); ++i) { 2187 if (!maps->at(i).is_null() && maps->at(i).is_identical_to(map)) return true; 2188 } 2189 return false; 2190} 2191 2192 2193template <class T> 2194static Handle<T> MaybeNull(T* p) { 2195 if (p == NULL) return Handle<T>::null(); 2196 return Handle<T>(p); 2197} 2198 2199 2200Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) { 2201 ElementsKind elms_kind = elements_kind(); 2202 if (elms_kind == FAST_DOUBLE_ELEMENTS) { 2203 bool dummy = true; 2204 Handle<Map> fast_map = 2205 MaybeNull(LookupElementsTransitionMap(FAST_ELEMENTS, &dummy)); 2206 if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { 2207 return fast_map; 2208 } 2209 return Handle<Map>::null(); 2210 } 2211 if (elms_kind == FAST_SMI_ONLY_ELEMENTS) { 2212 bool dummy = true; 2213 Handle<Map> double_map = 2214 MaybeNull(LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, &dummy)); 2215 // In the current implementation, if the DOUBLE map doesn't exist, the 2216 // FAST map can't exist either. 2217 if (double_map.is_null()) return Handle<Map>::null(); 2218 Handle<Map> fast_map = 2219 MaybeNull(double_map->LookupElementsTransitionMap(FAST_ELEMENTS, 2220 &dummy)); 2221 if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { 2222 return fast_map; 2223 } 2224 if (ContainsMap(candidates, double_map)) return double_map; 2225 } 2226 return Handle<Map>::null(); 2227} 2228 2229static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents, 2230 ElementsKind elements_kind) { 2231 if (descriptor_contents->IsMap()) { 2232 Map* map = Map::cast(descriptor_contents); 2233 if (map->elements_kind() == elements_kind) { 2234 return map; 2235 } 2236 return NULL; 2237 } 2238 2239 FixedArray* map_array = FixedArray::cast(descriptor_contents); 2240 for (int i = 0; i < map_array->length(); ++i) { 2241 Object* current = map_array->get(i); 2242 // Skip undefined slots, they are sentinels for reclaimed maps. 2243 if (!current->IsUndefined()) { 2244 Map* current_map = Map::cast(map_array->get(i)); 2245 if (current_map->elements_kind() == elements_kind) { 2246 return current_map; 2247 } 2248 } 2249 } 2250 2251 return NULL; 2252} 2253 2254 2255static MaybeObject* AddElementsTransitionMapToDescriptor( 2256 Object* descriptor_contents, 2257 Map* new_map) { 2258 // Nothing was in the descriptor for an ELEMENTS_TRANSITION, 2259 // simply add the map. 2260 if (descriptor_contents == NULL) { 2261 return new_map; 2262 } 2263 2264 // There was already a map in the descriptor, create a 2-element FixedArray 2265 // to contain the existing map plus the new one. 2266 FixedArray* new_array; 2267 Heap* heap = new_map->GetHeap(); 2268 if (descriptor_contents->IsMap()) { 2269 // Must tenure, DescriptorArray expects no new-space objects. 2270 MaybeObject* maybe_new_array = heap->AllocateFixedArray(2, TENURED); 2271 if (!maybe_new_array->To<FixedArray>(&new_array)) { 2272 return maybe_new_array; 2273 } 2274 new_array->set(0, descriptor_contents); 2275 new_array->set(1, new_map); 2276 return new_array; 2277 } 2278 2279 // The descriptor already contained a list of maps for different ElementKinds 2280 // of ELEMENTS_TRANSITION, first check the existing array for an undefined 2281 // slot, and if that's not available, create a FixedArray to hold the existing 2282 // maps plus the new one and fill it in. 2283 FixedArray* array = FixedArray::cast(descriptor_contents); 2284 for (int i = 0; i < array->length(); ++i) { 2285 if (array->get(i)->IsUndefined()) { 2286 array->set(i, new_map); 2287 return array; 2288 } 2289 } 2290 2291 // Must tenure, DescriptorArray expects no new-space objects. 2292 MaybeObject* maybe_new_array = 2293 heap->AllocateFixedArray(array->length() + 1, TENURED); 2294 if (!maybe_new_array->To<FixedArray>(&new_array)) { 2295 return maybe_new_array; 2296 } 2297 int i = 0; 2298 while (i < array->length()) { 2299 new_array->set(i, array->get(i)); 2300 ++i; 2301 } 2302 new_array->set(i, new_map); 2303 return new_array; 2304} 2305 2306 2307String* Map::elements_transition_sentinel_name() { 2308 return GetHeap()->empty_symbol(); 2309} 2310 2311 2312Object* Map::GetDescriptorContents(String* sentinel_name, 2313 bool* safe_to_add_transition) { 2314 // Get the cached index for the descriptors lookup, or find and cache it. 2315 DescriptorArray* descriptors = instance_descriptors(); 2316 DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache(); 2317 int index = cache->Lookup(descriptors, sentinel_name); 2318 if (index == DescriptorLookupCache::kAbsent) { 2319 index = descriptors->Search(sentinel_name); 2320 cache->Update(descriptors, sentinel_name, index); 2321 } 2322 // If the transition already exists, return its descriptor. 2323 if (index != DescriptorArray::kNotFound) { 2324 PropertyDetails details(descriptors->GetDetails(index)); 2325 if (details.type() == ELEMENTS_TRANSITION) { 2326 return descriptors->GetValue(index); 2327 } else { 2328 if (safe_to_add_transition != NULL) { 2329 *safe_to_add_transition = false; 2330 } 2331 } 2332 } 2333 return NULL; 2334} 2335 2336 2337Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind, 2338 bool* safe_to_add_transition) { 2339 // Special case: indirect SMI->FAST transition (cf. comment in 2340 // AddElementsTransition()). 2341 if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && 2342 elements_kind == FAST_ELEMENTS) { 2343 Map* double_map = this->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, 2344 safe_to_add_transition); 2345 if (double_map == NULL) return double_map; 2346 return double_map->LookupElementsTransitionMap(FAST_ELEMENTS, 2347 safe_to_add_transition); 2348 } 2349 Object* descriptor_contents = GetDescriptorContents( 2350 elements_transition_sentinel_name(), safe_to_add_transition); 2351 if (descriptor_contents != NULL) { 2352 Map* maybe_transition_map = 2353 GetElementsTransitionMapFromDescriptor(descriptor_contents, 2354 elements_kind); 2355 ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap()); 2356 return maybe_transition_map; 2357 } 2358 return NULL; 2359} 2360 2361 2362MaybeObject* Map::AddElementsTransition(ElementsKind elements_kind, 2363 Map* transitioned_map) { 2364 // The map transition graph should be a tree, therefore the transition 2365 // from SMI to FAST elements is not done directly, but by going through 2366 // DOUBLE elements first. 2367 if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && 2368 elements_kind == FAST_ELEMENTS) { 2369 bool safe_to_add = true; 2370 Map* double_map = this->LookupElementsTransitionMap( 2371 FAST_DOUBLE_ELEMENTS, &safe_to_add); 2372 // This method is only called when safe_to_add_transition has been found 2373 // to be true earlier. 2374 ASSERT(safe_to_add); 2375 2376 if (double_map == NULL) { 2377 MaybeObject* maybe_map = this->CopyDropTransitions(); 2378 if (!maybe_map->To(&double_map)) return maybe_map; 2379 double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); 2380 MaybeObject* maybe_double_transition = this->AddElementsTransition( 2381 FAST_DOUBLE_ELEMENTS, double_map); 2382 if (maybe_double_transition->IsFailure()) return maybe_double_transition; 2383 } 2384 return double_map->AddElementsTransition(FAST_ELEMENTS, transitioned_map); 2385 } 2386 2387 bool safe_to_add_transition = true; 2388 Object* descriptor_contents = GetDescriptorContents( 2389 elements_transition_sentinel_name(), &safe_to_add_transition); 2390 // This method is only called when safe_to_add_transition has been found 2391 // to be true earlier. 2392 ASSERT(safe_to_add_transition); 2393 MaybeObject* maybe_new_contents = 2394 AddElementsTransitionMapToDescriptor(descriptor_contents, 2395 transitioned_map); 2396 Object* new_contents; 2397 if (!maybe_new_contents->ToObject(&new_contents)) { 2398 return maybe_new_contents; 2399 } 2400 2401 ElementsTransitionDescriptor desc(elements_transition_sentinel_name(), 2402 new_contents); 2403 Object* new_descriptors; 2404 MaybeObject* maybe_new_descriptors = 2405 instance_descriptors()->CopyInsert(&desc, KEEP_TRANSITIONS); 2406 if (!maybe_new_descriptors->ToObject(&new_descriptors)) { 2407 return maybe_new_descriptors; 2408 } 2409 set_instance_descriptors(DescriptorArray::cast(new_descriptors)); 2410 return this; 2411} 2412 2413 2414Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object, 2415 ElementsKind to_kind) { 2416 Isolate* isolate = object->GetIsolate(); 2417 CALL_HEAP_FUNCTION(isolate, 2418 object->GetElementsTransitionMap(isolate, to_kind), 2419 Map); 2420} 2421 2422 2423MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { 2424 Map* current_map = map(); 2425 ElementsKind from_kind = current_map->elements_kind(); 2426 2427 if (from_kind == to_kind) return current_map; 2428 2429 // Only objects with FastProperties can have DescriptorArrays and can track 2430 // element-related maps. Also don't add descriptors to maps that are shared. 2431 bool safe_to_add_transition = HasFastProperties() && 2432 !current_map->IsUndefined() && 2433 !current_map->is_shared(); 2434 2435 // Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps caused by objects 2436 // with elements that switch back and forth between dictionary and fast 2437 // element mode. 2438 if (from_kind == DICTIONARY_ELEMENTS && to_kind == FAST_ELEMENTS) { 2439 safe_to_add_transition = false; 2440 } 2441 2442 if (safe_to_add_transition) { 2443 // It's only safe to manipulate the descriptor array if it would be 2444 // safe to add a transition. 2445 Map* maybe_transition_map = current_map->LookupElementsTransitionMap( 2446 to_kind, &safe_to_add_transition); 2447 if (maybe_transition_map != NULL) { 2448 return maybe_transition_map; 2449 } 2450 } 2451 2452 Map* new_map = NULL; 2453 2454 // No transition to an existing map for the given ElementsKind. Make a new 2455 // one. 2456 { MaybeObject* maybe_map = current_map->CopyDropTransitions(); 2457 if (!maybe_map->To(&new_map)) return maybe_map; 2458 } 2459 2460 new_map->set_elements_kind(to_kind); 2461 2462 // Only remember the map transition if the object's map is NOT equal to the 2463 // global object_function's map and there is not an already existing 2464 // non-matching element transition. 2465 Context* global_context = GetIsolate()->context()->global_context(); 2466 bool allow_map_transition = safe_to_add_transition && 2467 (global_context->object_function()->map() != map()); 2468 if (allow_map_transition) { 2469 MaybeObject* maybe_transition = 2470 current_map->AddElementsTransition(to_kind, new_map); 2471 if (maybe_transition->IsFailure()) return maybe_transition; 2472 } 2473 return new_map; 2474} 2475 2476 2477void JSObject::LocalLookupRealNamedProperty(String* name, 2478 LookupResult* result) { 2479 if (IsJSGlobalProxy()) { 2480 Object* proto = GetPrototype(); 2481 if (proto->IsNull()) return result->NotFound(); 2482 ASSERT(proto->IsJSGlobalObject()); 2483 // A GlobalProxy's prototype should always be a proper JSObject. 2484 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result); 2485 } 2486 2487 if (HasFastProperties()) { 2488 LookupInDescriptor(name, result); 2489 if (result->IsFound()) { 2490 // A property, a map transition or a null descriptor was found. 2491 // We return all of these result types because 2492 // LocalLookupRealNamedProperty is used when setting properties 2493 // where map transitions and null descriptors are handled. 2494 ASSERT(result->holder() == this && result->type() != NORMAL); 2495 // Disallow caching for uninitialized constants. These can only 2496 // occur as fields. 2497 if (result->IsReadOnly() && result->type() == FIELD && 2498 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) { 2499 result->DisallowCaching(); 2500 } 2501 return; 2502 } 2503 } else { 2504 int entry = property_dictionary()->FindEntry(name); 2505 if (entry != StringDictionary::kNotFound) { 2506 Object* value = property_dictionary()->ValueAt(entry); 2507 if (IsGlobalObject()) { 2508 PropertyDetails d = property_dictionary()->DetailsAt(entry); 2509 if (d.IsDeleted()) { 2510 result->NotFound(); 2511 return; 2512 } 2513 value = JSGlobalPropertyCell::cast(value)->value(); 2514 } 2515 // Make sure to disallow caching for uninitialized constants 2516 // found in the dictionary-mode objects. 2517 if (value->IsTheHole()) result->DisallowCaching(); 2518 result->DictionaryResult(this, entry); 2519 return; 2520 } 2521 } 2522 result->NotFound(); 2523} 2524 2525 2526void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) { 2527 LocalLookupRealNamedProperty(name, result); 2528 if (result->IsProperty()) return; 2529 2530 LookupRealNamedPropertyInPrototypes(name, result); 2531} 2532 2533 2534void JSObject::LookupRealNamedPropertyInPrototypes(String* name, 2535 LookupResult* result) { 2536 Heap* heap = GetHeap(); 2537 for (Object* pt = GetPrototype(); 2538 pt != heap->null_value(); 2539 pt = JSObject::cast(pt)->GetPrototype()) { 2540 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); 2541 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return; 2542 } 2543 result->NotFound(); 2544} 2545 2546 2547// We only need to deal with CALLBACKS and INTERCEPTORS 2548MaybeObject* JSObject::SetPropertyWithFailedAccessCheck( 2549 LookupResult* result, 2550 String* name, 2551 Object* value, 2552 bool check_prototype, 2553 StrictModeFlag strict_mode) { 2554 if (check_prototype && !result->IsProperty()) { 2555 LookupCallbackSetterInPrototypes(name, result); 2556 } 2557 2558 if (result->IsProperty()) { 2559 if (!result->IsReadOnly()) { 2560 switch (result->type()) { 2561 case CALLBACKS: { 2562 Object* obj = result->GetCallbackObject(); 2563 if (obj->IsAccessorInfo()) { 2564 AccessorInfo* info = AccessorInfo::cast(obj); 2565 if (info->all_can_write()) { 2566 return SetPropertyWithCallback(result->GetCallbackObject(), 2567 name, 2568 value, 2569 result->holder(), 2570 strict_mode); 2571 } 2572 } 2573 break; 2574 } 2575 case INTERCEPTOR: { 2576 // Try lookup real named properties. Note that only property can be 2577 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain. 2578 LookupResult r(GetIsolate()); 2579 LookupRealNamedProperty(name, &r); 2580 if (r.IsProperty()) { 2581 return SetPropertyWithFailedAccessCheck(&r, 2582 name, 2583 value, 2584 check_prototype, 2585 strict_mode); 2586 } 2587 break; 2588 } 2589 default: { 2590 break; 2591 } 2592 } 2593 } 2594 } 2595 2596 Isolate* isolate = GetIsolate(); 2597 HandleScope scope(isolate); 2598 Handle<Object> value_handle(value); 2599 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); 2600 return *value_handle; 2601} 2602 2603 2604MaybeObject* JSReceiver::SetProperty(LookupResult* result, 2605 String* key, 2606 Object* value, 2607 PropertyAttributes attributes, 2608 StrictModeFlag strict_mode) { 2609 if (result->IsFound() && result->type() == HANDLER) { 2610 return result->proxy()->SetPropertyWithHandler( 2611 key, value, attributes, strict_mode); 2612 } else { 2613 return JSObject::cast(this)->SetPropertyForResult( 2614 result, key, value, attributes, strict_mode); 2615 } 2616} 2617 2618 2619bool JSProxy::HasPropertyWithHandler(String* name_raw) { 2620 Isolate* isolate = GetIsolate(); 2621 HandleScope scope(isolate); 2622 Handle<Object> receiver(this); 2623 Handle<Object> name(name_raw); 2624 2625 Handle<Object> args[] = { name }; 2626 Handle<Object> result = CallTrap( 2627 "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args); 2628 if (isolate->has_pending_exception()) return Failure::Exception(); 2629 2630 return result->ToBoolean()->IsTrue(); 2631} 2632 2633 2634MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( 2635 String* name_raw, 2636 Object* value_raw, 2637 PropertyAttributes attributes, 2638 StrictModeFlag strict_mode) { 2639 Isolate* isolate = GetIsolate(); 2640 HandleScope scope(isolate); 2641 Handle<Object> receiver(this); 2642 Handle<Object> name(name_raw); 2643 Handle<Object> value(value_raw); 2644 2645 Handle<Object> args[] = { receiver, name, value }; 2646 CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args); 2647 if (isolate->has_pending_exception()) return Failure::Exception(); 2648 2649 return *value; 2650} 2651 2652 2653MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandlerIfDefiningSetter( 2654 String* name_raw, 2655 Object* value_raw, 2656 PropertyAttributes attributes, 2657 StrictModeFlag strict_mode, 2658 bool* found) { 2659 *found = true; // except where defined otherwise... 2660 Isolate* isolate = GetHeap()->isolate(); 2661 Handle<JSProxy> proxy(this); 2662 Handle<Object> handler(this->handler()); // Trap might morph proxy. 2663 Handle<String> name(name_raw); 2664 Handle<Object> value(value_raw); 2665 Handle<Object> args[] = { name }; 2666 Handle<Object> result = proxy->CallTrap( 2667 "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); 2668 if (isolate->has_pending_exception()) return Failure::Exception(); 2669 2670 if (!result->IsUndefined()) { 2671 // The proxy handler cares about this property. 2672 // Check whether it is virtualized as an accessor. 2673 // Emulate [[GetProperty]] semantics for proxies. 2674 bool has_pending_exception; 2675 Handle<Object> argv[] = { result }; 2676 Handle<Object> desc = 2677 Execution::Call(isolate->to_complete_property_descriptor(), result, 2678 ARRAY_SIZE(argv), argv, &has_pending_exception); 2679 if (has_pending_exception) return Failure::Exception(); 2680 2681 Handle<String> conf_name = 2682 isolate->factory()->LookupAsciiSymbol("configurable_"); 2683 Handle<Object> configurable(v8::internal::GetProperty(desc, conf_name)); 2684 ASSERT(!isolate->has_pending_exception()); 2685 if (configurable->IsFalse()) { 2686 Handle<String> trap = 2687 isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); 2688 Handle<Object> args[] = { handler, trap, name }; 2689 Handle<Object> error = isolate->factory()->NewTypeError( 2690 "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); 2691 return isolate->Throw(*error); 2692 } 2693 ASSERT(configurable->IsTrue()); 2694 2695 // Check for AccessorDescriptor. 2696 Handle<String> set_name = isolate->factory()->LookupAsciiSymbol("set_"); 2697 Handle<Object> setter(v8::internal::GetProperty(desc, set_name)); 2698 ASSERT(!isolate->has_pending_exception()); 2699 if (!setter->IsUndefined()) { 2700 // We have a setter -- invoke it. 2701 // TODO(rossberg): nicer would be to cast to some JSCallable here... 2702 return proxy->SetPropertyWithDefinedSetter( 2703 JSReceiver::cast(*setter), *value); 2704 } else { 2705 Handle<String> get_name = isolate->factory()->LookupAsciiSymbol("get_"); 2706 Handle<Object> getter(v8::internal::GetProperty(desc, get_name)); 2707 ASSERT(!isolate->has_pending_exception()); 2708 if (!getter->IsUndefined()) { 2709 // We have a getter but no setter -- the property may not be 2710 // written. In strict mode, throw an error. 2711 if (strict_mode == kNonStrictMode) return *value; 2712 Handle<Object> args[] = { name, proxy }; 2713 Handle<Object> error = isolate->factory()->NewTypeError( 2714 "no_setter_in_callback", HandleVector(args, ARRAY_SIZE(args))); 2715 return isolate->Throw(*error); 2716 } 2717 } 2718 // Fall-through. 2719 } 2720 2721 // The proxy does not define the property as an accessor. 2722 *found = false; 2723 return *value; 2724} 2725 2726 2727MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler( 2728 String* name_raw, DeleteMode mode) { 2729 Isolate* isolate = GetIsolate(); 2730 HandleScope scope(isolate); 2731 Handle<Object> receiver(this); 2732 Handle<Object> name(name_raw); 2733 2734 Handle<Object> args[] = { name }; 2735 Handle<Object> result = CallTrap( 2736 "delete", Handle<Object>(), ARRAY_SIZE(args), args); 2737 if (isolate->has_pending_exception()) return Failure::Exception(); 2738 2739 Object* bool_result = result->ToBoolean(); 2740 if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) { 2741 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); 2742 Handle<Object> args[] = { Handle<Object>(handler()), trap_name }; 2743 Handle<Object> error = isolate->factory()->NewTypeError( 2744 "handler_failed", HandleVector(args, ARRAY_SIZE(args))); 2745 isolate->Throw(*error); 2746 return Failure::Exception(); 2747 } 2748 return bool_result; 2749} 2750 2751 2752MUST_USE_RESULT MaybeObject* JSProxy::DeleteElementWithHandler( 2753 uint32_t index, 2754 DeleteMode mode) { 2755 Isolate* isolate = GetIsolate(); 2756 HandleScope scope(isolate); 2757 Handle<String> name = isolate->factory()->Uint32ToString(index); 2758 return JSProxy::DeletePropertyWithHandler(*name, mode); 2759} 2760 2761 2762MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( 2763 JSReceiver* receiver_raw, 2764 String* name_raw) { 2765 Isolate* isolate = GetIsolate(); 2766 HandleScope scope(isolate); 2767 Handle<JSProxy> proxy(this); 2768 Handle<Object> handler(this->handler()); // Trap might morph proxy. 2769 Handle<JSReceiver> receiver(receiver_raw); 2770 Handle<Object> name(name_raw); 2771 2772 Handle<Object> args[] = { name }; 2773 Handle<Object> result = CallTrap( 2774 "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); 2775 if (isolate->has_pending_exception()) return NONE; 2776 2777 if (result->IsUndefined()) return ABSENT; 2778 2779 bool has_pending_exception; 2780 Handle<Object> argv[] = { result }; 2781 Handle<Object> desc = 2782 Execution::Call(isolate->to_complete_property_descriptor(), result, 2783 ARRAY_SIZE(argv), argv, &has_pending_exception); 2784 if (has_pending_exception) return NONE; 2785 2786 // Convert result to PropertyAttributes. 2787 Handle<String> enum_n = isolate->factory()->LookupAsciiSymbol("enumerable"); 2788 Handle<Object> enumerable(v8::internal::GetProperty(desc, enum_n)); 2789 if (isolate->has_pending_exception()) return NONE; 2790 Handle<String> conf_n = isolate->factory()->LookupAsciiSymbol("configurable"); 2791 Handle<Object> configurable(v8::internal::GetProperty(desc, conf_n)); 2792 if (isolate->has_pending_exception()) return NONE; 2793 Handle<String> writ_n = isolate->factory()->LookupAsciiSymbol("writable"); 2794 Handle<Object> writable(v8::internal::GetProperty(desc, writ_n)); 2795 if (isolate->has_pending_exception()) return NONE; 2796 2797 if (configurable->IsFalse()) { 2798 Handle<String> trap = 2799 isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); 2800 Handle<Object> args[] = { handler, trap, name }; 2801 Handle<Object> error = isolate->factory()->NewTypeError( 2802 "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); 2803 isolate->Throw(*error); 2804 return NONE; 2805 } 2806 2807 int attributes = NONE; 2808 if (enumerable->ToBoolean()->IsFalse()) attributes |= DONT_ENUM; 2809 if (configurable->ToBoolean()->IsFalse()) attributes |= DONT_DELETE; 2810 if (writable->ToBoolean()->IsFalse()) attributes |= READ_ONLY; 2811 return static_cast<PropertyAttributes>(attributes); 2812} 2813 2814 2815MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( 2816 JSReceiver* receiver, 2817 uint32_t index) { 2818 Isolate* isolate = GetIsolate(); 2819 HandleScope scope(isolate); 2820 Handle<String> name = isolate->factory()->Uint32ToString(index); 2821 return GetPropertyAttributeWithHandler(receiver, *name); 2822} 2823 2824 2825void JSProxy::Fix() { 2826 Isolate* isolate = GetIsolate(); 2827 HandleScope scope(isolate); 2828 Handle<JSProxy> self(this); 2829 2830 // Save identity hash. 2831 MaybeObject* maybe_hash = GetIdentityHash(OMIT_CREATION); 2832 2833 if (IsJSFunctionProxy()) { 2834 isolate->factory()->BecomeJSFunction(self); 2835 // Code will be set on the JavaScript side. 2836 } else { 2837 isolate->factory()->BecomeJSObject(self); 2838 } 2839 ASSERT(self->IsJSObject()); 2840 2841 // Inherit identity, if it was present. 2842 Object* hash; 2843 if (maybe_hash->To<Object>(&hash) && hash->IsSmi()) { 2844 Handle<JSObject> new_self(JSObject::cast(*self)); 2845 isolate->factory()->SetIdentityHash(new_self, hash); 2846 } 2847} 2848 2849 2850MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name, 2851 Handle<Object> derived, 2852 int argc, 2853 Handle<Object> argv[]) { 2854 Isolate* isolate = GetIsolate(); 2855 Handle<Object> handler(this->handler()); 2856 2857 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol(name); 2858 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); 2859 if (isolate->has_pending_exception()) return trap; 2860 2861 if (trap->IsUndefined()) { 2862 if (derived.is_null()) { 2863 Handle<Object> args[] = { handler, trap_name }; 2864 Handle<Object> error = isolate->factory()->NewTypeError( 2865 "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); 2866 isolate->Throw(*error); 2867 return Handle<Object>(); 2868 } 2869 trap = Handle<Object>(derived); 2870 } 2871 2872 bool threw; 2873 return Execution::Call(trap, handler, argc, argv, &threw); 2874} 2875 2876 2877MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, 2878 String* name, 2879 Object* value, 2880 PropertyAttributes attributes, 2881 StrictModeFlag strict_mode) { 2882 Heap* heap = GetHeap(); 2883 // Make sure that the top context does not change when doing callbacks or 2884 // interceptor calls. 2885 AssertNoContextChange ncc; 2886 2887 // Optimization for 2-byte strings often used as keys in a decompression 2888 // dictionary. We make these short keys into symbols to avoid constantly 2889 // reallocating them. 2890 if (!name->IsSymbol() && name->length() <= 2) { 2891 Object* symbol_version; 2892 { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name); 2893 if (maybe_symbol_version->ToObject(&symbol_version)) { 2894 name = String::cast(symbol_version); 2895 } 2896 } 2897 } 2898 2899 // Check access rights if needed. 2900 if (IsAccessCheckNeeded()) { 2901 if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { 2902 return SetPropertyWithFailedAccessCheck( 2903 result, name, value, true, strict_mode); 2904 } 2905 } 2906 2907 if (IsJSGlobalProxy()) { 2908 Object* proto = GetPrototype(); 2909 if (proto->IsNull()) return value; 2910 ASSERT(proto->IsJSGlobalObject()); 2911 return JSObject::cast(proto)->SetPropertyForResult( 2912 result, name, value, attributes, strict_mode); 2913 } 2914 2915 if (!result->IsProperty() && !IsJSContextExtensionObject()) { 2916 bool found = false; 2917 MaybeObject* result_object; 2918 result_object = SetPropertyWithCallbackSetterInPrototypes(name, 2919 value, 2920 attributes, 2921 &found, 2922 strict_mode); 2923 if (found) return result_object; 2924 } 2925 2926 // At this point, no GC should have happened, as this would invalidate 2927 // 'result', which we cannot handlify! 2928 2929 if (!result->IsFound()) { 2930 // Neither properties nor transitions found. 2931 return AddProperty(name, value, attributes, strict_mode); 2932 } 2933 if (result->IsReadOnly() && result->IsProperty()) { 2934 if (strict_mode == kStrictMode) { 2935 Handle<JSObject> self(this); 2936 Handle<String> hname(name); 2937 Handle<Object> args[] = { hname, self }; 2938 return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError( 2939 "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); 2940 } else { 2941 return value; 2942 } 2943 } 2944 // This is a real property that is not read-only, or it is a 2945 // transition or null descriptor and there are no setters in the prototypes. 2946 switch (result->type()) { 2947 case NORMAL: 2948 return SetNormalizedProperty(result, value); 2949 case FIELD: 2950 return FastPropertyAtPut(result->GetFieldIndex(), value); 2951 case MAP_TRANSITION: 2952 if (attributes == result->GetAttributes()) { 2953 // Only use map transition if the attributes match. 2954 return AddFastPropertyUsingMap(result->GetTransitionMap(), 2955 name, 2956 value); 2957 } 2958 return ConvertDescriptorToField(name, value, attributes); 2959 case CONSTANT_FUNCTION: 2960 // Only replace the function if necessary. 2961 if (value == result->GetConstantFunction()) return value; 2962 // Preserve the attributes of this existing property. 2963 attributes = result->GetAttributes(); 2964 return ConvertDescriptorToField(name, value, attributes); 2965 case CALLBACKS: 2966 return SetPropertyWithCallback(result->GetCallbackObject(), 2967 name, 2968 value, 2969 result->holder(), 2970 strict_mode); 2971 case INTERCEPTOR: 2972 return SetPropertyWithInterceptor(name, value, attributes, strict_mode); 2973 case CONSTANT_TRANSITION: { 2974 // If the same constant function is being added we can simply 2975 // transition to the target map. 2976 Map* target_map = result->GetTransitionMap(); 2977 DescriptorArray* target_descriptors = target_map->instance_descriptors(); 2978 int number = target_descriptors->SearchWithCache(name); 2979 ASSERT(number != DescriptorArray::kNotFound); 2980 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION); 2981 JSFunction* function = 2982 JSFunction::cast(target_descriptors->GetValue(number)); 2983 if (value == function) { 2984 set_map(target_map); 2985 return value; 2986 } 2987 // Otherwise, replace with a MAP_TRANSITION to a new map with a 2988 // FIELD, even if the value is a constant function. 2989 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 2990 } 2991 case NULL_DESCRIPTOR: 2992 case ELEMENTS_TRANSITION: 2993 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 2994 case HANDLER: 2995 UNREACHABLE(); 2996 return value; 2997 } 2998 UNREACHABLE(); // keep the compiler happy 2999 return value; 3000} 3001 3002 3003// Set a real local property, even if it is READ_ONLY. If the property is not 3004// present, add it with attributes NONE. This code is an exact clone of 3005// SetProperty, with the check for IsReadOnly and the check for a 3006// callback setter removed. The two lines looking up the LookupResult 3007// result are also added. If one of the functions is changed, the other 3008// should be. 3009// Note that this method cannot be used to set the prototype of a function 3010// because ConvertDescriptorToField() which is called in "case CALLBACKS:" 3011// doesn't handle function prototypes correctly. 3012Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( 3013 Handle<JSObject> object, 3014 Handle<String> key, 3015 Handle<Object> value, 3016 PropertyAttributes attributes) { 3017 CALL_HEAP_FUNCTION( 3018 object->GetIsolate(), 3019 object->SetLocalPropertyIgnoreAttributes(*key, *value, attributes), 3020 Object); 3021} 3022 3023 3024MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( 3025 String* name, 3026 Object* value, 3027 PropertyAttributes attributes) { 3028 3029 // Make sure that the top context does not change when doing callbacks or 3030 // interceptor calls. 3031 AssertNoContextChange ncc; 3032 Isolate* isolate = GetIsolate(); 3033 LookupResult result(isolate); 3034 LocalLookup(name, &result); 3035 // Check access rights if needed. 3036 if (IsAccessCheckNeeded()) { 3037 if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { 3038 return SetPropertyWithFailedAccessCheck(&result, 3039 name, 3040 value, 3041 false, 3042 kNonStrictMode); 3043 } 3044 } 3045 3046 if (IsJSGlobalProxy()) { 3047 Object* proto = GetPrototype(); 3048 if (proto->IsNull()) return value; 3049 ASSERT(proto->IsJSGlobalObject()); 3050 return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes( 3051 name, 3052 value, 3053 attributes); 3054 } 3055 3056 // Check for accessor in prototype chain removed here in clone. 3057 if (!result.IsFound()) { 3058 // Neither properties nor transitions found. 3059 return AddProperty(name, value, attributes, kNonStrictMode); 3060 } 3061 3062 PropertyDetails details = PropertyDetails(attributes, NORMAL); 3063 3064 // Check of IsReadOnly removed from here in clone. 3065 switch (result.type()) { 3066 case NORMAL: 3067 return SetNormalizedProperty(name, value, details); 3068 case FIELD: 3069 return FastPropertyAtPut(result.GetFieldIndex(), value); 3070 case MAP_TRANSITION: 3071 if (attributes == result.GetAttributes()) { 3072 // Only use map transition if the attributes match. 3073 return AddFastPropertyUsingMap(result.GetTransitionMap(), 3074 name, 3075 value); 3076 } 3077 return ConvertDescriptorToField(name, value, attributes); 3078 case CONSTANT_FUNCTION: 3079 // Only replace the function if necessary. 3080 if (value == result.GetConstantFunction()) return value; 3081 // Preserve the attributes of this existing property. 3082 attributes = result.GetAttributes(); 3083 return ConvertDescriptorToField(name, value, attributes); 3084 case CALLBACKS: 3085 case INTERCEPTOR: 3086 // Override callback in clone 3087 return ConvertDescriptorToField(name, value, attributes); 3088 case CONSTANT_TRANSITION: 3089 // Replace with a MAP_TRANSITION to a new map with a FIELD, even 3090 // if the value is a function. 3091 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 3092 case NULL_DESCRIPTOR: 3093 case ELEMENTS_TRANSITION: 3094 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); 3095 case HANDLER: 3096 UNREACHABLE(); 3097 return value; 3098 } 3099 UNREACHABLE(); // keep the compiler happy 3100 return value; 3101} 3102 3103 3104PropertyAttributes JSObject::GetPropertyAttributePostInterceptor( 3105 JSObject* receiver, 3106 String* name, 3107 bool continue_search) { 3108 // Check local property, ignore interceptor. 3109 LookupResult result(GetIsolate()); 3110 LocalLookupRealNamedProperty(name, &result); 3111 if (result.IsProperty()) return result.GetAttributes(); 3112 3113 if (continue_search) { 3114 // Continue searching via the prototype chain. 3115 Object* pt = GetPrototype(); 3116 if (!pt->IsNull()) { 3117 return JSObject::cast(pt)-> 3118 GetPropertyAttributeWithReceiver(receiver, name); 3119 } 3120 } 3121 return ABSENT; 3122} 3123 3124 3125PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor( 3126 JSObject* receiver, 3127 String* name, 3128 bool continue_search) { 3129 Isolate* isolate = GetIsolate(); 3130 3131 // Make sure that the top context does not change when doing 3132 // callbacks or interceptor calls. 3133 AssertNoContextChange ncc; 3134 3135 HandleScope scope(isolate); 3136 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 3137 Handle<JSObject> receiver_handle(receiver); 3138 Handle<JSObject> holder_handle(this); 3139 Handle<String> name_handle(name); 3140 CustomArguments args(isolate, interceptor->data(), receiver, this); 3141 v8::AccessorInfo info(args.end()); 3142 if (!interceptor->query()->IsUndefined()) { 3143 v8::NamedPropertyQuery query = 3144 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query()); 3145 LOG(isolate, 3146 ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name)); 3147 v8::Handle<v8::Integer> result; 3148 { 3149 // Leaving JavaScript. 3150 VMState state(isolate, EXTERNAL); 3151 result = query(v8::Utils::ToLocal(name_handle), info); 3152 } 3153 if (!result.IsEmpty()) { 3154 ASSERT(result->IsInt32()); 3155 return static_cast<PropertyAttributes>(result->Int32Value()); 3156 } 3157 } else if (!interceptor->getter()->IsUndefined()) { 3158 v8::NamedPropertyGetter getter = 3159 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter()); 3160 LOG(isolate, 3161 ApiNamedPropertyAccess("interceptor-named-get-has", this, name)); 3162 v8::Handle<v8::Value> result; 3163 { 3164 // Leaving JavaScript. 3165 VMState state(isolate, EXTERNAL); 3166 result = getter(v8::Utils::ToLocal(name_handle), info); 3167 } 3168 if (!result.IsEmpty()) return DONT_ENUM; 3169 } 3170 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle, 3171 *name_handle, 3172 continue_search); 3173} 3174 3175 3176PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver( 3177 JSReceiver* receiver, 3178 String* key) { 3179 uint32_t index = 0; 3180 if (IsJSObject() && key->AsArrayIndex(&index)) { 3181 return JSObject::cast(this)->HasElementWithReceiver(receiver, index) 3182 ? NONE : ABSENT; 3183 } 3184 // Named property. 3185 LookupResult result(GetIsolate()); 3186 Lookup(key, &result); 3187 return GetPropertyAttribute(receiver, &result, key, true); 3188} 3189 3190 3191PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver, 3192 LookupResult* result, 3193 String* name, 3194 bool continue_search) { 3195 // Check access rights if needed. 3196 if (IsAccessCheckNeeded()) { 3197 JSObject* this_obj = JSObject::cast(this); 3198 Heap* heap = GetHeap(); 3199 if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) { 3200 return this_obj->GetPropertyAttributeWithFailedAccessCheck( 3201 receiver, result, name, continue_search); 3202 } 3203 } 3204 if (result->IsProperty()) { 3205 switch (result->type()) { 3206 case NORMAL: // fall through 3207 case FIELD: 3208 case CONSTANT_FUNCTION: 3209 case CALLBACKS: 3210 return result->GetAttributes(); 3211 case HANDLER: { 3212 return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler( 3213 receiver, name); 3214 } 3215 case INTERCEPTOR: 3216 return result->holder()->GetPropertyAttributeWithInterceptor( 3217 JSObject::cast(receiver), name, continue_search); 3218 default: 3219 UNREACHABLE(); 3220 } 3221 } 3222 return ABSENT; 3223} 3224 3225 3226PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) { 3227 // Check whether the name is an array index. 3228 uint32_t index = 0; 3229 if (IsJSObject() && name->AsArrayIndex(&index)) { 3230 if (JSObject::cast(this)->HasLocalElement(index)) return NONE; 3231 return ABSENT; 3232 } 3233 // Named property. 3234 LookupResult result(GetIsolate()); 3235 LocalLookup(name, &result); 3236 return GetPropertyAttribute(this, &result, name, false); 3237} 3238 3239 3240MaybeObject* NormalizedMapCache::Get(JSObject* obj, 3241 PropertyNormalizationMode mode) { 3242 Isolate* isolate = obj->GetIsolate(); 3243 Map* fast = obj->map(); 3244 int index = fast->Hash() % kEntries; 3245 Object* result = get(index); 3246 if (result->IsMap() && 3247 Map::cast(result)->EquivalentToForNormalization(fast, mode)) { 3248#ifdef DEBUG 3249 if (FLAG_verify_heap) { 3250 Map::cast(result)->SharedMapVerify(); 3251 } 3252 if (FLAG_enable_slow_asserts) { 3253 // The cached map should match newly created normalized map bit-by-bit. 3254 Object* fresh; 3255 { MaybeObject* maybe_fresh = 3256 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP); 3257 if (maybe_fresh->ToObject(&fresh)) { 3258 ASSERT(memcmp(Map::cast(fresh)->address(), 3259 Map::cast(result)->address(), 3260 Map::kSize) == 0); 3261 } 3262 } 3263 } 3264#endif 3265 return result; 3266 } 3267 3268 { MaybeObject* maybe_result = 3269 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP); 3270 if (!maybe_result->ToObject(&result)) return maybe_result; 3271 } 3272 set(index, result); 3273 isolate->counters()->normalized_maps()->Increment(); 3274 3275 return result; 3276} 3277 3278 3279void NormalizedMapCache::Clear() { 3280 int entries = length(); 3281 for (int i = 0; i != entries; i++) { 3282 set_undefined(i); 3283 } 3284} 3285 3286 3287void JSObject::UpdateMapCodeCache(Handle<JSObject> object, 3288 Handle<String> name, 3289 Handle<Code> code) { 3290 Isolate* isolate = object->GetIsolate(); 3291 CALL_HEAP_FUNCTION_VOID(isolate, 3292 object->UpdateMapCodeCache(*name, *code)); 3293} 3294 3295 3296MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) { 3297 if (map()->is_shared()) { 3298 // Fast case maps are never marked as shared. 3299 ASSERT(!HasFastProperties()); 3300 // Replace the map with an identical copy that can be safely modified. 3301 Object* obj; 3302 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES, 3303 UNIQUE_NORMALIZED_MAP); 3304 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 3305 } 3306 GetIsolate()->counters()->normalized_maps()->Increment(); 3307 3308 set_map(Map::cast(obj)); 3309 } 3310 return map()->UpdateCodeCache(name, code); 3311} 3312 3313 3314void JSObject::NormalizeProperties(Handle<JSObject> object, 3315 PropertyNormalizationMode mode, 3316 int expected_additional_properties) { 3317 CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), 3318 object->NormalizeProperties( 3319 mode, expected_additional_properties)); 3320} 3321 3322 3323MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, 3324 int expected_additional_properties) { 3325 if (!HasFastProperties()) return this; 3326 3327 // The global object is always normalized. 3328 ASSERT(!IsGlobalObject()); 3329 // JSGlobalProxy must never be normalized 3330 ASSERT(!IsJSGlobalProxy()); 3331 3332 Map* map_of_this = map(); 3333 3334 // Allocate new content. 3335 int property_count = map_of_this->NumberOfDescribedProperties(); 3336 if (expected_additional_properties > 0) { 3337 property_count += expected_additional_properties; 3338 } else { 3339 property_count += 2; // Make space for two more properties. 3340 } 3341 StringDictionary* dictionary; 3342 { MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count); 3343 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 3344 } 3345 3346 DescriptorArray* descs = map_of_this->instance_descriptors(); 3347 for (int i = 0; i < descs->number_of_descriptors(); i++) { 3348 PropertyDetails details(descs->GetDetails(i)); 3349 switch (details.type()) { 3350 case CONSTANT_FUNCTION: { 3351 PropertyDetails d = 3352 PropertyDetails(details.attributes(), NORMAL, details.index()); 3353 Object* value = descs->GetConstantFunction(i); 3354 MaybeObject* maybe_dictionary = 3355 dictionary->Add(descs->GetKey(i), value, d); 3356 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 3357 break; 3358 } 3359 case FIELD: { 3360 PropertyDetails d = 3361 PropertyDetails(details.attributes(), NORMAL, details.index()); 3362 Object* value = FastPropertyAt(descs->GetFieldIndex(i)); 3363 MaybeObject* maybe_dictionary = 3364 dictionary->Add(descs->GetKey(i), value, d); 3365 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 3366 break; 3367 } 3368 case CALLBACKS: { 3369 if (!descs->IsProperty(i)) break; 3370 Object* value = descs->GetCallbacksObject(i); 3371 if (value->IsAccessorPair()) { 3372 MaybeObject* maybe_copy = 3373 AccessorPair::cast(value)->CopyWithoutTransitions(); 3374 if (!maybe_copy->To(&value)) return maybe_copy; 3375 } 3376 MaybeObject* maybe_dictionary = 3377 dictionary->Add(descs->GetKey(i), value, details); 3378 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 3379 break; 3380 } 3381 case MAP_TRANSITION: 3382 case CONSTANT_TRANSITION: 3383 case NULL_DESCRIPTOR: 3384 case INTERCEPTOR: 3385 case ELEMENTS_TRANSITION: 3386 break; 3387 case HANDLER: 3388 case NORMAL: 3389 UNREACHABLE(); 3390 break; 3391 } 3392 } 3393 3394 Heap* current_heap = GetHeap(); 3395 3396 // Copy the next enumeration index from instance descriptor. 3397 int index = map_of_this->instance_descriptors()->NextEnumerationIndex(); 3398 dictionary->SetNextEnumerationIndex(index); 3399 3400 Map* new_map; 3401 { MaybeObject* maybe_map = 3402 current_heap->isolate()->context()->global_context()-> 3403 normalized_map_cache()->Get(this, mode); 3404 if (!maybe_map->To(&new_map)) return maybe_map; 3405 } 3406 3407 // We have now successfully allocated all the necessary objects. 3408 // Changes can now be made with the guarantee that all of them take effect. 3409 3410 // Resize the object in the heap if necessary. 3411 int new_instance_size = new_map->instance_size(); 3412 int instance_size_delta = map_of_this->instance_size() - new_instance_size; 3413 ASSERT(instance_size_delta >= 0); 3414 current_heap->CreateFillerObjectAt(this->address() + new_instance_size, 3415 instance_size_delta); 3416 if (Marking::IsBlack(Marking::MarkBitFrom(this))) { 3417 MemoryChunk::IncrementLiveBytesFromMutator(this->address(), 3418 -instance_size_delta); 3419 } 3420 3421 3422 set_map(new_map); 3423 new_map->clear_instance_descriptors(); 3424 3425 set_properties(dictionary); 3426 3427 current_heap->isolate()->counters()->props_to_dictionary()->Increment(); 3428 3429#ifdef DEBUG 3430 if (FLAG_trace_normalization) { 3431 PrintF("Object properties have been normalized:\n"); 3432 Print(); 3433 } 3434#endif 3435 return this; 3436} 3437 3438 3439void JSObject::TransformToFastProperties(Handle<JSObject> object, 3440 int unused_property_fields) { 3441 CALL_HEAP_FUNCTION_VOID( 3442 object->GetIsolate(), 3443 object->TransformToFastProperties(unused_property_fields)); 3444} 3445 3446 3447MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) { 3448 if (HasFastProperties()) return this; 3449 ASSERT(!IsGlobalObject()); 3450 return property_dictionary()-> 3451 TransformPropertiesToFastFor(this, unused_property_fields); 3452} 3453 3454 3455Handle<SeededNumberDictionary> JSObject::NormalizeElements( 3456 Handle<JSObject> object) { 3457 CALL_HEAP_FUNCTION(object->GetIsolate(), 3458 object->NormalizeElements(), 3459 SeededNumberDictionary); 3460} 3461 3462 3463MaybeObject* JSObject::NormalizeElements() { 3464 ASSERT(!HasExternalArrayElements()); 3465 3466 // Find the backing store. 3467 FixedArrayBase* array = FixedArrayBase::cast(elements()); 3468 Map* old_map = array->map(); 3469 bool is_arguments = 3470 (old_map == old_map->GetHeap()->non_strict_arguments_elements_map()); 3471 if (is_arguments) { 3472 array = FixedArrayBase::cast(FixedArray::cast(array)->get(1)); 3473 } 3474 if (array->IsDictionary()) return array; 3475 3476 ASSERT(HasFastElements() || 3477 HasFastSmiOnlyElements() || 3478 HasFastDoubleElements() || 3479 HasFastArgumentsElements()); 3480 // Compute the effective length and allocate a new backing store. 3481 int length = IsJSArray() 3482 ? Smi::cast(JSArray::cast(this)->length())->value() 3483 : array->length(); 3484 int old_capacity = 0; 3485 int used_elements = 0; 3486 GetElementsCapacityAndUsage(&old_capacity, &used_elements); 3487 SeededNumberDictionary* dictionary = NULL; 3488 { Object* object; 3489 MaybeObject* maybe = SeededNumberDictionary::Allocate(used_elements); 3490 if (!maybe->ToObject(&object)) return maybe; 3491 dictionary = SeededNumberDictionary::cast(object); 3492 } 3493 3494 // Copy the elements to the new backing store. 3495 bool has_double_elements = array->IsFixedDoubleArray(); 3496 for (int i = 0; i < length; i++) { 3497 Object* value = NULL; 3498 if (has_double_elements) { 3499 FixedDoubleArray* double_array = FixedDoubleArray::cast(array); 3500 if (double_array->is_the_hole(i)) { 3501 value = GetIsolate()->heap()->the_hole_value(); 3502 } else { 3503 // Objects must be allocated in the old object space, since the 3504 // overall number of HeapNumbers needed for the conversion might 3505 // exceed the capacity of new space, and we would fail repeatedly 3506 // trying to convert the FixedDoubleArray. 3507 MaybeObject* maybe_value_object = 3508 GetHeap()->AllocateHeapNumber(double_array->get_scalar(i), TENURED); 3509 if (!maybe_value_object->ToObject(&value)) return maybe_value_object; 3510 } 3511 } else { 3512 ASSERT(old_map->has_fast_elements() || 3513 old_map->has_fast_smi_only_elements()); 3514 value = FixedArray::cast(array)->get(i); 3515 } 3516 PropertyDetails details = PropertyDetails(NONE, NORMAL); 3517 if (!value->IsTheHole()) { 3518 Object* result; 3519 MaybeObject* maybe_result = 3520 dictionary->AddNumberEntry(i, value, details); 3521 if (!maybe_result->ToObject(&result)) return maybe_result; 3522 dictionary = SeededNumberDictionary::cast(result); 3523 } 3524 } 3525 3526 // Switch to using the dictionary as the backing storage for elements. 3527 if (is_arguments) { 3528 FixedArray::cast(elements())->set(1, dictionary); 3529 } else { 3530 // Set the new map first to satify the elements type assert in 3531 // set_elements(). 3532 Object* new_map; 3533 MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), 3534 DICTIONARY_ELEMENTS); 3535 if (!maybe->ToObject(&new_map)) return maybe; 3536 set_map(Map::cast(new_map)); 3537 set_elements(dictionary); 3538 } 3539 3540 old_map->GetHeap()->isolate()->counters()->elements_to_dictionary()-> 3541 Increment(); 3542 3543#ifdef DEBUG 3544 if (FLAG_trace_normalization) { 3545 PrintF("Object elements have been normalized:\n"); 3546 Print(); 3547 } 3548#endif 3549 3550 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 3551 return dictionary; 3552} 3553 3554 3555Smi* JSReceiver::GenerateIdentityHash() { 3556 Isolate* isolate = GetIsolate(); 3557 3558 int hash_value; 3559 int attempts = 0; 3560 do { 3561 // Generate a random 32-bit hash value but limit range to fit 3562 // within a smi. 3563 hash_value = V8::RandomPrivate(isolate) & Smi::kMaxValue; 3564 attempts++; 3565 } while (hash_value == 0 && attempts < 30); 3566 hash_value = hash_value != 0 ? hash_value : 1; // never return 0 3567 3568 return Smi::FromInt(hash_value); 3569} 3570 3571 3572MaybeObject* JSObject::SetIdentityHash(Object* hash, CreationFlag flag) { 3573 MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(), 3574 hash); 3575 if (maybe->IsFailure()) return maybe; 3576 return this; 3577} 3578 3579 3580int JSObject::GetIdentityHash(Handle<JSObject> obj) { 3581 CALL_AND_RETRY(obj->GetIsolate(), 3582 obj->GetIdentityHash(ALLOW_CREATION), 3583 return Smi::cast(__object__)->value(), 3584 return 0); 3585} 3586 3587 3588MaybeObject* JSObject::GetIdentityHash(CreationFlag flag) { 3589 Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_symbol()); 3590 if (stored_value->IsSmi()) return stored_value; 3591 3592 // Do not generate permanent identity hash code if not requested. 3593 if (flag == OMIT_CREATION) return GetHeap()->undefined_value(); 3594 3595 Smi* hash = GenerateIdentityHash(); 3596 MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_symbol(), 3597 hash); 3598 if (result->IsFailure()) return result; 3599 if (result->ToObjectUnchecked()->IsUndefined()) { 3600 // Trying to get hash of detached proxy. 3601 return Smi::FromInt(0); 3602 } 3603 return hash; 3604} 3605 3606 3607MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) { 3608 Object* hash = this->hash(); 3609 if (!hash->IsSmi() && flag == ALLOW_CREATION) { 3610 hash = GenerateIdentityHash(); 3611 set_hash(hash); 3612 } 3613 return hash; 3614} 3615 3616 3617Object* JSObject::GetHiddenProperty(String* key) { 3618 if (IsJSGlobalProxy()) { 3619 // For a proxy, use the prototype as target object. 3620 Object* proxy_parent = GetPrototype(); 3621 // If the proxy is detached, return undefined. 3622 if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); 3623 ASSERT(proxy_parent->IsJSGlobalObject()); 3624 return JSObject::cast(proxy_parent)->GetHiddenProperty(key); 3625 } 3626 ASSERT(!IsJSGlobalProxy()); 3627 MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); 3628 ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. 3629 if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) { 3630 return GetHeap()->undefined_value(); 3631 } 3632 StringDictionary* dictionary = 3633 StringDictionary::cast(hidden_lookup->ToObjectUnchecked()); 3634 int entry = dictionary->FindEntry(key); 3635 if (entry == StringDictionary::kNotFound) return GetHeap()->undefined_value(); 3636 return dictionary->ValueAt(entry); 3637} 3638 3639 3640Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj, 3641 Handle<String> key, 3642 Handle<Object> value) { 3643 CALL_HEAP_FUNCTION(obj->GetIsolate(), 3644 obj->SetHiddenProperty(*key, *value), 3645 Object); 3646} 3647 3648 3649MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { 3650 if (IsJSGlobalProxy()) { 3651 // For a proxy, use the prototype as target object. 3652 Object* proxy_parent = GetPrototype(); 3653 // If the proxy is detached, return undefined. 3654 if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); 3655 ASSERT(proxy_parent->IsJSGlobalObject()); 3656 return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value); 3657 } 3658 ASSERT(!IsJSGlobalProxy()); 3659 MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(true); 3660 StringDictionary* dictionary; 3661 if (!hidden_lookup->To<StringDictionary>(&dictionary)) return hidden_lookup; 3662 3663 // If it was found, check if the key is already in the dictionary. 3664 int entry = dictionary->FindEntry(key); 3665 if (entry != StringDictionary::kNotFound) { 3666 // If key was found, just update the value. 3667 dictionary->ValueAtPut(entry, value); 3668 return this; 3669 } 3670 // Key was not already in the dictionary, so add the entry. 3671 MaybeObject* insert_result = dictionary->Add(key, 3672 value, 3673 PropertyDetails(NONE, NORMAL)); 3674 StringDictionary* new_dict; 3675 if (!insert_result->To<StringDictionary>(&new_dict)) return insert_result; 3676 if (new_dict != dictionary) { 3677 // If adding the key expanded the dictionary (i.e., Add returned a new 3678 // dictionary), store it back to the object. 3679 MaybeObject* store_result = SetHiddenPropertiesDictionary(new_dict); 3680 if (store_result->IsFailure()) return store_result; 3681 } 3682 // Return this to mark success. 3683 return this; 3684} 3685 3686 3687void JSObject::DeleteHiddenProperty(String* key) { 3688 if (IsJSGlobalProxy()) { 3689 // For a proxy, use the prototype as target object. 3690 Object* proxy_parent = GetPrototype(); 3691 // If the proxy is detached, return immediately. 3692 if (proxy_parent->IsNull()) return; 3693 ASSERT(proxy_parent->IsJSGlobalObject()); 3694 JSObject::cast(proxy_parent)->DeleteHiddenProperty(key); 3695 return; 3696 } 3697 MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); 3698 ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. 3699 if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) return; 3700 StringDictionary* dictionary = 3701 StringDictionary::cast(hidden_lookup->ToObjectUnchecked()); 3702 int entry = dictionary->FindEntry(key); 3703 if (entry == StringDictionary::kNotFound) { 3704 // Key wasn't in dictionary. Deletion is a success. 3705 return; 3706 } 3707 // Key was in the dictionary. Remove it. 3708 dictionary->DeleteProperty(entry, JSReceiver::FORCE_DELETION); 3709} 3710 3711 3712bool JSObject::HasHiddenProperties() { 3713 return GetPropertyAttributePostInterceptor(this, 3714 GetHeap()->hidden_symbol(), 3715 false) != ABSENT; 3716} 3717 3718 3719MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) { 3720 ASSERT(!IsJSGlobalProxy()); 3721 if (HasFastProperties()) { 3722 // If the object has fast properties, check whether the first slot 3723 // in the descriptor array matches the hidden symbol. Since the 3724 // hidden symbols hash code is zero (and no other string has hash 3725 // code zero) it will always occupy the first entry if present. 3726 DescriptorArray* descriptors = this->map()->instance_descriptors(); 3727 if ((descriptors->number_of_descriptors() > 0) && 3728 (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) { 3729 if (descriptors->GetType(0) == FIELD) { 3730 Object* hidden_store = 3731 this->FastPropertyAt(descriptors->GetFieldIndex(0)); 3732 return StringDictionary::cast(hidden_store); 3733 } else { 3734 ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR || 3735 descriptors->GetType(0) == MAP_TRANSITION); 3736 } 3737 } 3738 } else { 3739 PropertyAttributes attributes; 3740 // You can't install a getter on a property indexed by the hidden symbol, 3741 // so we can be sure that GetLocalPropertyPostInterceptor returns a real 3742 // object. 3743 Object* lookup = 3744 GetLocalPropertyPostInterceptor(this, 3745 GetHeap()->hidden_symbol(), 3746 &attributes)->ToObjectUnchecked(); 3747 if (!lookup->IsUndefined()) { 3748 return StringDictionary::cast(lookup); 3749 } 3750 } 3751 if (!create_if_absent) return GetHeap()->undefined_value(); 3752 const int kInitialSize = 5; 3753 MaybeObject* dict_alloc = StringDictionary::Allocate(kInitialSize); 3754 StringDictionary* dictionary; 3755 if (!dict_alloc->To<StringDictionary>(&dictionary)) return dict_alloc; 3756 MaybeObject* store_result = 3757 SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), 3758 dictionary, 3759 DONT_ENUM, 3760 kNonStrictMode); 3761 if (store_result->IsFailure()) return store_result; 3762 return dictionary; 3763} 3764 3765 3766MaybeObject* JSObject::SetHiddenPropertiesDictionary( 3767 StringDictionary* dictionary) { 3768 ASSERT(!IsJSGlobalProxy()); 3769 ASSERT(HasHiddenProperties()); 3770 if (HasFastProperties()) { 3771 // If the object has fast properties, check whether the first slot 3772 // in the descriptor array matches the hidden symbol. Since the 3773 // hidden symbols hash code is zero (and no other string has hash 3774 // code zero) it will always occupy the first entry if present. 3775 DescriptorArray* descriptors = this->map()->instance_descriptors(); 3776 if ((descriptors->number_of_descriptors() > 0) && 3777 (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) { 3778 if (descriptors->GetType(0) == FIELD) { 3779 this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary); 3780 return this; 3781 } else { 3782 ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR || 3783 descriptors->GetType(0) == MAP_TRANSITION); 3784 } 3785 } 3786 } 3787 MaybeObject* store_result = 3788 SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), 3789 dictionary, 3790 DONT_ENUM, 3791 kNonStrictMode); 3792 if (store_result->IsFailure()) return store_result; 3793 return this; 3794} 3795 3796 3797MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, 3798 DeleteMode mode) { 3799 // Check local property, ignore interceptor. 3800 LookupResult result(GetIsolate()); 3801 LocalLookupRealNamedProperty(name, &result); 3802 if (!result.IsProperty()) return GetHeap()->true_value(); 3803 3804 // Normalize object if needed. 3805 Object* obj; 3806 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 3807 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 3808 } 3809 3810 return DeleteNormalizedProperty(name, mode); 3811} 3812 3813 3814MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) { 3815 Isolate* isolate = GetIsolate(); 3816 HandleScope scope(isolate); 3817 Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); 3818 Handle<String> name_handle(name); 3819 Handle<JSObject> this_handle(this); 3820 if (!interceptor->deleter()->IsUndefined()) { 3821 v8::NamedPropertyDeleter deleter = 3822 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter()); 3823 LOG(isolate, 3824 ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name)); 3825 CustomArguments args(isolate, interceptor->data(), this, this); 3826 v8::AccessorInfo info(args.end()); 3827 v8::Handle<v8::Boolean> result; 3828 { 3829 // Leaving JavaScript. 3830 VMState state(isolate, EXTERNAL); 3831 result = deleter(v8::Utils::ToLocal(name_handle), info); 3832 } 3833 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 3834 if (!result.IsEmpty()) { 3835 ASSERT(result->IsBoolean()); 3836 return *v8::Utils::OpenHandle(*result); 3837 } 3838 } 3839 MaybeObject* raw_result = 3840 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION); 3841 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 3842 return raw_result; 3843} 3844 3845 3846MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) { 3847 Isolate* isolate = GetIsolate(); 3848 Heap* heap = isolate->heap(); 3849 // Make sure that the top context does not change when doing 3850 // callbacks or interceptor calls. 3851 AssertNoContextChange ncc; 3852 HandleScope scope(isolate); 3853 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 3854 if (interceptor->deleter()->IsUndefined()) return heap->false_value(); 3855 v8::IndexedPropertyDeleter deleter = 3856 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter()); 3857 Handle<JSObject> this_handle(this); 3858 LOG(isolate, 3859 ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index)); 3860 CustomArguments args(isolate, interceptor->data(), this, this); 3861 v8::AccessorInfo info(args.end()); 3862 v8::Handle<v8::Boolean> result; 3863 { 3864 // Leaving JavaScript. 3865 VMState state(isolate, EXTERNAL); 3866 result = deleter(index, info); 3867 } 3868 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 3869 if (!result.IsEmpty()) { 3870 ASSERT(result->IsBoolean()); 3871 return *v8::Utils::OpenHandle(*result); 3872 } 3873 MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete( 3874 *this_handle, 3875 index, 3876 NORMAL_DELETION); 3877 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 3878 return raw_result; 3879} 3880 3881 3882Handle<Object> JSObject::DeleteElement(Handle<JSObject> obj, 3883 uint32_t index) { 3884 CALL_HEAP_FUNCTION(obj->GetIsolate(), 3885 obj->DeleteElement(index, JSObject::NORMAL_DELETION), 3886 Object); 3887} 3888 3889 3890MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { 3891 Isolate* isolate = GetIsolate(); 3892 // Check access rights if needed. 3893 if (IsAccessCheckNeeded() && 3894 !isolate->MayIndexedAccess(this, index, v8::ACCESS_DELETE)) { 3895 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE); 3896 return isolate->heap()->false_value(); 3897 } 3898 3899 if (IsJSGlobalProxy()) { 3900 Object* proto = GetPrototype(); 3901 if (proto->IsNull()) return isolate->heap()->false_value(); 3902 ASSERT(proto->IsJSGlobalObject()); 3903 return JSGlobalObject::cast(proto)->DeleteElement(index, mode); 3904 } 3905 3906 if (HasIndexedInterceptor()) { 3907 // Skip interceptor if forcing deletion. 3908 if (mode != FORCE_DELETION) { 3909 return DeleteElementWithInterceptor(index); 3910 } 3911 mode = JSReceiver::FORCE_DELETION; 3912 } 3913 3914 return GetElementsAccessor()->Delete(this, index, mode); 3915} 3916 3917 3918Handle<Object> JSObject::DeleteProperty(Handle<JSObject> obj, 3919 Handle<String> prop) { 3920 CALL_HEAP_FUNCTION(obj->GetIsolate(), 3921 obj->DeleteProperty(*prop, JSObject::NORMAL_DELETION), 3922 Object); 3923} 3924 3925 3926MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { 3927 Isolate* isolate = GetIsolate(); 3928 // ECMA-262, 3rd, 8.6.2.5 3929 ASSERT(name->IsString()); 3930 3931 // Check access rights if needed. 3932 if (IsAccessCheckNeeded() && 3933 !isolate->MayNamedAccess(this, name, v8::ACCESS_DELETE)) { 3934 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE); 3935 return isolate->heap()->false_value(); 3936 } 3937 3938 if (IsJSGlobalProxy()) { 3939 Object* proto = GetPrototype(); 3940 if (proto->IsNull()) return isolate->heap()->false_value(); 3941 ASSERT(proto->IsJSGlobalObject()); 3942 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode); 3943 } 3944 3945 uint32_t index = 0; 3946 if (name->AsArrayIndex(&index)) { 3947 return DeleteElement(index, mode); 3948 } else { 3949 LookupResult result(isolate); 3950 LocalLookup(name, &result); 3951 if (!result.IsProperty()) return isolate->heap()->true_value(); 3952 // Ignore attributes if forcing a deletion. 3953 if (result.IsDontDelete() && mode != FORCE_DELETION) { 3954 if (mode == STRICT_DELETION) { 3955 // Deleting a non-configurable property in strict mode. 3956 HandleScope scope(isolate); 3957 Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) }; 3958 return isolate->Throw(*isolate->factory()->NewTypeError( 3959 "strict_delete_property", HandleVector(args, 2))); 3960 } 3961 return isolate->heap()->false_value(); 3962 } 3963 // Check for interceptor. 3964 if (result.type() == INTERCEPTOR) { 3965 // Skip interceptor if forcing a deletion. 3966 if (mode == FORCE_DELETION) { 3967 return DeletePropertyPostInterceptor(name, mode); 3968 } 3969 return DeletePropertyWithInterceptor(name); 3970 } 3971 // Normalize object if needed. 3972 Object* obj; 3973 { MaybeObject* maybe_obj = 3974 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 3975 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 3976 } 3977 // Make sure the properties are normalized before removing the entry. 3978 return DeleteNormalizedProperty(name, mode); 3979 } 3980} 3981 3982 3983MaybeObject* JSReceiver::DeleteElement(uint32_t index, DeleteMode mode) { 3984 if (IsJSProxy()) { 3985 return JSProxy::cast(this)->DeleteElementWithHandler(index, mode); 3986 } 3987 return JSObject::cast(this)->DeleteElement(index, mode); 3988} 3989 3990 3991MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { 3992 if (IsJSProxy()) { 3993 return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode); 3994 } 3995 return JSObject::cast(this)->DeleteProperty(name, mode); 3996} 3997 3998 3999bool JSObject::ReferencesObjectFromElements(FixedArray* elements, 4000 ElementsKind kind, 4001 Object* object) { 4002 ASSERT(kind == FAST_ELEMENTS || 4003 kind == DICTIONARY_ELEMENTS); 4004 if (kind == FAST_ELEMENTS) { 4005 int length = IsJSArray() 4006 ? Smi::cast(JSArray::cast(this)->length())->value() 4007 : elements->length(); 4008 for (int i = 0; i < length; ++i) { 4009 Object* element = elements->get(i); 4010 if (!element->IsTheHole() && element == object) return true; 4011 } 4012 } else { 4013 Object* key = 4014 SeededNumberDictionary::cast(elements)->SlowReverseLookup(object); 4015 if (!key->IsUndefined()) return true; 4016 } 4017 return false; 4018} 4019 4020 4021// Check whether this object references another object. 4022bool JSObject::ReferencesObject(Object* obj) { 4023 Map* map_of_this = map(); 4024 Heap* heap = GetHeap(); 4025 AssertNoAllocation no_alloc; 4026 4027 // Is the object the constructor for this object? 4028 if (map_of_this->constructor() == obj) { 4029 return true; 4030 } 4031 4032 // Is the object the prototype for this object? 4033 if (map_of_this->prototype() == obj) { 4034 return true; 4035 } 4036 4037 // Check if the object is among the named properties. 4038 Object* key = SlowReverseLookup(obj); 4039 if (!key->IsUndefined()) { 4040 return true; 4041 } 4042 4043 // Check if the object is among the indexed properties. 4044 ElementsKind kind = GetElementsKind(); 4045 switch (kind) { 4046 case EXTERNAL_PIXEL_ELEMENTS: 4047 case EXTERNAL_BYTE_ELEMENTS: 4048 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 4049 case EXTERNAL_SHORT_ELEMENTS: 4050 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 4051 case EXTERNAL_INT_ELEMENTS: 4052 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 4053 case EXTERNAL_FLOAT_ELEMENTS: 4054 case EXTERNAL_DOUBLE_ELEMENTS: 4055 case FAST_DOUBLE_ELEMENTS: 4056 // Raw pixels and external arrays do not reference other 4057 // objects. 4058 break; 4059 case FAST_SMI_ONLY_ELEMENTS: 4060 break; 4061 case FAST_ELEMENTS: 4062 case DICTIONARY_ELEMENTS: { 4063 FixedArray* elements = FixedArray::cast(this->elements()); 4064 if (ReferencesObjectFromElements(elements, kind, obj)) return true; 4065 break; 4066 } 4067 case NON_STRICT_ARGUMENTS_ELEMENTS: { 4068 FixedArray* parameter_map = FixedArray::cast(elements()); 4069 // Check the mapped parameters. 4070 int length = parameter_map->length(); 4071 for (int i = 2; i < length; ++i) { 4072 Object* value = parameter_map->get(i); 4073 if (!value->IsTheHole() && value == obj) return true; 4074 } 4075 // Check the arguments. 4076 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 4077 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS; 4078 if (ReferencesObjectFromElements(arguments, kind, obj)) return true; 4079 break; 4080 } 4081 } 4082 4083 // For functions check the context. 4084 if (IsJSFunction()) { 4085 // Get the constructor function for arguments array. 4086 JSObject* arguments_boilerplate = 4087 heap->isolate()->context()->global_context()-> 4088 arguments_boilerplate(); 4089 JSFunction* arguments_function = 4090 JSFunction::cast(arguments_boilerplate->map()->constructor()); 4091 4092 // Get the context and don't check if it is the global context. 4093 JSFunction* f = JSFunction::cast(this); 4094 Context* context = f->context(); 4095 if (context->IsGlobalContext()) { 4096 return false; 4097 } 4098 4099 // Check the non-special context slots. 4100 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) { 4101 // Only check JS objects. 4102 if (context->get(i)->IsJSObject()) { 4103 JSObject* ctxobj = JSObject::cast(context->get(i)); 4104 // If it is an arguments array check the content. 4105 if (ctxobj->map()->constructor() == arguments_function) { 4106 if (ctxobj->ReferencesObject(obj)) { 4107 return true; 4108 } 4109 } else if (ctxobj == obj) { 4110 return true; 4111 } 4112 } 4113 } 4114 4115 // Check the context extension (if any) if it can have references. 4116 if (context->has_extension() && !context->IsCatchContext()) { 4117 return JSObject::cast(context->extension())->ReferencesObject(obj); 4118 } 4119 } 4120 4121 // No references to object. 4122 return false; 4123} 4124 4125 4126Handle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { 4127 CALL_HEAP_FUNCTION(object->GetIsolate(), object->PreventExtensions(), Object); 4128} 4129 4130 4131MaybeObject* JSObject::PreventExtensions() { 4132 Isolate* isolate = GetIsolate(); 4133 if (IsAccessCheckNeeded() && 4134 !isolate->MayNamedAccess(this, 4135 isolate->heap()->undefined_value(), 4136 v8::ACCESS_KEYS)) { 4137 isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS); 4138 return isolate->heap()->false_value(); 4139 } 4140 4141 if (IsJSGlobalProxy()) { 4142 Object* proto = GetPrototype(); 4143 if (proto->IsNull()) return this; 4144 ASSERT(proto->IsJSGlobalObject()); 4145 return JSObject::cast(proto)->PreventExtensions(); 4146 } 4147 4148 // It's not possible to seal objects with external array elements 4149 if (HasExternalArrayElements()) { 4150 HandleScope scope(isolate); 4151 Handle<Object> object(this); 4152 Handle<Object> error = 4153 isolate->factory()->NewTypeError( 4154 "cant_prevent_ext_external_array_elements", 4155 HandleVector(&object, 1)); 4156 return isolate->Throw(*error); 4157 } 4158 4159 // If there are fast elements we normalize. 4160 SeededNumberDictionary* dictionary = NULL; 4161 { MaybeObject* maybe = NormalizeElements(); 4162 if (!maybe->To<SeededNumberDictionary>(&dictionary)) return maybe; 4163 } 4164 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 4165 // Make sure that we never go back to fast case. 4166 dictionary->set_requires_slow_elements(); 4167 4168 // Do a map transition, other objects with this map may still 4169 // be extensible. 4170 Map* new_map; 4171 { MaybeObject* maybe = map()->CopyDropTransitions(); 4172 if (!maybe->To<Map>(&new_map)) return maybe; 4173 } 4174 new_map->set_is_extensible(false); 4175 set_map(new_map); 4176 ASSERT(!map()->is_extensible()); 4177 return new_map; 4178} 4179 4180 4181// Tests for the fast common case for property enumeration: 4182// - This object and all prototypes has an enum cache (which means that 4183// it is no proxy, has no interceptors and needs no access checks). 4184// - This object has no elements. 4185// - No prototype has enumerable properties/elements. 4186bool JSReceiver::IsSimpleEnum() { 4187 Heap* heap = GetHeap(); 4188 for (Object* o = this; 4189 o != heap->null_value(); 4190 o = JSObject::cast(o)->GetPrototype()) { 4191 if (!o->IsJSObject()) return false; 4192 JSObject* curr = JSObject::cast(o); 4193 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false; 4194 ASSERT(!curr->HasNamedInterceptor()); 4195 ASSERT(!curr->HasIndexedInterceptor()); 4196 ASSERT(!curr->IsAccessCheckNeeded()); 4197 if (curr->NumberOfEnumElements() > 0) return false; 4198 if (curr != this) { 4199 FixedArray* curr_fixed_array = 4200 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache()); 4201 if (curr_fixed_array->length() > 0) return false; 4202 } 4203 } 4204 return true; 4205} 4206 4207 4208int Map::NumberOfDescribedProperties(PropertyAttributes filter) { 4209 int result = 0; 4210 DescriptorArray* descs = instance_descriptors(); 4211 for (int i = 0; i < descs->number_of_descriptors(); i++) { 4212 PropertyDetails details(descs->GetDetails(i)); 4213 if (descs->IsProperty(i) && (details.attributes() & filter) == 0) { 4214 result++; 4215 } 4216 } 4217 return result; 4218} 4219 4220 4221int Map::PropertyIndexFor(String* name) { 4222 DescriptorArray* descs = instance_descriptors(); 4223 for (int i = 0; i < descs->number_of_descriptors(); i++) { 4224 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) { 4225 return descs->GetFieldIndex(i); 4226 } 4227 } 4228 return -1; 4229} 4230 4231 4232int Map::NextFreePropertyIndex() { 4233 int max_index = -1; 4234 DescriptorArray* descs = instance_descriptors(); 4235 for (int i = 0; i < descs->number_of_descriptors(); i++) { 4236 if (descs->GetType(i) == FIELD) { 4237 int current_index = descs->GetFieldIndex(i); 4238 if (current_index > max_index) max_index = current_index; 4239 } 4240 } 4241 return max_index + 1; 4242} 4243 4244 4245AccessorDescriptor* Map::FindAccessor(String* name) { 4246 DescriptorArray* descs = instance_descriptors(); 4247 for (int i = 0; i < descs->number_of_descriptors(); i++) { 4248 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) { 4249 return descs->GetCallbacks(i); 4250 } 4251 } 4252 return NULL; 4253} 4254 4255 4256void JSReceiver::LocalLookup(String* name, LookupResult* result) { 4257 ASSERT(name->IsString()); 4258 4259 Heap* heap = GetHeap(); 4260 4261 if (IsJSGlobalProxy()) { 4262 Object* proto = GetPrototype(); 4263 if (proto->IsNull()) return result->NotFound(); 4264 ASSERT(proto->IsJSGlobalObject()); 4265 return JSReceiver::cast(proto)->LocalLookup(name, result); 4266 } 4267 4268 if (IsJSProxy()) { 4269 result->HandlerResult(JSProxy::cast(this)); 4270 return; 4271 } 4272 4273 // Do not use inline caching if the object is a non-global object 4274 // that requires access checks. 4275 if (IsAccessCheckNeeded()) { 4276 result->DisallowCaching(); 4277 } 4278 4279 JSObject* js_object = JSObject::cast(this); 4280 4281 // Check __proto__ before interceptor. 4282 if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) { 4283 result->ConstantResult(js_object); 4284 return; 4285 } 4286 4287 // Check for lookup interceptor except when bootstrapping. 4288 if (js_object->HasNamedInterceptor() && 4289 !heap->isolate()->bootstrapper()->IsActive()) { 4290 result->InterceptorResult(js_object); 4291 return; 4292 } 4293 4294 js_object->LocalLookupRealNamedProperty(name, result); 4295} 4296 4297 4298void JSReceiver::Lookup(String* name, LookupResult* result) { 4299 // Ecma-262 3rd 8.6.2.4 4300 Heap* heap = GetHeap(); 4301 for (Object* current = this; 4302 current != heap->null_value(); 4303 current = JSObject::cast(current)->GetPrototype()) { 4304 JSReceiver::cast(current)->LocalLookup(name, result); 4305 if (result->IsProperty()) return; 4306 } 4307 result->NotFound(); 4308} 4309 4310 4311// Search object and it's prototype chain for callback properties. 4312void JSObject::LookupCallback(String* name, LookupResult* result) { 4313 Heap* heap = GetHeap(); 4314 for (Object* current = this; 4315 current != heap->null_value() && current->IsJSObject(); 4316 current = JSObject::cast(current)->GetPrototype()) { 4317 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result); 4318 if (result->IsFound() && result->type() == CALLBACKS) return; 4319 } 4320 result->NotFound(); 4321} 4322 4323 4324// Try to update an accessor in an elements dictionary. Return true if the 4325// update succeeded, and false otherwise. 4326static bool UpdateGetterSetterInDictionary( 4327 SeededNumberDictionary* dictionary, 4328 uint32_t index, 4329 Object* getter, 4330 Object* setter, 4331 PropertyAttributes attributes) { 4332 int entry = dictionary->FindEntry(index); 4333 if (entry != SeededNumberDictionary::kNotFound) { 4334 Object* result = dictionary->ValueAt(entry); 4335 PropertyDetails details = dictionary->DetailsAt(entry); 4336 if (details.type() == CALLBACKS && result->IsAccessorPair()) { 4337 ASSERT(!details.IsDontDelete()); 4338 if (details.attributes() != attributes) { 4339 dictionary->DetailsAtPut(entry, 4340 PropertyDetails(attributes, CALLBACKS, index)); 4341 } 4342 AccessorPair::cast(result)->SetComponents(getter, setter); 4343 return true; 4344 } 4345 } 4346 return false; 4347} 4348 4349 4350MaybeObject* JSObject::DefineElementAccessor(uint32_t index, 4351 Object* getter, 4352 Object* setter, 4353 PropertyAttributes attributes) { 4354 switch (GetElementsKind()) { 4355 case FAST_SMI_ONLY_ELEMENTS: 4356 case FAST_ELEMENTS: 4357 case FAST_DOUBLE_ELEMENTS: 4358 break; 4359 case EXTERNAL_PIXEL_ELEMENTS: 4360 case EXTERNAL_BYTE_ELEMENTS: 4361 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 4362 case EXTERNAL_SHORT_ELEMENTS: 4363 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 4364 case EXTERNAL_INT_ELEMENTS: 4365 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 4366 case EXTERNAL_FLOAT_ELEMENTS: 4367 case EXTERNAL_DOUBLE_ELEMENTS: 4368 // Ignore getters and setters on pixel and external array elements. 4369 return GetHeap()->undefined_value(); 4370 case DICTIONARY_ELEMENTS: 4371 if (UpdateGetterSetterInDictionary(element_dictionary(), 4372 index, 4373 getter, 4374 setter, 4375 attributes)) { 4376 return GetHeap()->undefined_value(); 4377 } 4378 break; 4379 case NON_STRICT_ARGUMENTS_ELEMENTS: { 4380 // Ascertain whether we have read-only properties or an existing 4381 // getter/setter pair in an arguments elements dictionary backing 4382 // store. 4383 FixedArray* parameter_map = FixedArray::cast(elements()); 4384 uint32_t length = parameter_map->length(); 4385 Object* probe = 4386 index < (length - 2) ? parameter_map->get(index + 2) : NULL; 4387 if (probe == NULL || probe->IsTheHole()) { 4388 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 4389 if (arguments->IsDictionary()) { 4390 SeededNumberDictionary* dictionary = 4391 SeededNumberDictionary::cast(arguments); 4392 if (UpdateGetterSetterInDictionary(dictionary, 4393 index, 4394 getter, 4395 setter, 4396 attributes)) { 4397 return GetHeap()->undefined_value(); 4398 } 4399 } 4400 } 4401 break; 4402 } 4403 } 4404 4405 AccessorPair* accessors; 4406 { MaybeObject* maybe_accessors = GetHeap()->AllocateAccessorPair(); 4407 if (!maybe_accessors->To(&accessors)) return maybe_accessors; 4408 } 4409 accessors->SetComponents(getter, setter); 4410 4411 return SetElementCallback(index, accessors, attributes); 4412} 4413 4414 4415MaybeObject* JSObject::DefinePropertyAccessor(String* name, 4416 Object* getter, 4417 Object* setter, 4418 PropertyAttributes attributes) { 4419 // Lookup the name. 4420 LookupResult result(GetHeap()->isolate()); 4421 LocalLookupRealNamedProperty(name, &result); 4422 if (result.IsFound()) { 4423 if (result.type() == CALLBACKS) { 4424 ASSERT(!result.IsDontDelete()); 4425 Object* obj = result.GetCallbackObject(); 4426 // Need to preserve old getters/setters. 4427 if (obj->IsAccessorPair()) { 4428 AccessorPair* copy; 4429 { MaybeObject* maybe_copy = 4430 AccessorPair::cast(obj)->CopyWithoutTransitions(); 4431 if (!maybe_copy->To(©)) return maybe_copy; 4432 } 4433 copy->SetComponents(getter, setter); 4434 // Use set to update attributes. 4435 return SetPropertyCallback(name, copy, attributes); 4436 } 4437 } 4438 } 4439 4440 AccessorPair* accessors; 4441 { MaybeObject* maybe_accessors = GetHeap()->AllocateAccessorPair(); 4442 if (!maybe_accessors->To(&accessors)) return maybe_accessors; 4443 } 4444 accessors->SetComponents(getter, setter); 4445 4446 return SetPropertyCallback(name, accessors, attributes); 4447} 4448 4449 4450bool JSObject::CanSetCallback(String* name) { 4451 ASSERT(!IsAccessCheckNeeded() || 4452 GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET)); 4453 4454 // Check if there is an API defined callback object which prohibits 4455 // callback overwriting in this object or it's prototype chain. 4456 // This mechanism is needed for instance in a browser setting, where 4457 // certain accessors such as window.location should not be allowed 4458 // to be overwritten because allowing overwriting could potentially 4459 // cause security problems. 4460 LookupResult callback_result(GetIsolate()); 4461 LookupCallback(name, &callback_result); 4462 if (callback_result.IsProperty()) { 4463 Object* obj = callback_result.GetCallbackObject(); 4464 if (obj->IsAccessorInfo() && 4465 AccessorInfo::cast(obj)->prohibits_overwriting()) { 4466 return false; 4467 } 4468 } 4469 4470 return true; 4471} 4472 4473 4474MaybeObject* JSObject::SetElementCallback(uint32_t index, 4475 Object* structure, 4476 PropertyAttributes attributes) { 4477 PropertyDetails details = PropertyDetails(attributes, CALLBACKS); 4478 4479 // Normalize elements to make this operation simple. 4480 SeededNumberDictionary* dictionary; 4481 { MaybeObject* maybe_dictionary = NormalizeElements(); 4482 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4483 } 4484 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 4485 4486 // Update the dictionary with the new CALLBACKS property. 4487 { MaybeObject* maybe_dictionary = dictionary->Set(index, structure, details); 4488 if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; 4489 } 4490 4491 dictionary->set_requires_slow_elements(); 4492 // Update the dictionary backing store on the object. 4493 if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) { 4494 // Also delete any parameter alias. 4495 // 4496 // TODO(kmillikin): when deleting the last parameter alias we could 4497 // switch to a direct backing store without the parameter map. This 4498 // would allow GC of the context. 4499 FixedArray* parameter_map = FixedArray::cast(elements()); 4500 if (index < static_cast<uint32_t>(parameter_map->length()) - 2) { 4501 parameter_map->set(index + 2, GetHeap()->the_hole_value()); 4502 } 4503 parameter_map->set(1, dictionary); 4504 } else { 4505 set_elements(dictionary); 4506 } 4507 4508 return GetHeap()->undefined_value(); 4509} 4510 4511 4512MaybeObject* JSObject::SetPropertyCallback(String* name, 4513 Object* structure, 4514 PropertyAttributes attributes) { 4515 // Normalize object to make this operation simple. 4516 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); 4517 if (maybe_ok->IsFailure()) return maybe_ok; 4518 } 4519 4520 // For the global object allocate a new map to invalidate the global inline 4521 // caches which have a global property cell reference directly in the code. 4522 if (IsGlobalObject()) { 4523 Map* new_map; 4524 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors(); 4525 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 4526 } 4527 set_map(new_map); 4528 // When running crankshaft, changing the map is not enough. We 4529 // need to deoptimize all functions that rely on this global 4530 // object. 4531 Deoptimizer::DeoptimizeGlobalObject(this); 4532 } 4533 4534 // Update the dictionary with the new CALLBACKS property. 4535 PropertyDetails details = PropertyDetails(attributes, CALLBACKS); 4536 { MaybeObject* maybe_ok = SetNormalizedProperty(name, structure, details); 4537 if (maybe_ok->IsFailure()) return maybe_ok; 4538 } 4539 4540 return GetHeap()->undefined_value(); 4541} 4542 4543 4544void JSObject::DefineAccessor(Handle<JSObject> object, 4545 Handle<String> name, 4546 Handle<Object> getter, 4547 Handle<Object> setter, 4548 PropertyAttributes attributes) { 4549 CALL_HEAP_FUNCTION_VOID( 4550 object->GetIsolate(), 4551 object->DefineAccessor(*name, *getter, *setter, attributes)); 4552} 4553 4554MaybeObject* JSObject::DefineAccessor(String* name, 4555 Object* getter, 4556 Object* setter, 4557 PropertyAttributes attributes) { 4558 Isolate* isolate = GetIsolate(); 4559 // Check access rights if needed. 4560 if (IsAccessCheckNeeded() && 4561 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { 4562 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); 4563 return isolate->heap()->undefined_value(); 4564 } 4565 4566 if (IsJSGlobalProxy()) { 4567 Object* proto = GetPrototype(); 4568 if (proto->IsNull()) return this; 4569 ASSERT(proto->IsJSGlobalObject()); 4570 return JSObject::cast(proto)->DefineAccessor( 4571 name, getter, setter, attributes); 4572 } 4573 4574 // Make sure that the top context does not change when doing callbacks or 4575 // interceptor calls. 4576 AssertNoContextChange ncc; 4577 4578 // Try to flatten before operating on the string. 4579 name->TryFlatten(); 4580 4581 if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); 4582 4583 uint32_t index = 0; 4584 return name->AsArrayIndex(&index) ? 4585 DefineElementAccessor(index, getter, setter, attributes) : 4586 DefinePropertyAccessor(name, getter, setter, attributes); 4587} 4588 4589 4590MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { 4591 Isolate* isolate = GetIsolate(); 4592 String* name = String::cast(info->name()); 4593 // Check access rights if needed. 4594 if (IsAccessCheckNeeded() && 4595 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { 4596 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); 4597 return isolate->heap()->undefined_value(); 4598 } 4599 4600 if (IsJSGlobalProxy()) { 4601 Object* proto = GetPrototype(); 4602 if (proto->IsNull()) return this; 4603 ASSERT(proto->IsJSGlobalObject()); 4604 return JSObject::cast(proto)->DefineAccessor(info); 4605 } 4606 4607 // Make sure that the top context does not change when doing callbacks or 4608 // interceptor calls. 4609 AssertNoContextChange ncc; 4610 4611 // Try to flatten before operating on the string. 4612 name->TryFlatten(); 4613 4614 if (!CanSetCallback(name)) { 4615 return isolate->heap()->undefined_value(); 4616 } 4617 4618 uint32_t index = 0; 4619 bool is_element = name->AsArrayIndex(&index); 4620 4621 if (is_element) { 4622 if (IsJSArray()) return isolate->heap()->undefined_value(); 4623 4624 // Accessors overwrite previous callbacks (cf. with getters/setters). 4625 switch (GetElementsKind()) { 4626 case FAST_SMI_ONLY_ELEMENTS: 4627 case FAST_ELEMENTS: 4628 case FAST_DOUBLE_ELEMENTS: 4629 break; 4630 case EXTERNAL_PIXEL_ELEMENTS: 4631 case EXTERNAL_BYTE_ELEMENTS: 4632 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 4633 case EXTERNAL_SHORT_ELEMENTS: 4634 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 4635 case EXTERNAL_INT_ELEMENTS: 4636 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 4637 case EXTERNAL_FLOAT_ELEMENTS: 4638 case EXTERNAL_DOUBLE_ELEMENTS: 4639 // Ignore getters and setters on pixel and external array 4640 // elements. 4641 return isolate->heap()->undefined_value(); 4642 case DICTIONARY_ELEMENTS: 4643 break; 4644 case NON_STRICT_ARGUMENTS_ELEMENTS: 4645 UNIMPLEMENTED(); 4646 break; 4647 } 4648 4649 { MaybeObject* maybe_ok = 4650 SetElementCallback(index, info, info->property_attributes()); 4651 if (maybe_ok->IsFailure()) return maybe_ok; 4652 } 4653 } else { 4654 // Lookup the name. 4655 LookupResult result(isolate); 4656 LocalLookup(name, &result); 4657 // ES5 forbids turning a property into an accessor if it's not 4658 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5). 4659 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) { 4660 return isolate->heap()->undefined_value(); 4661 } 4662 { MaybeObject* maybe_ok = 4663 SetPropertyCallback(name, info, info->property_attributes()); 4664 if (maybe_ok->IsFailure()) return maybe_ok; 4665 } 4666 } 4667 4668 return this; 4669} 4670 4671 4672Object* JSObject::LookupAccessor(String* name, AccessorComponent component) { 4673 Heap* heap = GetHeap(); 4674 4675 // Make sure that the top context does not change when doing callbacks or 4676 // interceptor calls. 4677 AssertNoContextChange ncc; 4678 4679 // Check access rights if needed. 4680 if (IsAccessCheckNeeded() && 4681 !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) { 4682 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 4683 return heap->undefined_value(); 4684 } 4685 4686 // Make the lookup and include prototypes. 4687 uint32_t index = 0; 4688 if (name->AsArrayIndex(&index)) { 4689 for (Object* obj = this; 4690 obj != heap->null_value(); 4691 obj = JSObject::cast(obj)->GetPrototype()) { 4692 JSObject* js_object = JSObject::cast(obj); 4693 if (js_object->HasDictionaryElements()) { 4694 SeededNumberDictionary* dictionary = js_object->element_dictionary(); 4695 int entry = dictionary->FindEntry(index); 4696 if (entry != SeededNumberDictionary::kNotFound) { 4697 Object* element = dictionary->ValueAt(entry); 4698 if (dictionary->DetailsAt(entry).type() == CALLBACKS && 4699 element->IsAccessorPair()) { 4700 return AccessorPair::cast(element)->GetComponent(component); 4701 } 4702 } 4703 } 4704 } 4705 } else { 4706 for (Object* obj = this; 4707 obj != heap->null_value(); 4708 obj = JSObject::cast(obj)->GetPrototype()) { 4709 LookupResult result(heap->isolate()); 4710 JSObject::cast(obj)->LocalLookup(name, &result); 4711 if (result.IsProperty()) { 4712 if (result.IsReadOnly()) return heap->undefined_value(); 4713 if (result.type() == CALLBACKS) { 4714 Object* obj = result.GetCallbackObject(); 4715 if (obj->IsAccessorPair()) { 4716 return AccessorPair::cast(obj)->GetComponent(component); 4717 } 4718 } 4719 } 4720 } 4721 } 4722 return heap->undefined_value(); 4723} 4724 4725 4726Object* JSObject::SlowReverseLookup(Object* value) { 4727 if (HasFastProperties()) { 4728 DescriptorArray* descs = map()->instance_descriptors(); 4729 for (int i = 0; i < descs->number_of_descriptors(); i++) { 4730 if (descs->GetType(i) == FIELD) { 4731 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) { 4732 return descs->GetKey(i); 4733 } 4734 } else if (descs->GetType(i) == CONSTANT_FUNCTION) { 4735 if (descs->GetConstantFunction(i) == value) { 4736 return descs->GetKey(i); 4737 } 4738 } 4739 } 4740 return GetHeap()->undefined_value(); 4741 } else { 4742 return property_dictionary()->SlowReverseLookup(value); 4743 } 4744} 4745 4746 4747MaybeObject* Map::CopyDropDescriptors() { 4748 Heap* heap = GetHeap(); 4749 Object* result; 4750 { MaybeObject* maybe_result = 4751 heap->AllocateMap(instance_type(), instance_size()); 4752 if (!maybe_result->ToObject(&result)) return maybe_result; 4753 } 4754 Map::cast(result)->set_prototype(prototype()); 4755 Map::cast(result)->set_constructor(constructor()); 4756 // Don't copy descriptors, so map transitions always remain a forest. 4757 // If we retained the same descriptors we would have two maps 4758 // pointing to the same transition which is bad because the garbage 4759 // collector relies on being able to reverse pointers from transitions 4760 // to maps. If properties need to be retained use CopyDropTransitions. 4761 Map::cast(result)->clear_instance_descriptors(); 4762 // Please note instance_type and instance_size are set when allocated. 4763 Map::cast(result)->set_inobject_properties(inobject_properties()); 4764 Map::cast(result)->set_unused_property_fields(unused_property_fields()); 4765 4766 // If the map has pre-allocated properties always start out with a descriptor 4767 // array describing these properties. 4768 if (pre_allocated_property_fields() > 0) { 4769 ASSERT(constructor()->IsJSFunction()); 4770 JSFunction* ctor = JSFunction::cast(constructor()); 4771 Object* descriptors; 4772 { MaybeObject* maybe_descriptors = 4773 ctor->initial_map()->instance_descriptors()->RemoveTransitions(); 4774 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors; 4775 } 4776 Map::cast(result)->set_instance_descriptors( 4777 DescriptorArray::cast(descriptors)); 4778 Map::cast(result)->set_pre_allocated_property_fields( 4779 pre_allocated_property_fields()); 4780 } 4781 Map::cast(result)->set_bit_field(bit_field()); 4782 Map::cast(result)->set_bit_field2(bit_field2()); 4783 Map::cast(result)->set_bit_field3(bit_field3()); 4784 Map::cast(result)->set_is_shared(false); 4785 Map::cast(result)->ClearCodeCache(heap); 4786 return result; 4787} 4788 4789 4790MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode, 4791 NormalizedMapSharingMode sharing) { 4792 int new_instance_size = instance_size(); 4793 if (mode == CLEAR_INOBJECT_PROPERTIES) { 4794 new_instance_size -= inobject_properties() * kPointerSize; 4795 } 4796 4797 Object* result; 4798 { MaybeObject* maybe_result = 4799 GetHeap()->AllocateMap(instance_type(), new_instance_size); 4800 if (!maybe_result->ToObject(&result)) return maybe_result; 4801 } 4802 4803 if (mode != CLEAR_INOBJECT_PROPERTIES) { 4804 Map::cast(result)->set_inobject_properties(inobject_properties()); 4805 } 4806 4807 Map::cast(result)->set_prototype(prototype()); 4808 Map::cast(result)->set_constructor(constructor()); 4809 4810 Map::cast(result)->set_bit_field(bit_field()); 4811 Map::cast(result)->set_bit_field2(bit_field2()); 4812 Map::cast(result)->set_bit_field3(bit_field3()); 4813 4814 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP); 4815 4816#ifdef DEBUG 4817 if (FLAG_verify_heap && Map::cast(result)->is_shared()) { 4818 Map::cast(result)->SharedMapVerify(); 4819 } 4820#endif 4821 4822 return result; 4823} 4824 4825 4826MaybeObject* Map::CopyDropTransitions() { 4827 Object* new_map; 4828 { MaybeObject* maybe_new_map = CopyDropDescriptors(); 4829 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 4830 } 4831 Object* descriptors; 4832 { MaybeObject* maybe_descriptors = 4833 instance_descriptors()->RemoveTransitions(); 4834 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors; 4835 } 4836 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors)); 4837 return new_map; 4838} 4839 4840void Map::UpdateCodeCache(Handle<Map> map, 4841 Handle<String> name, 4842 Handle<Code> code) { 4843 Isolate* isolate = map->GetIsolate(); 4844 CALL_HEAP_FUNCTION_VOID(isolate, 4845 map->UpdateCodeCache(*name, *code)); 4846} 4847 4848MaybeObject* Map::UpdateCodeCache(String* name, Code* code) { 4849 // Allocate the code cache if not present. 4850 if (code_cache()->IsFixedArray()) { 4851 Object* result; 4852 { MaybeObject* maybe_result = GetHeap()->AllocateCodeCache(); 4853 if (!maybe_result->ToObject(&result)) return maybe_result; 4854 } 4855 set_code_cache(result); 4856 } 4857 4858 // Update the code cache. 4859 return CodeCache::cast(code_cache())->Update(name, code); 4860} 4861 4862 4863Object* Map::FindInCodeCache(String* name, Code::Flags flags) { 4864 // Do a lookup if a code cache exists. 4865 if (!code_cache()->IsFixedArray()) { 4866 return CodeCache::cast(code_cache())->Lookup(name, flags); 4867 } else { 4868 return GetHeap()->undefined_value(); 4869 } 4870} 4871 4872 4873int Map::IndexInCodeCache(Object* name, Code* code) { 4874 // Get the internal index if a code cache exists. 4875 if (!code_cache()->IsFixedArray()) { 4876 return CodeCache::cast(code_cache())->GetIndex(name, code); 4877 } 4878 return -1; 4879} 4880 4881 4882void Map::RemoveFromCodeCache(String* name, Code* code, int index) { 4883 // No GC is supposed to happen between a call to IndexInCodeCache and 4884 // RemoveFromCodeCache so the code cache must be there. 4885 ASSERT(!code_cache()->IsFixedArray()); 4886 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index); 4887} 4888 4889 4890// An iterator over all map transitions in an descriptor array, reusing the map 4891// field of the contens array while it is running. 4892class IntrusiveMapTransitionIterator { 4893 public: 4894 explicit IntrusiveMapTransitionIterator(DescriptorArray* descriptor_array) 4895 : descriptor_array_(descriptor_array) { } 4896 4897 void Start() { 4898 ASSERT(!IsIterating()); 4899 if (HasContentArray()) *ContentHeader() = Smi::FromInt(0); 4900 } 4901 4902 bool IsIterating() { 4903 return HasContentArray() && (*ContentHeader())->IsSmi(); 4904 } 4905 4906 Map* Next() { 4907 ASSERT(IsIterating()); 4908 FixedArray* contents = ContentArray(); 4909 // Attention, tricky index manipulation ahead: Every entry in the contents 4910 // array consists of a value/details pair, so the index is typically even. 4911 // An exception is made for CALLBACKS entries: An even index means we look 4912 // at its getter, and an odd index means we look at its setter. 4913 int index = Smi::cast(*ContentHeader())->value(); 4914 while (index < contents->length()) { 4915 PropertyDetails details(Smi::cast(contents->get(index | 1))); 4916 switch (details.type()) { 4917 case MAP_TRANSITION: 4918 case CONSTANT_TRANSITION: 4919 case ELEMENTS_TRANSITION: 4920 // We definitely have a map transition. 4921 *ContentHeader() = Smi::FromInt(index + 2); 4922 return static_cast<Map*>(contents->get(index)); 4923 case CALLBACKS: { 4924 // We might have a map transition in a getter or in a setter. 4925 AccessorPair* accessors = 4926 static_cast<AccessorPair*>(contents->get(index & ~1)); 4927 Object* accessor = 4928 ((index & 1) == 0) ? accessors->getter() : accessors->setter(); 4929 index++; 4930 if (accessor->IsMap()) { 4931 *ContentHeader() = Smi::FromInt(index); 4932 return static_cast<Map*>(accessor); 4933 } 4934 break; 4935 } 4936 case NORMAL: 4937 case FIELD: 4938 case CONSTANT_FUNCTION: 4939 case HANDLER: 4940 case INTERCEPTOR: 4941 case NULL_DESCRIPTOR: 4942 // We definitely have no map transition. 4943 index += 2; 4944 break; 4945 } 4946 } 4947 *ContentHeader() = descriptor_array_->GetHeap()->fixed_array_map(); 4948 return NULL; 4949 } 4950 4951 private: 4952 bool HasContentArray() { 4953 return descriptor_array_-> length() > DescriptorArray::kContentArrayIndex; 4954 } 4955 4956 FixedArray* ContentArray() { 4957 Object* array = descriptor_array_->get(DescriptorArray::kContentArrayIndex); 4958 return static_cast<FixedArray*>(array); 4959 } 4960 4961 Object** ContentHeader() { 4962 return HeapObject::RawField(ContentArray(), DescriptorArray::kMapOffset); 4963 } 4964 4965 DescriptorArray* descriptor_array_; 4966}; 4967 4968 4969// An iterator over all prototype transitions, reusing the map field of the 4970// underlying array while it is running. 4971class IntrusivePrototypeTransitionIterator { 4972 public: 4973 explicit IntrusivePrototypeTransitionIterator(FixedArray* proto_trans) 4974 : proto_trans_(proto_trans) { } 4975 4976 void Start() { 4977 ASSERT(!IsIterating()); 4978 if (HasTransitions()) *Header() = Smi::FromInt(0); 4979 } 4980 4981 bool IsIterating() { 4982 return HasTransitions() && (*Header())->IsSmi(); 4983 } 4984 4985 Map* Next() { 4986 ASSERT(IsIterating()); 4987 int transitionNumber = Smi::cast(*Header())->value(); 4988 if (transitionNumber < NumberOfTransitions()) { 4989 *Header() = Smi::FromInt(transitionNumber + 1); 4990 return GetTransition(transitionNumber); 4991 } 4992 *Header() = proto_trans_->GetHeap()->fixed_array_map(); 4993 return NULL; 4994 } 4995 4996 private: 4997 bool HasTransitions() { 4998 return proto_trans_->length() >= Map::kProtoTransitionHeaderSize; 4999 } 5000 5001 Object** Header() { 5002 return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset); 5003 } 5004 5005 int NumberOfTransitions() { 5006 Object* num = proto_trans_->get(Map::kProtoTransitionNumberOfEntriesOffset); 5007 return Smi::cast(num)->value(); 5008 } 5009 5010 Map* GetTransition(int transitionNumber) { 5011 return Map::cast(proto_trans_->get(IndexFor(transitionNumber))); 5012 } 5013 5014 int IndexFor(int transitionNumber) { 5015 return Map::kProtoTransitionHeaderSize + 5016 Map::kProtoTransitionMapOffset + 5017 transitionNumber * Map::kProtoTransitionElementsPerEntry; 5018 } 5019 5020 FixedArray* proto_trans_; 5021}; 5022 5023 5024// To traverse the transition tree iteratively, we have to store two kinds of 5025// information in a map: The parent map in the traversal and which children of a 5026// node have already been visited. To do this without additional memory, we 5027// temporarily reuse two maps with known values: 5028// 5029// (1) The map of the map temporarily holds the parent, and is restored to the 5030// meta map afterwards. 5031// 5032// (2) The info which children have already been visited depends on which part 5033// of the map we currently iterate: 5034// 5035// (a) If we currently follow normal map transitions, we temporarily store 5036// the current index in the map of the FixedArray of the desciptor 5037// array's contents, and restore it to the fixed array map afterwards. 5038// Note that a single descriptor can have 0, 1, or 2 transitions. 5039// 5040// (b) If we currently follow prototype transitions, we temporarily store 5041// the current index in the map of the FixedArray holding the prototype 5042// transitions, and restore it to the fixed array map afterwards. 5043// 5044// Note that the child iterator is just a concatenation of two iterators: One 5045// iterating over map transitions and one iterating over prototype transisitons. 5046class TraversableMap : public Map { 5047 public: 5048 // Record the parent in the traversal within this map. Note that this destroys 5049 // this map's map! 5050 void SetParent(TraversableMap* parent) { set_map_no_write_barrier(parent); } 5051 5052 // Reset the current map's map, returning the parent previously stored in it. 5053 TraversableMap* GetAndResetParent() { 5054 TraversableMap* old_parent = static_cast<TraversableMap*>(map()); 5055 set_map_no_write_barrier(GetHeap()->meta_map()); 5056 return old_parent; 5057 } 5058 5059 // Start iterating over this map's children, possibly destroying a FixedArray 5060 // map (see explanation above). 5061 void ChildIteratorStart() { 5062 IntrusiveMapTransitionIterator(instance_descriptors()).Start(); 5063 IntrusivePrototypeTransitionIterator( 5064 unchecked_prototype_transitions()).Start(); 5065 } 5066 5067 // If we have an unvisited child map, return that one and advance. If we have 5068 // none, return NULL and reset any destroyed FixedArray maps. 5069 TraversableMap* ChildIteratorNext() { 5070 IntrusiveMapTransitionIterator descriptor_iterator(instance_descriptors()); 5071 if (descriptor_iterator.IsIterating()) { 5072 Map* next = descriptor_iterator.Next(); 5073 if (next != NULL) return static_cast<TraversableMap*>(next); 5074 } 5075 IntrusivePrototypeTransitionIterator 5076 proto_iterator(unchecked_prototype_transitions()); 5077 if (proto_iterator.IsIterating()) { 5078 Map* next = proto_iterator.Next(); 5079 if (next != NULL) return static_cast<TraversableMap*>(next); 5080 } 5081 return NULL; 5082 } 5083}; 5084 5085 5086// Traverse the transition tree in postorder without using the C++ stack by 5087// doing pointer reversal. 5088void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { 5089 TraversableMap* current = static_cast<TraversableMap*>(this); 5090 current->ChildIteratorStart(); 5091 while (true) { 5092 TraversableMap* child = current->ChildIteratorNext(); 5093 if (child != NULL) { 5094 child->ChildIteratorStart(); 5095 child->SetParent(current); 5096 current = child; 5097 } else { 5098 TraversableMap* parent = current->GetAndResetParent(); 5099 callback(current, data); 5100 if (current == this) break; 5101 current = parent; 5102 } 5103 } 5104} 5105 5106 5107MaybeObject* CodeCache::Update(String* name, Code* code) { 5108 // The number of monomorphic stubs for normal load/store/call IC's can grow to 5109 // a large number and therefore they need to go into a hash table. They are 5110 // used to load global properties from cells. 5111 if (code->type() == NORMAL) { 5112 // Make sure that a hash table is allocated for the normal load code cache. 5113 if (normal_type_cache()->IsUndefined()) { 5114 Object* result; 5115 { MaybeObject* maybe_result = 5116 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize); 5117 if (!maybe_result->ToObject(&result)) return maybe_result; 5118 } 5119 set_normal_type_cache(result); 5120 } 5121 return UpdateNormalTypeCache(name, code); 5122 } else { 5123 ASSERT(default_cache()->IsFixedArray()); 5124 return UpdateDefaultCache(name, code); 5125 } 5126} 5127 5128 5129MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) { 5130 // When updating the default code cache we disregard the type encoded in the 5131 // flags. This allows call constant stubs to overwrite call field 5132 // stubs, etc. 5133 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); 5134 5135 // First check whether we can update existing code cache without 5136 // extending it. 5137 FixedArray* cache = default_cache(); 5138 int length = cache->length(); 5139 int deleted_index = -1; 5140 for (int i = 0; i < length; i += kCodeCacheEntrySize) { 5141 Object* key = cache->get(i); 5142 if (key->IsNull()) { 5143 if (deleted_index < 0) deleted_index = i; 5144 continue; 5145 } 5146 if (key->IsUndefined()) { 5147 if (deleted_index >= 0) i = deleted_index; 5148 cache->set(i + kCodeCacheEntryNameOffset, name); 5149 cache->set(i + kCodeCacheEntryCodeOffset, code); 5150 return this; 5151 } 5152 if (name->Equals(String::cast(key))) { 5153 Code::Flags found = 5154 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags(); 5155 if (Code::RemoveTypeFromFlags(found) == flags) { 5156 cache->set(i + kCodeCacheEntryCodeOffset, code); 5157 return this; 5158 } 5159 } 5160 } 5161 5162 // Reached the end of the code cache. If there were deleted 5163 // elements, reuse the space for the first of them. 5164 if (deleted_index >= 0) { 5165 cache->set(deleted_index + kCodeCacheEntryNameOffset, name); 5166 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code); 5167 return this; 5168 } 5169 5170 // Extend the code cache with some new entries (at least one). Must be a 5171 // multiple of the entry size. 5172 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize; 5173 new_length = new_length - new_length % kCodeCacheEntrySize; 5174 ASSERT((new_length % kCodeCacheEntrySize) == 0); 5175 Object* result; 5176 { MaybeObject* maybe_result = cache->CopySize(new_length); 5177 if (!maybe_result->ToObject(&result)) return maybe_result; 5178 } 5179 5180 // Add the (name, code) pair to the new cache. 5181 cache = FixedArray::cast(result); 5182 cache->set(length + kCodeCacheEntryNameOffset, name); 5183 cache->set(length + kCodeCacheEntryCodeOffset, code); 5184 set_default_cache(cache); 5185 return this; 5186} 5187 5188 5189MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) { 5190 // Adding a new entry can cause a new cache to be allocated. 5191 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 5192 Object* new_cache; 5193 { MaybeObject* maybe_new_cache = cache->Put(name, code); 5194 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache; 5195 } 5196 set_normal_type_cache(new_cache); 5197 return this; 5198} 5199 5200 5201Object* CodeCache::Lookup(String* name, Code::Flags flags) { 5202 if (Code::ExtractTypeFromFlags(flags) == NORMAL) { 5203 return LookupNormalTypeCache(name, flags); 5204 } else { 5205 return LookupDefaultCache(name, flags); 5206 } 5207} 5208 5209 5210Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) { 5211 FixedArray* cache = default_cache(); 5212 int length = cache->length(); 5213 for (int i = 0; i < length; i += kCodeCacheEntrySize) { 5214 Object* key = cache->get(i + kCodeCacheEntryNameOffset); 5215 // Skip deleted elements. 5216 if (key->IsNull()) continue; 5217 if (key->IsUndefined()) return key; 5218 if (name->Equals(String::cast(key))) { 5219 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset)); 5220 if (code->flags() == flags) { 5221 return code; 5222 } 5223 } 5224 } 5225 return GetHeap()->undefined_value(); 5226} 5227 5228 5229Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) { 5230 if (!normal_type_cache()->IsUndefined()) { 5231 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 5232 return cache->Lookup(name, flags); 5233 } else { 5234 return GetHeap()->undefined_value(); 5235 } 5236} 5237 5238 5239int CodeCache::GetIndex(Object* name, Code* code) { 5240 if (code->type() == NORMAL) { 5241 if (normal_type_cache()->IsUndefined()) return -1; 5242 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 5243 return cache->GetIndex(String::cast(name), code->flags()); 5244 } 5245 5246 FixedArray* array = default_cache(); 5247 int len = array->length(); 5248 for (int i = 0; i < len; i += kCodeCacheEntrySize) { 5249 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1; 5250 } 5251 return -1; 5252} 5253 5254 5255void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { 5256 if (code->type() == NORMAL) { 5257 ASSERT(!normal_type_cache()->IsUndefined()); 5258 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); 5259 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index); 5260 cache->RemoveByIndex(index); 5261 } else { 5262 FixedArray* array = default_cache(); 5263 ASSERT(array->length() >= index && array->get(index)->IsCode()); 5264 // Use null instead of undefined for deleted elements to distinguish 5265 // deleted elements from unused elements. This distinction is used 5266 // when looking up in the cache and when updating the cache. 5267 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset); 5268 array->set_null(index - 1); // Name. 5269 array->set_null(index); // Code. 5270 } 5271} 5272 5273 5274// The key in the code cache hash table consists of the property name and the 5275// code object. The actual match is on the name and the code flags. If a key 5276// is created using the flags and not a code object it can only be used for 5277// lookup not to create a new entry. 5278class CodeCacheHashTableKey : public HashTableKey { 5279 public: 5280 CodeCacheHashTableKey(String* name, Code::Flags flags) 5281 : name_(name), flags_(flags), code_(NULL) { } 5282 5283 CodeCacheHashTableKey(String* name, Code* code) 5284 : name_(name), 5285 flags_(code->flags()), 5286 code_(code) { } 5287 5288 5289 bool IsMatch(Object* other) { 5290 if (!other->IsFixedArray()) return false; 5291 FixedArray* pair = FixedArray::cast(other); 5292 String* name = String::cast(pair->get(0)); 5293 Code::Flags flags = Code::cast(pair->get(1))->flags(); 5294 if (flags != flags_) { 5295 return false; 5296 } 5297 return name_->Equals(name); 5298 } 5299 5300 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) { 5301 return name->Hash() ^ flags; 5302 } 5303 5304 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); } 5305 5306 uint32_t HashForObject(Object* obj) { 5307 FixedArray* pair = FixedArray::cast(obj); 5308 String* name = String::cast(pair->get(0)); 5309 Code* code = Code::cast(pair->get(1)); 5310 return NameFlagsHashHelper(name, code->flags()); 5311 } 5312 5313 MUST_USE_RESULT MaybeObject* AsObject() { 5314 ASSERT(code_ != NULL); 5315 Object* obj; 5316 { MaybeObject* maybe_obj = code_->GetHeap()->AllocateFixedArray(2); 5317 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 5318 } 5319 FixedArray* pair = FixedArray::cast(obj); 5320 pair->set(0, name_); 5321 pair->set(1, code_); 5322 return pair; 5323 } 5324 5325 private: 5326 String* name_; 5327 Code::Flags flags_; 5328 // TODO(jkummerow): We should be able to get by without this. 5329 Code* code_; 5330}; 5331 5332 5333Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) { 5334 CodeCacheHashTableKey key(name, flags); 5335 int entry = FindEntry(&key); 5336 if (entry == kNotFound) return GetHeap()->undefined_value(); 5337 return get(EntryToIndex(entry) + 1); 5338} 5339 5340 5341MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) { 5342 CodeCacheHashTableKey key(name, code); 5343 Object* obj; 5344 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 5345 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 5346 } 5347 5348 // Don't use |this|, as the table might have grown. 5349 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj); 5350 5351 int entry = cache->FindInsertionEntry(key.Hash()); 5352 Object* k; 5353 { MaybeObject* maybe_k = key.AsObject(); 5354 if (!maybe_k->ToObject(&k)) return maybe_k; 5355 } 5356 5357 cache->set(EntryToIndex(entry), k); 5358 cache->set(EntryToIndex(entry) + 1, code); 5359 cache->ElementAdded(); 5360 return cache; 5361} 5362 5363 5364int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) { 5365 CodeCacheHashTableKey key(name, flags); 5366 int entry = FindEntry(&key); 5367 return (entry == kNotFound) ? -1 : entry; 5368} 5369 5370 5371void CodeCacheHashTable::RemoveByIndex(int index) { 5372 ASSERT(index >= 0); 5373 Heap* heap = GetHeap(); 5374 set(EntryToIndex(index), heap->the_hole_value()); 5375 set(EntryToIndex(index) + 1, heap->the_hole_value()); 5376 ElementRemoved(); 5377} 5378 5379 5380void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> cache, 5381 MapHandleList* maps, 5382 Code::Flags flags, 5383 Handle<Code> code) { 5384 Isolate* isolate = cache->GetIsolate(); 5385 CALL_HEAP_FUNCTION_VOID(isolate, cache->Update(maps, flags, *code)); 5386} 5387 5388 5389MaybeObject* PolymorphicCodeCache::Update(MapHandleList* maps, 5390 Code::Flags flags, 5391 Code* code) { 5392 // Initialize cache if necessary. 5393 if (cache()->IsUndefined()) { 5394 Object* result; 5395 { MaybeObject* maybe_result = 5396 PolymorphicCodeCacheHashTable::Allocate( 5397 PolymorphicCodeCacheHashTable::kInitialSize); 5398 if (!maybe_result->ToObject(&result)) return maybe_result; 5399 } 5400 set_cache(result); 5401 } else { 5402 // This entry shouldn't be contained in the cache yet. 5403 ASSERT(PolymorphicCodeCacheHashTable::cast(cache()) 5404 ->Lookup(maps, flags)->IsUndefined()); 5405 } 5406 PolymorphicCodeCacheHashTable* hash_table = 5407 PolymorphicCodeCacheHashTable::cast(cache()); 5408 Object* new_cache; 5409 { MaybeObject* maybe_new_cache = hash_table->Put(maps, flags, code); 5410 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache; 5411 } 5412 set_cache(new_cache); 5413 return this; 5414} 5415 5416 5417Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps, 5418 Code::Flags flags) { 5419 if (!cache()->IsUndefined()) { 5420 PolymorphicCodeCacheHashTable* hash_table = 5421 PolymorphicCodeCacheHashTable::cast(cache()); 5422 return Handle<Object>(hash_table->Lookup(maps, flags)); 5423 } else { 5424 return GetIsolate()->factory()->undefined_value(); 5425 } 5426} 5427 5428 5429// Despite their name, object of this class are not stored in the actual 5430// hash table; instead they're temporarily used for lookups. It is therefore 5431// safe to have a weak (non-owning) pointer to a MapList as a member field. 5432class PolymorphicCodeCacheHashTableKey : public HashTableKey { 5433 public: 5434 // Callers must ensure that |maps| outlives the newly constructed object. 5435 PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags) 5436 : maps_(maps), 5437 code_flags_(code_flags) {} 5438 5439 bool IsMatch(Object* other) { 5440 MapHandleList other_maps(kDefaultListAllocationSize); 5441 int other_flags; 5442 FromObject(other, &other_flags, &other_maps); 5443 if (code_flags_ != other_flags) return false; 5444 if (maps_->length() != other_maps.length()) return false; 5445 // Compare just the hashes first because it's faster. 5446 int this_hash = MapsHashHelper(maps_, code_flags_); 5447 int other_hash = MapsHashHelper(&other_maps, other_flags); 5448 if (this_hash != other_hash) return false; 5449 5450 // Full comparison: for each map in maps_, look for an equivalent map in 5451 // other_maps. This implementation is slow, but probably good enough for 5452 // now because the lists are short (<= 4 elements currently). 5453 for (int i = 0; i < maps_->length(); ++i) { 5454 bool match_found = false; 5455 for (int j = 0; j < other_maps.length(); ++j) { 5456 if (*(maps_->at(i)) == *(other_maps.at(j))) { 5457 match_found = true; 5458 break; 5459 } 5460 } 5461 if (!match_found) return false; 5462 } 5463 return true; 5464 } 5465 5466 static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) { 5467 uint32_t hash = code_flags; 5468 for (int i = 0; i < maps->length(); ++i) { 5469 hash ^= maps->at(i)->Hash(); 5470 } 5471 return hash; 5472 } 5473 5474 uint32_t Hash() { 5475 return MapsHashHelper(maps_, code_flags_); 5476 } 5477 5478 uint32_t HashForObject(Object* obj) { 5479 MapHandleList other_maps(kDefaultListAllocationSize); 5480 int other_flags; 5481 FromObject(obj, &other_flags, &other_maps); 5482 return MapsHashHelper(&other_maps, other_flags); 5483 } 5484 5485 MUST_USE_RESULT MaybeObject* AsObject() { 5486 Object* obj; 5487 // The maps in |maps_| must be copied to a newly allocated FixedArray, 5488 // both because the referenced MapList is short-lived, and because C++ 5489 // objects can't be stored in the heap anyway. 5490 { MaybeObject* maybe_obj = 5491 HEAP->AllocateUninitializedFixedArray(maps_->length() + 1); 5492 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 5493 } 5494 FixedArray* list = FixedArray::cast(obj); 5495 list->set(0, Smi::FromInt(code_flags_)); 5496 for (int i = 0; i < maps_->length(); ++i) { 5497 list->set(i + 1, *maps_->at(i)); 5498 } 5499 return list; 5500 } 5501 5502 private: 5503 static MapHandleList* FromObject(Object* obj, 5504 int* code_flags, 5505 MapHandleList* maps) { 5506 FixedArray* list = FixedArray::cast(obj); 5507 maps->Rewind(0); 5508 *code_flags = Smi::cast(list->get(0))->value(); 5509 for (int i = 1; i < list->length(); ++i) { 5510 maps->Add(Handle<Map>(Map::cast(list->get(i)))); 5511 } 5512 return maps; 5513 } 5514 5515 MapHandleList* maps_; // weak. 5516 int code_flags_; 5517 static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1; 5518}; 5519 5520 5521Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps, 5522 int code_flags) { 5523 PolymorphicCodeCacheHashTableKey key(maps, code_flags); 5524 int entry = FindEntry(&key); 5525 if (entry == kNotFound) return GetHeap()->undefined_value(); 5526 return get(EntryToIndex(entry) + 1); 5527} 5528 5529 5530MaybeObject* PolymorphicCodeCacheHashTable::Put(MapHandleList* maps, 5531 int code_flags, 5532 Code* code) { 5533 PolymorphicCodeCacheHashTableKey key(maps, code_flags); 5534 Object* obj; 5535 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 5536 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 5537 } 5538 PolymorphicCodeCacheHashTable* cache = 5539 reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj); 5540 int entry = cache->FindInsertionEntry(key.Hash()); 5541 { MaybeObject* maybe_obj = key.AsObject(); 5542 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 5543 } 5544 cache->set(EntryToIndex(entry), obj); 5545 cache->set(EntryToIndex(entry) + 1, code); 5546 cache->ElementAdded(); 5547 return cache; 5548} 5549 5550 5551MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) { 5552 ElementsAccessor* accessor = array->GetElementsAccessor(); 5553 MaybeObject* maybe_result = 5554 accessor->AddElementsToFixedArray(array, array, this); 5555 FixedArray* result; 5556 if (!maybe_result->To<FixedArray>(&result)) return maybe_result; 5557#ifdef DEBUG 5558 if (FLAG_enable_slow_asserts) { 5559 for (int i = 0; i < result->length(); i++) { 5560 Object* current = result->get(i); 5561 ASSERT(current->IsNumber() || current->IsString()); 5562 } 5563 } 5564#endif 5565 return result; 5566} 5567 5568 5569MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) { 5570 ElementsAccessor* accessor = ElementsAccessor::ForArray(other); 5571 MaybeObject* maybe_result = 5572 accessor->AddElementsToFixedArray(NULL, NULL, this, other); 5573 FixedArray* result; 5574 if (!maybe_result->To<FixedArray>(&result)) return maybe_result; 5575#ifdef DEBUG 5576 if (FLAG_enable_slow_asserts) { 5577 for (int i = 0; i < result->length(); i++) { 5578 Object* current = result->get(i); 5579 ASSERT(current->IsNumber() || current->IsString()); 5580 } 5581 } 5582#endif 5583 return result; 5584} 5585 5586 5587MaybeObject* FixedArray::CopySize(int new_length) { 5588 Heap* heap = GetHeap(); 5589 if (new_length == 0) return heap->empty_fixed_array(); 5590 Object* obj; 5591 { MaybeObject* maybe_obj = heap->AllocateFixedArray(new_length); 5592 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 5593 } 5594 FixedArray* result = FixedArray::cast(obj); 5595 // Copy the content 5596 AssertNoAllocation no_gc; 5597 int len = length(); 5598 if (new_length < len) len = new_length; 5599 // We are taking the map from the old fixed array so the map is sure to 5600 // be an immortal immutable object. 5601 result->set_map_no_write_barrier(map()); 5602 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); 5603 for (int i = 0; i < len; i++) { 5604 result->set(i, get(i), mode); 5605 } 5606 return result; 5607} 5608 5609 5610void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { 5611 AssertNoAllocation no_gc; 5612 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc); 5613 for (int index = 0; index < len; index++) { 5614 dest->set(dest_pos+index, get(pos+index), mode); 5615 } 5616} 5617 5618 5619#ifdef DEBUG 5620bool FixedArray::IsEqualTo(FixedArray* other) { 5621 if (length() != other->length()) return false; 5622 for (int i = 0 ; i < length(); ++i) { 5623 if (get(i) != other->get(i)) return false; 5624 } 5625 return true; 5626} 5627#endif 5628 5629 5630MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) { 5631 Heap* heap = Isolate::Current()->heap(); 5632 if (number_of_descriptors == 0) { 5633 return heap->empty_descriptor_array(); 5634 } 5635 // Allocate the array of keys. 5636 Object* array; 5637 { MaybeObject* maybe_array = 5638 heap->AllocateFixedArray(ToKeyIndex(number_of_descriptors)); 5639 if (!maybe_array->ToObject(&array)) return maybe_array; 5640 } 5641 // Do not use DescriptorArray::cast on incomplete object. 5642 FixedArray* result = FixedArray::cast(array); 5643 5644 // Allocate the content array and set it in the descriptor array. 5645 { MaybeObject* maybe_array = 5646 heap->AllocateFixedArray(number_of_descriptors << 1); 5647 if (!maybe_array->ToObject(&array)) return maybe_array; 5648 } 5649 result->set(kBitField3StorageIndex, Smi::FromInt(0)); 5650 result->set(kContentArrayIndex, array); 5651 result->set(kEnumerationIndexIndex, 5652 Smi::FromInt(PropertyDetails::kInitialIndex)); 5653 return result; 5654} 5655 5656 5657void DescriptorArray::SetEnumCache(FixedArray* bridge_storage, 5658 FixedArray* new_cache, 5659 Object* new_index_cache) { 5660 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength); 5661 ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray()); 5662 if (HasEnumCache()) { 5663 FixedArray::cast(get(kEnumerationIndexIndex))-> 5664 set(kEnumCacheBridgeCacheIndex, new_cache); 5665 FixedArray::cast(get(kEnumerationIndexIndex))-> 5666 set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); 5667 } else { 5668 if (IsEmpty()) return; // Do nothing for empty descriptor array. 5669 FixedArray::cast(bridge_storage)-> 5670 set(kEnumCacheBridgeCacheIndex, new_cache); 5671 FixedArray::cast(bridge_storage)-> 5672 set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache); 5673 NoWriteBarrierSet(FixedArray::cast(bridge_storage), 5674 kEnumCacheBridgeEnumIndex, 5675 get(kEnumerationIndexIndex)); 5676 set(kEnumerationIndexIndex, bridge_storage); 5677 } 5678} 5679 5680 5681static bool InsertionPointFound(String* key1, String* key2) { 5682 return key1->Hash() > key2->Hash() || key1 == key2; 5683} 5684 5685 5686void DescriptorArray::CopyFrom(Handle<DescriptorArray> dst, 5687 int dst_index, 5688 Handle<DescriptorArray> src, 5689 int src_index, 5690 const WhitenessWitness& witness) { 5691 CALL_HEAP_FUNCTION_VOID(dst->GetIsolate(), 5692 dst->CopyFrom(dst_index, *src, src_index, witness)); 5693} 5694 5695 5696MaybeObject* DescriptorArray::CopyFrom(int dst_index, 5697 DescriptorArray* src, 5698 int src_index, 5699 const WhitenessWitness& witness) { 5700 Object* value = src->GetValue(src_index); 5701 PropertyDetails details(src->GetDetails(src_index)); 5702 if (details.type() == CALLBACKS && value->IsAccessorPair()) { 5703 MaybeObject* maybe_copy = 5704 AccessorPair::cast(value)->CopyWithoutTransitions(); 5705 if (!maybe_copy->To(&value)) return maybe_copy; 5706 } 5707 Descriptor desc(src->GetKey(src_index), value, details); 5708 Set(dst_index, &desc, witness); 5709 return this; 5710} 5711 5712 5713MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor, 5714 TransitionFlag transition_flag) { 5715 // Transitions are only kept when inserting another transition. 5716 // This precondition is not required by this function's implementation, but 5717 // is currently required by the semantics of maps, so we check it. 5718 // Conversely, we filter after replacing, so replacing a transition and 5719 // removing all other transitions is not supported. 5720 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS; 5721 ASSERT(remove_transitions == !descriptor->ContainsTransition()); 5722 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR); 5723 5724 // Ensure the key is a symbol. 5725 { MaybeObject* maybe_result = descriptor->KeyToSymbol(); 5726 if (maybe_result->IsFailure()) return maybe_result; 5727 } 5728 5729 int new_size = 0; 5730 for (int i = 0; i < number_of_descriptors(); i++) { 5731 if (IsNullDescriptor(i)) continue; 5732 if (remove_transitions && IsTransitionOnly(i)) continue; 5733 new_size++; 5734 } 5735 5736 // If key is in descriptor, we replace it in-place when filtering. 5737 // Count a null descriptor for key as inserted, not replaced. 5738 int index = Search(descriptor->GetKey()); 5739 const bool replacing = (index != kNotFound); 5740 bool keep_enumeration_index = false; 5741 if (replacing) { 5742 // We are replacing an existing descriptor. We keep the enumeration 5743 // index of a visible property. 5744 PropertyType t = PropertyDetails(GetDetails(index)).type(); 5745 if (t == CONSTANT_FUNCTION || 5746 t == FIELD || 5747 t == CALLBACKS || 5748 t == INTERCEPTOR) { 5749 keep_enumeration_index = true; 5750 } else if (remove_transitions) { 5751 // Replaced descriptor has been counted as removed if it is 5752 // a transition that will be replaced. Adjust count in this case. 5753 ++new_size; 5754 } 5755 } else { 5756 ++new_size; 5757 } 5758 5759 DescriptorArray* new_descriptors; 5760 { MaybeObject* maybe_result = Allocate(new_size); 5761 if (!maybe_result->To(&new_descriptors)) return maybe_result; 5762 } 5763 5764 DescriptorArray::WhitenessWitness witness(new_descriptors); 5765 5766 // Set the enumeration index in the descriptors and set the enumeration index 5767 // in the result. 5768 int enumeration_index = NextEnumerationIndex(); 5769 if (!descriptor->ContainsTransition()) { 5770 if (keep_enumeration_index) { 5771 descriptor->SetEnumerationIndex( 5772 PropertyDetails(GetDetails(index)).index()); 5773 } else { 5774 descriptor->SetEnumerationIndex(enumeration_index); 5775 ++enumeration_index; 5776 } 5777 } 5778 new_descriptors->SetNextEnumerationIndex(enumeration_index); 5779 5780 // Copy the descriptors, filtering out transitions and null descriptors, 5781 // and inserting or replacing a descriptor. 5782 int to_index = 0; 5783 int insertion_index = -1; 5784 int from_index = 0; 5785 while (from_index < number_of_descriptors()) { 5786 if (insertion_index < 0 && 5787 InsertionPointFound(GetKey(from_index), descriptor->GetKey())) { 5788 insertion_index = to_index++; 5789 if (replacing) from_index++; 5790 } else { 5791 if (!(IsNullDescriptor(from_index) || 5792 (remove_transitions && IsTransitionOnly(from_index)))) { 5793 MaybeObject* copy_result = 5794 new_descriptors->CopyFrom(to_index++, this, from_index, witness); 5795 if (copy_result->IsFailure()) return copy_result; 5796 } 5797 from_index++; 5798 } 5799 } 5800 if (insertion_index < 0) insertion_index = to_index++; 5801 new_descriptors->Set(insertion_index, descriptor, witness); 5802 5803 ASSERT(to_index == new_descriptors->number_of_descriptors()); 5804 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates()); 5805 5806 return new_descriptors; 5807} 5808 5809 5810MaybeObject* DescriptorArray::RemoveTransitions() { 5811 // Allocate the new descriptor array. 5812 int new_number_of_descriptors = 0; 5813 for (int i = 0; i < number_of_descriptors(); i++) { 5814 if (IsProperty(i)) new_number_of_descriptors++; 5815 } 5816 DescriptorArray* new_descriptors; 5817 { MaybeObject* maybe_result = Allocate(new_number_of_descriptors); 5818 if (!maybe_result->To(&new_descriptors)) return maybe_result; 5819 } 5820 5821 // Copy the content. 5822 DescriptorArray::WhitenessWitness witness(new_descriptors); 5823 int next_descriptor = 0; 5824 for (int i = 0; i < number_of_descriptors(); i++) { 5825 if (IsProperty(i)) { 5826 MaybeObject* copy_result = 5827 new_descriptors->CopyFrom(next_descriptor++, this, i, witness); 5828 if (copy_result->IsFailure()) return copy_result; 5829 } 5830 } 5831 ASSERT(next_descriptor == new_descriptors->number_of_descriptors()); 5832 5833 return new_descriptors; 5834} 5835 5836 5837void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) { 5838 // In-place heap sort. 5839 int len = number_of_descriptors(); 5840 5841 // Bottom-up max-heap construction. 5842 // Index of the last node with children 5843 const int max_parent_index = (len / 2) - 1; 5844 for (int i = max_parent_index; i >= 0; --i) { 5845 int parent_index = i; 5846 const uint32_t parent_hash = GetKey(i)->Hash(); 5847 while (parent_index <= max_parent_index) { 5848 int child_index = 2 * parent_index + 1; 5849 uint32_t child_hash = GetKey(child_index)->Hash(); 5850 if (child_index + 1 < len) { 5851 uint32_t right_child_hash = GetKey(child_index + 1)->Hash(); 5852 if (right_child_hash > child_hash) { 5853 child_index++; 5854 child_hash = right_child_hash; 5855 } 5856 } 5857 if (child_hash <= parent_hash) break; 5858 NoIncrementalWriteBarrierSwapDescriptors(parent_index, child_index); 5859 // Now element at child_index could be < its children. 5860 parent_index = child_index; // parent_hash remains correct. 5861 } 5862 } 5863 5864 // Extract elements and create sorted array. 5865 for (int i = len - 1; i > 0; --i) { 5866 // Put max element at the back of the array. 5867 NoIncrementalWriteBarrierSwapDescriptors(0, i); 5868 // Shift down the new top element. 5869 int parent_index = 0; 5870 const uint32_t parent_hash = GetKey(parent_index)->Hash(); 5871 const int max_parent_index = (i / 2) - 1; 5872 while (parent_index <= max_parent_index) { 5873 int child_index = parent_index * 2 + 1; 5874 uint32_t child_hash = GetKey(child_index)->Hash(); 5875 if (child_index + 1 < i) { 5876 uint32_t right_child_hash = GetKey(child_index + 1)->Hash(); 5877 if (right_child_hash > child_hash) { 5878 child_index++; 5879 child_hash = right_child_hash; 5880 } 5881 } 5882 if (child_hash <= parent_hash) break; 5883 NoIncrementalWriteBarrierSwapDescriptors(parent_index, child_index); 5884 parent_index = child_index; 5885 } 5886 } 5887} 5888 5889 5890void DescriptorArray::Sort(const WhitenessWitness& witness) { 5891 SortUnchecked(witness); 5892 SLOW_ASSERT(IsSortedNoDuplicates()); 5893} 5894 5895 5896int DescriptorArray::BinarySearch(String* name, int low, int high) { 5897 uint32_t hash = name->Hash(); 5898 5899 while (low <= high) { 5900 int mid = (low + high) / 2; 5901 String* mid_name = GetKey(mid); 5902 uint32_t mid_hash = mid_name->Hash(); 5903 5904 if (mid_hash > hash) { 5905 high = mid - 1; 5906 continue; 5907 } 5908 if (mid_hash < hash) { 5909 low = mid + 1; 5910 continue; 5911 } 5912 // Found an element with the same hash-code. 5913 ASSERT(hash == mid_hash); 5914 // There might be more, so we find the first one and 5915 // check them all to see if we have a match. 5916 if (name == mid_name && !is_null_descriptor(mid)) return mid; 5917 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--; 5918 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) { 5919 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid; 5920 } 5921 break; 5922 } 5923 return kNotFound; 5924} 5925 5926 5927int DescriptorArray::LinearSearch(String* name, int len) { 5928 uint32_t hash = name->Hash(); 5929 for (int number = 0; number < len; number++) { 5930 String* entry = GetKey(number); 5931 if ((entry->Hash() == hash) && 5932 name->Equals(entry) && 5933 !is_null_descriptor(number)) { 5934 return number; 5935 } 5936 } 5937 return kNotFound; 5938} 5939 5940 5941MaybeObject* AccessorPair::CopyWithoutTransitions() { 5942 Heap* heap = GetHeap(); 5943 AccessorPair* copy; 5944 { MaybeObject* maybe_copy = heap->AllocateAccessorPair(); 5945 if (!maybe_copy->To(©)) return maybe_copy; 5946 } 5947 copy->set_getter(getter()->IsMap() ? heap->the_hole_value() : getter()); 5948 copy->set_setter(setter()->IsMap() ? heap->the_hole_value() : setter()); 5949 return copy; 5950} 5951 5952 5953Object* AccessorPair::GetComponent(AccessorComponent component) { 5954 Object* accessor = (component == ACCESSOR_GETTER) ? getter() : setter(); 5955 return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor; 5956} 5957 5958 5959MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count, 5960 PretenureFlag pretenure) { 5961 ASSERT(deopt_entry_count > 0); 5962 return HEAP->AllocateFixedArray(LengthFor(deopt_entry_count), 5963 pretenure); 5964} 5965 5966 5967MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points, 5968 PretenureFlag pretenure) { 5969 if (number_of_deopt_points == 0) return HEAP->empty_fixed_array(); 5970 return HEAP->AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points), 5971 pretenure); 5972} 5973 5974 5975#ifdef DEBUG 5976bool DescriptorArray::IsEqualTo(DescriptorArray* other) { 5977 if (IsEmpty()) return other->IsEmpty(); 5978 if (other->IsEmpty()) return false; 5979 if (length() != other->length()) return false; 5980 for (int i = 0; i < length(); ++i) { 5981 if (get(i) != other->get(i) && i != kContentArrayIndex) return false; 5982 } 5983 return GetContentArray()->IsEqualTo(other->GetContentArray()); 5984} 5985#endif 5986 5987 5988bool String::LooksValid() { 5989 if (!Isolate::Current()->heap()->Contains(this)) return false; 5990 return true; 5991} 5992 5993 5994String::FlatContent String::GetFlatContent() { 5995 int length = this->length(); 5996 StringShape shape(this); 5997 String* string = this; 5998 int offset = 0; 5999 if (shape.representation_tag() == kConsStringTag) { 6000 ConsString* cons = ConsString::cast(string); 6001 if (cons->second()->length() != 0) { 6002 return FlatContent(); 6003 } 6004 string = cons->first(); 6005 shape = StringShape(string); 6006 } 6007 if (shape.representation_tag() == kSlicedStringTag) { 6008 SlicedString* slice = SlicedString::cast(string); 6009 offset = slice->offset(); 6010 string = slice->parent(); 6011 shape = StringShape(string); 6012 ASSERT(shape.representation_tag() != kConsStringTag && 6013 shape.representation_tag() != kSlicedStringTag); 6014 } 6015 if (shape.encoding_tag() == kAsciiStringTag) { 6016 const char* start; 6017 if (shape.representation_tag() == kSeqStringTag) { 6018 start = SeqAsciiString::cast(string)->GetChars(); 6019 } else { 6020 start = ExternalAsciiString::cast(string)->GetChars(); 6021 } 6022 return FlatContent(Vector<const char>(start + offset, length)); 6023 } else { 6024 ASSERT(shape.encoding_tag() == kTwoByteStringTag); 6025 const uc16* start; 6026 if (shape.representation_tag() == kSeqStringTag) { 6027 start = SeqTwoByteString::cast(string)->GetChars(); 6028 } else { 6029 start = ExternalTwoByteString::cast(string)->GetChars(); 6030 } 6031 return FlatContent(Vector<const uc16>(start + offset, length)); 6032 } 6033} 6034 6035 6036SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, 6037 RobustnessFlag robust_flag, 6038 int offset, 6039 int length, 6040 int* length_return) { 6041 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { 6042 return SmartArrayPointer<char>(NULL); 6043 } 6044 Heap* heap = GetHeap(); 6045 6046 // Negative length means the to the end of the string. 6047 if (length < 0) length = kMaxInt - offset; 6048 6049 // Compute the size of the UTF-8 string. Start at the specified offset. 6050 Access<StringInputBuffer> buffer( 6051 heap->isolate()->objects_string_input_buffer()); 6052 buffer->Reset(offset, this); 6053 int character_position = offset; 6054 int utf8_bytes = 0; 6055 int last = unibrow::Utf16::kNoPreviousCharacter; 6056 while (buffer->has_more() && character_position++ < offset + length) { 6057 uint16_t character = buffer->GetNext(); 6058 utf8_bytes += unibrow::Utf8::Length(character, last); 6059 last = character; 6060 } 6061 6062 if (length_return) { 6063 *length_return = utf8_bytes; 6064 } 6065 6066 char* result = NewArray<char>(utf8_bytes + 1); 6067 6068 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset. 6069 buffer->Rewind(); 6070 buffer->Seek(offset); 6071 character_position = offset; 6072 int utf8_byte_position = 0; 6073 last = unibrow::Utf16::kNoPreviousCharacter; 6074 while (buffer->has_more() && character_position++ < offset + length) { 6075 uint16_t character = buffer->GetNext(); 6076 if (allow_nulls == DISALLOW_NULLS && character == 0) { 6077 character = ' '; 6078 } 6079 utf8_byte_position += 6080 unibrow::Utf8::Encode(result + utf8_byte_position, character, last); 6081 last = character; 6082 } 6083 result[utf8_byte_position] = 0; 6084 return SmartArrayPointer<char>(result); 6085} 6086 6087 6088SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, 6089 RobustnessFlag robust_flag, 6090 int* length_return) { 6091 return ToCString(allow_nulls, robust_flag, 0, -1, length_return); 6092} 6093 6094 6095const uc16* String::GetTwoByteData() { 6096 return GetTwoByteData(0); 6097} 6098 6099 6100const uc16* String::GetTwoByteData(unsigned start) { 6101 ASSERT(!IsAsciiRepresentationUnderneath()); 6102 switch (StringShape(this).representation_tag()) { 6103 case kSeqStringTag: 6104 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start); 6105 case kExternalStringTag: 6106 return ExternalTwoByteString::cast(this)-> 6107 ExternalTwoByteStringGetData(start); 6108 case kSlicedStringTag: { 6109 SlicedString* slice = SlicedString::cast(this); 6110 return slice->parent()->GetTwoByteData(start + slice->offset()); 6111 } 6112 case kConsStringTag: 6113 UNREACHABLE(); 6114 return NULL; 6115 } 6116 UNREACHABLE(); 6117 return NULL; 6118} 6119 6120 6121SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { 6122 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { 6123 return SmartArrayPointer<uc16>(); 6124 } 6125 Heap* heap = GetHeap(); 6126 6127 Access<StringInputBuffer> buffer( 6128 heap->isolate()->objects_string_input_buffer()); 6129 buffer->Reset(this); 6130 6131 uc16* result = NewArray<uc16>(length() + 1); 6132 6133 int i = 0; 6134 while (buffer->has_more()) { 6135 uint16_t character = buffer->GetNext(); 6136 result[i++] = character; 6137 } 6138 result[i] = 0; 6139 return SmartArrayPointer<uc16>(result); 6140} 6141 6142 6143const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { 6144 return reinterpret_cast<uc16*>( 6145 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start; 6146} 6147 6148 6149void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 6150 unsigned* offset_ptr, 6151 unsigned max_chars) { 6152 unsigned chars_read = 0; 6153 unsigned offset = *offset_ptr; 6154 while (chars_read < max_chars) { 6155 uint16_t c = *reinterpret_cast<uint16_t*>( 6156 reinterpret_cast<char*>(this) - 6157 kHeapObjectTag + kHeaderSize + offset * kShortSize); 6158 if (c <= kMaxAsciiCharCode) { 6159 // Fast case for ASCII characters. Cursor is an input output argument. 6160 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, 6161 rbb->util_buffer, 6162 rbb->capacity, 6163 rbb->cursor)) { 6164 break; 6165 } 6166 } else { 6167 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c, 6168 rbb->util_buffer, 6169 rbb->capacity, 6170 rbb->cursor)) { 6171 break; 6172 } 6173 } 6174 offset++; 6175 chars_read++; 6176 } 6177 *offset_ptr = offset; 6178 rbb->remaining += chars_read; 6179} 6180 6181 6182const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock( 6183 unsigned* remaining, 6184 unsigned* offset_ptr, 6185 unsigned max_chars) { 6186 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) - 6187 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize; 6188 *remaining = max_chars; 6189 *offset_ptr += max_chars; 6190 return b; 6191} 6192 6193 6194// This will iterate unless the block of string data spans two 'halves' of 6195// a ConsString, in which case it will recurse. Since the block of string 6196// data to be read has a maximum size this limits the maximum recursion 6197// depth to something sane. Since C++ does not have tail call recursion 6198// elimination, the iteration must be explicit. Since this is not an 6199// -IntoBuffer method it can delegate to one of the efficient 6200// *AsciiStringReadBlock routines. 6201const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, 6202 unsigned* offset_ptr, 6203 unsigned max_chars) { 6204 ConsString* current = this; 6205 unsigned offset = *offset_ptr; 6206 int offset_correction = 0; 6207 6208 while (true) { 6209 String* left = current->first(); 6210 unsigned left_length = (unsigned)left->length(); 6211 if (left_length > offset && 6212 (max_chars <= left_length - offset || 6213 (rbb->capacity <= left_length - offset && 6214 (max_chars = left_length - offset, true)))) { // comma operator! 6215 // Left hand side only - iterate unless we have reached the bottom of 6216 // the cons tree. The assignment on the left of the comma operator is 6217 // in order to make use of the fact that the -IntoBuffer routines can 6218 // produce at most 'capacity' characters. This enables us to postpone 6219 // the point where we switch to the -IntoBuffer routines (below) in order 6220 // to maximize the chances of delegating a big chunk of work to the 6221 // efficient *AsciiStringReadBlock routines. 6222 if (StringShape(left).IsCons()) { 6223 current = ConsString::cast(left); 6224 continue; 6225 } else { 6226 const unibrow::byte* answer = 6227 String::ReadBlock(left, rbb, &offset, max_chars); 6228 *offset_ptr = offset + offset_correction; 6229 return answer; 6230 } 6231 } else if (left_length <= offset) { 6232 // Right hand side only - iterate unless we have reached the bottom of 6233 // the cons tree. 6234 String* right = current->second(); 6235 offset -= left_length; 6236 offset_correction += left_length; 6237 if (StringShape(right).IsCons()) { 6238 current = ConsString::cast(right); 6239 continue; 6240 } else { 6241 const unibrow::byte* answer = 6242 String::ReadBlock(right, rbb, &offset, max_chars); 6243 *offset_ptr = offset + offset_correction; 6244 return answer; 6245 } 6246 } else { 6247 // The block to be read spans two sides of the ConsString, so we call the 6248 // -IntoBuffer version, which will recurse. The -IntoBuffer methods 6249 // are able to assemble data from several part strings because they use 6250 // the util_buffer to store their data and never return direct pointers 6251 // to their storage. We don't try to read more than the buffer capacity 6252 // here or we can get too much recursion. 6253 ASSERT(rbb->remaining == 0); 6254 ASSERT(rbb->cursor == 0); 6255 current->ConsStringReadBlockIntoBuffer( 6256 rbb, 6257 &offset, 6258 max_chars > rbb->capacity ? rbb->capacity : max_chars); 6259 *offset_ptr = offset + offset_correction; 6260 return rbb->util_buffer; 6261 } 6262 } 6263} 6264 6265 6266const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock( 6267 unsigned* remaining, 6268 unsigned* offset_ptr, 6269 unsigned max_chars) { 6270 // Cast const char* to unibrow::byte* (signedness difference). 6271 const unibrow::byte* b = 6272 reinterpret_cast<const unibrow::byte*>(GetChars()) + *offset_ptr; 6273 *remaining = max_chars; 6274 *offset_ptr += max_chars; 6275 return b; 6276} 6277 6278 6279void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer( 6280 ReadBlockBuffer* rbb, 6281 unsigned* offset_ptr, 6282 unsigned max_chars) { 6283 unsigned chars_read = 0; 6284 unsigned offset = *offset_ptr; 6285 const uint16_t* data = GetChars(); 6286 while (chars_read < max_chars) { 6287 uint16_t c = data[offset]; 6288 if (c <= kMaxAsciiCharCode) { 6289 // Fast case for ASCII characters. Cursor is an input output argument. 6290 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, 6291 rbb->util_buffer, 6292 rbb->capacity, 6293 rbb->cursor)) 6294 break; 6295 } else { 6296 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c, 6297 rbb->util_buffer, 6298 rbb->capacity, 6299 rbb->cursor)) 6300 break; 6301 } 6302 offset++; 6303 chars_read++; 6304 } 6305 *offset_ptr = offset; 6306 rbb->remaining += chars_read; 6307} 6308 6309 6310void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 6311 unsigned* offset_ptr, 6312 unsigned max_chars) { 6313 unsigned capacity = rbb->capacity - rbb->cursor; 6314 if (max_chars > capacity) max_chars = capacity; 6315 memcpy(rbb->util_buffer + rbb->cursor, 6316 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize + 6317 *offset_ptr * kCharSize, 6318 max_chars); 6319 rbb->remaining += max_chars; 6320 *offset_ptr += max_chars; 6321 rbb->cursor += max_chars; 6322} 6323 6324 6325void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer( 6326 ReadBlockBuffer* rbb, 6327 unsigned* offset_ptr, 6328 unsigned max_chars) { 6329 unsigned capacity = rbb->capacity - rbb->cursor; 6330 if (max_chars > capacity) max_chars = capacity; 6331 memcpy(rbb->util_buffer + rbb->cursor, GetChars() + *offset_ptr, max_chars); 6332 rbb->remaining += max_chars; 6333 *offset_ptr += max_chars; 6334 rbb->cursor += max_chars; 6335} 6336 6337 6338// This method determines the type of string involved and then copies 6339// a whole chunk of characters into a buffer, or returns a pointer to a buffer 6340// where they can be found. The pointer is not necessarily valid across a GC 6341// (see AsciiStringReadBlock). 6342const unibrow::byte* String::ReadBlock(String* input, 6343 ReadBlockBuffer* rbb, 6344 unsigned* offset_ptr, 6345 unsigned max_chars) { 6346 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length())); 6347 if (max_chars == 0) { 6348 rbb->remaining = 0; 6349 return NULL; 6350 } 6351 switch (StringShape(input).representation_tag()) { 6352 case kSeqStringTag: 6353 if (input->IsAsciiRepresentation()) { 6354 SeqAsciiString* str = SeqAsciiString::cast(input); 6355 return str->SeqAsciiStringReadBlock(&rbb->remaining, 6356 offset_ptr, 6357 max_chars); 6358 } else { 6359 SeqTwoByteString* str = SeqTwoByteString::cast(input); 6360 str->SeqTwoByteStringReadBlockIntoBuffer(rbb, 6361 offset_ptr, 6362 max_chars); 6363 return rbb->util_buffer; 6364 } 6365 case kConsStringTag: 6366 return ConsString::cast(input)->ConsStringReadBlock(rbb, 6367 offset_ptr, 6368 max_chars); 6369 case kExternalStringTag: 6370 if (input->IsAsciiRepresentation()) { 6371 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock( 6372 &rbb->remaining, 6373 offset_ptr, 6374 max_chars); 6375 } else { 6376 ExternalTwoByteString::cast(input)-> 6377 ExternalTwoByteStringReadBlockIntoBuffer(rbb, 6378 offset_ptr, 6379 max_chars); 6380 return rbb->util_buffer; 6381 } 6382 case kSlicedStringTag: 6383 return SlicedString::cast(input)->SlicedStringReadBlock(rbb, 6384 offset_ptr, 6385 max_chars); 6386 default: 6387 break; 6388 } 6389 6390 UNREACHABLE(); 6391 return 0; 6392} 6393 6394 6395void Relocatable::PostGarbageCollectionProcessing() { 6396 Isolate* isolate = Isolate::Current(); 6397 Relocatable* current = isolate->relocatable_top(); 6398 while (current != NULL) { 6399 current->PostGarbageCollection(); 6400 current = current->prev_; 6401 } 6402} 6403 6404 6405// Reserve space for statics needing saving and restoring. 6406int Relocatable::ArchiveSpacePerThread() { 6407 return sizeof(Isolate::Current()->relocatable_top()); 6408} 6409 6410 6411// Archive statics that are thread local. 6412char* Relocatable::ArchiveState(Isolate* isolate, char* to) { 6413 *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top(); 6414 isolate->set_relocatable_top(NULL); 6415 return to + ArchiveSpacePerThread(); 6416} 6417 6418 6419// Restore statics that are thread local. 6420char* Relocatable::RestoreState(Isolate* isolate, char* from) { 6421 isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from)); 6422 return from + ArchiveSpacePerThread(); 6423} 6424 6425 6426char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) { 6427 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage); 6428 Iterate(v, top); 6429 return thread_storage + ArchiveSpacePerThread(); 6430} 6431 6432 6433void Relocatable::Iterate(ObjectVisitor* v) { 6434 Isolate* isolate = Isolate::Current(); 6435 Iterate(v, isolate->relocatable_top()); 6436} 6437 6438 6439void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) { 6440 Relocatable* current = top; 6441 while (current != NULL) { 6442 current->IterateInstance(v); 6443 current = current->prev_; 6444 } 6445} 6446 6447 6448FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str) 6449 : Relocatable(isolate), 6450 str_(str.location()), 6451 length_(str->length()) { 6452 PostGarbageCollection(); 6453} 6454 6455 6456FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input) 6457 : Relocatable(isolate), 6458 str_(0), 6459 is_ascii_(true), 6460 length_(input.length()), 6461 start_(input.start()) { } 6462 6463 6464void FlatStringReader::PostGarbageCollection() { 6465 if (str_ == NULL) return; 6466 Handle<String> str(str_); 6467 ASSERT(str->IsFlat()); 6468 String::FlatContent content = str->GetFlatContent(); 6469 ASSERT(content.IsFlat()); 6470 is_ascii_ = content.IsAscii(); 6471 if (is_ascii_) { 6472 start_ = content.ToAsciiVector().start(); 6473 } else { 6474 start_ = content.ToUC16Vector().start(); 6475 } 6476} 6477 6478 6479void StringInputBuffer::Seek(unsigned pos) { 6480 Reset(pos, input_); 6481} 6482 6483 6484void SafeStringInputBuffer::Seek(unsigned pos) { 6485 Reset(pos, input_); 6486} 6487 6488 6489// This method determines the type of string involved and then copies 6490// a whole chunk of characters into a buffer. It can be used with strings 6491// that have been glued together to form a ConsString and which must cooperate 6492// to fill up a buffer. 6493void String::ReadBlockIntoBuffer(String* input, 6494 ReadBlockBuffer* rbb, 6495 unsigned* offset_ptr, 6496 unsigned max_chars) { 6497 ASSERT(*offset_ptr <= (unsigned)input->length()); 6498 if (max_chars == 0) return; 6499 6500 switch (StringShape(input).representation_tag()) { 6501 case kSeqStringTag: 6502 if (input->IsAsciiRepresentation()) { 6503 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb, 6504 offset_ptr, 6505 max_chars); 6506 return; 6507 } else { 6508 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb, 6509 offset_ptr, 6510 max_chars); 6511 return; 6512 } 6513 case kConsStringTag: 6514 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb, 6515 offset_ptr, 6516 max_chars); 6517 return; 6518 case kExternalStringTag: 6519 if (input->IsAsciiRepresentation()) { 6520 ExternalAsciiString::cast(input)-> 6521 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars); 6522 } else { 6523 ExternalTwoByteString::cast(input)-> 6524 ExternalTwoByteStringReadBlockIntoBuffer(rbb, 6525 offset_ptr, 6526 max_chars); 6527 } 6528 return; 6529 case kSlicedStringTag: 6530 SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(rbb, 6531 offset_ptr, 6532 max_chars); 6533 return; 6534 default: 6535 break; 6536 } 6537 6538 UNREACHABLE(); 6539 return; 6540} 6541 6542 6543const unibrow::byte* String::ReadBlock(String* input, 6544 unibrow::byte* util_buffer, 6545 unsigned capacity, 6546 unsigned* remaining, 6547 unsigned* offset_ptr) { 6548 ASSERT(*offset_ptr <= (unsigned)input->length()); 6549 unsigned chars = input->length() - *offset_ptr; 6550 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0); 6551 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars); 6552 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length())); 6553 *remaining = rbb.remaining; 6554 return answer; 6555} 6556 6557 6558const unibrow::byte* String::ReadBlock(String** raw_input, 6559 unibrow::byte* util_buffer, 6560 unsigned capacity, 6561 unsigned* remaining, 6562 unsigned* offset_ptr) { 6563 Handle<String> input(raw_input); 6564 ASSERT(*offset_ptr <= (unsigned)input->length()); 6565 unsigned chars = input->length() - *offset_ptr; 6566 if (chars > capacity) chars = capacity; 6567 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0); 6568 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars); 6569 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length())); 6570 *remaining = rbb.remaining; 6571 return rbb.util_buffer; 6572} 6573 6574 6575// This will iterate unless the block of string data spans two 'halves' of 6576// a ConsString, in which case it will recurse. Since the block of string 6577// data to be read has a maximum size this limits the maximum recursion 6578// depth to something sane. Since C++ does not have tail call recursion 6579// elimination, the iteration must be explicit. 6580void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, 6581 unsigned* offset_ptr, 6582 unsigned max_chars) { 6583 ConsString* current = this; 6584 unsigned offset = *offset_ptr; 6585 int offset_correction = 0; 6586 6587 while (true) { 6588 String* left = current->first(); 6589 unsigned left_length = (unsigned)left->length(); 6590 if (left_length > offset && 6591 max_chars <= left_length - offset) { 6592 // Left hand side only - iterate unless we have reached the bottom of 6593 // the cons tree. 6594 if (StringShape(left).IsCons()) { 6595 current = ConsString::cast(left); 6596 continue; 6597 } else { 6598 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars); 6599 *offset_ptr = offset + offset_correction; 6600 return; 6601 } 6602 } else if (left_length <= offset) { 6603 // Right hand side only - iterate unless we have reached the bottom of 6604 // the cons tree. 6605 offset -= left_length; 6606 offset_correction += left_length; 6607 String* right = current->second(); 6608 if (StringShape(right).IsCons()) { 6609 current = ConsString::cast(right); 6610 continue; 6611 } else { 6612 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars); 6613 *offset_ptr = offset + offset_correction; 6614 return; 6615 } 6616 } else { 6617 // The block to be read spans two sides of the ConsString, so we recurse. 6618 // First recurse on the left. 6619 max_chars -= left_length - offset; 6620 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset); 6621 // We may have reached the max or there may not have been enough space 6622 // in the buffer for the characters in the left hand side. 6623 if (offset == left_length) { 6624 // Recurse on the right. 6625 String* right = String::cast(current->second()); 6626 offset -= left_length; 6627 offset_correction += left_length; 6628 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars); 6629 } 6630 *offset_ptr = offset + offset_correction; 6631 return; 6632 } 6633 } 6634} 6635 6636 6637uint16_t ConsString::ConsStringGet(int index) { 6638 ASSERT(index >= 0 && index < this->length()); 6639 6640 // Check for a flattened cons string 6641 if (second()->length() == 0) { 6642 String* left = first(); 6643 return left->Get(index); 6644 } 6645 6646 String* string = String::cast(this); 6647 6648 while (true) { 6649 if (StringShape(string).IsCons()) { 6650 ConsString* cons_string = ConsString::cast(string); 6651 String* left = cons_string->first(); 6652 if (left->length() > index) { 6653 string = left; 6654 } else { 6655 index -= left->length(); 6656 string = cons_string->second(); 6657 } 6658 } else { 6659 return string->Get(index); 6660 } 6661 } 6662 6663 UNREACHABLE(); 6664 return 0; 6665} 6666 6667 6668uint16_t SlicedString::SlicedStringGet(int index) { 6669 return parent()->Get(offset() + index); 6670} 6671 6672 6673const unibrow::byte* SlicedString::SlicedStringReadBlock( 6674 ReadBlockBuffer* buffer, unsigned* offset_ptr, unsigned chars) { 6675 unsigned offset = this->offset(); 6676 *offset_ptr += offset; 6677 const unibrow::byte* answer = String::ReadBlock(String::cast(parent()), 6678 buffer, offset_ptr, chars); 6679 *offset_ptr -= offset; 6680 return answer; 6681} 6682 6683 6684void SlicedString::SlicedStringReadBlockIntoBuffer( 6685 ReadBlockBuffer* buffer, unsigned* offset_ptr, unsigned chars) { 6686 unsigned offset = this->offset(); 6687 *offset_ptr += offset; 6688 String::ReadBlockIntoBuffer(String::cast(parent()), 6689 buffer, offset_ptr, chars); 6690 *offset_ptr -= offset; 6691} 6692 6693template <typename sinkchar> 6694void String::WriteToFlat(String* src, 6695 sinkchar* sink, 6696 int f, 6697 int t) { 6698 String* source = src; 6699 int from = f; 6700 int to = t; 6701 while (true) { 6702 ASSERT(0 <= from && from <= to && to <= source->length()); 6703 switch (StringShape(source).full_representation_tag()) { 6704 case kAsciiStringTag | kExternalStringTag: { 6705 CopyChars(sink, 6706 ExternalAsciiString::cast(source)->GetChars() + from, 6707 to - from); 6708 return; 6709 } 6710 case kTwoByteStringTag | kExternalStringTag: { 6711 const uc16* data = 6712 ExternalTwoByteString::cast(source)->GetChars(); 6713 CopyChars(sink, 6714 data + from, 6715 to - from); 6716 return; 6717 } 6718 case kAsciiStringTag | kSeqStringTag: { 6719 CopyChars(sink, 6720 SeqAsciiString::cast(source)->GetChars() + from, 6721 to - from); 6722 return; 6723 } 6724 case kTwoByteStringTag | kSeqStringTag: { 6725 CopyChars(sink, 6726 SeqTwoByteString::cast(source)->GetChars() + from, 6727 to - from); 6728 return; 6729 } 6730 case kAsciiStringTag | kConsStringTag: 6731 case kTwoByteStringTag | kConsStringTag: { 6732 ConsString* cons_string = ConsString::cast(source); 6733 String* first = cons_string->first(); 6734 int boundary = first->length(); 6735 if (to - boundary >= boundary - from) { 6736 // Right hand side is longer. Recurse over left. 6737 if (from < boundary) { 6738 WriteToFlat(first, sink, from, boundary); 6739 sink += boundary - from; 6740 from = 0; 6741 } else { 6742 from -= boundary; 6743 } 6744 to -= boundary; 6745 source = cons_string->second(); 6746 } else { 6747 // Left hand side is longer. Recurse over right. 6748 if (to > boundary) { 6749 String* second = cons_string->second(); 6750 // When repeatedly appending to a string, we get a cons string that 6751 // is unbalanced to the left, a list, essentially. We inline the 6752 // common case of sequential ascii right child. 6753 if (to - boundary == 1) { 6754 sink[boundary - from] = static_cast<sinkchar>(second->Get(0)); 6755 } else if (second->IsSeqAsciiString()) { 6756 CopyChars(sink + boundary - from, 6757 SeqAsciiString::cast(second)->GetChars(), 6758 to - boundary); 6759 } else { 6760 WriteToFlat(second, 6761 sink + boundary - from, 6762 0, 6763 to - boundary); 6764 } 6765 to = boundary; 6766 } 6767 source = first; 6768 } 6769 break; 6770 } 6771 case kAsciiStringTag | kSlicedStringTag: 6772 case kTwoByteStringTag | kSlicedStringTag: { 6773 SlicedString* slice = SlicedString::cast(source); 6774 unsigned offset = slice->offset(); 6775 WriteToFlat(slice->parent(), sink, from + offset, to + offset); 6776 return; 6777 } 6778 } 6779 } 6780} 6781 6782 6783template <typename IteratorA, typename IteratorB> 6784static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) { 6785 // General slow case check. We know that the ia and ib iterators 6786 // have the same length. 6787 while (ia->has_more()) { 6788 uint32_t ca = ia->GetNext(); 6789 uint32_t cb = ib->GetNext(); 6790 ASSERT(ca <= unibrow::Utf16::kMaxNonSurrogateCharCode); 6791 ASSERT(cb <= unibrow::Utf16::kMaxNonSurrogateCharCode); 6792 if (ca != cb) 6793 return false; 6794 } 6795 return true; 6796} 6797 6798 6799// Compares the contents of two strings by reading and comparing 6800// int-sized blocks of characters. 6801template <typename Char> 6802static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) { 6803 int length = a.length(); 6804 ASSERT_EQ(length, b.length()); 6805 const Char* pa = a.start(); 6806 const Char* pb = b.start(); 6807 int i = 0; 6808#ifndef V8_HOST_CAN_READ_UNALIGNED 6809 // If this architecture isn't comfortable reading unaligned ints 6810 // then we have to check that the strings are aligned before 6811 // comparing them blockwise. 6812 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT 6813 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa); 6814 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb); 6815 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) { 6816#endif 6817 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT 6818 int endpoint = length - kStepSize; 6819 // Compare blocks until we reach near the end of the string. 6820 for (; i <= endpoint; i += kStepSize) { 6821 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i); 6822 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i); 6823 if (wa != wb) { 6824 return false; 6825 } 6826 } 6827#ifndef V8_HOST_CAN_READ_UNALIGNED 6828 } 6829#endif 6830 // Compare the remaining characters that didn't fit into a block. 6831 for (; i < length; i++) { 6832 if (a[i] != b[i]) { 6833 return false; 6834 } 6835 } 6836 return true; 6837} 6838 6839 6840template <typename IteratorA> 6841static inline bool CompareStringContentsPartial(Isolate* isolate, 6842 IteratorA* ia, 6843 String* b) { 6844 String::FlatContent content = b->GetFlatContent(); 6845 if (content.IsFlat()) { 6846 if (content.IsAscii()) { 6847 VectorIterator<char> ib(content.ToAsciiVector()); 6848 return CompareStringContents(ia, &ib); 6849 } else { 6850 VectorIterator<uc16> ib(content.ToUC16Vector()); 6851 return CompareStringContents(ia, &ib); 6852 } 6853 } else { 6854 isolate->objects_string_compare_buffer_b()->Reset(0, b); 6855 return CompareStringContents(ia, 6856 isolate->objects_string_compare_buffer_b()); 6857 } 6858} 6859 6860 6861bool String::SlowEquals(String* other) { 6862 // Fast check: negative check with lengths. 6863 int len = length(); 6864 if (len != other->length()) return false; 6865 if (len == 0) return true; 6866 6867 // Fast check: if hash code is computed for both strings 6868 // a fast negative check can be performed. 6869 if (HasHashCode() && other->HasHashCode()) { 6870#ifdef DEBUG 6871 if (FLAG_enable_slow_asserts) { 6872 if (Hash() != other->Hash()) { 6873 bool found_difference = false; 6874 for (int i = 0; i < len; i++) { 6875 if (Get(i) != other->Get(i)) { 6876 found_difference = true; 6877 break; 6878 } 6879 } 6880 ASSERT(found_difference); 6881 } 6882 } 6883#endif 6884 if (Hash() != other->Hash()) return false; 6885 } 6886 6887 // We know the strings are both non-empty. Compare the first chars 6888 // before we try to flatten the strings. 6889 if (this->Get(0) != other->Get(0)) return false; 6890 6891 String* lhs = this->TryFlattenGetString(); 6892 String* rhs = other->TryFlattenGetString(); 6893 6894 if (StringShape(lhs).IsSequentialAscii() && 6895 StringShape(rhs).IsSequentialAscii()) { 6896 const char* str1 = SeqAsciiString::cast(lhs)->GetChars(); 6897 const char* str2 = SeqAsciiString::cast(rhs)->GetChars(); 6898 return CompareRawStringContents(Vector<const char>(str1, len), 6899 Vector<const char>(str2, len)); 6900 } 6901 6902 Isolate* isolate = GetIsolate(); 6903 String::FlatContent lhs_content = lhs->GetFlatContent(); 6904 String::FlatContent rhs_content = rhs->GetFlatContent(); 6905 if (lhs_content.IsFlat()) { 6906 if (lhs_content.IsAscii()) { 6907 Vector<const char> vec1 = lhs_content.ToAsciiVector(); 6908 if (rhs_content.IsFlat()) { 6909 if (rhs_content.IsAscii()) { 6910 Vector<const char> vec2 = rhs_content.ToAsciiVector(); 6911 return CompareRawStringContents(vec1, vec2); 6912 } else { 6913 VectorIterator<char> buf1(vec1); 6914 VectorIterator<uc16> ib(rhs_content.ToUC16Vector()); 6915 return CompareStringContents(&buf1, &ib); 6916 } 6917 } else { 6918 VectorIterator<char> buf1(vec1); 6919 isolate->objects_string_compare_buffer_b()->Reset(0, rhs); 6920 return CompareStringContents(&buf1, 6921 isolate->objects_string_compare_buffer_b()); 6922 } 6923 } else { 6924 Vector<const uc16> vec1 = lhs_content.ToUC16Vector(); 6925 if (rhs_content.IsFlat()) { 6926 if (rhs_content.IsAscii()) { 6927 VectorIterator<uc16> buf1(vec1); 6928 VectorIterator<char> ib(rhs_content.ToAsciiVector()); 6929 return CompareStringContents(&buf1, &ib); 6930 } else { 6931 Vector<const uc16> vec2(rhs_content.ToUC16Vector()); 6932 return CompareRawStringContents(vec1, vec2); 6933 } 6934 } else { 6935 VectorIterator<uc16> buf1(vec1); 6936 isolate->objects_string_compare_buffer_b()->Reset(0, rhs); 6937 return CompareStringContents(&buf1, 6938 isolate->objects_string_compare_buffer_b()); 6939 } 6940 } 6941 } else { 6942 isolate->objects_string_compare_buffer_a()->Reset(0, lhs); 6943 return CompareStringContentsPartial(isolate, 6944 isolate->objects_string_compare_buffer_a(), rhs); 6945 } 6946} 6947 6948 6949bool String::MarkAsUndetectable() { 6950 if (StringShape(this).IsSymbol()) return false; 6951 6952 Map* map = this->map(); 6953 Heap* heap = GetHeap(); 6954 if (map == heap->string_map()) { 6955 this->set_map(heap->undetectable_string_map()); 6956 return true; 6957 } else if (map == heap->ascii_string_map()) { 6958 this->set_map(heap->undetectable_ascii_string_map()); 6959 return true; 6960 } 6961 // Rest cannot be marked as undetectable 6962 return false; 6963} 6964 6965 6966bool String::IsEqualTo(Vector<const char> str) { 6967 Isolate* isolate = GetIsolate(); 6968 int slen = length(); 6969 Access<UnicodeCache::Utf8Decoder> 6970 decoder(isolate->unicode_cache()->utf8_decoder()); 6971 decoder->Reset(str.start(), str.length()); 6972 int i; 6973 for (i = 0; i < slen && decoder->has_more(); i++) { 6974 uint32_t r = decoder->GetNext(); 6975 if (r > unibrow::Utf16::kMaxNonSurrogateCharCode) { 6976 if (i > slen - 1) return false; 6977 if (Get(i++) != unibrow::Utf16::LeadSurrogate(r)) return false; 6978 if (Get(i) != unibrow::Utf16::TrailSurrogate(r)) return false; 6979 } else { 6980 if (Get(i) != r) return false; 6981 } 6982 } 6983 return i == slen && !decoder->has_more(); 6984} 6985 6986 6987bool String::IsAsciiEqualTo(Vector<const char> str) { 6988 int slen = length(); 6989 if (str.length() != slen) return false; 6990 FlatContent content = GetFlatContent(); 6991 if (content.IsAscii()) { 6992 return CompareChars(content.ToAsciiVector().start(), 6993 str.start(), slen) == 0; 6994 } 6995 for (int i = 0; i < slen; i++) { 6996 if (Get(i) != static_cast<uint16_t>(str[i])) return false; 6997 } 6998 return true; 6999} 7000 7001 7002bool String::IsTwoByteEqualTo(Vector<const uc16> str) { 7003 int slen = length(); 7004 if (str.length() != slen) return false; 7005 FlatContent content = GetFlatContent(); 7006 if (content.IsTwoByte()) { 7007 return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0; 7008 } 7009 for (int i = 0; i < slen; i++) { 7010 if (Get(i) != str[i]) return false; 7011 } 7012 return true; 7013} 7014 7015 7016uint32_t String::ComputeAndSetHash() { 7017 // Should only be called if hash code has not yet been computed. 7018 ASSERT(!HasHashCode()); 7019 7020 const int len = length(); 7021 7022 // Compute the hash code. 7023 uint32_t field = 0; 7024 if (StringShape(this).IsSequentialAscii()) { 7025 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), 7026 len, 7027 GetHeap()->HashSeed()); 7028 } else if (StringShape(this).IsSequentialTwoByte()) { 7029 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), 7030 len, 7031 GetHeap()->HashSeed()); 7032 } else { 7033 StringInputBuffer buffer(this); 7034 field = ComputeHashField(&buffer, len, GetHeap()->HashSeed()); 7035 } 7036 7037 // Store the hash code in the object. 7038 set_hash_field(field); 7039 7040 // Check the hash code is there. 7041 ASSERT(HasHashCode()); 7042 uint32_t result = field >> kHashShift; 7043 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 7044 return result; 7045} 7046 7047 7048bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer, 7049 uint32_t* index, 7050 int length) { 7051 if (length == 0 || length > kMaxArrayIndexSize) return false; 7052 uc32 ch = buffer->GetNext(); 7053 7054 // If the string begins with a '0' character, it must only consist 7055 // of it to be a legal array index. 7056 if (ch == '0') { 7057 *index = 0; 7058 return length == 1; 7059 } 7060 7061 // Convert string to uint32 array index; character by character. 7062 int d = ch - '0'; 7063 if (d < 0 || d > 9) return false; 7064 uint32_t result = d; 7065 while (buffer->has_more()) { 7066 d = buffer->GetNext() - '0'; 7067 if (d < 0 || d > 9) return false; 7068 // Check that the new result is below the 32 bit limit. 7069 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false; 7070 result = (result * 10) + d; 7071 } 7072 7073 *index = result; 7074 return true; 7075} 7076 7077 7078bool String::SlowAsArrayIndex(uint32_t* index) { 7079 if (length() <= kMaxCachedArrayIndexLength) { 7080 Hash(); // force computation of hash code 7081 uint32_t field = hash_field(); 7082 if ((field & kIsNotArrayIndexMask) != 0) return false; 7083 // Isolate the array index form the full hash field. 7084 *index = (kArrayIndexHashMask & field) >> kHashShift; 7085 return true; 7086 } else { 7087 StringInputBuffer buffer(this); 7088 return ComputeArrayIndex(&buffer, index, length()); 7089 } 7090} 7091 7092 7093uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { 7094 // For array indexes mix the length into the hash as an array index could 7095 // be zero. 7096 ASSERT(length > 0); 7097 ASSERT(length <= String::kMaxArrayIndexSize); 7098 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < 7099 (1 << String::kArrayIndexValueBits)); 7100 7101 value <<= String::kHashShift; 7102 value |= length << String::kArrayIndexHashLengthShift; 7103 7104 ASSERT((value & String::kIsNotArrayIndexMask) == 0); 7105 ASSERT((length > String::kMaxCachedArrayIndexLength) || 7106 (value & String::kContainsCachedArrayIndexMask) == 0); 7107 return value; 7108} 7109 7110 7111void StringHasher::AddSurrogatePair(uc32 c) { 7112 uint16_t lead = unibrow::Utf16::LeadSurrogate(c); 7113 AddCharacter(lead); 7114 uint16_t trail = unibrow::Utf16::TrailSurrogate(c); 7115 AddCharacter(trail); 7116} 7117 7118 7119void StringHasher::AddSurrogatePairNoIndex(uc32 c) { 7120 uint16_t lead = unibrow::Utf16::LeadSurrogate(c); 7121 AddCharacterNoIndex(lead); 7122 uint16_t trail = unibrow::Utf16::TrailSurrogate(c); 7123 AddCharacterNoIndex(trail); 7124} 7125 7126 7127uint32_t StringHasher::GetHashField() { 7128 ASSERT(is_valid()); 7129 if (length_ <= String::kMaxHashCalcLength) { 7130 if (is_array_index()) { 7131 return MakeArrayIndexHash(array_index(), length_); 7132 } 7133 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask; 7134 } else { 7135 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask; 7136 } 7137} 7138 7139 7140uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer, 7141 int length, 7142 uint32_t seed) { 7143 StringHasher hasher(length, seed); 7144 7145 // Very long strings have a trivial hash that doesn't inspect the 7146 // string contents. 7147 if (hasher.has_trivial_hash()) { 7148 return hasher.GetHashField(); 7149 } 7150 7151 // Do the iterative array index computation as long as there is a 7152 // chance this is an array index. 7153 while (buffer->has_more() && hasher.is_array_index()) { 7154 hasher.AddCharacter(buffer->GetNext()); 7155 } 7156 7157 // Process the remaining characters without updating the array 7158 // index. 7159 while (buffer->has_more()) { 7160 hasher.AddCharacterNoIndex(buffer->GetNext()); 7161 } 7162 7163 return hasher.GetHashField(); 7164} 7165 7166 7167MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) { 7168 Heap* heap = GetHeap(); 7169 if (start == 0 && end == length()) return this; 7170 MaybeObject* result = heap->AllocateSubString(this, start, end, pretenure); 7171 return result; 7172} 7173 7174 7175void String::PrintOn(FILE* file) { 7176 int length = this->length(); 7177 for (int i = 0; i < length; i++) { 7178 fprintf(file, "%c", Get(i)); 7179 } 7180} 7181 7182 7183void Map::CreateOneBackPointer(Object* transition_target) { 7184 if (!transition_target->IsMap()) return; 7185 Map* target = Map::cast(transition_target); 7186#ifdef DEBUG 7187 // Verify target. 7188 Object* source_prototype = prototype(); 7189 Object* target_prototype = target->prototype(); 7190 ASSERT(source_prototype->IsJSReceiver() || 7191 source_prototype->IsMap() || 7192 source_prototype->IsNull()); 7193 ASSERT(target_prototype->IsJSReceiver() || 7194 target_prototype->IsNull()); 7195 ASSERT(source_prototype->IsMap() || 7196 source_prototype == target_prototype); 7197#endif 7198 // Point target back to source. set_prototype() will not let us set 7199 // the prototype to a map, as we do here. 7200 *RawField(target, kPrototypeOffset) = this; 7201} 7202 7203 7204void Map::CreateBackPointers() { 7205 DescriptorArray* descriptors = instance_descriptors(); 7206 for (int i = 0; i < descriptors->number_of_descriptors(); i++) { 7207 switch (descriptors->GetType(i)) { 7208 case MAP_TRANSITION: 7209 case CONSTANT_TRANSITION: 7210 CreateOneBackPointer(descriptors->GetValue(i)); 7211 break; 7212 case ELEMENTS_TRANSITION: { 7213 Object* object = descriptors->GetValue(i); 7214 if (object->IsMap()) { 7215 CreateOneBackPointer(object); 7216 } else { 7217 FixedArray* array = FixedArray::cast(object); 7218 for (int i = 0; i < array->length(); ++i) { 7219 CreateOneBackPointer(array->get(i)); 7220 } 7221 } 7222 break; 7223 } 7224 case CALLBACKS: { 7225 Object* object = descriptors->GetValue(i); 7226 if (object->IsAccessorPair()) { 7227 AccessorPair* accessors = AccessorPair::cast(object); 7228 CreateOneBackPointer(accessors->getter()); 7229 CreateOneBackPointer(accessors->setter()); 7230 } 7231 break; 7232 } 7233 case NORMAL: 7234 case FIELD: 7235 case CONSTANT_FUNCTION: 7236 case HANDLER: 7237 case INTERCEPTOR: 7238 case NULL_DESCRIPTOR: 7239 break; 7240 } 7241 } 7242} 7243 7244 7245bool Map::RestoreOneBackPointer(Object* object, 7246 Object* real_prototype, 7247 bool* keep_entry) { 7248 if (!object->IsMap()) return false; 7249 Map* map = Map::cast(object); 7250 if (Marking::MarkBitFrom(map).Get()) { 7251 *keep_entry = true; 7252 return false; 7253 } 7254 ASSERT(map->prototype() == this || map->prototype() == real_prototype); 7255 // Getter prototype() is read-only, set_prototype() has side effects. 7256 *RawField(map, Map::kPrototypeOffset) = real_prototype; 7257 return true; 7258} 7259 7260 7261void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { 7262 DescriptorArray* d = DescriptorArray::cast( 7263 *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset)); 7264 if (d->IsEmpty()) return; 7265 Smi* NullDescriptorDetails = 7266 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi(); 7267 FixedArray* contents = FixedArray::cast( 7268 d->get(DescriptorArray::kContentArrayIndex)); 7269 ASSERT(contents->length() >= 2); 7270 for (int i = 0; i < contents->length(); i += 2) { 7271 // If the pair (value, details) is a map transition, check if the target is 7272 // live. If not, null the descriptor. Also drop the back pointer for that 7273 // map transition, so that this map is not reached again by following a back 7274 // pointer from a non-live object. 7275 bool keep_entry = false; 7276 PropertyDetails details(Smi::cast(contents->get(i + 1))); 7277 switch (details.type()) { 7278 case MAP_TRANSITION: 7279 case CONSTANT_TRANSITION: 7280 RestoreOneBackPointer(contents->get(i), real_prototype, &keep_entry); 7281 break; 7282 case ELEMENTS_TRANSITION: { 7283 Object* object = contents->get(i); 7284 if (object->IsMap()) { 7285 RestoreOneBackPointer(object, real_prototype, &keep_entry); 7286 } else { 7287 FixedArray* array = FixedArray::cast(object); 7288 for (int j = 0; j < array->length(); ++j) { 7289 if (RestoreOneBackPointer(array->get(j), 7290 real_prototype, 7291 &keep_entry)) { 7292 array->set_undefined(j); 7293 } 7294 } 7295 } 7296 break; 7297 } 7298 case CALLBACKS: { 7299 Object* object = contents->get(i); 7300 if (object->IsAccessorPair()) { 7301 AccessorPair* accessors = AccessorPair::cast(object); 7302 if (RestoreOneBackPointer(accessors->getter(), 7303 real_prototype, 7304 &keep_entry)) { 7305 accessors->set_getter(heap->the_hole_value()); 7306 } 7307 if (RestoreOneBackPointer(accessors->setter(), 7308 real_prototype, 7309 &keep_entry)) { 7310 accessors->set_setter(heap->the_hole_value()); 7311 } 7312 } else { 7313 keep_entry = true; 7314 } 7315 break; 7316 } 7317 case NORMAL: 7318 case FIELD: 7319 case CONSTANT_FUNCTION: 7320 case HANDLER: 7321 case INTERCEPTOR: 7322 case NULL_DESCRIPTOR: 7323 keep_entry = true; 7324 break; 7325 } 7326 // Make sure that an entry containing only dead transitions gets collected. 7327 // What we *really* want to do here is removing this entry completely, but 7328 // for technical reasons we can't do this, so we zero it out instead. 7329 if (!keep_entry) { 7330 contents->set_unchecked(i + 1, NullDescriptorDetails); 7331 contents->set_null_unchecked(heap, i); 7332 } 7333 } 7334} 7335 7336 7337int Map::Hash() { 7338 // For performance reasons we only hash the 3 most variable fields of a map: 7339 // constructor, prototype and bit_field2. 7340 7341 // Shift away the tag. 7342 int hash = (static_cast<uint32_t>( 7343 reinterpret_cast<uintptr_t>(constructor())) >> 2); 7344 7345 // XOR-ing the prototype and constructor directly yields too many zero bits 7346 // when the two pointers are close (which is fairly common). 7347 // To avoid this we shift the prototype 4 bits relatively to the constructor. 7348 hash ^= (static_cast<uint32_t>( 7349 reinterpret_cast<uintptr_t>(prototype())) << 2); 7350 7351 return hash ^ (hash >> 16) ^ bit_field2(); 7352} 7353 7354 7355bool Map::EquivalentToForNormalization(Map* other, 7356 PropertyNormalizationMode mode) { 7357 return 7358 constructor() == other->constructor() && 7359 prototype() == other->prototype() && 7360 inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ? 7361 0 : 7362 other->inobject_properties()) && 7363 instance_type() == other->instance_type() && 7364 bit_field() == other->bit_field() && 7365 bit_field2() == other->bit_field2() && 7366 (bit_field3() & ~(1<<Map::kIsShared)) == 7367 (other->bit_field3() & ~(1<<Map::kIsShared)); 7368} 7369 7370 7371void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) { 7372 // Iterate over all fields in the body but take care in dealing with 7373 // the code entry. 7374 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset); 7375 v->VisitCodeEntry(this->address() + kCodeEntryOffset); 7376 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size); 7377} 7378 7379 7380void JSFunction::MarkForLazyRecompilation() { 7381 ASSERT(is_compiled() && !IsOptimized()); 7382 ASSERT(shared()->allows_lazy_compilation() || 7383 code()->optimizable()); 7384 Builtins* builtins = GetIsolate()->builtins(); 7385 ReplaceCode(builtins->builtin(Builtins::kLazyRecompile)); 7386} 7387 7388 7389bool SharedFunctionInfo::EnsureCompiled(Handle<SharedFunctionInfo> shared, 7390 ClearExceptionFlag flag) { 7391 return shared->is_compiled() || CompileLazy(shared, flag); 7392} 7393 7394 7395static bool CompileLazyHelper(CompilationInfo* info, 7396 ClearExceptionFlag flag) { 7397 // Compile the source information to a code object. 7398 ASSERT(info->IsOptimizing() || !info->shared_info()->is_compiled()); 7399 ASSERT(!info->isolate()->has_pending_exception()); 7400 bool result = Compiler::CompileLazy(info); 7401 ASSERT(result != Isolate::Current()->has_pending_exception()); 7402 if (!result && flag == CLEAR_EXCEPTION) { 7403 info->isolate()->clear_pending_exception(); 7404 } 7405 return result; 7406} 7407 7408 7409bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared, 7410 ClearExceptionFlag flag) { 7411 CompilationInfo info(shared); 7412 return CompileLazyHelper(&info, flag); 7413} 7414 7415 7416bool JSFunction::CompileLazy(Handle<JSFunction> function, 7417 ClearExceptionFlag flag) { 7418 bool result = true; 7419 if (function->shared()->is_compiled()) { 7420 function->ReplaceCode(function->shared()->code()); 7421 function->shared()->set_code_age(0); 7422 } else { 7423 CompilationInfo info(function); 7424 result = CompileLazyHelper(&info, flag); 7425 ASSERT(!result || function->is_compiled()); 7426 } 7427 return result; 7428} 7429 7430 7431bool JSFunction::CompileOptimized(Handle<JSFunction> function, 7432 int osr_ast_id, 7433 ClearExceptionFlag flag) { 7434 CompilationInfo info(function); 7435 info.SetOptimizing(osr_ast_id); 7436 return CompileLazyHelper(&info, flag); 7437} 7438 7439 7440bool JSFunction::IsInlineable() { 7441 if (IsBuiltin()) return false; 7442 SharedFunctionInfo* shared_info = shared(); 7443 // Check that the function has a script associated with it. 7444 if (!shared_info->script()->IsScript()) return false; 7445 if (shared_info->optimization_disabled()) return false; 7446 Code* code = shared_info->code(); 7447 if (code->kind() == Code::OPTIMIZED_FUNCTION) return true; 7448 // If we never ran this (unlikely) then lets try to optimize it. 7449 if (code->kind() != Code::FUNCTION) return true; 7450 return code->optimizable(); 7451} 7452 7453 7454MaybeObject* JSFunction::SetInstancePrototype(Object* value) { 7455 ASSERT(value->IsJSReceiver()); 7456 Heap* heap = GetHeap(); 7457 if (has_initial_map()) { 7458 // If the function has allocated the initial map 7459 // replace it with a copy containing the new prototype. 7460 Map* new_map; 7461 MaybeObject* maybe_new_map = initial_map()->CopyDropTransitions(); 7462 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 7463 new_map->set_prototype(value); 7464 MaybeObject* maybe_object = 7465 set_initial_map_and_cache_transitions(new_map); 7466 if (maybe_object->IsFailure()) return maybe_object; 7467 } else { 7468 // Put the value in the initial map field until an initial map is 7469 // needed. At that point, a new initial map is created and the 7470 // prototype is put into the initial map where it belongs. 7471 set_prototype_or_initial_map(value); 7472 } 7473 heap->ClearInstanceofCache(); 7474 return value; 7475} 7476 7477 7478MaybeObject* JSFunction::SetPrototype(Object* value) { 7479 ASSERT(should_have_prototype()); 7480 Object* construct_prototype = value; 7481 7482 // If the value is not a JSReceiver, store the value in the map's 7483 // constructor field so it can be accessed. Also, set the prototype 7484 // used for constructing objects to the original object prototype. 7485 // See ECMA-262 13.2.2. 7486 if (!value->IsJSReceiver()) { 7487 // Copy the map so this does not affect unrelated functions. 7488 // Remove map transitions because they point to maps with a 7489 // different prototype. 7490 Map* new_map; 7491 { MaybeObject* maybe_new_map = map()->CopyDropTransitions(); 7492 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 7493 } 7494 Heap* heap = new_map->GetHeap(); 7495 set_map(new_map); 7496 new_map->set_constructor(value); 7497 new_map->set_non_instance_prototype(true); 7498 construct_prototype = 7499 heap->isolate()->context()->global_context()-> 7500 initial_object_prototype(); 7501 } else { 7502 map()->set_non_instance_prototype(false); 7503 } 7504 7505 return SetInstancePrototype(construct_prototype); 7506} 7507 7508 7509Object* JSFunction::RemovePrototype() { 7510 Context* global_context = context()->global_context(); 7511 Map* no_prototype_map = shared()->is_classic_mode() 7512 ? global_context->function_without_prototype_map() 7513 : global_context->strict_mode_function_without_prototype_map(); 7514 7515 if (map() == no_prototype_map) { 7516 // Be idempotent. 7517 return this; 7518 } 7519 7520 ASSERT(map() == (shared()->is_classic_mode() 7521 ? global_context->function_map() 7522 : global_context->strict_mode_function_map())); 7523 7524 set_map(no_prototype_map); 7525 set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value()); 7526 return this; 7527} 7528 7529 7530Object* JSFunction::SetInstanceClassName(String* name) { 7531 shared()->set_instance_class_name(name); 7532 return this; 7533} 7534 7535 7536void JSFunction::PrintName(FILE* out) { 7537 SmartArrayPointer<char> name = shared()->DebugName()->ToCString(); 7538 PrintF(out, "%s", *name); 7539} 7540 7541 7542Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) { 7543 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex)); 7544} 7545 7546 7547MaybeObject* Oddball::Initialize(const char* to_string, 7548 Object* to_number, 7549 byte kind) { 7550 String* symbol; 7551 { MaybeObject* maybe_symbol = 7552 Isolate::Current()->heap()->LookupAsciiSymbol(to_string); 7553 if (!maybe_symbol->To(&symbol)) return maybe_symbol; 7554 } 7555 set_to_string(symbol); 7556 set_to_number(to_number); 7557 set_kind(kind); 7558 return this; 7559} 7560 7561 7562String* SharedFunctionInfo::DebugName() { 7563 Object* n = name(); 7564 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name(); 7565 return String::cast(n); 7566} 7567 7568 7569bool SharedFunctionInfo::HasSourceCode() { 7570 return !script()->IsUndefined() && 7571 !reinterpret_cast<Script*>(script())->source()->IsUndefined(); 7572} 7573 7574 7575Handle<Object> SharedFunctionInfo::GetSourceCode() { 7576 if (!HasSourceCode()) return GetIsolate()->factory()->undefined_value(); 7577 Handle<String> source(String::cast(Script::cast(script())->source())); 7578 return SubString(source, start_position(), end_position()); 7579} 7580 7581 7582int SharedFunctionInfo::SourceSize() { 7583 return end_position() - start_position(); 7584} 7585 7586 7587int SharedFunctionInfo::CalculateInstanceSize() { 7588 int instance_size = 7589 JSObject::kHeaderSize + 7590 expected_nof_properties() * kPointerSize; 7591 if (instance_size > JSObject::kMaxInstanceSize) { 7592 instance_size = JSObject::kMaxInstanceSize; 7593 } 7594 return instance_size; 7595} 7596 7597 7598int SharedFunctionInfo::CalculateInObjectProperties() { 7599 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize; 7600} 7601 7602 7603bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) { 7604 // Check the basic conditions for generating inline constructor code. 7605 if (!FLAG_inline_new 7606 || !has_only_simple_this_property_assignments() 7607 || this_property_assignments_count() == 0) { 7608 return false; 7609 } 7610 7611 // If the prototype is null inline constructors cause no problems. 7612 if (!prototype->IsJSObject()) { 7613 ASSERT(prototype->IsNull()); 7614 return true; 7615 } 7616 7617 Heap* heap = GetHeap(); 7618 7619 // Traverse the proposed prototype chain looking for setters for properties of 7620 // the same names as are set by the inline constructor. 7621 for (Object* obj = prototype; 7622 obj != heap->null_value(); 7623 obj = obj->GetPrototype()) { 7624 JSObject* js_object = JSObject::cast(obj); 7625 for (int i = 0; i < this_property_assignments_count(); i++) { 7626 LookupResult result(heap->isolate()); 7627 String* name = GetThisPropertyAssignmentName(i); 7628 js_object->LocalLookupRealNamedProperty(name, &result); 7629 if (result.IsFound() && result.type() == CALLBACKS) { 7630 return false; 7631 } 7632 } 7633 } 7634 7635 return true; 7636} 7637 7638 7639void SharedFunctionInfo::ForbidInlineConstructor() { 7640 set_compiler_hints(BooleanBit::set(compiler_hints(), 7641 kHasOnlySimpleThisPropertyAssignments, 7642 false)); 7643} 7644 7645 7646void SharedFunctionInfo::SetThisPropertyAssignmentsInfo( 7647 bool only_simple_this_property_assignments, 7648 FixedArray* assignments) { 7649 set_compiler_hints(BooleanBit::set(compiler_hints(), 7650 kHasOnlySimpleThisPropertyAssignments, 7651 only_simple_this_property_assignments)); 7652 set_this_property_assignments(assignments); 7653 set_this_property_assignments_count(assignments->length() / 3); 7654} 7655 7656 7657void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() { 7658 Heap* heap = GetHeap(); 7659 set_compiler_hints(BooleanBit::set(compiler_hints(), 7660 kHasOnlySimpleThisPropertyAssignments, 7661 false)); 7662 set_this_property_assignments(heap->undefined_value()); 7663 set_this_property_assignments_count(0); 7664} 7665 7666 7667String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) { 7668 Object* obj = this_property_assignments(); 7669 ASSERT(obj->IsFixedArray()); 7670 ASSERT(index < this_property_assignments_count()); 7671 obj = FixedArray::cast(obj)->get(index * 3); 7672 ASSERT(obj->IsString()); 7673 return String::cast(obj); 7674} 7675 7676 7677bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) { 7678 Object* obj = this_property_assignments(); 7679 ASSERT(obj->IsFixedArray()); 7680 ASSERT(index < this_property_assignments_count()); 7681 obj = FixedArray::cast(obj)->get(index * 3 + 1); 7682 return Smi::cast(obj)->value() != -1; 7683} 7684 7685 7686int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) { 7687 ASSERT(IsThisPropertyAssignmentArgument(index)); 7688 Object* obj = 7689 FixedArray::cast(this_property_assignments())->get(index * 3 + 1); 7690 return Smi::cast(obj)->value(); 7691} 7692 7693 7694Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) { 7695 ASSERT(!IsThisPropertyAssignmentArgument(index)); 7696 Object* obj = 7697 FixedArray::cast(this_property_assignments())->get(index * 3 + 2); 7698 return obj; 7699} 7700 7701 7702// Support function for printing the source code to a StringStream 7703// without any allocation in the heap. 7704void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, 7705 int max_length) { 7706 // For some native functions there is no source. 7707 if (!HasSourceCode()) { 7708 accumulator->Add("<No Source>"); 7709 return; 7710 } 7711 7712 // Get the source for the script which this function came from. 7713 // Don't use String::cast because we don't want more assertion errors while 7714 // we are already creating a stack dump. 7715 String* script_source = 7716 reinterpret_cast<String*>(Script::cast(script())->source()); 7717 7718 if (!script_source->LooksValid()) { 7719 accumulator->Add("<Invalid Source>"); 7720 return; 7721 } 7722 7723 if (!is_toplevel()) { 7724 accumulator->Add("function "); 7725 Object* name = this->name(); 7726 if (name->IsString() && String::cast(name)->length() > 0) { 7727 accumulator->PrintName(name); 7728 } 7729 } 7730 7731 int len = end_position() - start_position(); 7732 if (len <= max_length || max_length < 0) { 7733 accumulator->Put(script_source, start_position(), end_position()); 7734 } else { 7735 accumulator->Put(script_source, 7736 start_position(), 7737 start_position() + max_length); 7738 accumulator->Add("...\n"); 7739 } 7740} 7741 7742 7743static bool IsCodeEquivalent(Code* code, Code* recompiled) { 7744 if (code->instruction_size() != recompiled->instruction_size()) return false; 7745 ByteArray* code_relocation = code->relocation_info(); 7746 ByteArray* recompiled_relocation = recompiled->relocation_info(); 7747 int length = code_relocation->length(); 7748 if (length != recompiled_relocation->length()) return false; 7749 int compare = memcmp(code_relocation->GetDataStartAddress(), 7750 recompiled_relocation->GetDataStartAddress(), 7751 length); 7752 return compare == 0; 7753} 7754 7755 7756void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) { 7757 ASSERT(!has_deoptimization_support()); 7758 AssertNoAllocation no_allocation; 7759 Code* code = this->code(); 7760 if (IsCodeEquivalent(code, recompiled)) { 7761 // Copy the deoptimization data from the recompiled code. 7762 code->set_deoptimization_data(recompiled->deoptimization_data()); 7763 code->set_has_deoptimization_support(true); 7764 } else { 7765 // TODO(3025757): In case the recompiled isn't equivalent to the 7766 // old code, we have to replace it. We should try to avoid this 7767 // altogether because it flushes valuable type feedback by 7768 // effectively resetting all IC state. 7769 set_code(recompiled); 7770 } 7771 ASSERT(has_deoptimization_support()); 7772} 7773 7774 7775void SharedFunctionInfo::DisableOptimization() { 7776 // Disable optimization for the shared function info and mark the 7777 // code as non-optimizable. The marker on the shared function info 7778 // is there because we flush non-optimized code thereby loosing the 7779 // non-optimizable information for the code. When the code is 7780 // regenerated and set on the shared function info it is marked as 7781 // non-optimizable if optimization is disabled for the shared 7782 // function info. 7783 set_optimization_disabled(true); 7784 // Code should be the lazy compilation stub or else unoptimized. If the 7785 // latter, disable optimization for the code too. 7786 ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN); 7787 if (code()->kind() == Code::FUNCTION) { 7788 code()->set_optimizable(false); 7789 } 7790 if (FLAG_trace_opt) { 7791 PrintF("[disabled optimization for %s]\n", *DebugName()->ToCString()); 7792 } 7793} 7794 7795 7796bool SharedFunctionInfo::VerifyBailoutId(int id) { 7797 ASSERT(id != AstNode::kNoNumber); 7798 Code* unoptimized = code(); 7799 DeoptimizationOutputData* data = 7800 DeoptimizationOutputData::cast(unoptimized->deoptimization_data()); 7801 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this); 7802 USE(ignore); 7803 return true; // Return true if there was no ASSERT. 7804} 7805 7806 7807void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) { 7808 ASSERT(!IsInobjectSlackTrackingInProgress()); 7809 7810 if (!FLAG_clever_optimizations) return; 7811 7812 // Only initiate the tracking the first time. 7813 if (live_objects_may_exist()) return; 7814 set_live_objects_may_exist(true); 7815 7816 // No tracking during the snapshot construction phase. 7817 if (Serializer::enabled()) return; 7818 7819 if (map->unused_property_fields() == 0) return; 7820 7821 // Nonzero counter is a leftover from the previous attempt interrupted 7822 // by GC, keep it. 7823 if (construction_count() == 0) { 7824 set_construction_count(kGenerousAllocationCount); 7825 } 7826 set_initial_map(map); 7827 Builtins* builtins = map->GetHeap()->isolate()->builtins(); 7828 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), 7829 construct_stub()); 7830 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); 7831} 7832 7833 7834// Called from GC, hence reinterpret_cast and unchecked accessors. 7835void SharedFunctionInfo::DetachInitialMap() { 7836 Map* map = reinterpret_cast<Map*>(initial_map()); 7837 7838 // Make the map remember to restore the link if it survives the GC. 7839 map->set_bit_field2( 7840 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo)); 7841 7842 // Undo state changes made by StartInobjectTracking (except the 7843 // construction_count). This way if the initial map does not survive the GC 7844 // then StartInobjectTracking will be called again the next time the 7845 // constructor is called. The countdown will continue and (possibly after 7846 // several more GCs) CompleteInobjectSlackTracking will eventually be called. 7847 Heap* heap = map->GetHeap(); 7848 set_initial_map(heap->raw_unchecked_undefined_value()); 7849 Builtins* builtins = heap->isolate()->builtins(); 7850 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), 7851 *RawField(this, kConstructStubOffset)); 7852 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric)); 7853 // It is safe to clear the flag: it will be set again if the map is live. 7854 set_live_objects_may_exist(false); 7855} 7856 7857 7858// Called from GC, hence reinterpret_cast and unchecked accessors. 7859void SharedFunctionInfo::AttachInitialMap(Map* map) { 7860 map->set_bit_field2( 7861 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo)); 7862 7863 // Resume inobject slack tracking. 7864 set_initial_map(map); 7865 Builtins* builtins = map->GetHeap()->isolate()->builtins(); 7866 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), 7867 *RawField(this, kConstructStubOffset)); 7868 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); 7869 // The map survived the gc, so there may be objects referencing it. 7870 set_live_objects_may_exist(true); 7871} 7872 7873 7874void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { 7875 code()->ClearInlineCaches(); 7876 set_ic_age(new_ic_age); 7877 if (code()->kind() == Code::FUNCTION) { 7878 code()->set_profiler_ticks(0); 7879 if (optimization_disabled() && 7880 opt_count() >= Compiler::kDefaultMaxOptCount) { 7881 // Re-enable optimizations if they were disabled due to opt_count limit. 7882 set_optimization_disabled(false); 7883 code()->set_optimizable(true); 7884 } 7885 set_opt_count(0); 7886 } 7887} 7888 7889 7890static void GetMinInobjectSlack(Map* map, void* data) { 7891 int slack = map->unused_property_fields(); 7892 if (*reinterpret_cast<int*>(data) > slack) { 7893 *reinterpret_cast<int*>(data) = slack; 7894 } 7895} 7896 7897 7898static void ShrinkInstanceSize(Map* map, void* data) { 7899 int slack = *reinterpret_cast<int*>(data); 7900 map->set_inobject_properties(map->inobject_properties() - slack); 7901 map->set_unused_property_fields(map->unused_property_fields() - slack); 7902 map->set_instance_size(map->instance_size() - slack * kPointerSize); 7903 7904 // Visitor id might depend on the instance size, recalculate it. 7905 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map)); 7906} 7907 7908 7909void SharedFunctionInfo::CompleteInobjectSlackTracking() { 7910 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress()); 7911 Map* map = Map::cast(initial_map()); 7912 7913 Heap* heap = map->GetHeap(); 7914 set_initial_map(heap->undefined_value()); 7915 Builtins* builtins = heap->isolate()->builtins(); 7916 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), 7917 construct_stub()); 7918 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric)); 7919 7920 int slack = map->unused_property_fields(); 7921 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack); 7922 if (slack != 0) { 7923 // Resize the initial map and all maps in its transition tree. 7924 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack); 7925 7926 // Give the correct expected_nof_properties to initial maps created later. 7927 ASSERT(expected_nof_properties() >= slack); 7928 set_expected_nof_properties(expected_nof_properties() - slack); 7929 } 7930} 7931 7932 7933void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) { 7934 v->VisitSharedFunctionInfo(this); 7935 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v); 7936} 7937 7938 7939#define DECLARE_TAG(ignore1, name, ignore2) name, 7940const char* const VisitorSynchronization::kTags[ 7941 VisitorSynchronization::kNumberOfSyncTags] = { 7942 VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) 7943}; 7944#undef DECLARE_TAG 7945 7946 7947#define DECLARE_TAG(ignore1, ignore2, name) name, 7948const char* const VisitorSynchronization::kTagNames[ 7949 VisitorSynchronization::kNumberOfSyncTags] = { 7950 VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) 7951}; 7952#undef DECLARE_TAG 7953 7954 7955void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { 7956 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); 7957 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); 7958 Object* old_target = target; 7959 VisitPointer(&target); 7960 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. 7961} 7962 7963 7964void ObjectVisitor::VisitCodeEntry(Address entry_address) { 7965 Object* code = Code::GetObjectFromEntryAddress(entry_address); 7966 Object* old_code = code; 7967 VisitPointer(&code); 7968 if (code != old_code) { 7969 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry(); 7970 } 7971} 7972 7973 7974void ObjectVisitor::VisitGlobalPropertyCell(RelocInfo* rinfo) { 7975 ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL); 7976 Object* cell = rinfo->target_cell(); 7977 Object* old_cell = cell; 7978 VisitPointer(&cell); 7979 if (cell != old_cell) { 7980 rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell)); 7981 } 7982} 7983 7984 7985void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) { 7986 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) && 7987 rinfo->IsPatchedReturnSequence()) || 7988 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && 7989 rinfo->IsPatchedDebugBreakSlotSequence())); 7990 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); 7991 Object* old_target = target; 7992 VisitPointer(&target); 7993 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. 7994} 7995 7996void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) { 7997 ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); 7998 VisitPointer(rinfo->target_object_address()); 7999} 8000 8001void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) { 8002 Address* p = rinfo->target_reference_address(); 8003 VisitExternalReferences(p, p + 1); 8004} 8005 8006void Code::InvalidateRelocation() { 8007 set_relocation_info(GetHeap()->empty_byte_array()); 8008} 8009 8010 8011void Code::Relocate(intptr_t delta) { 8012 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) { 8013 it.rinfo()->apply(delta); 8014 } 8015 CPU::FlushICache(instruction_start(), instruction_size()); 8016} 8017 8018 8019void Code::CopyFrom(const CodeDesc& desc) { 8020 ASSERT(Marking::Color(this) == Marking::WHITE_OBJECT); 8021 8022 // copy code 8023 memmove(instruction_start(), desc.buffer, desc.instr_size); 8024 8025 // copy reloc info 8026 memmove(relocation_start(), 8027 desc.buffer + desc.buffer_size - desc.reloc_size, 8028 desc.reloc_size); 8029 8030 // unbox handles and relocate 8031 intptr_t delta = instruction_start() - desc.buffer; 8032 int mode_mask = RelocInfo::kCodeTargetMask | 8033 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | 8034 RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) | 8035 RelocInfo::kApplyMask; 8036 Assembler* origin = desc.origin; // Needed to find target_object on X64. 8037 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { 8038 RelocInfo::Mode mode = it.rinfo()->rmode(); 8039 if (mode == RelocInfo::EMBEDDED_OBJECT) { 8040 Handle<Object> p = it.rinfo()->target_object_handle(origin); 8041 it.rinfo()->set_target_object(*p, SKIP_WRITE_BARRIER); 8042 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { 8043 Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle(); 8044 it.rinfo()->set_target_cell(*cell, SKIP_WRITE_BARRIER); 8045 } else if (RelocInfo::IsCodeTarget(mode)) { 8046 // rewrite code handles in inline cache targets to direct 8047 // pointers to the first instruction in the code object 8048 Handle<Object> p = it.rinfo()->target_object_handle(origin); 8049 Code* code = Code::cast(*p); 8050 it.rinfo()->set_target_address(code->instruction_start(), 8051 SKIP_WRITE_BARRIER); 8052 } else { 8053 it.rinfo()->apply(delta); 8054 } 8055 } 8056 CPU::FlushICache(instruction_start(), instruction_size()); 8057} 8058 8059 8060// Locate the source position which is closest to the address in the code. This 8061// is using the source position information embedded in the relocation info. 8062// The position returned is relative to the beginning of the script where the 8063// source for this function is found. 8064int Code::SourcePosition(Address pc) { 8065 int distance = kMaxInt; 8066 int position = RelocInfo::kNoPosition; // Initially no position found. 8067 // Run through all the relocation info to find the best matching source 8068 // position. All the code needs to be considered as the sequence of the 8069 // instructions in the code does not necessarily follow the same order as the 8070 // source. 8071 RelocIterator it(this, RelocInfo::kPositionMask); 8072 while (!it.done()) { 8073 // Only look at positions after the current pc. 8074 if (it.rinfo()->pc() < pc) { 8075 // Get position and distance. 8076 8077 int dist = static_cast<int>(pc - it.rinfo()->pc()); 8078 int pos = static_cast<int>(it.rinfo()->data()); 8079 // If this position is closer than the current candidate or if it has the 8080 // same distance as the current candidate and the position is higher then 8081 // this position is the new candidate. 8082 if ((dist < distance) || 8083 (dist == distance && pos > position)) { 8084 position = pos; 8085 distance = dist; 8086 } 8087 } 8088 it.next(); 8089 } 8090 return position; 8091} 8092 8093 8094// Same as Code::SourcePosition above except it only looks for statement 8095// positions. 8096int Code::SourceStatementPosition(Address pc) { 8097 // First find the position as close as possible using all position 8098 // information. 8099 int position = SourcePosition(pc); 8100 // Now find the closest statement position before the position. 8101 int statement_position = 0; 8102 RelocIterator it(this, RelocInfo::kPositionMask); 8103 while (!it.done()) { 8104 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) { 8105 int p = static_cast<int>(it.rinfo()->data()); 8106 if (statement_position < p && p <= position) { 8107 statement_position = p; 8108 } 8109 } 8110 it.next(); 8111 } 8112 return statement_position; 8113} 8114 8115 8116SafepointEntry Code::GetSafepointEntry(Address pc) { 8117 SafepointTable table(this); 8118 return table.FindEntry(pc); 8119} 8120 8121 8122void Code::SetNoStackCheckTable() { 8123 // Indicate the absence of a stack-check table by a table start after the 8124 // end of the instructions. Table start must be aligned, so round up. 8125 set_stack_check_table_offset(RoundUp(instruction_size(), kIntSize)); 8126} 8127 8128 8129Map* Code::FindFirstMap() { 8130 ASSERT(is_inline_cache_stub()); 8131 AssertNoAllocation no_allocation; 8132 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); 8133 for (RelocIterator it(this, mask); !it.done(); it.next()) { 8134 RelocInfo* info = it.rinfo(); 8135 Object* object = info->target_object(); 8136 if (object->IsMap()) return Map::cast(object); 8137 } 8138 return NULL; 8139} 8140 8141 8142void Code::ClearInlineCaches() { 8143 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | 8144 RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | 8145 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID) | 8146 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_CONTEXT); 8147 for (RelocIterator it(this, mask); !it.done(); it.next()) { 8148 RelocInfo* info = it.rinfo(); 8149 Code* target(Code::GetCodeFromTargetAddress(info->target_address())); 8150 if (target->is_inline_cache_stub()) { 8151 IC::Clear(info->pc()); 8152 } 8153 } 8154} 8155 8156 8157#ifdef ENABLE_DISASSEMBLER 8158 8159void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { 8160 disasm::NameConverter converter; 8161 int deopt_count = DeoptCount(); 8162 PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count); 8163 if (0 == deopt_count) return; 8164 8165 PrintF(out, "%6s %6s %6s %6s %12s\n", "index", "ast id", "argc", "pc", 8166 FLAG_print_code_verbose ? "commands" : ""); 8167 for (int i = 0; i < deopt_count; i++) { 8168 PrintF(out, "%6d %6d %6d %6d", 8169 i, 8170 AstId(i)->value(), 8171 ArgumentsStackHeight(i)->value(), 8172 Pc(i)->value()); 8173 8174 if (!FLAG_print_code_verbose) { 8175 PrintF(out, "\n"); 8176 continue; 8177 } 8178 // Print details of the frame translation. 8179 int translation_index = TranslationIndex(i)->value(); 8180 TranslationIterator iterator(TranslationByteArray(), translation_index); 8181 Translation::Opcode opcode = 8182 static_cast<Translation::Opcode>(iterator.Next()); 8183 ASSERT(Translation::BEGIN == opcode); 8184 int frame_count = iterator.Next(); 8185 int jsframe_count = iterator.Next(); 8186 PrintF(out, " %s {frame count=%d, js frame count=%d}\n", 8187 Translation::StringFor(opcode), 8188 frame_count, 8189 jsframe_count); 8190 8191 while (iterator.HasNext() && 8192 Translation::BEGIN != 8193 (opcode = static_cast<Translation::Opcode>(iterator.Next()))) { 8194 PrintF(out, "%24s %s ", "", Translation::StringFor(opcode)); 8195 8196 switch (opcode) { 8197 case Translation::BEGIN: 8198 UNREACHABLE(); 8199 break; 8200 8201 case Translation::JS_FRAME: { 8202 int ast_id = iterator.Next(); 8203 int function_id = iterator.Next(); 8204 JSFunction* function = 8205 JSFunction::cast(LiteralArray()->get(function_id)); 8206 unsigned height = iterator.Next(); 8207 PrintF(out, "{ast_id=%d, function=", ast_id); 8208 function->PrintName(out); 8209 PrintF(out, ", height=%u}", height); 8210 break; 8211 } 8212 8213 case Translation::ARGUMENTS_ADAPTOR_FRAME: 8214 case Translation::CONSTRUCT_STUB_FRAME: { 8215 int function_id = iterator.Next(); 8216 JSFunction* function = 8217 JSFunction::cast(LiteralArray()->get(function_id)); 8218 unsigned height = iterator.Next(); 8219 PrintF(out, "{function="); 8220 function->PrintName(out); 8221 PrintF(out, ", height=%u}", height); 8222 break; 8223 } 8224 8225 case Translation::DUPLICATE: 8226 break; 8227 8228 case Translation::REGISTER: { 8229 int reg_code = iterator.Next(); 8230 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code)); 8231 break; 8232 } 8233 8234 case Translation::INT32_REGISTER: { 8235 int reg_code = iterator.Next(); 8236 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code)); 8237 break; 8238 } 8239 8240 case Translation::DOUBLE_REGISTER: { 8241 int reg_code = iterator.Next(); 8242 PrintF(out, "{input=%s}", 8243 DoubleRegister::AllocationIndexToString(reg_code)); 8244 break; 8245 } 8246 8247 case Translation::STACK_SLOT: { 8248 int input_slot_index = iterator.Next(); 8249 PrintF(out, "{input=%d}", input_slot_index); 8250 break; 8251 } 8252 8253 case Translation::INT32_STACK_SLOT: { 8254 int input_slot_index = iterator.Next(); 8255 PrintF(out, "{input=%d}", input_slot_index); 8256 break; 8257 } 8258 8259 case Translation::DOUBLE_STACK_SLOT: { 8260 int input_slot_index = iterator.Next(); 8261 PrintF(out, "{input=%d}", input_slot_index); 8262 break; 8263 } 8264 8265 case Translation::LITERAL: { 8266 unsigned literal_index = iterator.Next(); 8267 PrintF(out, "{literal_id=%u}", literal_index); 8268 break; 8269 } 8270 8271 case Translation::ARGUMENTS_OBJECT: 8272 break; 8273 } 8274 PrintF(out, "\n"); 8275 } 8276 } 8277} 8278 8279 8280void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) { 8281 PrintF(out, "Deoptimization Output Data (deopt points = %d)\n", 8282 this->DeoptPoints()); 8283 if (this->DeoptPoints() == 0) return; 8284 8285 PrintF("%6s %8s %s\n", "ast id", "pc", "state"); 8286 for (int i = 0; i < this->DeoptPoints(); i++) { 8287 int pc_and_state = this->PcAndState(i)->value(); 8288 PrintF("%6d %8d %s\n", 8289 this->AstId(i)->value(), 8290 FullCodeGenerator::PcField::decode(pc_and_state), 8291 FullCodeGenerator::State2String( 8292 FullCodeGenerator::StateField::decode(pc_and_state))); 8293 } 8294} 8295 8296 8297// Identify kind of code. 8298const char* Code::Kind2String(Kind kind) { 8299 switch (kind) { 8300 case FUNCTION: return "FUNCTION"; 8301 case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION"; 8302 case STUB: return "STUB"; 8303 case BUILTIN: return "BUILTIN"; 8304 case LOAD_IC: return "LOAD_IC"; 8305 case KEYED_LOAD_IC: return "KEYED_LOAD_IC"; 8306 case STORE_IC: return "STORE_IC"; 8307 case KEYED_STORE_IC: return "KEYED_STORE_IC"; 8308 case CALL_IC: return "CALL_IC"; 8309 case KEYED_CALL_IC: return "KEYED_CALL_IC"; 8310 case UNARY_OP_IC: return "UNARY_OP_IC"; 8311 case BINARY_OP_IC: return "BINARY_OP_IC"; 8312 case COMPARE_IC: return "COMPARE_IC"; 8313 case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC"; 8314 } 8315 UNREACHABLE(); 8316 return NULL; 8317} 8318 8319 8320const char* Code::ICState2String(InlineCacheState state) { 8321 switch (state) { 8322 case UNINITIALIZED: return "UNINITIALIZED"; 8323 case PREMONOMORPHIC: return "PREMONOMORPHIC"; 8324 case MONOMORPHIC: return "MONOMORPHIC"; 8325 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE"; 8326 case MEGAMORPHIC: return "MEGAMORPHIC"; 8327 case DEBUG_BREAK: return "DEBUG_BREAK"; 8328 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN"; 8329 } 8330 UNREACHABLE(); 8331 return NULL; 8332} 8333 8334 8335const char* Code::PropertyType2String(PropertyType type) { 8336 switch (type) { 8337 case NORMAL: return "NORMAL"; 8338 case FIELD: return "FIELD"; 8339 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION"; 8340 case CALLBACKS: return "CALLBACKS"; 8341 case HANDLER: return "HANDLER"; 8342 case INTERCEPTOR: return "INTERCEPTOR"; 8343 case MAP_TRANSITION: return "MAP_TRANSITION"; 8344 case ELEMENTS_TRANSITION: return "ELEMENTS_TRANSITION"; 8345 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION"; 8346 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR"; 8347 } 8348 UNREACHABLE(); // keep the compiler happy 8349 return NULL; 8350} 8351 8352 8353void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) { 8354 const char* name = NULL; 8355 switch (kind) { 8356 case CALL_IC: 8357 if (extra == STRING_INDEX_OUT_OF_BOUNDS) { 8358 name = "STRING_INDEX_OUT_OF_BOUNDS"; 8359 } 8360 break; 8361 case STORE_IC: 8362 case KEYED_STORE_IC: 8363 if (extra == kStrictMode) { 8364 name = "STRICT"; 8365 } 8366 break; 8367 default: 8368 break; 8369 } 8370 if (name != NULL) { 8371 PrintF(out, "extra_ic_state = %s\n", name); 8372 } else { 8373 PrintF(out, "extra_ic_state = %d\n", extra); 8374 } 8375} 8376 8377 8378void Code::Disassemble(const char* name, FILE* out) { 8379 PrintF(out, "kind = %s\n", Kind2String(kind())); 8380 if (is_inline_cache_stub()) { 8381 PrintF(out, "ic_state = %s\n", ICState2String(ic_state())); 8382 PrintExtraICState(out, kind(), extra_ic_state()); 8383 if (ic_state() == MONOMORPHIC) { 8384 PrintF(out, "type = %s\n", PropertyType2String(type())); 8385 } 8386 if (is_call_stub() || is_keyed_call_stub()) { 8387 PrintF(out, "argc = %d\n", arguments_count()); 8388 } 8389 } 8390 if ((name != NULL) && (name[0] != '\0')) { 8391 PrintF(out, "name = %s\n", name); 8392 } 8393 if (kind() == OPTIMIZED_FUNCTION) { 8394 PrintF(out, "stack_slots = %d\n", stack_slots()); 8395 } 8396 8397 PrintF(out, "Instructions (size = %d)\n", instruction_size()); 8398 Disassembler::Decode(out, this); 8399 PrintF(out, "\n"); 8400 8401 if (kind() == FUNCTION) { 8402 DeoptimizationOutputData* data = 8403 DeoptimizationOutputData::cast(this->deoptimization_data()); 8404 data->DeoptimizationOutputDataPrint(out); 8405 } else if (kind() == OPTIMIZED_FUNCTION) { 8406 DeoptimizationInputData* data = 8407 DeoptimizationInputData::cast(this->deoptimization_data()); 8408 data->DeoptimizationInputDataPrint(out); 8409 } 8410 PrintF("\n"); 8411 8412 if (kind() == OPTIMIZED_FUNCTION) { 8413 SafepointTable table(this); 8414 PrintF(out, "Safepoints (size = %u)\n", table.size()); 8415 for (unsigned i = 0; i < table.length(); i++) { 8416 unsigned pc_offset = table.GetPcOffset(i); 8417 PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset); 8418 table.PrintEntry(i); 8419 PrintF(out, " (sp -> fp)"); 8420 SafepointEntry entry = table.GetEntry(i); 8421 if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) { 8422 PrintF(out, " %6d", entry.deoptimization_index()); 8423 } else { 8424 PrintF(out, " <none>"); 8425 } 8426 if (entry.argument_count() > 0) { 8427 PrintF(out, " argc: %d", entry.argument_count()); 8428 } 8429 PrintF(out, "\n"); 8430 } 8431 PrintF(out, "\n"); 8432 } else if (kind() == FUNCTION) { 8433 unsigned offset = stack_check_table_offset(); 8434 // If there is no stack check table, the "table start" will at or after 8435 // (due to alignment) the end of the instruction stream. 8436 if (static_cast<int>(offset) < instruction_size()) { 8437 unsigned* address = 8438 reinterpret_cast<unsigned*>(instruction_start() + offset); 8439 unsigned length = address[0]; 8440 PrintF(out, "Stack checks (size = %u)\n", length); 8441 PrintF(out, "ast_id pc_offset\n"); 8442 for (unsigned i = 0; i < length; ++i) { 8443 unsigned index = (2 * i) + 1; 8444 PrintF(out, "%6u %9u\n", address[index], address[index + 1]); 8445 } 8446 PrintF(out, "\n"); 8447 } 8448 } 8449 8450 PrintF("RelocInfo (size = %d)\n", relocation_size()); 8451 for (RelocIterator it(this); !it.done(); it.next()) it.rinfo()->Print(out); 8452 PrintF(out, "\n"); 8453} 8454#endif // ENABLE_DISASSEMBLER 8455 8456 8457MaybeObject* JSObject::SetFastElementsCapacityAndLength( 8458 int capacity, 8459 int length, 8460 SetFastElementsCapacityMode set_capacity_mode) { 8461 Heap* heap = GetHeap(); 8462 // We should never end in here with a pixel or external array. 8463 ASSERT(!HasExternalArrayElements()); 8464 8465 // Allocate a new fast elements backing store. 8466 FixedArray* new_elements; 8467 { MaybeObject* maybe = heap->AllocateFixedArrayWithHoles(capacity); 8468 if (!maybe->To(&new_elements)) return maybe; 8469 } 8470 8471 // Find the new map to use for this object if there is a map change. 8472 Map* new_map = NULL; 8473 if (elements()->map() != heap->non_strict_arguments_elements_map()) { 8474 // The resized array has FAST_SMI_ONLY_ELEMENTS if the capacity mode forces 8475 // it, or if it's allowed and the old elements array contained only SMIs. 8476 bool has_fast_smi_only_elements = 8477 (set_capacity_mode == kForceSmiOnlyElements) || 8478 ((set_capacity_mode == kAllowSmiOnlyElements) && 8479 (elements()->map()->has_fast_smi_only_elements() || 8480 elements() == heap->empty_fixed_array())); 8481 ElementsKind elements_kind = has_fast_smi_only_elements 8482 ? FAST_SMI_ONLY_ELEMENTS 8483 : FAST_ELEMENTS; 8484 MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), elements_kind); 8485 if (!maybe->To(&new_map)) return maybe; 8486 } 8487 8488 FixedArrayBase* old_elements = elements(); 8489 ElementsKind elements_kind = GetElementsKind(); 8490 ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); 8491 ElementsKind to_kind = (elements_kind == FAST_SMI_ONLY_ELEMENTS) 8492 ? FAST_SMI_ONLY_ELEMENTS 8493 : FAST_ELEMENTS; 8494 // int copy_size = Min(old_elements_raw->length(), new_elements->length()); 8495 accessor->CopyElements(this, new_elements, to_kind); 8496 if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { 8497 set_map_and_elements(new_map, new_elements); 8498 } else { 8499 FixedArray* parameter_map = FixedArray::cast(old_elements); 8500 parameter_map->set(1, new_elements); 8501 } 8502 8503 if (FLAG_trace_elements_transitions) { 8504 PrintElementsTransition(stdout, elements_kind, old_elements, 8505 GetElementsKind(), new_elements); 8506 } 8507 8508 // Update the length if necessary. 8509 if (IsJSArray()) { 8510 JSArray::cast(this)->set_length(Smi::FromInt(length)); 8511 } 8512 8513 return new_elements; 8514} 8515 8516 8517MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( 8518 int capacity, 8519 int length) { 8520 Heap* heap = GetHeap(); 8521 // We should never end in here with a pixel or external array. 8522 ASSERT(!HasExternalArrayElements()); 8523 8524 FixedArrayBase* elems; 8525 { MaybeObject* maybe_obj = 8526 heap->AllocateUninitializedFixedDoubleArray(capacity); 8527 if (!maybe_obj->To(&elems)) return maybe_obj; 8528 } 8529 8530 Map* new_map; 8531 { MaybeObject* maybe_obj = 8532 GetElementsTransitionMap(heap->isolate(), FAST_DOUBLE_ELEMENTS); 8533 if (!maybe_obj->To(&new_map)) return maybe_obj; 8534 } 8535 8536 FixedArrayBase* old_elements = elements(); 8537 ElementsKind elements_kind = GetElementsKind(); 8538 ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); 8539 accessor->CopyElements(this, elems, FAST_DOUBLE_ELEMENTS); 8540 if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { 8541 set_map_and_elements(new_map, elems); 8542 } else { 8543 FixedArray* parameter_map = FixedArray::cast(old_elements); 8544 parameter_map->set(1, elems); 8545 } 8546 8547 if (FLAG_trace_elements_transitions) { 8548 PrintElementsTransition(stdout, elements_kind, old_elements, 8549 FAST_DOUBLE_ELEMENTS, elems); 8550 } 8551 8552 if (IsJSArray()) { 8553 JSArray::cast(this)->set_length(Smi::FromInt(length)); 8554 } 8555 8556 return this; 8557} 8558 8559 8560MaybeObject* JSArray::Initialize(int capacity) { 8561 Heap* heap = GetHeap(); 8562 ASSERT(capacity >= 0); 8563 set_length(Smi::FromInt(0)); 8564 FixedArray* new_elements; 8565 if (capacity == 0) { 8566 new_elements = heap->empty_fixed_array(); 8567 } else { 8568 MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(capacity); 8569 if (!maybe_obj->To(&new_elements)) return maybe_obj; 8570 } 8571 set_elements(new_elements); 8572 return this; 8573} 8574 8575 8576void JSArray::Expand(int required_size) { 8577 GetIsolate()->factory()->SetElementsCapacityAndLength( 8578 Handle<JSArray>(this), required_size, required_size); 8579} 8580 8581 8582MaybeObject* JSArray::SetElementsLength(Object* len) { 8583 // We should never end in here with a pixel or external array. 8584 ASSERT(AllowsSetElementsLength()); 8585 return GetElementsAccessor()->SetLength(this, len); 8586} 8587 8588 8589Object* Map::GetPrototypeTransition(Object* prototype) { 8590 FixedArray* cache = prototype_transitions(); 8591 int number_of_transitions = NumberOfProtoTransitions(); 8592 const int proto_offset = 8593 kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset; 8594 const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset; 8595 const int step = kProtoTransitionElementsPerEntry; 8596 for (int i = 0; i < number_of_transitions; i++) { 8597 if (cache->get(proto_offset + i * step) == prototype) { 8598 Object* map = cache->get(map_offset + i * step); 8599 ASSERT(map->IsMap()); 8600 return map; 8601 } 8602 } 8603 return NULL; 8604} 8605 8606 8607MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) { 8608 ASSERT(map->IsMap()); 8609 ASSERT(HeapObject::cast(prototype)->map()->IsMap()); 8610 // Don't cache prototype transition if this map is shared. 8611 if (is_shared() || !FLAG_cache_prototype_transitions) return this; 8612 8613 FixedArray* cache = prototype_transitions(); 8614 8615 const int step = kProtoTransitionElementsPerEntry; 8616 const int header = kProtoTransitionHeaderSize; 8617 8618 int capacity = (cache->length() - header) / step; 8619 8620 int transitions = NumberOfProtoTransitions() + 1; 8621 8622 if (transitions > capacity) { 8623 if (capacity > kMaxCachedPrototypeTransitions) return this; 8624 8625 FixedArray* new_cache; 8626 // Grow array by factor 2 over and above what we need. 8627 { MaybeObject* maybe_cache = 8628 GetHeap()->AllocateFixedArray(transitions * 2 * step + header); 8629 if (!maybe_cache->To(&new_cache)) return maybe_cache; 8630 } 8631 8632 for (int i = 0; i < capacity * step; i++) { 8633 new_cache->set(i + header, cache->get(i + header)); 8634 } 8635 cache = new_cache; 8636 set_prototype_transitions(cache); 8637 } 8638 8639 int last = transitions - 1; 8640 8641 cache->set(header + last * step + kProtoTransitionPrototypeOffset, prototype); 8642 cache->set(header + last * step + kProtoTransitionMapOffset, map); 8643 SetNumberOfProtoTransitions(transitions); 8644 8645 return cache; 8646} 8647 8648 8649MaybeObject* JSReceiver::SetPrototype(Object* value, 8650 bool skip_hidden_prototypes) { 8651#ifdef DEBUG 8652 int size = Size(); 8653#endif 8654 8655 Heap* heap = GetHeap(); 8656 // Silently ignore the change if value is not a JSObject or null. 8657 // SpiderMonkey behaves this way. 8658 if (!value->IsJSReceiver() && !value->IsNull()) return value; 8659 8660 // From 8.6.2 Object Internal Methods 8661 // ... 8662 // In addition, if [[Extensible]] is false the value of the [[Class]] and 8663 // [[Prototype]] internal properties of the object may not be modified. 8664 // ... 8665 // Implementation specific extensions that modify [[Class]], [[Prototype]] 8666 // or [[Extensible]] must not violate the invariants defined in the preceding 8667 // paragraph. 8668 if (!this->map()->is_extensible()) { 8669 HandleScope scope(heap->isolate()); 8670 Handle<Object> handle(this, heap->isolate()); 8671 return heap->isolate()->Throw( 8672 *FACTORY->NewTypeError("non_extensible_proto", 8673 HandleVector<Object>(&handle, 1))); 8674 } 8675 8676 // Before we can set the prototype we need to be sure 8677 // prototype cycles are prevented. 8678 // It is sufficient to validate that the receiver is not in the new prototype 8679 // chain. 8680 for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) { 8681 if (JSReceiver::cast(pt) == this) { 8682 // Cycle detected. 8683 HandleScope scope(heap->isolate()); 8684 return heap->isolate()->Throw( 8685 *FACTORY->NewError("cyclic_proto", HandleVector<Object>(NULL, 0))); 8686 } 8687 } 8688 8689 JSReceiver* real_receiver = this; 8690 8691 if (skip_hidden_prototypes) { 8692 // Find the first object in the chain whose prototype object is not 8693 // hidden and set the new prototype on that object. 8694 Object* current_proto = real_receiver->GetPrototype(); 8695 while (current_proto->IsJSObject() && 8696 JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) { 8697 real_receiver = JSReceiver::cast(current_proto); 8698 current_proto = current_proto->GetPrototype(); 8699 } 8700 } 8701 8702 // Set the new prototype of the object. 8703 Map* map = real_receiver->map(); 8704 8705 // Nothing to do if prototype is already set. 8706 if (map->prototype() == value) return value; 8707 8708 Object* new_map = map->GetPrototypeTransition(value); 8709 if (new_map == NULL) { 8710 { MaybeObject* maybe_new_map = map->CopyDropTransitions(); 8711 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 8712 } 8713 8714 { MaybeObject* maybe_new_cache = 8715 map->PutPrototypeTransition(value, Map::cast(new_map)); 8716 if (maybe_new_cache->IsFailure()) return maybe_new_cache; 8717 } 8718 8719 Map::cast(new_map)->set_prototype(value); 8720 } 8721 ASSERT(Map::cast(new_map)->prototype() == value); 8722 real_receiver->set_map(Map::cast(new_map)); 8723 8724 heap->ClearInstanceofCache(); 8725 ASSERT(size == Size()); 8726 return value; 8727} 8728 8729 8730MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, 8731 uint32_t first_arg, 8732 uint32_t arg_count, 8733 EnsureElementsMode mode) { 8734 // Elements in |Arguments| are ordered backwards (because they're on the 8735 // stack), but the method that's called here iterates over them in forward 8736 // direction. 8737 return EnsureCanContainElements( 8738 args->arguments() - first_arg - (arg_count - 1), 8739 arg_count, mode); 8740} 8741 8742 8743bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) { 8744 Isolate* isolate = GetIsolate(); 8745 // Make sure that the top context does not change when doing 8746 // callbacks or interceptor calls. 8747 AssertNoContextChange ncc; 8748 HandleScope scope(isolate); 8749 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 8750 Handle<JSReceiver> receiver_handle(receiver); 8751 Handle<JSObject> holder_handle(this); 8752 CustomArguments args(isolate, interceptor->data(), receiver, this); 8753 v8::AccessorInfo info(args.end()); 8754 if (!interceptor->query()->IsUndefined()) { 8755 v8::IndexedPropertyQuery query = 8756 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); 8757 LOG(isolate, 8758 ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); 8759 v8::Handle<v8::Integer> result; 8760 { 8761 // Leaving JavaScript. 8762 VMState state(isolate, EXTERNAL); 8763 result = query(index, info); 8764 } 8765 if (!result.IsEmpty()) { 8766 ASSERT(result->IsInt32()); 8767 return true; // absence of property is signaled by empty handle. 8768 } 8769 } else if (!interceptor->getter()->IsUndefined()) { 8770 v8::IndexedPropertyGetter getter = 8771 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); 8772 LOG(isolate, 8773 ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index)); 8774 v8::Handle<v8::Value> result; 8775 { 8776 // Leaving JavaScript. 8777 VMState state(isolate, EXTERNAL); 8778 result = getter(index, info); 8779 } 8780 if (!result.IsEmpty()) return true; 8781 } 8782 8783 if (holder_handle->GetElementsAccessor()->HasElement( 8784 *receiver_handle, *holder_handle, index)) { 8785 return true; 8786 } 8787 8788 if (holder_handle->IsStringObjectWithCharacterAt(index)) return true; 8789 Object* pt = holder_handle->GetPrototype(); 8790 if (pt->IsJSProxy()) { 8791 // We need to follow the spec and simulate a call to [[GetOwnProperty]]. 8792 return JSProxy::cast(pt)->GetElementAttributeWithHandler( 8793 receiver, index) != ABSENT; 8794 } 8795 if (pt->IsNull()) return false; 8796 return JSObject::cast(pt)->HasElementWithReceiver(*receiver_handle, index); 8797} 8798 8799 8800JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { 8801 // Check access rights if needed. 8802 if (IsAccessCheckNeeded()) { 8803 Heap* heap = GetHeap(); 8804 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 8805 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 8806 return UNDEFINED_ELEMENT; 8807 } 8808 } 8809 8810 if (IsJSGlobalProxy()) { 8811 Object* proto = GetPrototype(); 8812 if (proto->IsNull()) return UNDEFINED_ELEMENT; 8813 ASSERT(proto->IsJSGlobalObject()); 8814 return JSObject::cast(proto)->HasLocalElement(index); 8815 } 8816 8817 // Check for lookup interceptor 8818 if (HasIndexedInterceptor()) { 8819 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT 8820 : UNDEFINED_ELEMENT; 8821 } 8822 8823 // Handle [] on String objects. 8824 if (this->IsStringObjectWithCharacterAt(index)) { 8825 return STRING_CHARACTER_ELEMENT; 8826 } 8827 8828 switch (GetElementsKind()) { 8829 case FAST_SMI_ONLY_ELEMENTS: 8830 case FAST_ELEMENTS: { 8831 uint32_t length = IsJSArray() ? 8832 static_cast<uint32_t> 8833 (Smi::cast(JSArray::cast(this)->length())->value()) : 8834 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 8835 if ((index < length) && 8836 !FixedArray::cast(elements())->get(index)->IsTheHole()) { 8837 return FAST_ELEMENT; 8838 } 8839 break; 8840 } 8841 case FAST_DOUBLE_ELEMENTS: { 8842 uint32_t length = IsJSArray() ? 8843 static_cast<uint32_t> 8844 (Smi::cast(JSArray::cast(this)->length())->value()) : 8845 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length()); 8846 if ((index < length) && 8847 !FixedDoubleArray::cast(elements())->is_the_hole(index)) { 8848 return FAST_ELEMENT; 8849 } 8850 break; 8851 } 8852 case EXTERNAL_PIXEL_ELEMENTS: { 8853 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); 8854 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT; 8855 break; 8856 } 8857 case EXTERNAL_BYTE_ELEMENTS: 8858 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 8859 case EXTERNAL_SHORT_ELEMENTS: 8860 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 8861 case EXTERNAL_INT_ELEMENTS: 8862 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 8863 case EXTERNAL_FLOAT_ELEMENTS: 8864 case EXTERNAL_DOUBLE_ELEMENTS: { 8865 ExternalArray* array = ExternalArray::cast(elements()); 8866 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT; 8867 break; 8868 } 8869 case DICTIONARY_ELEMENTS: { 8870 if (element_dictionary()->FindEntry(index) != 8871 SeededNumberDictionary::kNotFound) { 8872 return DICTIONARY_ELEMENT; 8873 } 8874 break; 8875 } 8876 case NON_STRICT_ARGUMENTS_ELEMENTS: { 8877 // Aliased parameters and non-aliased elements in a fast backing store 8878 // behave as FAST_ELEMENT. Non-aliased elements in a dictionary 8879 // backing store behave as DICTIONARY_ELEMENT. 8880 FixedArray* parameter_map = FixedArray::cast(elements()); 8881 uint32_t length = parameter_map->length(); 8882 Object* probe = 8883 index < (length - 2) ? parameter_map->get(index + 2) : NULL; 8884 if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT; 8885 // If not aliased, check the arguments. 8886 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 8887 if (arguments->IsDictionary()) { 8888 SeededNumberDictionary* dictionary = 8889 SeededNumberDictionary::cast(arguments); 8890 if (dictionary->FindEntry(index) != SeededNumberDictionary::kNotFound) { 8891 return DICTIONARY_ELEMENT; 8892 } 8893 } else { 8894 length = arguments->length(); 8895 probe = (index < length) ? arguments->get(index) : NULL; 8896 if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT; 8897 } 8898 break; 8899 } 8900 } 8901 8902 return UNDEFINED_ELEMENT; 8903} 8904 8905 8906bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { 8907 // Check access rights if needed. 8908 if (IsAccessCheckNeeded()) { 8909 Heap* heap = GetHeap(); 8910 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 8911 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 8912 return false; 8913 } 8914 } 8915 8916 // Check for lookup interceptor 8917 if (HasIndexedInterceptor()) { 8918 return HasElementWithInterceptor(receiver, index); 8919 } 8920 8921 ElementsAccessor* accessor = GetElementsAccessor(); 8922 if (accessor->HasElement(receiver, this, index)) { 8923 return true; 8924 } 8925 8926 // Handle [] on String objects. 8927 if (this->IsStringObjectWithCharacterAt(index)) return true; 8928 8929 Object* pt = GetPrototype(); 8930 if (pt->IsNull()) return false; 8931 if (pt->IsJSProxy()) { 8932 // We need to follow the spec and simulate a call to [[GetOwnProperty]]. 8933 return JSProxy::cast(pt)->GetElementAttributeWithHandler( 8934 receiver, index) != ABSENT; 8935 } 8936 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); 8937} 8938 8939 8940MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index, 8941 Object* value, 8942 PropertyAttributes attributes, 8943 StrictModeFlag strict_mode, 8944 bool check_prototype, 8945 SetPropertyMode set_mode) { 8946 Isolate* isolate = GetIsolate(); 8947 // Make sure that the top context does not change when doing 8948 // callbacks or interceptor calls. 8949 AssertNoContextChange ncc; 8950 HandleScope scope(isolate); 8951 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); 8952 Handle<JSObject> this_handle(this); 8953 Handle<Object> value_handle(value, isolate); 8954 if (!interceptor->setter()->IsUndefined()) { 8955 v8::IndexedPropertySetter setter = 8956 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter()); 8957 LOG(isolate, 8958 ApiIndexedPropertyAccess("interceptor-indexed-set", this, index)); 8959 CustomArguments args(isolate, interceptor->data(), this, this); 8960 v8::AccessorInfo info(args.end()); 8961 v8::Handle<v8::Value> result; 8962 { 8963 // Leaving JavaScript. 8964 VMState state(isolate, EXTERNAL); 8965 result = setter(index, v8::Utils::ToLocal(value_handle), info); 8966 } 8967 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 8968 if (!result.IsEmpty()) return *value_handle; 8969 } 8970 MaybeObject* raw_result = 8971 this_handle->SetElementWithoutInterceptor(index, 8972 *value_handle, 8973 attributes, 8974 strict_mode, 8975 check_prototype, 8976 set_mode); 8977 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 8978 return raw_result; 8979} 8980 8981 8982MaybeObject* JSObject::GetElementWithCallback(Object* receiver, 8983 Object* structure, 8984 uint32_t index, 8985 Object* holder) { 8986 Isolate* isolate = GetIsolate(); 8987 ASSERT(!structure->IsForeign()); 8988 8989 // api style callbacks. 8990 if (structure->IsAccessorInfo()) { 8991 Handle<AccessorInfo> data(AccessorInfo::cast(structure)); 8992 Object* fun_obj = data->getter(); 8993 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); 8994 HandleScope scope(isolate); 8995 Handle<JSObject> self(JSObject::cast(receiver)); 8996 Handle<JSObject> holder_handle(JSObject::cast(holder)); 8997 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 8998 Handle<String> key = isolate->factory()->NumberToString(number); 8999 LOG(isolate, ApiNamedPropertyAccess("load", *self, *key)); 9000 CustomArguments args(isolate, data->data(), *self, *holder_handle); 9001 v8::AccessorInfo info(args.end()); 9002 v8::Handle<v8::Value> result; 9003 { 9004 // Leaving JavaScript. 9005 VMState state(isolate, EXTERNAL); 9006 result = call_fun(v8::Utils::ToLocal(key), info); 9007 } 9008 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 9009 if (result.IsEmpty()) return isolate->heap()->undefined_value(); 9010 return *v8::Utils::OpenHandle(*result); 9011 } 9012 9013 // __defineGetter__ callback 9014 if (structure->IsAccessorPair()) { 9015 Object* getter = AccessorPair::cast(structure)->getter(); 9016 if (getter->IsSpecFunction()) { 9017 // TODO(rossberg): nicer would be to cast to some JSCallable here... 9018 return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); 9019 } 9020 // Getter is not a function. 9021 return isolate->heap()->undefined_value(); 9022 } 9023 9024 UNREACHABLE(); 9025 return NULL; 9026} 9027 9028 9029MaybeObject* JSObject::SetElementWithCallback(Object* structure, 9030 uint32_t index, 9031 Object* value, 9032 JSObject* holder, 9033 StrictModeFlag strict_mode) { 9034 Isolate* isolate = GetIsolate(); 9035 HandleScope scope(isolate); 9036 9037 // We should never get here to initialize a const with the hole 9038 // value since a const declaration would conflict with the setter. 9039 ASSERT(!value->IsTheHole()); 9040 Handle<Object> value_handle(value, isolate); 9041 9042 // To accommodate both the old and the new api we switch on the 9043 // data structure used to store the callbacks. Eventually foreign 9044 // callbacks should be phased out. 9045 ASSERT(!structure->IsForeign()); 9046 9047 if (structure->IsAccessorInfo()) { 9048 // api style callbacks 9049 Handle<JSObject> self(this); 9050 Handle<JSObject> holder_handle(JSObject::cast(holder)); 9051 Handle<AccessorInfo> data(AccessorInfo::cast(structure)); 9052 Object* call_obj = data->setter(); 9053 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); 9054 if (call_fun == NULL) return value; 9055 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 9056 Handle<String> key(isolate->factory()->NumberToString(number)); 9057 LOG(isolate, ApiNamedPropertyAccess("store", *self, *key)); 9058 CustomArguments args(isolate, data->data(), *self, *holder_handle); 9059 v8::AccessorInfo info(args.end()); 9060 { 9061 // Leaving JavaScript. 9062 VMState state(isolate, EXTERNAL); 9063 call_fun(v8::Utils::ToLocal(key), 9064 v8::Utils::ToLocal(value_handle), 9065 info); 9066 } 9067 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 9068 return *value_handle; 9069 } 9070 9071 if (structure->IsAccessorPair()) { 9072 Handle<Object> setter(AccessorPair::cast(structure)->setter()); 9073 if (setter->IsSpecFunction()) { 9074 // TODO(rossberg): nicer would be to cast to some JSCallable here... 9075 return SetPropertyWithDefinedSetter(JSReceiver::cast(*setter), value); 9076 } else { 9077 if (strict_mode == kNonStrictMode) { 9078 return value; 9079 } 9080 Handle<Object> holder_handle(holder, isolate); 9081 Handle<Object> key(isolate->factory()->NewNumberFromUint(index)); 9082 Handle<Object> args[2] = { key, holder_handle }; 9083 return isolate->Throw( 9084 *isolate->factory()->NewTypeError("no_setter_in_callback", 9085 HandleVector(args, 2))); 9086 } 9087 } 9088 9089 UNREACHABLE(); 9090 return NULL; 9091} 9092 9093 9094bool JSObject::HasFastArgumentsElements() { 9095 Heap* heap = GetHeap(); 9096 if (!elements()->IsFixedArray()) return false; 9097 FixedArray* elements = FixedArray::cast(this->elements()); 9098 if (elements->map() != heap->non_strict_arguments_elements_map()) { 9099 return false; 9100 } 9101 FixedArray* arguments = FixedArray::cast(elements->get(1)); 9102 return !arguments->IsDictionary(); 9103} 9104 9105 9106bool JSObject::HasDictionaryArgumentsElements() { 9107 Heap* heap = GetHeap(); 9108 if (!elements()->IsFixedArray()) return false; 9109 FixedArray* elements = FixedArray::cast(this->elements()); 9110 if (elements->map() != heap->non_strict_arguments_elements_map()) { 9111 return false; 9112 } 9113 FixedArray* arguments = FixedArray::cast(elements->get(1)); 9114 return arguments->IsDictionary(); 9115} 9116 9117 9118// Adding n elements in fast case is O(n*n). 9119// Note: revisit design to have dual undefined values to capture absent 9120// elements. 9121MaybeObject* JSObject::SetFastElement(uint32_t index, 9122 Object* value, 9123 StrictModeFlag strict_mode, 9124 bool check_prototype) { 9125 ASSERT(HasFastTypeElements() || 9126 HasFastArgumentsElements()); 9127 9128 FixedArray* backing_store = FixedArray::cast(elements()); 9129 if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) { 9130 backing_store = FixedArray::cast(backing_store->get(1)); 9131 } else { 9132 MaybeObject* maybe = EnsureWritableFastElements(); 9133 if (!maybe->To(&backing_store)) return maybe; 9134 } 9135 uint32_t capacity = static_cast<uint32_t>(backing_store->length()); 9136 9137 if (check_prototype && 9138 (index >= capacity || backing_store->get(index)->IsTheHole())) { 9139 bool found; 9140 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index, 9141 value, 9142 &found, 9143 strict_mode); 9144 if (found) return result; 9145 } 9146 9147 uint32_t new_capacity = capacity; 9148 // Check if the length property of this object needs to be updated. 9149 uint32_t array_length = 0; 9150 bool must_update_array_length = false; 9151 if (IsJSArray()) { 9152 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); 9153 if (index >= array_length) { 9154 must_update_array_length = true; 9155 array_length = index + 1; 9156 } 9157 } 9158 // Check if the capacity of the backing store needs to be increased, or if 9159 // a transition to slow elements is necessary. 9160 if (index >= capacity) { 9161 bool convert_to_slow = true; 9162 if ((index - capacity) < kMaxGap) { 9163 new_capacity = NewElementsCapacity(index + 1); 9164 ASSERT(new_capacity > index); 9165 if (!ShouldConvertToSlowElements(new_capacity)) { 9166 convert_to_slow = false; 9167 } 9168 } 9169 if (convert_to_slow) { 9170 MaybeObject* result = NormalizeElements(); 9171 if (result->IsFailure()) return result; 9172 return SetDictionaryElement(index, value, NONE, strict_mode, 9173 check_prototype); 9174 } 9175 } 9176 // Convert to fast double elements if appropriate. 9177 if (HasFastSmiOnlyElements() && !value->IsSmi() && value->IsNumber()) { 9178 MaybeObject* maybe = 9179 SetFastDoubleElementsCapacityAndLength(new_capacity, array_length); 9180 if (maybe->IsFailure()) return maybe; 9181 FixedDoubleArray::cast(elements())->set(index, value->Number()); 9182 return value; 9183 } 9184 // Change elements kind from SMI_ONLY to generic FAST if necessary. 9185 if (HasFastSmiOnlyElements() && !value->IsSmi()) { 9186 Map* new_map; 9187 { MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), 9188 FAST_ELEMENTS); 9189 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 9190 } 9191 set_map(new_map); 9192 if (FLAG_trace_elements_transitions) { 9193 PrintElementsTransition(stdout, FAST_SMI_ONLY_ELEMENTS, elements(), 9194 FAST_ELEMENTS, elements()); 9195 } 9196 } 9197 // Increase backing store capacity if that's been decided previously. 9198 if (new_capacity != capacity) { 9199 FixedArray* new_elements; 9200 SetFastElementsCapacityMode set_capacity_mode = 9201 value->IsSmi() && HasFastSmiOnlyElements() 9202 ? kAllowSmiOnlyElements 9203 : kDontAllowSmiOnlyElements; 9204 { MaybeObject* maybe = 9205 SetFastElementsCapacityAndLength(new_capacity, 9206 array_length, 9207 set_capacity_mode); 9208 if (!maybe->To(&new_elements)) return maybe; 9209 } 9210 new_elements->set(index, value); 9211 return value; 9212 } 9213 // Finally, set the new element and length. 9214 ASSERT(elements()->IsFixedArray()); 9215 backing_store->set(index, value); 9216 if (must_update_array_length) { 9217 JSArray::cast(this)->set_length(Smi::FromInt(array_length)); 9218 } 9219 return value; 9220} 9221 9222 9223MaybeObject* JSObject::SetDictionaryElement(uint32_t index, 9224 Object* value, 9225 PropertyAttributes attributes, 9226 StrictModeFlag strict_mode, 9227 bool check_prototype, 9228 SetPropertyMode set_mode) { 9229 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 9230 Isolate* isolate = GetIsolate(); 9231 Heap* heap = isolate->heap(); 9232 9233 // Insert element in the dictionary. 9234 FixedArray* elements = FixedArray::cast(this->elements()); 9235 bool is_arguments = 9236 (elements->map() == heap->non_strict_arguments_elements_map()); 9237 SeededNumberDictionary* dictionary = NULL; 9238 if (is_arguments) { 9239 dictionary = SeededNumberDictionary::cast(elements->get(1)); 9240 } else { 9241 dictionary = SeededNumberDictionary::cast(elements); 9242 } 9243 9244 int entry = dictionary->FindEntry(index); 9245 if (entry != SeededNumberDictionary::kNotFound) { 9246 Object* element = dictionary->ValueAt(entry); 9247 PropertyDetails details = dictionary->DetailsAt(entry); 9248 if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) { 9249 return SetElementWithCallback(element, index, value, this, strict_mode); 9250 } else { 9251 dictionary->UpdateMaxNumberKey(index); 9252 // If a value has not been initialized we allow writing to it even if it 9253 // is read-only (a declared const that has not been initialized). If a 9254 // value is being defined we skip attribute checks completely. 9255 if (set_mode == DEFINE_PROPERTY) { 9256 details = PropertyDetails(attributes, NORMAL, details.index()); 9257 dictionary->DetailsAtPut(entry, details); 9258 } else if (details.IsReadOnly() && !element->IsTheHole()) { 9259 if (strict_mode == kNonStrictMode) { 9260 return isolate->heap()->undefined_value(); 9261 } else { 9262 Handle<Object> holder(this); 9263 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 9264 Handle<Object> args[2] = { number, holder }; 9265 Handle<Object> error = 9266 isolate->factory()->NewTypeError("strict_read_only_property", 9267 HandleVector(args, 2)); 9268 return isolate->Throw(*error); 9269 } 9270 } 9271 // Elements of the arguments object in slow mode might be slow aliases. 9272 if (is_arguments && element->IsAliasedArgumentsEntry()) { 9273 AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(element); 9274 Context* context = Context::cast(elements->get(0)); 9275 int context_index = entry->aliased_context_slot(); 9276 ASSERT(!context->get(context_index)->IsTheHole()); 9277 context->set(context_index, value); 9278 // For elements that are still writable we keep slow aliasing. 9279 if (!details.IsReadOnly()) value = element; 9280 } 9281 dictionary->ValueAtPut(entry, value); 9282 } 9283 } else { 9284 // Index not already used. Look for an accessor in the prototype chain. 9285 if (check_prototype) { 9286 bool found; 9287 MaybeObject* result = 9288 SetElementWithCallbackSetterInPrototypes( 9289 index, value, &found, strict_mode); 9290 if (found) return result; 9291 } 9292 // When we set the is_extensible flag to false we always force the 9293 // element into dictionary mode (and force them to stay there). 9294 if (!map()->is_extensible()) { 9295 if (strict_mode == kNonStrictMode) { 9296 return isolate->heap()->undefined_value(); 9297 } else { 9298 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 9299 Handle<String> name = isolate->factory()->NumberToString(number); 9300 Handle<Object> args[1] = { name }; 9301 Handle<Object> error = 9302 isolate->factory()->NewTypeError("object_not_extensible", 9303 HandleVector(args, 1)); 9304 return isolate->Throw(*error); 9305 } 9306 } 9307 FixedArrayBase* new_dictionary; 9308 PropertyDetails details = PropertyDetails(attributes, NORMAL); 9309 MaybeObject* maybe = dictionary->AddNumberEntry(index, value, details); 9310 if (!maybe->To(&new_dictionary)) return maybe; 9311 if (dictionary != SeededNumberDictionary::cast(new_dictionary)) { 9312 if (is_arguments) { 9313 elements->set(1, new_dictionary); 9314 } else { 9315 set_elements(new_dictionary); 9316 } 9317 dictionary = SeededNumberDictionary::cast(new_dictionary); 9318 } 9319 } 9320 9321 // Update the array length if this JSObject is an array. 9322 if (IsJSArray()) { 9323 MaybeObject* result = 9324 JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value); 9325 if (result->IsFailure()) return result; 9326 } 9327 9328 // Attempt to put this object back in fast case. 9329 if (ShouldConvertToFastElements()) { 9330 uint32_t new_length = 0; 9331 if (IsJSArray()) { 9332 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length)); 9333 } else { 9334 new_length = dictionary->max_number_key() + 1; 9335 } 9336 SetFastElementsCapacityMode set_capacity_mode = FLAG_smi_only_arrays 9337 ? kAllowSmiOnlyElements 9338 : kDontAllowSmiOnlyElements; 9339 bool has_smi_only_elements = false; 9340 bool should_convert_to_fast_double_elements = 9341 ShouldConvertToFastDoubleElements(&has_smi_only_elements); 9342 if (has_smi_only_elements) { 9343 set_capacity_mode = kForceSmiOnlyElements; 9344 } 9345 MaybeObject* result = should_convert_to_fast_double_elements 9346 ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) 9347 : SetFastElementsCapacityAndLength(new_length, 9348 new_length, 9349 set_capacity_mode); 9350 if (result->IsFailure()) return result; 9351#ifdef DEBUG 9352 if (FLAG_trace_normalization) { 9353 PrintF("Object elements are fast case again:\n"); 9354 Print(); 9355 } 9356#endif 9357 } 9358 return value; 9359} 9360 9361 9362MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( 9363 uint32_t index, 9364 Object* value, 9365 StrictModeFlag strict_mode, 9366 bool check_prototype) { 9367 ASSERT(HasFastDoubleElements()); 9368 9369 FixedArrayBase* base_elms = FixedArrayBase::cast(elements()); 9370 uint32_t elms_length = static_cast<uint32_t>(base_elms->length()); 9371 9372 // If storing to an element that isn't in the array, pass the store request 9373 // up the prototype chain before storing in the receiver's elements. 9374 if (check_prototype && 9375 (index >= elms_length || 9376 FixedDoubleArray::cast(base_elms)->is_the_hole(index))) { 9377 bool found; 9378 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index, 9379 value, 9380 &found, 9381 strict_mode); 9382 if (found) return result; 9383 } 9384 9385 // If the value object is not a heap number, switch to fast elements and try 9386 // again. 9387 bool value_is_smi = value->IsSmi(); 9388 if (!value->IsNumber()) { 9389 Object* obj; 9390 uint32_t length = elms_length; 9391 if (IsJSArray()) { 9392 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); 9393 } 9394 MaybeObject* maybe_obj = SetFastElementsCapacityAndLength( 9395 elms_length, 9396 length, 9397 kDontAllowSmiOnlyElements); 9398 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 9399 return SetFastElement(index, 9400 value, 9401 strict_mode, 9402 check_prototype); 9403 } 9404 9405 double double_value = value_is_smi 9406 ? static_cast<double>(Smi::cast(value)->value()) 9407 : HeapNumber::cast(value)->value(); 9408 9409 // Check whether there is extra space in the fixed array. 9410 if (index < elms_length) { 9411 FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); 9412 elms->set(index, double_value); 9413 if (IsJSArray()) { 9414 // Update the length of the array if needed. 9415 uint32_t array_length = 0; 9416 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); 9417 if (index >= array_length) { 9418 JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); 9419 } 9420 } 9421 return value; 9422 } 9423 9424 // Allow gap in fast case. 9425 if ((index - elms_length) < kMaxGap) { 9426 // Try allocating extra space. 9427 int new_capacity = NewElementsCapacity(index+1); 9428 if (!ShouldConvertToSlowElements(new_capacity)) { 9429 ASSERT(static_cast<uint32_t>(new_capacity) > index); 9430 Object* obj; 9431 { MaybeObject* maybe_obj = 9432 SetFastDoubleElementsCapacityAndLength(new_capacity, 9433 index + 1); 9434 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 9435 } 9436 FixedDoubleArray::cast(elements())->set(index, double_value); 9437 return value; 9438 } 9439 } 9440 9441 // Otherwise default to slow case. 9442 ASSERT(HasFastDoubleElements()); 9443 ASSERT(map()->has_fast_double_elements()); 9444 ASSERT(elements()->IsFixedDoubleArray()); 9445 Object* obj; 9446 { MaybeObject* maybe_obj = NormalizeElements(); 9447 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 9448 } 9449 ASSERT(HasDictionaryElements()); 9450 return SetElement(index, value, NONE, strict_mode, check_prototype); 9451} 9452 9453 9454MaybeObject* JSReceiver::SetElement(uint32_t index, 9455 Object* value, 9456 PropertyAttributes attributes, 9457 StrictModeFlag strict_mode, 9458 bool check_proto) { 9459 if (IsJSProxy()) { 9460 return JSProxy::cast(this)->SetElementWithHandler( 9461 index, value, strict_mode); 9462 } else { 9463 return JSObject::cast(this)->SetElement( 9464 index, value, attributes, strict_mode, check_proto); 9465 } 9466} 9467 9468 9469Handle<Object> JSObject::SetOwnElement(Handle<JSObject> object, 9470 uint32_t index, 9471 Handle<Object> value, 9472 StrictModeFlag strict_mode) { 9473 ASSERT(!object->HasExternalArrayElements()); 9474 CALL_HEAP_FUNCTION( 9475 object->GetIsolate(), 9476 object->SetElement(index, *value, NONE, strict_mode, false), 9477 Object); 9478} 9479 9480 9481Handle<Object> JSObject::SetElement(Handle<JSObject> object, 9482 uint32_t index, 9483 Handle<Object> value, 9484 PropertyAttributes attr, 9485 StrictModeFlag strict_mode, 9486 SetPropertyMode set_mode) { 9487 if (object->HasExternalArrayElements()) { 9488 if (!value->IsSmi() && !value->IsHeapNumber() && !value->IsUndefined()) { 9489 bool has_exception; 9490 Handle<Object> number = Execution::ToNumber(value, &has_exception); 9491 if (has_exception) return Handle<Object>(); 9492 value = number; 9493 } 9494 } 9495 CALL_HEAP_FUNCTION( 9496 object->GetIsolate(), 9497 object->SetElement(index, *value, attr, strict_mode, true, set_mode), 9498 Object); 9499} 9500 9501 9502MaybeObject* JSObject::SetElement(uint32_t index, 9503 Object* value, 9504 PropertyAttributes attributes, 9505 StrictModeFlag strict_mode, 9506 bool check_prototype, 9507 SetPropertyMode set_mode) { 9508 // Check access rights if needed. 9509 if (IsAccessCheckNeeded()) { 9510 Heap* heap = GetHeap(); 9511 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) { 9512 HandleScope scope(heap->isolate()); 9513 Handle<Object> value_handle(value); 9514 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET); 9515 return *value_handle; 9516 } 9517 } 9518 9519 if (IsJSGlobalProxy()) { 9520 Object* proto = GetPrototype(); 9521 if (proto->IsNull()) return value; 9522 ASSERT(proto->IsJSGlobalObject()); 9523 return JSObject::cast(proto)->SetElement(index, 9524 value, 9525 attributes, 9526 strict_mode, 9527 check_prototype, 9528 set_mode); 9529 } 9530 9531 // Don't allow element properties to be redefined for external arrays. 9532 if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) { 9533 Isolate* isolate = GetHeap()->isolate(); 9534 Handle<Object> number = isolate->factory()->NewNumberFromUint(index); 9535 Handle<Object> args[] = { Handle<Object>(this), number }; 9536 Handle<Object> error = isolate->factory()->NewTypeError( 9537 "redef_external_array_element", HandleVector(args, ARRAY_SIZE(args))); 9538 return isolate->Throw(*error); 9539 } 9540 9541 // Normalize the elements to enable attributes on the property. 9542 if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) { 9543 SeededNumberDictionary* dictionary; 9544 MaybeObject* maybe_object = NormalizeElements(); 9545 if (!maybe_object->To(&dictionary)) return maybe_object; 9546 // Make sure that we never go back to fast case. 9547 dictionary->set_requires_slow_elements(); 9548 } 9549 9550 // Check for lookup interceptor 9551 if (HasIndexedInterceptor()) { 9552 return SetElementWithInterceptor(index, 9553 value, 9554 attributes, 9555 strict_mode, 9556 check_prototype, 9557 set_mode); 9558 } 9559 9560 return SetElementWithoutInterceptor(index, 9561 value, 9562 attributes, 9563 strict_mode, 9564 check_prototype, 9565 set_mode); 9566} 9567 9568 9569MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, 9570 Object* value, 9571 PropertyAttributes attr, 9572 StrictModeFlag strict_mode, 9573 bool check_prototype, 9574 SetPropertyMode set_mode) { 9575 ASSERT(HasDictionaryElements() || 9576 HasDictionaryArgumentsElements() || 9577 (attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); 9578 Isolate* isolate = GetIsolate(); 9579 switch (GetElementsKind()) { 9580 case FAST_SMI_ONLY_ELEMENTS: 9581 case FAST_ELEMENTS: 9582 return SetFastElement(index, value, strict_mode, check_prototype); 9583 case FAST_DOUBLE_ELEMENTS: 9584 return SetFastDoubleElement(index, value, strict_mode, check_prototype); 9585 case EXTERNAL_PIXEL_ELEMENTS: { 9586 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); 9587 return pixels->SetValue(index, value); 9588 } 9589 case EXTERNAL_BYTE_ELEMENTS: { 9590 ExternalByteArray* array = ExternalByteArray::cast(elements()); 9591 return array->SetValue(index, value); 9592 } 9593 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { 9594 ExternalUnsignedByteArray* array = 9595 ExternalUnsignedByteArray::cast(elements()); 9596 return array->SetValue(index, value); 9597 } 9598 case EXTERNAL_SHORT_ELEMENTS: { 9599 ExternalShortArray* array = ExternalShortArray::cast(elements()); 9600 return array->SetValue(index, value); 9601 } 9602 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { 9603 ExternalUnsignedShortArray* array = 9604 ExternalUnsignedShortArray::cast(elements()); 9605 return array->SetValue(index, value); 9606 } 9607 case EXTERNAL_INT_ELEMENTS: { 9608 ExternalIntArray* array = ExternalIntArray::cast(elements()); 9609 return array->SetValue(index, value); 9610 } 9611 case EXTERNAL_UNSIGNED_INT_ELEMENTS: { 9612 ExternalUnsignedIntArray* array = 9613 ExternalUnsignedIntArray::cast(elements()); 9614 return array->SetValue(index, value); 9615 } 9616 case EXTERNAL_FLOAT_ELEMENTS: { 9617 ExternalFloatArray* array = ExternalFloatArray::cast(elements()); 9618 return array->SetValue(index, value); 9619 } 9620 case EXTERNAL_DOUBLE_ELEMENTS: { 9621 ExternalDoubleArray* array = ExternalDoubleArray::cast(elements()); 9622 return array->SetValue(index, value); 9623 } 9624 case DICTIONARY_ELEMENTS: 9625 return SetDictionaryElement(index, value, attr, strict_mode, 9626 check_prototype, set_mode); 9627 case NON_STRICT_ARGUMENTS_ELEMENTS: { 9628 FixedArray* parameter_map = FixedArray::cast(elements()); 9629 uint32_t length = parameter_map->length(); 9630 Object* probe = 9631 (index < length - 2) ? parameter_map->get(index + 2) : NULL; 9632 if (probe != NULL && !probe->IsTheHole()) { 9633 Context* context = Context::cast(parameter_map->get(0)); 9634 int context_index = Smi::cast(probe)->value(); 9635 ASSERT(!context->get(context_index)->IsTheHole()); 9636 context->set(context_index, value); 9637 // Redefining attributes of an aliased element destroys fast aliasing. 9638 if (set_mode == SET_PROPERTY || attr == NONE) return value; 9639 parameter_map->set_the_hole(index + 2); 9640 // For elements that are still writable we re-establish slow aliasing. 9641 if ((attr & READ_ONLY) == 0) { 9642 MaybeObject* maybe_entry = 9643 isolate->heap()->AllocateAliasedArgumentsEntry(context_index); 9644 if (!maybe_entry->ToObject(&value)) return maybe_entry; 9645 } 9646 } 9647 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 9648 if (arguments->IsDictionary()) { 9649 return SetDictionaryElement(index, value, attr, strict_mode, 9650 check_prototype, set_mode); 9651 } else { 9652 return SetFastElement(index, value, strict_mode, check_prototype); 9653 } 9654 } 9655 } 9656 // All possible cases have been handled above. Add a return to avoid the 9657 // complaints from the compiler. 9658 UNREACHABLE(); 9659 return isolate->heap()->null_value(); 9660} 9661 9662 9663Handle<Object> JSObject::TransitionElementsKind(Handle<JSObject> object, 9664 ElementsKind to_kind) { 9665 CALL_HEAP_FUNCTION(object->GetIsolate(), 9666 object->TransitionElementsKind(to_kind), 9667 Object); 9668} 9669 9670 9671MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { 9672 ElementsKind from_kind = map()->elements_kind(); 9673 9674 Isolate* isolate = GetIsolate(); 9675 if ((from_kind == FAST_SMI_ONLY_ELEMENTS || 9676 elements() == isolate->heap()->empty_fixed_array()) && 9677 to_kind == FAST_ELEMENTS) { 9678 ASSERT(from_kind != FAST_ELEMENTS); 9679 MaybeObject* maybe_new_map = GetElementsTransitionMap(isolate, to_kind); 9680 Map* new_map; 9681 if (!maybe_new_map->To(&new_map)) return maybe_new_map; 9682 set_map(new_map); 9683 if (FLAG_trace_elements_transitions) { 9684 FixedArrayBase* elms = FixedArrayBase::cast(elements()); 9685 PrintElementsTransition(stdout, from_kind, elms, to_kind, elms); 9686 } 9687 return this; 9688 } 9689 9690 FixedArrayBase* elms = FixedArrayBase::cast(elements()); 9691 uint32_t capacity = static_cast<uint32_t>(elms->length()); 9692 uint32_t length = capacity; 9693 9694 if (IsJSArray()) { 9695 Object* raw_length = JSArray::cast(this)->length(); 9696 if (raw_length->IsUndefined()) { 9697 // If length is undefined, then JSArray is being initialized and has no 9698 // elements, assume a length of zero. 9699 length = 0; 9700 } else { 9701 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); 9702 } 9703 } 9704 9705 if (from_kind == FAST_SMI_ONLY_ELEMENTS && 9706 to_kind == FAST_DOUBLE_ELEMENTS) { 9707 MaybeObject* maybe_result = 9708 SetFastDoubleElementsCapacityAndLength(capacity, length); 9709 if (maybe_result->IsFailure()) return maybe_result; 9710 return this; 9711 } 9712 9713 if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { 9714 MaybeObject* maybe_result = SetFastElementsCapacityAndLength( 9715 capacity, length, kDontAllowSmiOnlyElements); 9716 if (maybe_result->IsFailure()) return maybe_result; 9717 return this; 9718 } 9719 9720 // This method should never be called for any other case than the ones 9721 // handled above. 9722 UNREACHABLE(); 9723 return GetIsolate()->heap()->null_value(); 9724} 9725 9726 9727// static 9728bool Map::IsValidElementsTransition(ElementsKind from_kind, 9729 ElementsKind to_kind) { 9730 return 9731 (from_kind == FAST_SMI_ONLY_ELEMENTS && 9732 (to_kind == FAST_DOUBLE_ELEMENTS || to_kind == FAST_ELEMENTS)) || 9733 (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS); 9734} 9735 9736 9737MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, 9738 Object* value) { 9739 uint32_t old_len = 0; 9740 CHECK(length()->ToArrayIndex(&old_len)); 9741 // Check to see if we need to update the length. For now, we make 9742 // sure that the length stays within 32-bits (unsigned). 9743 if (index >= old_len && index != 0xffffffff) { 9744 Object* len; 9745 { MaybeObject* maybe_len = 9746 GetHeap()->NumberFromDouble(static_cast<double>(index) + 1); 9747 if (!maybe_len->ToObject(&len)) return maybe_len; 9748 } 9749 set_length(len); 9750 } 9751 return value; 9752} 9753 9754 9755MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver, 9756 uint32_t index) { 9757 Isolate* isolate = GetIsolate(); 9758 // Make sure that the top context does not change when doing 9759 // callbacks or interceptor calls. 9760 AssertNoContextChange ncc; 9761 HandleScope scope(isolate); 9762 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor(), isolate); 9763 Handle<Object> this_handle(receiver, isolate); 9764 Handle<JSObject> holder_handle(this, isolate); 9765 if (!interceptor->getter()->IsUndefined()) { 9766 v8::IndexedPropertyGetter getter = 9767 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); 9768 LOG(isolate, 9769 ApiIndexedPropertyAccess("interceptor-indexed-get", this, index)); 9770 CustomArguments args(isolate, interceptor->data(), receiver, this); 9771 v8::AccessorInfo info(args.end()); 9772 v8::Handle<v8::Value> result; 9773 { 9774 // Leaving JavaScript. 9775 VMState state(isolate, EXTERNAL); 9776 result = getter(index, info); 9777 } 9778 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 9779 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result); 9780 } 9781 9782 Heap* heap = holder_handle->GetHeap(); 9783 ElementsAccessor* handler = holder_handle->GetElementsAccessor(); 9784 MaybeObject* raw_result = handler->Get(*this_handle, 9785 *holder_handle, 9786 index); 9787 if (raw_result != heap->the_hole_value()) return raw_result; 9788 9789 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 9790 9791 Object* pt = holder_handle->GetPrototype(); 9792 if (pt == heap->null_value()) return heap->undefined_value(); 9793 return pt->GetElementWithReceiver(*this_handle, index); 9794} 9795 9796 9797bool JSObject::HasDenseElements() { 9798 int capacity = 0; 9799 int used = 0; 9800 GetElementsCapacityAndUsage(&capacity, &used); 9801 return (capacity == 0) || (used > (capacity / 2)); 9802} 9803 9804 9805void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { 9806 *capacity = 0; 9807 *used = 0; 9808 9809 FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements()); 9810 FixedArray* backing_store = NULL; 9811 switch (GetElementsKind()) { 9812 case NON_STRICT_ARGUMENTS_ELEMENTS: 9813 backing_store_base = 9814 FixedArray::cast(FixedArray::cast(backing_store_base)->get(1)); 9815 backing_store = FixedArray::cast(backing_store_base); 9816 if (backing_store->IsDictionary()) { 9817 SeededNumberDictionary* dictionary = 9818 SeededNumberDictionary::cast(backing_store); 9819 *capacity = dictionary->Capacity(); 9820 *used = dictionary->NumberOfElements(); 9821 break; 9822 } 9823 // Fall through. 9824 case FAST_SMI_ONLY_ELEMENTS: 9825 case FAST_ELEMENTS: 9826 backing_store = FixedArray::cast(backing_store_base); 9827 *capacity = backing_store->length(); 9828 for (int i = 0; i < *capacity; ++i) { 9829 if (!backing_store->get(i)->IsTheHole()) ++(*used); 9830 } 9831 break; 9832 case DICTIONARY_ELEMENTS: { 9833 SeededNumberDictionary* dictionary = 9834 SeededNumberDictionary::cast(FixedArray::cast(elements())); 9835 *capacity = dictionary->Capacity(); 9836 *used = dictionary->NumberOfElements(); 9837 break; 9838 } 9839 case FAST_DOUBLE_ELEMENTS: { 9840 FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); 9841 *capacity = elms->length(); 9842 for (int i = 0; i < *capacity; i++) { 9843 if (!elms->is_the_hole(i)) ++(*used); 9844 } 9845 break; 9846 } 9847 case EXTERNAL_BYTE_ELEMENTS: 9848 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 9849 case EXTERNAL_SHORT_ELEMENTS: 9850 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 9851 case EXTERNAL_INT_ELEMENTS: 9852 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 9853 case EXTERNAL_FLOAT_ELEMENTS: 9854 case EXTERNAL_DOUBLE_ELEMENTS: 9855 case EXTERNAL_PIXEL_ELEMENTS: 9856 // External arrays are considered 100% used. 9857 ExternalArray* external_array = ExternalArray::cast(elements()); 9858 *capacity = external_array->length(); 9859 *used = external_array->length(); 9860 break; 9861 } 9862} 9863 9864 9865bool JSObject::ShouldConvertToSlowElements(int new_capacity) { 9866 STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <= 9867 kMaxUncheckedFastElementsLength); 9868 if (new_capacity <= kMaxUncheckedOldFastElementsLength || 9869 (new_capacity <= kMaxUncheckedFastElementsLength && 9870 GetHeap()->InNewSpace(this))) { 9871 return false; 9872 } 9873 // If the fast-case backing storage takes up roughly three times as 9874 // much space (in machine words) as a dictionary backing storage 9875 // would, the object should have slow elements. 9876 int old_capacity = 0; 9877 int used_elements = 0; 9878 GetElementsCapacityAndUsage(&old_capacity, &used_elements); 9879 int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) * 9880 SeededNumberDictionary::kEntrySize; 9881 return 3 * dictionary_size <= new_capacity; 9882} 9883 9884 9885bool JSObject::ShouldConvertToFastElements() { 9886 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); 9887 // If the elements are sparse, we should not go back to fast case. 9888 if (!HasDenseElements()) return false; 9889 // An object requiring access checks is never allowed to have fast 9890 // elements. If it had fast elements we would skip security checks. 9891 if (IsAccessCheckNeeded()) return false; 9892 9893 FixedArray* elements = FixedArray::cast(this->elements()); 9894 SeededNumberDictionary* dictionary = NULL; 9895 if (elements->map() == GetHeap()->non_strict_arguments_elements_map()) { 9896 dictionary = SeededNumberDictionary::cast(elements->get(1)); 9897 } else { 9898 dictionary = SeededNumberDictionary::cast(elements); 9899 } 9900 // If an element has been added at a very high index in the elements 9901 // dictionary, we cannot go back to fast case. 9902 if (dictionary->requires_slow_elements()) return false; 9903 // If the dictionary backing storage takes up roughly half as much 9904 // space (in machine words) as a fast-case backing storage would, 9905 // the object should have fast elements. 9906 uint32_t array_size = 0; 9907 if (IsJSArray()) { 9908 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size)); 9909 } else { 9910 array_size = dictionary->max_number_key(); 9911 } 9912 uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) * 9913 SeededNumberDictionary::kEntrySize; 9914 return 2 * dictionary_size >= array_size; 9915} 9916 9917 9918bool JSObject::ShouldConvertToFastDoubleElements( 9919 bool* has_smi_only_elements) { 9920 *has_smi_only_elements = false; 9921 if (FLAG_unbox_double_arrays) { 9922 ASSERT(HasDictionaryElements()); 9923 SeededNumberDictionary* dictionary = 9924 SeededNumberDictionary::cast(elements()); 9925 bool found_double = false; 9926 for (int i = 0; i < dictionary->Capacity(); i++) { 9927 Object* key = dictionary->KeyAt(i); 9928 if (key->IsNumber()) { 9929 Object* value = dictionary->ValueAt(i); 9930 if (!value->IsNumber()) return false; 9931 if (!value->IsSmi()) { 9932 found_double = true; 9933 } 9934 } 9935 } 9936 *has_smi_only_elements = !found_double; 9937 return found_double; 9938 } else { 9939 return false; 9940 } 9941} 9942 9943 9944// Certain compilers request function template instantiation when they 9945// see the definition of the other template functions in the 9946// class. This requires us to have the template functions put 9947// together, so even though this function belongs in objects-debug.cc, 9948// we keep it here instead to satisfy certain compilers. 9949#ifdef OBJECT_PRINT 9950template<typename Shape, typename Key> 9951void Dictionary<Shape, Key>::Print(FILE* out) { 9952 int capacity = HashTable<Shape, Key>::Capacity(); 9953 for (int i = 0; i < capacity; i++) { 9954 Object* k = HashTable<Shape, Key>::KeyAt(i); 9955 if (HashTable<Shape, Key>::IsKey(k)) { 9956 PrintF(out, " "); 9957 if (k->IsString()) { 9958 String::cast(k)->StringPrint(out); 9959 } else { 9960 k->ShortPrint(out); 9961 } 9962 PrintF(out, ": "); 9963 ValueAt(i)->ShortPrint(out); 9964 PrintF(out, "\n"); 9965 } 9966 } 9967} 9968#endif 9969 9970 9971template<typename Shape, typename Key> 9972void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) { 9973 int pos = 0; 9974 int capacity = HashTable<Shape, Key>::Capacity(); 9975 AssertNoAllocation no_gc; 9976 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); 9977 for (int i = 0; i < capacity; i++) { 9978 Object* k = Dictionary<Shape, Key>::KeyAt(i); 9979 if (Dictionary<Shape, Key>::IsKey(k)) { 9980 elements->set(pos++, ValueAt(i), mode); 9981 } 9982 } 9983 ASSERT(pos == elements->length()); 9984} 9985 9986 9987InterceptorInfo* JSObject::GetNamedInterceptor() { 9988 ASSERT(map()->has_named_interceptor()); 9989 JSFunction* constructor = JSFunction::cast(map()->constructor()); 9990 ASSERT(constructor->shared()->IsApiFunction()); 9991 Object* result = 9992 constructor->shared()->get_api_func_data()->named_property_handler(); 9993 return InterceptorInfo::cast(result); 9994} 9995 9996 9997InterceptorInfo* JSObject::GetIndexedInterceptor() { 9998 ASSERT(map()->has_indexed_interceptor()); 9999 JSFunction* constructor = JSFunction::cast(map()->constructor()); 10000 ASSERT(constructor->shared()->IsApiFunction()); 10001 Object* result = 10002 constructor->shared()->get_api_func_data()->indexed_property_handler(); 10003 return InterceptorInfo::cast(result); 10004} 10005 10006 10007MaybeObject* JSObject::GetPropertyPostInterceptor( 10008 JSReceiver* receiver, 10009 String* name, 10010 PropertyAttributes* attributes) { 10011 // Check local property in holder, ignore interceptor. 10012 LookupResult result(GetIsolate()); 10013 LocalLookupRealNamedProperty(name, &result); 10014 if (result.IsProperty()) { 10015 return GetProperty(receiver, &result, name, attributes); 10016 } 10017 // Continue searching via the prototype chain. 10018 Object* pt = GetPrototype(); 10019 *attributes = ABSENT; 10020 if (pt->IsNull()) return GetHeap()->undefined_value(); 10021 return pt->GetPropertyWithReceiver(receiver, name, attributes); 10022} 10023 10024 10025MaybeObject* JSObject::GetLocalPropertyPostInterceptor( 10026 JSReceiver* receiver, 10027 String* name, 10028 PropertyAttributes* attributes) { 10029 // Check local property in holder, ignore interceptor. 10030 LookupResult result(GetIsolate()); 10031 LocalLookupRealNamedProperty(name, &result); 10032 if (result.IsProperty()) { 10033 return GetProperty(receiver, &result, name, attributes); 10034 } 10035 return GetHeap()->undefined_value(); 10036} 10037 10038 10039MaybeObject* JSObject::GetPropertyWithInterceptor( 10040 JSReceiver* receiver, 10041 String* name, 10042 PropertyAttributes* attributes) { 10043 Isolate* isolate = GetIsolate(); 10044 InterceptorInfo* interceptor = GetNamedInterceptor(); 10045 HandleScope scope(isolate); 10046 Handle<JSReceiver> receiver_handle(receiver); 10047 Handle<JSObject> holder_handle(this); 10048 Handle<String> name_handle(name); 10049 10050 if (!interceptor->getter()->IsUndefined()) { 10051 v8::NamedPropertyGetter getter = 10052 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter()); 10053 LOG(isolate, 10054 ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name)); 10055 CustomArguments args(isolate, interceptor->data(), receiver, this); 10056 v8::AccessorInfo info(args.end()); 10057 v8::Handle<v8::Value> result; 10058 { 10059 // Leaving JavaScript. 10060 VMState state(isolate, EXTERNAL); 10061 result = getter(v8::Utils::ToLocal(name_handle), info); 10062 } 10063 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 10064 if (!result.IsEmpty()) { 10065 *attributes = NONE; 10066 return *v8::Utils::OpenHandle(*result); 10067 } 10068 } 10069 10070 MaybeObject* result = holder_handle->GetPropertyPostInterceptor( 10071 *receiver_handle, 10072 *name_handle, 10073 attributes); 10074 RETURN_IF_SCHEDULED_EXCEPTION(isolate); 10075 return result; 10076} 10077 10078 10079bool JSObject::HasRealNamedProperty(String* key) { 10080 // Check access rights if needed. 10081 Isolate* isolate = GetIsolate(); 10082 if (IsAccessCheckNeeded()) { 10083 if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) { 10084 isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 10085 return false; 10086 } 10087 } 10088 10089 LookupResult result(isolate); 10090 LocalLookupRealNamedProperty(key, &result); 10091 return result.IsProperty() && (result.type() != INTERCEPTOR); 10092} 10093 10094 10095bool JSObject::HasRealElementProperty(uint32_t index) { 10096 // Check access rights if needed. 10097 if (IsAccessCheckNeeded()) { 10098 Heap* heap = GetHeap(); 10099 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { 10100 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 10101 return false; 10102 } 10103 } 10104 10105 // Handle [] on String objects. 10106 if (this->IsStringObjectWithCharacterAt(index)) return true; 10107 10108 switch (GetElementsKind()) { 10109 case FAST_SMI_ONLY_ELEMENTS: 10110 case FAST_ELEMENTS: { 10111 uint32_t length = IsJSArray() ? 10112 static_cast<uint32_t>( 10113 Smi::cast(JSArray::cast(this)->length())->value()) : 10114 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 10115 return (index < length) && 10116 !FixedArray::cast(elements())->get(index)->IsTheHole(); 10117 } 10118 case FAST_DOUBLE_ELEMENTS: { 10119 uint32_t length = IsJSArray() ? 10120 static_cast<uint32_t>( 10121 Smi::cast(JSArray::cast(this)->length())->value()) : 10122 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length()); 10123 return (index < length) && 10124 !FixedDoubleArray::cast(elements())->is_the_hole(index); 10125 break; 10126 } 10127 case EXTERNAL_PIXEL_ELEMENTS: { 10128 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); 10129 return index < static_cast<uint32_t>(pixels->length()); 10130 } 10131 case EXTERNAL_BYTE_ELEMENTS: 10132 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 10133 case EXTERNAL_SHORT_ELEMENTS: 10134 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 10135 case EXTERNAL_INT_ELEMENTS: 10136 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 10137 case EXTERNAL_FLOAT_ELEMENTS: 10138 case EXTERNAL_DOUBLE_ELEMENTS: { 10139 ExternalArray* array = ExternalArray::cast(elements()); 10140 return index < static_cast<uint32_t>(array->length()); 10141 } 10142 case DICTIONARY_ELEMENTS: { 10143 return element_dictionary()->FindEntry(index) 10144 != SeededNumberDictionary::kNotFound; 10145 } 10146 case NON_STRICT_ARGUMENTS_ELEMENTS: 10147 UNIMPLEMENTED(); 10148 break; 10149 } 10150 // All possibilities have been handled above already. 10151 UNREACHABLE(); 10152 return GetHeap()->null_value(); 10153} 10154 10155 10156bool JSObject::HasRealNamedCallbackProperty(String* key) { 10157 // Check access rights if needed. 10158 Isolate* isolate = GetIsolate(); 10159 if (IsAccessCheckNeeded()) { 10160 if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) { 10161 isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); 10162 return false; 10163 } 10164 } 10165 10166 LookupResult result(isolate); 10167 LocalLookupRealNamedProperty(key, &result); 10168 return result.IsFound() && (result.type() == CALLBACKS); 10169} 10170 10171 10172int JSObject::NumberOfLocalProperties(PropertyAttributes filter) { 10173 return HasFastProperties() ? 10174 map()->NumberOfDescribedProperties(filter) : 10175 property_dictionary()->NumberOfElementsFilterAttributes(filter); 10176} 10177 10178 10179void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { 10180 Object* temp = get(i); 10181 set(i, get(j)); 10182 set(j, temp); 10183 if (this != numbers) { 10184 temp = numbers->get(i); 10185 numbers->set(i, Smi::cast(numbers->get(j))); 10186 numbers->set(j, Smi::cast(temp)); 10187 } 10188} 10189 10190 10191static void InsertionSortPairs(FixedArray* content, 10192 FixedArray* numbers, 10193 int len) { 10194 for (int i = 1; i < len; i++) { 10195 int j = i; 10196 while (j > 0 && 10197 (NumberToUint32(numbers->get(j - 1)) > 10198 NumberToUint32(numbers->get(j)))) { 10199 content->SwapPairs(numbers, j - 1, j); 10200 j--; 10201 } 10202 } 10203} 10204 10205 10206void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) { 10207 // In-place heap sort. 10208 ASSERT(content->length() == numbers->length()); 10209 10210 // Bottom-up max-heap construction. 10211 for (int i = 1; i < len; ++i) { 10212 int child_index = i; 10213 while (child_index > 0) { 10214 int parent_index = ((child_index + 1) >> 1) - 1; 10215 uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); 10216 uint32_t child_value = NumberToUint32(numbers->get(child_index)); 10217 if (parent_value < child_value) { 10218 content->SwapPairs(numbers, parent_index, child_index); 10219 } else { 10220 break; 10221 } 10222 child_index = parent_index; 10223 } 10224 } 10225 10226 // Extract elements and create sorted array. 10227 for (int i = len - 1; i > 0; --i) { 10228 // Put max element at the back of the array. 10229 content->SwapPairs(numbers, 0, i); 10230 // Sift down the new top element. 10231 int parent_index = 0; 10232 while (true) { 10233 int child_index = ((parent_index + 1) << 1) - 1; 10234 if (child_index >= i) break; 10235 uint32_t child1_value = NumberToUint32(numbers->get(child_index)); 10236 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1)); 10237 uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); 10238 if (child_index + 1 >= i || child1_value > child2_value) { 10239 if (parent_value > child1_value) break; 10240 content->SwapPairs(numbers, parent_index, child_index); 10241 parent_index = child_index; 10242 } else { 10243 if (parent_value > child2_value) break; 10244 content->SwapPairs(numbers, parent_index, child_index + 1); 10245 parent_index = child_index + 1; 10246 } 10247 } 10248 } 10249} 10250 10251 10252// Sort this array and the numbers as pairs wrt. the (distinct) numbers. 10253void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { 10254 ASSERT(this->length() == numbers->length()); 10255 // For small arrays, simply use insertion sort. 10256 if (len <= 10) { 10257 InsertionSortPairs(this, numbers, len); 10258 return; 10259 } 10260 // Check the range of indices. 10261 uint32_t min_index = NumberToUint32(numbers->get(0)); 10262 uint32_t max_index = min_index; 10263 uint32_t i; 10264 for (i = 1; i < len; i++) { 10265 if (NumberToUint32(numbers->get(i)) < min_index) { 10266 min_index = NumberToUint32(numbers->get(i)); 10267 } else if (NumberToUint32(numbers->get(i)) > max_index) { 10268 max_index = NumberToUint32(numbers->get(i)); 10269 } 10270 } 10271 if (max_index - min_index + 1 == len) { 10272 // Indices form a contiguous range, unless there are duplicates. 10273 // Do an in-place linear time sort assuming distinct numbers, but 10274 // avoid hanging in case they are not. 10275 for (i = 0; i < len; i++) { 10276 uint32_t p; 10277 uint32_t j = 0; 10278 // While the current element at i is not at its correct position p, 10279 // swap the elements at these two positions. 10280 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i && 10281 j++ < len) { 10282 SwapPairs(numbers, i, p); 10283 } 10284 } 10285 } else { 10286 HeapSortPairs(this, numbers, len); 10287 return; 10288 } 10289} 10290 10291 10292// Fill in the names of local properties into the supplied storage. The main 10293// purpose of this function is to provide reflection information for the object 10294// mirrors. 10295void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) { 10296 ASSERT(storage->length() >= (NumberOfLocalProperties() - index)); 10297 if (HasFastProperties()) { 10298 DescriptorArray* descs = map()->instance_descriptors(); 10299 for (int i = 0; i < descs->number_of_descriptors(); i++) { 10300 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i)); 10301 } 10302 ASSERT(storage->length() >= index); 10303 } else { 10304 property_dictionary()->CopyKeysTo(storage, 10305 index, 10306 StringDictionary::UNSORTED); 10307 } 10308} 10309 10310 10311int JSObject::NumberOfLocalElements(PropertyAttributes filter) { 10312 return GetLocalElementKeys(NULL, filter); 10313} 10314 10315 10316int JSObject::NumberOfEnumElements() { 10317 // Fast case for objects with no elements. 10318 if (!IsJSValue() && HasFastElements()) { 10319 uint32_t length = IsJSArray() ? 10320 static_cast<uint32_t>( 10321 Smi::cast(JSArray::cast(this)->length())->value()) : 10322 static_cast<uint32_t>(FixedArray::cast(elements())->length()); 10323 if (length == 0) return 0; 10324 } 10325 // Compute the number of enumerable elements. 10326 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM)); 10327} 10328 10329 10330int JSObject::GetLocalElementKeys(FixedArray* storage, 10331 PropertyAttributes filter) { 10332 int counter = 0; 10333 switch (GetElementsKind()) { 10334 case FAST_SMI_ONLY_ELEMENTS: 10335 case FAST_ELEMENTS: { 10336 int length = IsJSArray() ? 10337 Smi::cast(JSArray::cast(this)->length())->value() : 10338 FixedArray::cast(elements())->length(); 10339 for (int i = 0; i < length; i++) { 10340 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) { 10341 if (storage != NULL) { 10342 storage->set(counter, Smi::FromInt(i)); 10343 } 10344 counter++; 10345 } 10346 } 10347 ASSERT(!storage || storage->length() >= counter); 10348 break; 10349 } 10350 case FAST_DOUBLE_ELEMENTS: { 10351 int length = IsJSArray() ? 10352 Smi::cast(JSArray::cast(this)->length())->value() : 10353 FixedDoubleArray::cast(elements())->length(); 10354 for (int i = 0; i < length; i++) { 10355 if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) { 10356 if (storage != NULL) { 10357 storage->set(counter, Smi::FromInt(i)); 10358 } 10359 counter++; 10360 } 10361 } 10362 ASSERT(!storage || storage->length() >= counter); 10363 break; 10364 } 10365 case EXTERNAL_PIXEL_ELEMENTS: { 10366 int length = ExternalPixelArray::cast(elements())->length(); 10367 while (counter < length) { 10368 if (storage != NULL) { 10369 storage->set(counter, Smi::FromInt(counter)); 10370 } 10371 counter++; 10372 } 10373 ASSERT(!storage || storage->length() >= counter); 10374 break; 10375 } 10376 case EXTERNAL_BYTE_ELEMENTS: 10377 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 10378 case EXTERNAL_SHORT_ELEMENTS: 10379 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 10380 case EXTERNAL_INT_ELEMENTS: 10381 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 10382 case EXTERNAL_FLOAT_ELEMENTS: 10383 case EXTERNAL_DOUBLE_ELEMENTS: { 10384 int length = ExternalArray::cast(elements())->length(); 10385 while (counter < length) { 10386 if (storage != NULL) { 10387 storage->set(counter, Smi::FromInt(counter)); 10388 } 10389 counter++; 10390 } 10391 ASSERT(!storage || storage->length() >= counter); 10392 break; 10393 } 10394 case DICTIONARY_ELEMENTS: { 10395 if (storage != NULL) { 10396 element_dictionary()->CopyKeysTo(storage, 10397 filter, 10398 SeededNumberDictionary::SORTED); 10399 } 10400 counter += element_dictionary()->NumberOfElementsFilterAttributes(filter); 10401 break; 10402 } 10403 case NON_STRICT_ARGUMENTS_ELEMENTS: { 10404 FixedArray* parameter_map = FixedArray::cast(elements()); 10405 int mapped_length = parameter_map->length() - 2; 10406 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); 10407 if (arguments->IsDictionary()) { 10408 // Copy the keys from arguments first, because Dictionary::CopyKeysTo 10409 // will insert in storage starting at index 0. 10410 SeededNumberDictionary* dictionary = 10411 SeededNumberDictionary::cast(arguments); 10412 if (storage != NULL) { 10413 dictionary->CopyKeysTo( 10414 storage, filter, SeededNumberDictionary::UNSORTED); 10415 } 10416 counter += dictionary->NumberOfElementsFilterAttributes(filter); 10417 for (int i = 0; i < mapped_length; ++i) { 10418 if (!parameter_map->get(i + 2)->IsTheHole()) { 10419 if (storage != NULL) storage->set(counter, Smi::FromInt(i)); 10420 ++counter; 10421 } 10422 } 10423 if (storage != NULL) storage->SortPairs(storage, counter); 10424 10425 } else { 10426 int backing_length = arguments->length(); 10427 int i = 0; 10428 for (; i < mapped_length; ++i) { 10429 if (!parameter_map->get(i + 2)->IsTheHole()) { 10430 if (storage != NULL) storage->set(counter, Smi::FromInt(i)); 10431 ++counter; 10432 } else if (i < backing_length && !arguments->get(i)->IsTheHole()) { 10433 if (storage != NULL) storage->set(counter, Smi::FromInt(i)); 10434 ++counter; 10435 } 10436 } 10437 for (; i < backing_length; ++i) { 10438 if (storage != NULL) storage->set(counter, Smi::FromInt(i)); 10439 ++counter; 10440 } 10441 } 10442 break; 10443 } 10444 } 10445 10446 if (this->IsJSValue()) { 10447 Object* val = JSValue::cast(this)->value(); 10448 if (val->IsString()) { 10449 String* str = String::cast(val); 10450 if (storage) { 10451 for (int i = 0; i < str->length(); i++) { 10452 storage->set(counter + i, Smi::FromInt(i)); 10453 } 10454 } 10455 counter += str->length(); 10456 } 10457 } 10458 ASSERT(!storage || storage->length() == counter); 10459 return counter; 10460} 10461 10462 10463int JSObject::GetEnumElementKeys(FixedArray* storage) { 10464 return GetLocalElementKeys(storage, 10465 static_cast<PropertyAttributes>(DONT_ENUM)); 10466} 10467 10468 10469// StringKey simply carries a string object as key. 10470class StringKey : public HashTableKey { 10471 public: 10472 explicit StringKey(String* string) : 10473 string_(string), 10474 hash_(HashForObject(string)) { } 10475 10476 bool IsMatch(Object* string) { 10477 // We know that all entries in a hash table had their hash keys created. 10478 // Use that knowledge to have fast failure. 10479 if (hash_ != HashForObject(string)) { 10480 return false; 10481 } 10482 return string_->Equals(String::cast(string)); 10483 } 10484 10485 uint32_t Hash() { return hash_; } 10486 10487 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); } 10488 10489 Object* AsObject() { return string_; } 10490 10491 String* string_; 10492 uint32_t hash_; 10493}; 10494 10495 10496// StringSharedKeys are used as keys in the eval cache. 10497class StringSharedKey : public HashTableKey { 10498 public: 10499 StringSharedKey(String* source, 10500 SharedFunctionInfo* shared, 10501 LanguageMode language_mode, 10502 int scope_position) 10503 : source_(source), 10504 shared_(shared), 10505 language_mode_(language_mode), 10506 scope_position_(scope_position) { } 10507 10508 bool IsMatch(Object* other) { 10509 if (!other->IsFixedArray()) return false; 10510 FixedArray* other_array = FixedArray::cast(other); 10511 SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0)); 10512 if (shared != shared_) return false; 10513 int language_unchecked = Smi::cast(other_array->get(2))->value(); 10514 ASSERT(language_unchecked == CLASSIC_MODE || 10515 language_unchecked == STRICT_MODE || 10516 language_unchecked == EXTENDED_MODE); 10517 LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); 10518 if (language_mode != language_mode_) return false; 10519 int scope_position = Smi::cast(other_array->get(3))->value(); 10520 if (scope_position != scope_position_) return false; 10521 String* source = String::cast(other_array->get(1)); 10522 return source->Equals(source_); 10523 } 10524 10525 static uint32_t StringSharedHashHelper(String* source, 10526 SharedFunctionInfo* shared, 10527 LanguageMode language_mode, 10528 int scope_position) { 10529 uint32_t hash = source->Hash(); 10530 if (shared->HasSourceCode()) { 10531 // Instead of using the SharedFunctionInfo pointer in the hash 10532 // code computation, we use a combination of the hash of the 10533 // script source code and the start position of the calling scope. 10534 // We do this to ensure that the cache entries can survive garbage 10535 // collection. 10536 Script* script = Script::cast(shared->script()); 10537 hash ^= String::cast(script->source())->Hash(); 10538 if (language_mode == STRICT_MODE) hash ^= 0x8000; 10539 if (language_mode == EXTENDED_MODE) hash ^= 0x0080; 10540 hash += scope_position; 10541 } 10542 return hash; 10543 } 10544 10545 uint32_t Hash() { 10546 return StringSharedHashHelper( 10547 source_, shared_, language_mode_, scope_position_); 10548 } 10549 10550 uint32_t HashForObject(Object* obj) { 10551 FixedArray* other_array = FixedArray::cast(obj); 10552 SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0)); 10553 String* source = String::cast(other_array->get(1)); 10554 int language_unchecked = Smi::cast(other_array->get(2))->value(); 10555 ASSERT(language_unchecked == CLASSIC_MODE || 10556 language_unchecked == STRICT_MODE || 10557 language_unchecked == EXTENDED_MODE); 10558 LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); 10559 int scope_position = Smi::cast(other_array->get(3))->value(); 10560 return StringSharedHashHelper( 10561 source, shared, language_mode, scope_position); 10562 } 10563 10564 MUST_USE_RESULT MaybeObject* AsObject() { 10565 Object* obj; 10566 { MaybeObject* maybe_obj = source_->GetHeap()->AllocateFixedArray(4); 10567 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 10568 } 10569 FixedArray* other_array = FixedArray::cast(obj); 10570 other_array->set(0, shared_); 10571 other_array->set(1, source_); 10572 other_array->set(2, Smi::FromInt(language_mode_)); 10573 other_array->set(3, Smi::FromInt(scope_position_)); 10574 return other_array; 10575 } 10576 10577 private: 10578 String* source_; 10579 SharedFunctionInfo* shared_; 10580 LanguageMode language_mode_; 10581 int scope_position_; 10582}; 10583 10584 10585// RegExpKey carries the source and flags of a regular expression as key. 10586class RegExpKey : public HashTableKey { 10587 public: 10588 RegExpKey(String* string, JSRegExp::Flags flags) 10589 : string_(string), 10590 flags_(Smi::FromInt(flags.value())) { } 10591 10592 // Rather than storing the key in the hash table, a pointer to the 10593 // stored value is stored where the key should be. IsMatch then 10594 // compares the search key to the found object, rather than comparing 10595 // a key to a key. 10596 bool IsMatch(Object* obj) { 10597 FixedArray* val = FixedArray::cast(obj); 10598 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex))) 10599 && (flags_ == val->get(JSRegExp::kFlagsIndex)); 10600 } 10601 10602 uint32_t Hash() { return RegExpHash(string_, flags_); } 10603 10604 Object* AsObject() { 10605 // Plain hash maps, which is where regexp keys are used, don't 10606 // use this function. 10607 UNREACHABLE(); 10608 return NULL; 10609 } 10610 10611 uint32_t HashForObject(Object* obj) { 10612 FixedArray* val = FixedArray::cast(obj); 10613 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)), 10614 Smi::cast(val->get(JSRegExp::kFlagsIndex))); 10615 } 10616 10617 static uint32_t RegExpHash(String* string, Smi* flags) { 10618 return string->Hash() + flags->value(); 10619 } 10620 10621 String* string_; 10622 Smi* flags_; 10623}; 10624 10625// Utf8SymbolKey carries a vector of chars as key. 10626class Utf8SymbolKey : public HashTableKey { 10627 public: 10628 explicit Utf8SymbolKey(Vector<const char> string, uint32_t seed) 10629 : string_(string), hash_field_(0), seed_(seed) { } 10630 10631 bool IsMatch(Object* string) { 10632 return String::cast(string)->IsEqualTo(string_); 10633 } 10634 10635 uint32_t Hash() { 10636 if (hash_field_ != 0) return hash_field_ >> String::kHashShift; 10637 unibrow::Utf8InputBuffer<> buffer(string_.start(), 10638 static_cast<unsigned>(string_.length())); 10639 chars_ = buffer.Utf16Length(); 10640 hash_field_ = String::ComputeHashField(&buffer, chars_, seed_); 10641 uint32_t result = hash_field_ >> String::kHashShift; 10642 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 10643 return result; 10644 } 10645 10646 uint32_t HashForObject(Object* other) { 10647 return String::cast(other)->Hash(); 10648 } 10649 10650 MaybeObject* AsObject() { 10651 if (hash_field_ == 0) Hash(); 10652 return Isolate::Current()->heap()->AllocateSymbol( 10653 string_, chars_, hash_field_); 10654 } 10655 10656 Vector<const char> string_; 10657 uint32_t hash_field_; 10658 int chars_; // Caches the number of characters when computing the hash code. 10659 uint32_t seed_; 10660}; 10661 10662 10663template <typename Char> 10664class SequentialSymbolKey : public HashTableKey { 10665 public: 10666 explicit SequentialSymbolKey(Vector<const Char> string, uint32_t seed) 10667 : string_(string), hash_field_(0), seed_(seed) { } 10668 10669 uint32_t Hash() { 10670 StringHasher hasher(string_.length(), seed_); 10671 10672 // Very long strings have a trivial hash that doesn't inspect the 10673 // string contents. 10674 if (hasher.has_trivial_hash()) { 10675 hash_field_ = hasher.GetHashField(); 10676 } else { 10677 int i = 0; 10678 // Do the iterative array index computation as long as there is a 10679 // chance this is an array index. 10680 while (i < string_.length() && hasher.is_array_index()) { 10681 hasher.AddCharacter(static_cast<uc32>(string_[i])); 10682 i++; 10683 } 10684 10685 // Process the remaining characters without updating the array 10686 // index. 10687 while (i < string_.length()) { 10688 hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i])); 10689 i++; 10690 } 10691 hash_field_ = hasher.GetHashField(); 10692 } 10693 10694 uint32_t result = hash_field_ >> String::kHashShift; 10695 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 10696 return result; 10697 } 10698 10699 10700 uint32_t HashForObject(Object* other) { 10701 return String::cast(other)->Hash(); 10702 } 10703 10704 Vector<const Char> string_; 10705 uint32_t hash_field_; 10706 uint32_t seed_; 10707}; 10708 10709 10710 10711class AsciiSymbolKey : public SequentialSymbolKey<char> { 10712 public: 10713 AsciiSymbolKey(Vector<const char> str, uint32_t seed) 10714 : SequentialSymbolKey<char>(str, seed) { } 10715 10716 bool IsMatch(Object* string) { 10717 return String::cast(string)->IsAsciiEqualTo(string_); 10718 } 10719 10720 MaybeObject* AsObject() { 10721 if (hash_field_ == 0) Hash(); 10722 return HEAP->AllocateAsciiSymbol(string_, hash_field_); 10723 } 10724}; 10725 10726 10727class SubStringAsciiSymbolKey : public HashTableKey { 10728 public: 10729 explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string, 10730 int from, 10731 int length, 10732 uint32_t seed) 10733 : string_(string), from_(from), length_(length), seed_(seed) { } 10734 10735 uint32_t Hash() { 10736 ASSERT(length_ >= 0); 10737 ASSERT(from_ + length_ <= string_->length()); 10738 StringHasher hasher(length_, string_->GetHeap()->HashSeed()); 10739 10740 // Very long strings have a trivial hash that doesn't inspect the 10741 // string contents. 10742 if (hasher.has_trivial_hash()) { 10743 hash_field_ = hasher.GetHashField(); 10744 } else { 10745 int i = 0; 10746 // Do the iterative array index computation as long as there is a 10747 // chance this is an array index. 10748 while (i < length_ && hasher.is_array_index()) { 10749 hasher.AddCharacter(static_cast<uc32>( 10750 string_->SeqAsciiStringGet(i + from_))); 10751 i++; 10752 } 10753 10754 // Process the remaining characters without updating the array 10755 // index. 10756 while (i < length_) { 10757 hasher.AddCharacterNoIndex(static_cast<uc32>( 10758 string_->SeqAsciiStringGet(i + from_))); 10759 i++; 10760 } 10761 hash_field_ = hasher.GetHashField(); 10762 } 10763 10764 uint32_t result = hash_field_ >> String::kHashShift; 10765 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. 10766 return result; 10767 } 10768 10769 10770 uint32_t HashForObject(Object* other) { 10771 return String::cast(other)->Hash(); 10772 } 10773 10774 bool IsMatch(Object* string) { 10775 Vector<const char> chars(string_->GetChars() + from_, length_); 10776 return String::cast(string)->IsAsciiEqualTo(chars); 10777 } 10778 10779 MaybeObject* AsObject() { 10780 if (hash_field_ == 0) Hash(); 10781 Vector<const char> chars(string_->GetChars() + from_, length_); 10782 return HEAP->AllocateAsciiSymbol(chars, hash_field_); 10783 } 10784 10785 private: 10786 Handle<SeqAsciiString> string_; 10787 int from_; 10788 int length_; 10789 uint32_t hash_field_; 10790 uint32_t seed_; 10791}; 10792 10793 10794class TwoByteSymbolKey : public SequentialSymbolKey<uc16> { 10795 public: 10796 explicit TwoByteSymbolKey(Vector<const uc16> str, uint32_t seed) 10797 : SequentialSymbolKey<uc16>(str, seed) { } 10798 10799 bool IsMatch(Object* string) { 10800 return String::cast(string)->IsTwoByteEqualTo(string_); 10801 } 10802 10803 MaybeObject* AsObject() { 10804 if (hash_field_ == 0) Hash(); 10805 return HEAP->AllocateTwoByteSymbol(string_, hash_field_); 10806 } 10807}; 10808 10809 10810// SymbolKey carries a string/symbol object as key. 10811class SymbolKey : public HashTableKey { 10812 public: 10813 explicit SymbolKey(String* string) 10814 : string_(string) { } 10815 10816 bool IsMatch(Object* string) { 10817 return String::cast(string)->Equals(string_); 10818 } 10819 10820 uint32_t Hash() { return string_->Hash(); } 10821 10822 uint32_t HashForObject(Object* other) { 10823 return String::cast(other)->Hash(); 10824 } 10825 10826 MaybeObject* AsObject() { 10827 // Attempt to flatten the string, so that symbols will most often 10828 // be flat strings. 10829 string_ = string_->TryFlattenGetString(); 10830 Heap* heap = string_->GetHeap(); 10831 // Transform string to symbol if possible. 10832 Map* map = heap->SymbolMapForString(string_); 10833 if (map != NULL) { 10834 string_->set_map_no_write_barrier(map); 10835 ASSERT(string_->IsSymbol()); 10836 return string_; 10837 } 10838 // Otherwise allocate a new symbol. 10839 StringInputBuffer buffer(string_); 10840 return heap->AllocateInternalSymbol(&buffer, 10841 string_->length(), 10842 string_->hash_field()); 10843 } 10844 10845 static uint32_t StringHash(Object* obj) { 10846 return String::cast(obj)->Hash(); 10847 } 10848 10849 String* string_; 10850}; 10851 10852 10853template<typename Shape, typename Key> 10854void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) { 10855 IteratePointers(v, 0, kElementsStartOffset); 10856} 10857 10858 10859template<typename Shape, typename Key> 10860void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) { 10861 IteratePointers(v, 10862 kElementsStartOffset, 10863 kHeaderSize + length() * kPointerSize); 10864} 10865 10866 10867template<typename Shape, typename Key> 10868MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for, 10869 PretenureFlag pretenure) { 10870 int capacity = ComputeCapacity(at_least_space_for); 10871 if (capacity > HashTable::kMaxCapacity) { 10872 return Failure::OutOfMemoryException(); 10873 } 10874 10875 Object* obj; 10876 { MaybeObject* maybe_obj = Isolate::Current()->heap()-> 10877 AllocateHashTable(EntryToIndex(capacity), pretenure); 10878 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 10879 } 10880 HashTable::cast(obj)->SetNumberOfElements(0); 10881 HashTable::cast(obj)->SetNumberOfDeletedElements(0); 10882 HashTable::cast(obj)->SetCapacity(capacity); 10883 return obj; 10884} 10885 10886 10887// Find entry for key otherwise return kNotFound. 10888int StringDictionary::FindEntry(String* key) { 10889 if (!key->IsSymbol()) { 10890 return HashTable<StringDictionaryShape, String*>::FindEntry(key); 10891 } 10892 10893 // Optimized for symbol key. Knowledge of the key type allows: 10894 // 1. Move the check if the key is a symbol out of the loop. 10895 // 2. Avoid comparing hash codes in symbol to symbol comparison. 10896 // 3. Detect a case when a dictionary key is not a symbol but the key is. 10897 // In case of positive result the dictionary key may be replaced by 10898 // the symbol with minimal performance penalty. It gives a chance to 10899 // perform further lookups in code stubs (and significant performance boost 10900 // a certain style of code). 10901 10902 // EnsureCapacity will guarantee the hash table is never full. 10903 uint32_t capacity = Capacity(); 10904 uint32_t entry = FirstProbe(key->Hash(), capacity); 10905 uint32_t count = 1; 10906 10907 while (true) { 10908 int index = EntryToIndex(entry); 10909 Object* element = get(index); 10910 if (element->IsUndefined()) break; // Empty entry. 10911 if (key == element) return entry; 10912 if (!element->IsSymbol() && 10913 !element->IsTheHole() && 10914 String::cast(element)->Equals(key)) { 10915 // Replace a non-symbol key by the equivalent symbol for faster further 10916 // lookups. 10917 set(index, key); 10918 return entry; 10919 } 10920 ASSERT(element->IsTheHole() || !String::cast(element)->Equals(key)); 10921 entry = NextProbe(entry, count++, capacity); 10922 } 10923 return kNotFound; 10924} 10925 10926 10927bool StringDictionary::ContainsTransition(int entry) { 10928 switch (DetailsAt(entry).type()) { 10929 case MAP_TRANSITION: 10930 case CONSTANT_TRANSITION: 10931 case ELEMENTS_TRANSITION: 10932 return true; 10933 case CALLBACKS: { 10934 Object* value = ValueAt(entry); 10935 if (!value->IsAccessorPair()) return false; 10936 AccessorPair* accessors = AccessorPair::cast(value); 10937 return accessors->getter()->IsMap() || accessors->setter()->IsMap(); 10938 } 10939 case NORMAL: 10940 case FIELD: 10941 case CONSTANT_FUNCTION: 10942 case HANDLER: 10943 case INTERCEPTOR: 10944 case NULL_DESCRIPTOR: 10945 return false; 10946 } 10947 UNREACHABLE(); // Keep the compiler happy. 10948 return false; 10949} 10950 10951 10952template<typename Shape, typename Key> 10953MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) { 10954 ASSERT(NumberOfElements() < new_table->Capacity()); 10955 10956 AssertNoAllocation no_gc; 10957 WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc); 10958 10959 // Copy prefix to new array. 10960 for (int i = kPrefixStartIndex; 10961 i < kPrefixStartIndex + Shape::kPrefixSize; 10962 i++) { 10963 new_table->set(i, get(i), mode); 10964 } 10965 10966 // Rehash the elements. 10967 int capacity = Capacity(); 10968 for (int i = 0; i < capacity; i++) { 10969 uint32_t from_index = EntryToIndex(i); 10970 Object* k = get(from_index); 10971 if (IsKey(k)) { 10972 uint32_t hash = HashTable<Shape, Key>::HashForObject(key, k); 10973 uint32_t insertion_index = 10974 EntryToIndex(new_table->FindInsertionEntry(hash)); 10975 for (int j = 0; j < Shape::kEntrySize; j++) { 10976 new_table->set(insertion_index + j, get(from_index + j), mode); 10977 } 10978 } 10979 } 10980 new_table->SetNumberOfElements(NumberOfElements()); 10981 new_table->SetNumberOfDeletedElements(0); 10982 return new_table; 10983} 10984 10985 10986template<typename Shape, typename Key> 10987MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) { 10988 int capacity = Capacity(); 10989 int nof = NumberOfElements() + n; 10990 int nod = NumberOfDeletedElements(); 10991 // Return if: 10992 // 50% is still free after adding n elements and 10993 // at most 50% of the free elements are deleted elements. 10994 if (nod <= (capacity - nof) >> 1) { 10995 int needed_free = nof >> 1; 10996 if (nof + needed_free <= capacity) return this; 10997 } 10998 10999 const int kMinCapacityForPretenure = 256; 11000 bool pretenure = 11001 (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this); 11002 Object* obj; 11003 { MaybeObject* maybe_obj = 11004 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED); 11005 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11006 } 11007 11008 return Rehash(HashTable::cast(obj), key); 11009} 11010 11011 11012template<typename Shape, typename Key> 11013MaybeObject* HashTable<Shape, Key>::Shrink(Key key) { 11014 int capacity = Capacity(); 11015 int nof = NumberOfElements(); 11016 11017 // Shrink to fit the number of elements if only a quarter of the 11018 // capacity is filled with elements. 11019 if (nof > (capacity >> 2)) return this; 11020 // Allocate a new dictionary with room for at least the current 11021 // number of elements. The allocation method will make sure that 11022 // there is extra room in the dictionary for additions. Don't go 11023 // lower than room for 16 elements. 11024 int at_least_room_for = nof; 11025 if (at_least_room_for < 16) return this; 11026 11027 const int kMinCapacityForPretenure = 256; 11028 bool pretenure = 11029 (at_least_room_for > kMinCapacityForPretenure) && 11030 !GetHeap()->InNewSpace(this); 11031 Object* obj; 11032 { MaybeObject* maybe_obj = 11033 Allocate(at_least_room_for, pretenure ? TENURED : NOT_TENURED); 11034 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11035 } 11036 11037 return Rehash(HashTable::cast(obj), key); 11038} 11039 11040 11041template<typename Shape, typename Key> 11042uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) { 11043 uint32_t capacity = Capacity(); 11044 uint32_t entry = FirstProbe(hash, capacity); 11045 uint32_t count = 1; 11046 // EnsureCapacity will guarantee the hash table is never full. 11047 while (true) { 11048 Object* element = KeyAt(entry); 11049 if (element->IsUndefined() || element->IsTheHole()) break; 11050 entry = NextProbe(entry, count++, capacity); 11051 } 11052 return entry; 11053} 11054 11055// Force instantiation of template instances class. 11056// Please note this list is compiler dependent. 11057 11058template class HashTable<SymbolTableShape, HashTableKey*>; 11059 11060template class HashTable<CompilationCacheShape, HashTableKey*>; 11061 11062template class HashTable<MapCacheShape, HashTableKey*>; 11063 11064template class HashTable<ObjectHashTableShape<1>, Object*>; 11065 11066template class HashTable<ObjectHashTableShape<2>, Object*>; 11067 11068template class Dictionary<StringDictionaryShape, String*>; 11069 11070template class Dictionary<SeededNumberDictionaryShape, uint32_t>; 11071 11072template class Dictionary<UnseededNumberDictionaryShape, uint32_t>; 11073 11074template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: 11075 Allocate(int at_least_space_for); 11076 11077template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: 11078 Allocate(int at_least_space_for); 11079 11080template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate( 11081 int); 11082 11083template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::AtPut( 11084 uint32_t, Object*); 11085 11086template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: 11087 AtPut(uint32_t, Object*); 11088 11089template Object* Dictionary<SeededNumberDictionaryShape, uint32_t>:: 11090 SlowReverseLookup(Object* value); 11091 11092template Object* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: 11093 SlowReverseLookup(Object* value); 11094 11095template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup( 11096 Object*); 11097 11098template void Dictionary<SeededNumberDictionaryShape, uint32_t>::CopyKeysTo( 11099 FixedArray*, 11100 PropertyAttributes, 11101 Dictionary<SeededNumberDictionaryShape, uint32_t>::SortMode); 11102 11103template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty( 11104 int, JSObject::DeleteMode); 11105 11106template Object* Dictionary<SeededNumberDictionaryShape, uint32_t>:: 11107 DeleteProperty(int, JSObject::DeleteMode); 11108 11109template MaybeObject* Dictionary<StringDictionaryShape, String*>::Shrink( 11110 String*); 11111 11112template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::Shrink( 11113 uint32_t); 11114 11115template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo( 11116 FixedArray*, 11117 int, 11118 Dictionary<StringDictionaryShape, String*>::SortMode); 11119 11120template int 11121Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes( 11122 PropertyAttributes); 11123 11124template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add( 11125 String*, Object*, PropertyDetails); 11126 11127template MaybeObject* 11128Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices(); 11129 11130template int 11131Dictionary<SeededNumberDictionaryShape, uint32_t>:: 11132 NumberOfElementsFilterAttributes(PropertyAttributes); 11133 11134template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::Add( 11135 uint32_t, Object*, PropertyDetails); 11136 11137template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>::Add( 11138 uint32_t, Object*, PropertyDetails); 11139 11140template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: 11141 EnsureCapacity(int, uint32_t); 11142 11143template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: 11144 EnsureCapacity(int, uint32_t); 11145 11146template MaybeObject* Dictionary<StringDictionaryShape, String*>:: 11147 EnsureCapacity(int, String*); 11148 11149template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: 11150 AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); 11151 11152template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: 11153 AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); 11154 11155template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry( 11156 String*, Object*, PropertyDetails, uint32_t); 11157 11158template 11159int Dictionary<SeededNumberDictionaryShape, uint32_t>::NumberOfEnumElements(); 11160 11161template 11162int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements(); 11163 11164template 11165int HashTable<SeededNumberDictionaryShape, uint32_t>::FindEntry(uint32_t); 11166 11167 11168// Collates undefined and unexisting elements below limit from position 11169// zero of the elements. The object stays in Dictionary mode. 11170MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { 11171 ASSERT(HasDictionaryElements()); 11172 // Must stay in dictionary mode, either because of requires_slow_elements, 11173 // or because we are not going to sort (and therefore compact) all of the 11174 // elements. 11175 SeededNumberDictionary* dict = element_dictionary(); 11176 HeapNumber* result_double = NULL; 11177 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) { 11178 // Allocate space for result before we start mutating the object. 11179 Object* new_double; 11180 { MaybeObject* maybe_new_double = GetHeap()->AllocateHeapNumber(0.0); 11181 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double; 11182 } 11183 result_double = HeapNumber::cast(new_double); 11184 } 11185 11186 Object* obj; 11187 { MaybeObject* maybe_obj = 11188 SeededNumberDictionary::Allocate(dict->NumberOfElements()); 11189 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11190 } 11191 SeededNumberDictionary* new_dict = SeededNumberDictionary::cast(obj); 11192 11193 AssertNoAllocation no_alloc; 11194 11195 uint32_t pos = 0; 11196 uint32_t undefs = 0; 11197 int capacity = dict->Capacity(); 11198 for (int i = 0; i < capacity; i++) { 11199 Object* k = dict->KeyAt(i); 11200 if (dict->IsKey(k)) { 11201 ASSERT(k->IsNumber()); 11202 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0); 11203 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0); 11204 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); 11205 Object* value = dict->ValueAt(i); 11206 PropertyDetails details = dict->DetailsAt(i); 11207 if (details.type() == CALLBACKS) { 11208 // Bail out and do the sorting of undefineds and array holes in JS. 11209 return Smi::FromInt(-1); 11210 } 11211 uint32_t key = NumberToUint32(k); 11212 // In the following we assert that adding the entry to the new dictionary 11213 // does not cause GC. This is the case because we made sure to allocate 11214 // the dictionary big enough above, so it need not grow. 11215 if (key < limit) { 11216 if (value->IsUndefined()) { 11217 undefs++; 11218 } else { 11219 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { 11220 // Adding an entry with the key beyond smi-range requires 11221 // allocation. Bailout. 11222 return Smi::FromInt(-1); 11223 } 11224 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked(); 11225 pos++; 11226 } 11227 } else { 11228 if (key > static_cast<uint32_t>(Smi::kMaxValue)) { 11229 // Adding an entry with the key beyond smi-range requires 11230 // allocation. Bailout. 11231 return Smi::FromInt(-1); 11232 } 11233 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked(); 11234 } 11235 } 11236 } 11237 11238 uint32_t result = pos; 11239 PropertyDetails no_details = PropertyDetails(NONE, NORMAL); 11240 Heap* heap = GetHeap(); 11241 while (undefs > 0) { 11242 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { 11243 // Adding an entry with the key beyond smi-range requires 11244 // allocation. Bailout. 11245 return Smi::FromInt(-1); 11246 } 11247 new_dict->AddNumberEntry(pos, heap->undefined_value(), no_details)-> 11248 ToObjectUnchecked(); 11249 pos++; 11250 undefs--; 11251 } 11252 11253 set_elements(new_dict); 11254 11255 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) { 11256 return Smi::FromInt(static_cast<int>(result)); 11257 } 11258 11259 ASSERT_NE(NULL, result_double); 11260 result_double->set_value(static_cast<double>(result)); 11261 return result_double; 11262} 11263 11264 11265// Collects all defined (non-hole) and non-undefined (array) elements at 11266// the start of the elements array. 11267// If the object is in dictionary mode, it is converted to fast elements 11268// mode. 11269MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { 11270 Heap* heap = GetHeap(); 11271 11272 if (HasDictionaryElements()) { 11273 // Convert to fast elements containing only the existing properties. 11274 // Ordering is irrelevant, since we are going to sort anyway. 11275 SeededNumberDictionary* dict = element_dictionary(); 11276 if (IsJSArray() || dict->requires_slow_elements() || 11277 dict->max_number_key() >= limit) { 11278 return PrepareSlowElementsForSort(limit); 11279 } 11280 // Convert to fast elements. 11281 11282 Object* obj; 11283 { MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), 11284 FAST_ELEMENTS); 11285 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11286 } 11287 Map* new_map = Map::cast(obj); 11288 11289 PretenureFlag tenure = heap->InNewSpace(this) ? NOT_TENURED: TENURED; 11290 Object* new_array; 11291 { MaybeObject* maybe_new_array = 11292 heap->AllocateFixedArray(dict->NumberOfElements(), tenure); 11293 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array; 11294 } 11295 FixedArray* fast_elements = FixedArray::cast(new_array); 11296 dict->CopyValuesTo(fast_elements); 11297 11298 set_map(new_map); 11299 set_elements(fast_elements); 11300 } else if (HasExternalArrayElements()) { 11301 // External arrays cannot have holes or undefined elements. 11302 return Smi::FromInt(ExternalArray::cast(elements())->length()); 11303 } else if (!HasFastDoubleElements()) { 11304 Object* obj; 11305 { MaybeObject* maybe_obj = EnsureWritableFastElements(); 11306 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11307 } 11308 } 11309 ASSERT(HasFastTypeElements() || HasFastDoubleElements()); 11310 11311 // Collect holes at the end, undefined before that and the rest at the 11312 // start, and return the number of non-hole, non-undefined values. 11313 11314 FixedArrayBase* elements_base = FixedArrayBase::cast(this->elements()); 11315 uint32_t elements_length = static_cast<uint32_t>(elements_base->length()); 11316 if (limit > elements_length) { 11317 limit = elements_length ; 11318 } 11319 if (limit == 0) { 11320 return Smi::FromInt(0); 11321 } 11322 11323 HeapNumber* result_double = NULL; 11324 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) { 11325 // Pessimistically allocate space for return value before 11326 // we start mutating the array. 11327 Object* new_double; 11328 { MaybeObject* maybe_new_double = heap->AllocateHeapNumber(0.0); 11329 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double; 11330 } 11331 result_double = HeapNumber::cast(new_double); 11332 } 11333 11334 uint32_t result = 0; 11335 if (elements_base->map() == heap->fixed_double_array_map()) { 11336 FixedDoubleArray* elements = FixedDoubleArray::cast(elements_base); 11337 // Split elements into defined and the_hole, in that order. 11338 unsigned int holes = limit; 11339 // Assume most arrays contain no holes and undefined values, so minimize the 11340 // number of stores of non-undefined, non-the-hole values. 11341 for (unsigned int i = 0; i < holes; i++) { 11342 if (elements->is_the_hole(i)) { 11343 holes--; 11344 } else { 11345 continue; 11346 } 11347 // Position i needs to be filled. 11348 while (holes > i) { 11349 if (elements->is_the_hole(holes)) { 11350 holes--; 11351 } else { 11352 elements->set(i, elements->get_scalar(holes)); 11353 break; 11354 } 11355 } 11356 } 11357 result = holes; 11358 while (holes < limit) { 11359 elements->set_the_hole(holes); 11360 holes++; 11361 } 11362 } else { 11363 FixedArray* elements = FixedArray::cast(elements_base); 11364 AssertNoAllocation no_alloc; 11365 11366 // Split elements into defined, undefined and the_hole, in that order. Only 11367 // count locations for undefined and the hole, and fill them afterwards. 11368 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc); 11369 unsigned int undefs = limit; 11370 unsigned int holes = limit; 11371 // Assume most arrays contain no holes and undefined values, so minimize the 11372 // number of stores of non-undefined, non-the-hole values. 11373 for (unsigned int i = 0; i < undefs; i++) { 11374 Object* current = elements->get(i); 11375 if (current->IsTheHole()) { 11376 holes--; 11377 undefs--; 11378 } else if (current->IsUndefined()) { 11379 undefs--; 11380 } else { 11381 continue; 11382 } 11383 // Position i needs to be filled. 11384 while (undefs > i) { 11385 current = elements->get(undefs); 11386 if (current->IsTheHole()) { 11387 holes--; 11388 undefs--; 11389 } else if (current->IsUndefined()) { 11390 undefs--; 11391 } else { 11392 elements->set(i, current, write_barrier); 11393 break; 11394 } 11395 } 11396 } 11397 result = undefs; 11398 while (undefs < holes) { 11399 elements->set_undefined(undefs); 11400 undefs++; 11401 } 11402 while (holes < limit) { 11403 elements->set_the_hole(holes); 11404 holes++; 11405 } 11406 } 11407 11408 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) { 11409 return Smi::FromInt(static_cast<int>(result)); 11410 } 11411 ASSERT_NE(NULL, result_double); 11412 result_double->set_value(static_cast<double>(result)); 11413 return result_double; 11414} 11415 11416 11417Object* ExternalPixelArray::SetValue(uint32_t index, Object* value) { 11418 uint8_t clamped_value = 0; 11419 if (index < static_cast<uint32_t>(length())) { 11420 if (value->IsSmi()) { 11421 int int_value = Smi::cast(value)->value(); 11422 if (int_value < 0) { 11423 clamped_value = 0; 11424 } else if (int_value > 255) { 11425 clamped_value = 255; 11426 } else { 11427 clamped_value = static_cast<uint8_t>(int_value); 11428 } 11429 } else if (value->IsHeapNumber()) { 11430 double double_value = HeapNumber::cast(value)->value(); 11431 if (!(double_value > 0)) { 11432 // NaN and less than zero clamp to zero. 11433 clamped_value = 0; 11434 } else if (double_value > 255) { 11435 // Greater than 255 clamp to 255. 11436 clamped_value = 255; 11437 } else { 11438 // Other doubles are rounded to the nearest integer. 11439 clamped_value = static_cast<uint8_t>(double_value + 0.5); 11440 } 11441 } else { 11442 // Clamp undefined to zero (default). All other types have been 11443 // converted to a number type further up in the call chain. 11444 ASSERT(value->IsUndefined()); 11445 } 11446 set(index, clamped_value); 11447 } 11448 return Smi::FromInt(clamped_value); 11449} 11450 11451 11452template<typename ExternalArrayClass, typename ValueType> 11453static MaybeObject* ExternalArrayIntSetter(Heap* heap, 11454 ExternalArrayClass* receiver, 11455 uint32_t index, 11456 Object* value) { 11457 ValueType cast_value = 0; 11458 if (index < static_cast<uint32_t>(receiver->length())) { 11459 if (value->IsSmi()) { 11460 int int_value = Smi::cast(value)->value(); 11461 cast_value = static_cast<ValueType>(int_value); 11462 } else if (value->IsHeapNumber()) { 11463 double double_value = HeapNumber::cast(value)->value(); 11464 cast_value = static_cast<ValueType>(DoubleToInt32(double_value)); 11465 } else { 11466 // Clamp undefined to zero (default). All other types have been 11467 // converted to a number type further up in the call chain. 11468 ASSERT(value->IsUndefined()); 11469 } 11470 receiver->set(index, cast_value); 11471 } 11472 return heap->NumberFromInt32(cast_value); 11473} 11474 11475 11476MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) { 11477 return ExternalArrayIntSetter<ExternalByteArray, int8_t> 11478 (GetHeap(), this, index, value); 11479} 11480 11481 11482MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index, 11483 Object* value) { 11484 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t> 11485 (GetHeap(), this, index, value); 11486} 11487 11488 11489MaybeObject* ExternalShortArray::SetValue(uint32_t index, 11490 Object* value) { 11491 return ExternalArrayIntSetter<ExternalShortArray, int16_t> 11492 (GetHeap(), this, index, value); 11493} 11494 11495 11496MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index, 11497 Object* value) { 11498 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t> 11499 (GetHeap(), this, index, value); 11500} 11501 11502 11503MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) { 11504 return ExternalArrayIntSetter<ExternalIntArray, int32_t> 11505 (GetHeap(), this, index, value); 11506} 11507 11508 11509MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) { 11510 uint32_t cast_value = 0; 11511 Heap* heap = GetHeap(); 11512 if (index < static_cast<uint32_t>(length())) { 11513 if (value->IsSmi()) { 11514 int int_value = Smi::cast(value)->value(); 11515 cast_value = static_cast<uint32_t>(int_value); 11516 } else if (value->IsHeapNumber()) { 11517 double double_value = HeapNumber::cast(value)->value(); 11518 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value)); 11519 } else { 11520 // Clamp undefined to zero (default). All other types have been 11521 // converted to a number type further up in the call chain. 11522 ASSERT(value->IsUndefined()); 11523 } 11524 set(index, cast_value); 11525 } 11526 return heap->NumberFromUint32(cast_value); 11527} 11528 11529 11530MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) { 11531 float cast_value = static_cast<float>(OS::nan_value()); 11532 Heap* heap = GetHeap(); 11533 if (index < static_cast<uint32_t>(length())) { 11534 if (value->IsSmi()) { 11535 int int_value = Smi::cast(value)->value(); 11536 cast_value = static_cast<float>(int_value); 11537 } else if (value->IsHeapNumber()) { 11538 double double_value = HeapNumber::cast(value)->value(); 11539 cast_value = static_cast<float>(double_value); 11540 } else { 11541 // Clamp undefined to NaN (default). All other types have been 11542 // converted to a number type further up in the call chain. 11543 ASSERT(value->IsUndefined()); 11544 } 11545 set(index, cast_value); 11546 } 11547 return heap->AllocateHeapNumber(cast_value); 11548} 11549 11550 11551MaybeObject* ExternalDoubleArray::SetValue(uint32_t index, Object* value) { 11552 double double_value = OS::nan_value(); 11553 Heap* heap = GetHeap(); 11554 if (index < static_cast<uint32_t>(length())) { 11555 if (value->IsSmi()) { 11556 int int_value = Smi::cast(value)->value(); 11557 double_value = static_cast<double>(int_value); 11558 } else if (value->IsHeapNumber()) { 11559 double_value = HeapNumber::cast(value)->value(); 11560 } else { 11561 // Clamp undefined to NaN (default). All other types have been 11562 // converted to a number type further up in the call chain. 11563 ASSERT(value->IsUndefined()); 11564 } 11565 set(index, double_value); 11566 } 11567 return heap->AllocateHeapNumber(double_value); 11568} 11569 11570 11571JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) { 11572 ASSERT(!HasFastProperties()); 11573 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); 11574 return JSGlobalPropertyCell::cast(value); 11575} 11576 11577 11578Handle<JSGlobalPropertyCell> GlobalObject::EnsurePropertyCell( 11579 Handle<GlobalObject> global, 11580 Handle<String> name) { 11581 Isolate* isolate = global->GetIsolate(); 11582 CALL_HEAP_FUNCTION(isolate, 11583 global->EnsurePropertyCell(*name), 11584 JSGlobalPropertyCell); 11585} 11586 11587 11588MaybeObject* GlobalObject::EnsurePropertyCell(String* name) { 11589 ASSERT(!HasFastProperties()); 11590 int entry = property_dictionary()->FindEntry(name); 11591 if (entry == StringDictionary::kNotFound) { 11592 Heap* heap = GetHeap(); 11593 Object* cell; 11594 { MaybeObject* maybe_cell = 11595 heap->AllocateJSGlobalPropertyCell(heap->the_hole_value()); 11596 if (!maybe_cell->ToObject(&cell)) return maybe_cell; 11597 } 11598 PropertyDetails details(NONE, NORMAL); 11599 details = details.AsDeleted(); 11600 Object* dictionary; 11601 { MaybeObject* maybe_dictionary = 11602 property_dictionary()->Add(name, cell, details); 11603 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary; 11604 } 11605 set_properties(StringDictionary::cast(dictionary)); 11606 return cell; 11607 } else { 11608 Object* value = property_dictionary()->ValueAt(entry); 11609 ASSERT(value->IsJSGlobalPropertyCell()); 11610 return value; 11611 } 11612} 11613 11614 11615MaybeObject* SymbolTable::LookupString(String* string, Object** s) { 11616 SymbolKey key(string); 11617 return LookupKey(&key, s); 11618} 11619 11620 11621// This class is used for looking up two character strings in the symbol table. 11622// If we don't have a hit we don't want to waste much time so we unroll the 11623// string hash calculation loop here for speed. Doesn't work if the two 11624// characters form a decimal integer, since such strings have a different hash 11625// algorithm. 11626class TwoCharHashTableKey : public HashTableKey { 11627 public: 11628 TwoCharHashTableKey(uint32_t c1, uint32_t c2, uint32_t seed) 11629 : c1_(c1), c2_(c2) { 11630 // Char 1. 11631 uint32_t hash = seed; 11632 hash += c1; 11633 hash += hash << 10; 11634 hash ^= hash >> 6; 11635 // Char 2. 11636 hash += c2; 11637 hash += hash << 10; 11638 hash ^= hash >> 6; 11639 // GetHash. 11640 hash += hash << 3; 11641 hash ^= hash >> 11; 11642 hash += hash << 15; 11643 if ((hash & String::kHashBitMask) == 0) hash = String::kZeroHash; 11644#ifdef DEBUG 11645 StringHasher hasher(2, seed); 11646 hasher.AddCharacter(c1); 11647 hasher.AddCharacter(c2); 11648 // If this assert fails then we failed to reproduce the two-character 11649 // version of the string hashing algorithm above. One reason could be 11650 // that we were passed two digits as characters, since the hash 11651 // algorithm is different in that case. 11652 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash)); 11653#endif 11654 hash_ = hash; 11655 } 11656 11657 bool IsMatch(Object* o) { 11658 if (!o->IsString()) return false; 11659 String* other = String::cast(o); 11660 if (other->length() != 2) return false; 11661 if (other->Get(0) != c1_) return false; 11662 return other->Get(1) == c2_; 11663 } 11664 11665 uint32_t Hash() { return hash_; } 11666 uint32_t HashForObject(Object* key) { 11667 if (!key->IsString()) return 0; 11668 return String::cast(key)->Hash(); 11669 } 11670 11671 Object* AsObject() { 11672 // The TwoCharHashTableKey is only used for looking in the symbol 11673 // table, not for adding to it. 11674 UNREACHABLE(); 11675 return NULL; 11676 } 11677 11678 private: 11679 uint32_t c1_; 11680 uint32_t c2_; 11681 uint32_t hash_; 11682}; 11683 11684 11685bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { 11686 SymbolKey key(string); 11687 int entry = FindEntry(&key); 11688 if (entry == kNotFound) { 11689 return false; 11690 } else { 11691 String* result = String::cast(KeyAt(entry)); 11692 ASSERT(StringShape(result).IsSymbol()); 11693 *symbol = result; 11694 return true; 11695 } 11696} 11697 11698 11699bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, 11700 uint32_t c2, 11701 String** symbol) { 11702 TwoCharHashTableKey key(c1, c2, GetHeap()->HashSeed()); 11703 int entry = FindEntry(&key); 11704 if (entry == kNotFound) { 11705 return false; 11706 } else { 11707 String* result = String::cast(KeyAt(entry)); 11708 ASSERT(StringShape(result).IsSymbol()); 11709 *symbol = result; 11710 return true; 11711 } 11712} 11713 11714 11715MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, 11716 Object** s) { 11717 Utf8SymbolKey key(str, GetHeap()->HashSeed()); 11718 return LookupKey(&key, s); 11719} 11720 11721 11722MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str, 11723 Object** s) { 11724 AsciiSymbolKey key(str, GetHeap()->HashSeed()); 11725 return LookupKey(&key, s); 11726} 11727 11728 11729MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str, 11730 int from, 11731 int length, 11732 Object** s) { 11733 SubStringAsciiSymbolKey key(str, from, length, GetHeap()->HashSeed()); 11734 return LookupKey(&key, s); 11735} 11736 11737 11738MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str, 11739 Object** s) { 11740 TwoByteSymbolKey key(str, GetHeap()->HashSeed()); 11741 return LookupKey(&key, s); 11742} 11743 11744MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) { 11745 int entry = FindEntry(key); 11746 11747 // Symbol already in table. 11748 if (entry != kNotFound) { 11749 *s = KeyAt(entry); 11750 return this; 11751 } 11752 11753 // Adding new symbol. Grow table if needed. 11754 Object* obj; 11755 { MaybeObject* maybe_obj = EnsureCapacity(1, key); 11756 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11757 } 11758 11759 // Create symbol object. 11760 Object* symbol; 11761 { MaybeObject* maybe_symbol = key->AsObject(); 11762 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol; 11763 } 11764 11765 // If the symbol table grew as part of EnsureCapacity, obj is not 11766 // the current symbol table and therefore we cannot use 11767 // SymbolTable::cast here. 11768 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj); 11769 11770 // Add the new symbol and return it along with the symbol table. 11771 entry = table->FindInsertionEntry(key->Hash()); 11772 table->set(EntryToIndex(entry), symbol); 11773 table->ElementAdded(); 11774 *s = symbol; 11775 return table; 11776} 11777 11778 11779Object* CompilationCacheTable::Lookup(String* src) { 11780 StringKey key(src); 11781 int entry = FindEntry(&key); 11782 if (entry == kNotFound) return GetHeap()->undefined_value(); 11783 return get(EntryToIndex(entry) + 1); 11784} 11785 11786 11787Object* CompilationCacheTable::LookupEval(String* src, 11788 Context* context, 11789 LanguageMode language_mode, 11790 int scope_position) { 11791 StringSharedKey key(src, 11792 context->closure()->shared(), 11793 language_mode, 11794 scope_position); 11795 int entry = FindEntry(&key); 11796 if (entry == kNotFound) return GetHeap()->undefined_value(); 11797 return get(EntryToIndex(entry) + 1); 11798} 11799 11800 11801Object* CompilationCacheTable::LookupRegExp(String* src, 11802 JSRegExp::Flags flags) { 11803 RegExpKey key(src, flags); 11804 int entry = FindEntry(&key); 11805 if (entry == kNotFound) return GetHeap()->undefined_value(); 11806 return get(EntryToIndex(entry) + 1); 11807} 11808 11809 11810MaybeObject* CompilationCacheTable::Put(String* src, Object* value) { 11811 StringKey key(src); 11812 Object* obj; 11813 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 11814 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11815 } 11816 11817 CompilationCacheTable* cache = 11818 reinterpret_cast<CompilationCacheTable*>(obj); 11819 int entry = cache->FindInsertionEntry(key.Hash()); 11820 cache->set(EntryToIndex(entry), src); 11821 cache->set(EntryToIndex(entry) + 1, value); 11822 cache->ElementAdded(); 11823 return cache; 11824} 11825 11826 11827MaybeObject* CompilationCacheTable::PutEval(String* src, 11828 Context* context, 11829 SharedFunctionInfo* value, 11830 int scope_position) { 11831 StringSharedKey key(src, 11832 context->closure()->shared(), 11833 value->language_mode(), 11834 scope_position); 11835 Object* obj; 11836 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 11837 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11838 } 11839 11840 CompilationCacheTable* cache = 11841 reinterpret_cast<CompilationCacheTable*>(obj); 11842 int entry = cache->FindInsertionEntry(key.Hash()); 11843 11844 Object* k; 11845 { MaybeObject* maybe_k = key.AsObject(); 11846 if (!maybe_k->ToObject(&k)) return maybe_k; 11847 } 11848 11849 cache->set(EntryToIndex(entry), k); 11850 cache->set(EntryToIndex(entry) + 1, value); 11851 cache->ElementAdded(); 11852 return cache; 11853} 11854 11855 11856MaybeObject* CompilationCacheTable::PutRegExp(String* src, 11857 JSRegExp::Flags flags, 11858 FixedArray* value) { 11859 RegExpKey key(src, flags); 11860 Object* obj; 11861 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 11862 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11863 } 11864 11865 CompilationCacheTable* cache = 11866 reinterpret_cast<CompilationCacheTable*>(obj); 11867 int entry = cache->FindInsertionEntry(key.Hash()); 11868 // We store the value in the key slot, and compare the search key 11869 // to the stored value with a custon IsMatch function during lookups. 11870 cache->set(EntryToIndex(entry), value); 11871 cache->set(EntryToIndex(entry) + 1, value); 11872 cache->ElementAdded(); 11873 return cache; 11874} 11875 11876 11877void CompilationCacheTable::Remove(Object* value) { 11878 Object* the_hole_value = GetHeap()->the_hole_value(); 11879 for (int entry = 0, size = Capacity(); entry < size; entry++) { 11880 int entry_index = EntryToIndex(entry); 11881 int value_index = entry_index + 1; 11882 if (get(value_index) == value) { 11883 NoWriteBarrierSet(this, entry_index, the_hole_value); 11884 NoWriteBarrierSet(this, value_index, the_hole_value); 11885 ElementRemoved(); 11886 } 11887 } 11888 return; 11889} 11890 11891 11892// SymbolsKey used for HashTable where key is array of symbols. 11893class SymbolsKey : public HashTableKey { 11894 public: 11895 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { } 11896 11897 bool IsMatch(Object* symbols) { 11898 FixedArray* o = FixedArray::cast(symbols); 11899 int len = symbols_->length(); 11900 if (o->length() != len) return false; 11901 for (int i = 0; i < len; i++) { 11902 if (o->get(i) != symbols_->get(i)) return false; 11903 } 11904 return true; 11905 } 11906 11907 uint32_t Hash() { return HashForObject(symbols_); } 11908 11909 uint32_t HashForObject(Object* obj) { 11910 FixedArray* symbols = FixedArray::cast(obj); 11911 int len = symbols->length(); 11912 uint32_t hash = 0; 11913 for (int i = 0; i < len; i++) { 11914 hash ^= String::cast(symbols->get(i))->Hash(); 11915 } 11916 return hash; 11917 } 11918 11919 Object* AsObject() { return symbols_; } 11920 11921 private: 11922 FixedArray* symbols_; 11923}; 11924 11925 11926Object* MapCache::Lookup(FixedArray* array) { 11927 SymbolsKey key(array); 11928 int entry = FindEntry(&key); 11929 if (entry == kNotFound) return GetHeap()->undefined_value(); 11930 return get(EntryToIndex(entry) + 1); 11931} 11932 11933 11934MaybeObject* MapCache::Put(FixedArray* array, Map* value) { 11935 SymbolsKey key(array); 11936 Object* obj; 11937 { MaybeObject* maybe_obj = EnsureCapacity(1, &key); 11938 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11939 } 11940 11941 MapCache* cache = reinterpret_cast<MapCache*>(obj); 11942 int entry = cache->FindInsertionEntry(key.Hash()); 11943 cache->set(EntryToIndex(entry), array); 11944 cache->set(EntryToIndex(entry) + 1, value); 11945 cache->ElementAdded(); 11946 return cache; 11947} 11948 11949 11950template<typename Shape, typename Key> 11951MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) { 11952 Object* obj; 11953 { MaybeObject* maybe_obj = 11954 HashTable<Shape, Key>::Allocate(at_least_space_for); 11955 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11956 } 11957 // Initialize the next enumeration index. 11958 Dictionary<Shape, Key>::cast(obj)-> 11959 SetNextEnumerationIndex(PropertyDetails::kInitialIndex); 11960 return obj; 11961} 11962 11963 11964template<typename Shape, typename Key> 11965MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() { 11966 Heap* heap = Dictionary<Shape, Key>::GetHeap(); 11967 int length = HashTable<Shape, Key>::NumberOfElements(); 11968 11969 // Allocate and initialize iteration order array. 11970 Object* obj; 11971 { MaybeObject* maybe_obj = heap->AllocateFixedArray(length); 11972 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11973 } 11974 FixedArray* iteration_order = FixedArray::cast(obj); 11975 for (int i = 0; i < length; i++) { 11976 iteration_order->set(i, Smi::FromInt(i)); 11977 } 11978 11979 // Allocate array with enumeration order. 11980 { MaybeObject* maybe_obj = heap->AllocateFixedArray(length); 11981 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 11982 } 11983 FixedArray* enumeration_order = FixedArray::cast(obj); 11984 11985 // Fill the enumeration order array with property details. 11986 int capacity = HashTable<Shape, Key>::Capacity(); 11987 int pos = 0; 11988 for (int i = 0; i < capacity; i++) { 11989 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) { 11990 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index())); 11991 } 11992 } 11993 11994 // Sort the arrays wrt. enumeration order. 11995 iteration_order->SortPairs(enumeration_order, enumeration_order->length()); 11996 11997 // Overwrite the enumeration_order with the enumeration indices. 11998 for (int i = 0; i < length; i++) { 11999 int index = Smi::cast(iteration_order->get(i))->value(); 12000 int enum_index = PropertyDetails::kInitialIndex + i; 12001 enumeration_order->set(index, Smi::FromInt(enum_index)); 12002 } 12003 12004 // Update the dictionary with new indices. 12005 capacity = HashTable<Shape, Key>::Capacity(); 12006 pos = 0; 12007 for (int i = 0; i < capacity; i++) { 12008 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) { 12009 int enum_index = Smi::cast(enumeration_order->get(pos++))->value(); 12010 PropertyDetails details = DetailsAt(i); 12011 PropertyDetails new_details = 12012 PropertyDetails(details.attributes(), details.type(), enum_index); 12013 DetailsAtPut(i, new_details); 12014 } 12015 } 12016 12017 // Set the next enumeration index. 12018 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length); 12019 return this; 12020} 12021 12022template<typename Shape, typename Key> 12023MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) { 12024 // Check whether there are enough enumeration indices to add n elements. 12025 if (Shape::kIsEnumerable && 12026 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) { 12027 // If not, we generate new indices for the properties. 12028 Object* result; 12029 { MaybeObject* maybe_result = GenerateNewEnumerationIndices(); 12030 if (!maybe_result->ToObject(&result)) return maybe_result; 12031 } 12032 } 12033 return HashTable<Shape, Key>::EnsureCapacity(n, key); 12034} 12035 12036 12037template<typename Shape, typename Key> 12038Object* Dictionary<Shape, Key>::DeleteProperty(int entry, 12039 JSReceiver::DeleteMode mode) { 12040 Heap* heap = Dictionary<Shape, Key>::GetHeap(); 12041 PropertyDetails details = DetailsAt(entry); 12042 // Ignore attributes if forcing a deletion. 12043 if (details.IsDontDelete() && mode != JSReceiver::FORCE_DELETION) { 12044 return heap->false_value(); 12045 } 12046 SetEntry(entry, heap->the_hole_value(), heap->the_hole_value()); 12047 HashTable<Shape, Key>::ElementRemoved(); 12048 return heap->true_value(); 12049} 12050 12051 12052template<typename Shape, typename Key> 12053MaybeObject* Dictionary<Shape, Key>::Shrink(Key key) { 12054 return HashTable<Shape, Key>::Shrink(key); 12055} 12056 12057 12058template<typename Shape, typename Key> 12059MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) { 12060 int entry = this->FindEntry(key); 12061 12062 // If the entry is present set the value; 12063 if (entry != Dictionary<Shape, Key>::kNotFound) { 12064 ValueAtPut(entry, value); 12065 return this; 12066 } 12067 12068 // Check whether the dictionary should be extended. 12069 Object* obj; 12070 { MaybeObject* maybe_obj = EnsureCapacity(1, key); 12071 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 12072 } 12073 12074 Object* k; 12075 { MaybeObject* maybe_k = Shape::AsObject(key); 12076 if (!maybe_k->ToObject(&k)) return maybe_k; 12077 } 12078 PropertyDetails details = PropertyDetails(NONE, NORMAL); 12079 12080 return Dictionary<Shape, Key>::cast(obj)->AddEntry(key, value, details, 12081 Dictionary<Shape, Key>::Hash(key)); 12082} 12083 12084 12085template<typename Shape, typename Key> 12086MaybeObject* Dictionary<Shape, Key>::Add(Key key, 12087 Object* value, 12088 PropertyDetails details) { 12089 // Valdate key is absent. 12090 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound)); 12091 // Check whether the dictionary should be extended. 12092 Object* obj; 12093 { MaybeObject* maybe_obj = EnsureCapacity(1, key); 12094 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 12095 } 12096 12097 return Dictionary<Shape, Key>::cast(obj)->AddEntry(key, value, details, 12098 Dictionary<Shape, Key>::Hash(key)); 12099} 12100 12101 12102// Add a key, value pair to the dictionary. 12103template<typename Shape, typename Key> 12104MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key, 12105 Object* value, 12106 PropertyDetails details, 12107 uint32_t hash) { 12108 // Compute the key object. 12109 Object* k; 12110 { MaybeObject* maybe_k = Shape::AsObject(key); 12111 if (!maybe_k->ToObject(&k)) return maybe_k; 12112 } 12113 12114 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash); 12115 // Insert element at empty or deleted entry 12116 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) { 12117 // Assign an enumeration index to the property and update 12118 // SetNextEnumerationIndex. 12119 int index = NextEnumerationIndex(); 12120 details = PropertyDetails(details.attributes(), details.type(), index); 12121 SetNextEnumerationIndex(index + 1); 12122 } 12123 SetEntry(entry, k, value, details); 12124 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber() 12125 || Dictionary<Shape, Key>::KeyAt(entry)->IsString())); 12126 HashTable<Shape, Key>::ElementAdded(); 12127 return this; 12128} 12129 12130 12131void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) { 12132 // If the dictionary requires slow elements an element has already 12133 // been added at a high index. 12134 if (requires_slow_elements()) return; 12135 // Check if this index is high enough that we should require slow 12136 // elements. 12137 if (key > kRequiresSlowElementsLimit) { 12138 set_requires_slow_elements(); 12139 return; 12140 } 12141 // Update max key value. 12142 Object* max_index_object = get(kMaxNumberKeyIndex); 12143 if (!max_index_object->IsSmi() || max_number_key() < key) { 12144 FixedArray::set(kMaxNumberKeyIndex, 12145 Smi::FromInt(key << kRequiresSlowElementsTagSize)); 12146 } 12147} 12148 12149 12150MaybeObject* SeededNumberDictionary::AddNumberEntry(uint32_t key, 12151 Object* value, 12152 PropertyDetails details) { 12153 UpdateMaxNumberKey(key); 12154 SLOW_ASSERT(this->FindEntry(key) == kNotFound); 12155 return Add(key, value, details); 12156} 12157 12158 12159MaybeObject* UnseededNumberDictionary::AddNumberEntry(uint32_t key, 12160 Object* value) { 12161 SLOW_ASSERT(this->FindEntry(key) == kNotFound); 12162 return Add(key, value, PropertyDetails(NONE, NORMAL)); 12163} 12164 12165 12166MaybeObject* SeededNumberDictionary::AtNumberPut(uint32_t key, Object* value) { 12167 UpdateMaxNumberKey(key); 12168 return AtPut(key, value); 12169} 12170 12171 12172MaybeObject* UnseededNumberDictionary::AtNumberPut(uint32_t key, 12173 Object* value) { 12174 return AtPut(key, value); 12175} 12176 12177 12178Handle<SeededNumberDictionary> SeededNumberDictionary::Set( 12179 Handle<SeededNumberDictionary> dictionary, 12180 uint32_t index, 12181 Handle<Object> value, 12182 PropertyDetails details) { 12183 CALL_HEAP_FUNCTION(dictionary->GetIsolate(), 12184 dictionary->Set(index, *value, details), 12185 SeededNumberDictionary); 12186} 12187 12188 12189Handle<UnseededNumberDictionary> UnseededNumberDictionary::Set( 12190 Handle<UnseededNumberDictionary> dictionary, 12191 uint32_t index, 12192 Handle<Object> value) { 12193 CALL_HEAP_FUNCTION(dictionary->GetIsolate(), 12194 dictionary->Set(index, *value), 12195 UnseededNumberDictionary); 12196} 12197 12198 12199MaybeObject* SeededNumberDictionary::Set(uint32_t key, 12200 Object* value, 12201 PropertyDetails details) { 12202 int entry = FindEntry(key); 12203 if (entry == kNotFound) return AddNumberEntry(key, value, details); 12204 // Preserve enumeration index. 12205 details = PropertyDetails(details.attributes(), 12206 details.type(), 12207 DetailsAt(entry).index()); 12208 MaybeObject* maybe_object_key = SeededNumberDictionaryShape::AsObject(key); 12209 Object* object_key; 12210 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; 12211 SetEntry(entry, object_key, value, details); 12212 return this; 12213} 12214 12215 12216MaybeObject* UnseededNumberDictionary::Set(uint32_t key, 12217 Object* value) { 12218 int entry = FindEntry(key); 12219 if (entry == kNotFound) return AddNumberEntry(key, value); 12220 MaybeObject* maybe_object_key = UnseededNumberDictionaryShape::AsObject(key); 12221 Object* object_key; 12222 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; 12223 SetEntry(entry, object_key, value); 12224 return this; 12225} 12226 12227 12228 12229template<typename Shape, typename Key> 12230int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes( 12231 PropertyAttributes filter) { 12232 int capacity = HashTable<Shape, Key>::Capacity(); 12233 int result = 0; 12234 for (int i = 0; i < capacity; i++) { 12235 Object* k = HashTable<Shape, Key>::KeyAt(i); 12236 if (HashTable<Shape, Key>::IsKey(k)) { 12237 PropertyDetails details = DetailsAt(i); 12238 if (details.IsDeleted()) continue; 12239 PropertyAttributes attr = details.attributes(); 12240 if ((attr & filter) == 0) result++; 12241 } 12242 } 12243 return result; 12244} 12245 12246 12247template<typename Shape, typename Key> 12248int Dictionary<Shape, Key>::NumberOfEnumElements() { 12249 return NumberOfElementsFilterAttributes( 12250 static_cast<PropertyAttributes>(DONT_ENUM)); 12251} 12252 12253 12254template<typename Shape, typename Key> 12255void Dictionary<Shape, Key>::CopyKeysTo( 12256 FixedArray* storage, 12257 PropertyAttributes filter, 12258 typename Dictionary<Shape, Key>::SortMode sort_mode) { 12259 ASSERT(storage->length() >= NumberOfEnumElements()); 12260 int capacity = HashTable<Shape, Key>::Capacity(); 12261 int index = 0; 12262 for (int i = 0; i < capacity; i++) { 12263 Object* k = HashTable<Shape, Key>::KeyAt(i); 12264 if (HashTable<Shape, Key>::IsKey(k)) { 12265 PropertyDetails details = DetailsAt(i); 12266 if (details.IsDeleted()) continue; 12267 PropertyAttributes attr = details.attributes(); 12268 if ((attr & filter) == 0) storage->set(index++, k); 12269 } 12270 } 12271 if (sort_mode == Dictionary<Shape, Key>::SORTED) { 12272 storage->SortPairs(storage, index); 12273 } 12274 ASSERT(storage->length() >= index); 12275} 12276 12277 12278void StringDictionary::CopyEnumKeysTo(FixedArray* storage, 12279 FixedArray* sort_array) { 12280 ASSERT(storage->length() >= NumberOfEnumElements()); 12281 int capacity = Capacity(); 12282 int index = 0; 12283 for (int i = 0; i < capacity; i++) { 12284 Object* k = KeyAt(i); 12285 if (IsKey(k)) { 12286 PropertyDetails details = DetailsAt(i); 12287 if (details.IsDeleted() || details.IsDontEnum()) continue; 12288 storage->set(index, k); 12289 sort_array->set(index, Smi::FromInt(details.index())); 12290 index++; 12291 } 12292 } 12293 storage->SortPairs(sort_array, sort_array->length()); 12294 ASSERT(storage->length() >= index); 12295} 12296 12297 12298template<typename Shape, typename Key> 12299void Dictionary<Shape, Key>::CopyKeysTo( 12300 FixedArray* storage, 12301 int index, 12302 typename Dictionary<Shape, Key>::SortMode sort_mode) { 12303 ASSERT(storage->length() >= NumberOfElementsFilterAttributes( 12304 static_cast<PropertyAttributes>(NONE))); 12305 int capacity = HashTable<Shape, Key>::Capacity(); 12306 for (int i = 0; i < capacity; i++) { 12307 Object* k = HashTable<Shape, Key>::KeyAt(i); 12308 if (HashTable<Shape, Key>::IsKey(k)) { 12309 PropertyDetails details = DetailsAt(i); 12310 if (details.IsDeleted()) continue; 12311 storage->set(index++, k); 12312 } 12313 } 12314 if (sort_mode == Dictionary<Shape, Key>::SORTED) { 12315 storage->SortPairs(storage, index); 12316 } 12317 ASSERT(storage->length() >= index); 12318} 12319 12320 12321// Backwards lookup (slow). 12322template<typename Shape, typename Key> 12323Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) { 12324 int capacity = HashTable<Shape, Key>::Capacity(); 12325 for (int i = 0; i < capacity; i++) { 12326 Object* k = HashTable<Shape, Key>::KeyAt(i); 12327 if (Dictionary<Shape, Key>::IsKey(k)) { 12328 Object* e = ValueAt(i); 12329 if (e->IsJSGlobalPropertyCell()) { 12330 e = JSGlobalPropertyCell::cast(e)->value(); 12331 } 12332 if (e == value) return k; 12333 } 12334 } 12335 Heap* heap = Dictionary<Shape, Key>::GetHeap(); 12336 return heap->undefined_value(); 12337} 12338 12339 12340MaybeObject* StringDictionary::TransformPropertiesToFastFor( 12341 JSObject* obj, int unused_property_fields) { 12342 // Make sure we preserve dictionary representation if there are too many 12343 // descriptors. 12344 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj; 12345 12346 // Figure out if it is necessary to generate new enumeration indices. 12347 int max_enumeration_index = 12348 NextEnumerationIndex() + 12349 (DescriptorArray::kMaxNumberOfDescriptors - 12350 NumberOfElements()); 12351 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) { 12352 Object* result; 12353 { MaybeObject* maybe_result = GenerateNewEnumerationIndices(); 12354 if (!maybe_result->ToObject(&result)) return maybe_result; 12355 } 12356 } 12357 12358 int instance_descriptor_length = 0; 12359 int number_of_fields = 0; 12360 12361 Heap* heap = GetHeap(); 12362 12363 // Compute the length of the instance descriptor. 12364 int capacity = Capacity(); 12365 for (int i = 0; i < capacity; i++) { 12366 Object* k = KeyAt(i); 12367 if (IsKey(k)) { 12368 Object* value = ValueAt(i); 12369 PropertyType type = DetailsAt(i).type(); 12370 ASSERT(type != FIELD); 12371 instance_descriptor_length++; 12372 if (type == NORMAL && 12373 (!value->IsJSFunction() || heap->InNewSpace(value))) { 12374 number_of_fields += 1; 12375 } 12376 } 12377 } 12378 12379 // Allocate the instance descriptor. 12380 DescriptorArray* descriptors; 12381 { MaybeObject* maybe_descriptors = 12382 DescriptorArray::Allocate(instance_descriptor_length); 12383 if (!maybe_descriptors->To<DescriptorArray>(&descriptors)) { 12384 return maybe_descriptors; 12385 } 12386 } 12387 12388 DescriptorArray::WhitenessWitness witness(descriptors); 12389 12390 int inobject_props = obj->map()->inobject_properties(); 12391 int number_of_allocated_fields = 12392 number_of_fields + unused_property_fields - inobject_props; 12393 if (number_of_allocated_fields < 0) { 12394 // There is enough inobject space for all fields (including unused). 12395 number_of_allocated_fields = 0; 12396 unused_property_fields = inobject_props - number_of_fields; 12397 } 12398 12399 // Allocate the fixed array for the fields. 12400 Object* fields; 12401 { MaybeObject* maybe_fields = 12402 heap->AllocateFixedArray(number_of_allocated_fields); 12403 if (!maybe_fields->ToObject(&fields)) return maybe_fields; 12404 } 12405 12406 // Fill in the instance descriptor and the fields. 12407 int next_descriptor = 0; 12408 int current_offset = 0; 12409 for (int i = 0; i < capacity; i++) { 12410 Object* k = KeyAt(i); 12411 if (IsKey(k)) { 12412 Object* value = ValueAt(i); 12413 // Ensure the key is a symbol before writing into the instance descriptor. 12414 Object* key; 12415 { MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k)); 12416 if (!maybe_key->ToObject(&key)) return maybe_key; 12417 } 12418 PropertyDetails details = DetailsAt(i); 12419 PropertyType type = details.type(); 12420 12421 if (value->IsJSFunction() && !heap->InNewSpace(value)) { 12422 ConstantFunctionDescriptor d(String::cast(key), 12423 JSFunction::cast(value), 12424 details.attributes(), 12425 details.index()); 12426 descriptors->Set(next_descriptor++, &d, witness); 12427 } else if (type == NORMAL) { 12428 if (current_offset < inobject_props) { 12429 obj->InObjectPropertyAtPut(current_offset, 12430 value, 12431 UPDATE_WRITE_BARRIER); 12432 } else { 12433 int offset = current_offset - inobject_props; 12434 FixedArray::cast(fields)->set(offset, value); 12435 } 12436 FieldDescriptor d(String::cast(key), 12437 current_offset++, 12438 details.attributes(), 12439 details.index()); 12440 descriptors->Set(next_descriptor++, &d, witness); 12441 } else if (type == CALLBACKS) { 12442 if (value->IsAccessorPair()) { 12443 MaybeObject* maybe_copy = 12444 AccessorPair::cast(value)->CopyWithoutTransitions(); 12445 if (!maybe_copy->To(&value)) return maybe_copy; 12446 } 12447 CallbacksDescriptor d(String::cast(key), 12448 value, 12449 details.attributes(), 12450 details.index()); 12451 descriptors->Set(next_descriptor++, &d, witness); 12452 } else { 12453 UNREACHABLE(); 12454 } 12455 } 12456 } 12457 ASSERT(current_offset == number_of_fields); 12458 12459 descriptors->Sort(witness); 12460 // Allocate new map. 12461 Object* new_map; 12462 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors(); 12463 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 12464 } 12465 12466 // Transform the object. 12467 obj->set_map(Map::cast(new_map)); 12468 obj->map()->set_instance_descriptors(descriptors); 12469 obj->map()->set_unused_property_fields(unused_property_fields); 12470 12471 obj->set_properties(FixedArray::cast(fields)); 12472 ASSERT(obj->IsJSObject()); 12473 12474 descriptors->SetNextEnumerationIndex(NextEnumerationIndex()); 12475 // Check that it really works. 12476 ASSERT(obj->HasFastProperties()); 12477 12478 return obj; 12479} 12480 12481 12482bool ObjectHashSet::Contains(Object* key) { 12483 ASSERT(IsKey(key)); 12484 12485 // If the object does not have an identity hash, it was never used as a key. 12486 { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); 12487 if (maybe_hash->ToObjectUnchecked()->IsUndefined()) return false; 12488 } 12489 return (FindEntry(key) != kNotFound); 12490} 12491 12492 12493MaybeObject* ObjectHashSet::Add(Object* key) { 12494 ASSERT(IsKey(key)); 12495 12496 // Make sure the key object has an identity hash code. 12497 int hash; 12498 { MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION); 12499 if (maybe_hash->IsFailure()) return maybe_hash; 12500 hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value(); 12501 } 12502 int entry = FindEntry(key); 12503 12504 // Check whether key is already present. 12505 if (entry != kNotFound) return this; 12506 12507 // Check whether the hash set should be extended and add entry. 12508 Object* obj; 12509 { MaybeObject* maybe_obj = EnsureCapacity(1, key); 12510 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 12511 } 12512 ObjectHashSet* table = ObjectHashSet::cast(obj); 12513 entry = table->FindInsertionEntry(hash); 12514 table->set(EntryToIndex(entry), key); 12515 table->ElementAdded(); 12516 return table; 12517} 12518 12519 12520MaybeObject* ObjectHashSet::Remove(Object* key) { 12521 ASSERT(IsKey(key)); 12522 12523 // If the object does not have an identity hash, it was never used as a key. 12524 { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); 12525 if (maybe_hash->ToObjectUnchecked()->IsUndefined()) return this; 12526 } 12527 int entry = FindEntry(key); 12528 12529 // Check whether key is actually present. 12530 if (entry == kNotFound) return this; 12531 12532 // Remove entry and try to shrink this hash set. 12533 set_the_hole(EntryToIndex(entry)); 12534 ElementRemoved(); 12535 return Shrink(key); 12536} 12537 12538 12539Object* ObjectHashTable::Lookup(Object* key) { 12540 ASSERT(IsKey(key)); 12541 12542 // If the object does not have an identity hash, it was never used as a key. 12543 { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); 12544 if (maybe_hash->ToObjectUnchecked()->IsUndefined()) { 12545 return GetHeap()->undefined_value(); 12546 } 12547 } 12548 int entry = FindEntry(key); 12549 if (entry == kNotFound) return GetHeap()->undefined_value(); 12550 return get(EntryToIndex(entry) + 1); 12551} 12552 12553 12554MaybeObject* ObjectHashTable::Put(Object* key, Object* value) { 12555 ASSERT(IsKey(key)); 12556 12557 // Make sure the key object has an identity hash code. 12558 int hash; 12559 { MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION); 12560 if (maybe_hash->IsFailure()) return maybe_hash; 12561 hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value(); 12562 } 12563 int entry = FindEntry(key); 12564 12565 // Check whether to perform removal operation. 12566 if (value->IsUndefined()) { 12567 if (entry == kNotFound) return this; 12568 RemoveEntry(entry); 12569 return Shrink(key); 12570 } 12571 12572 // Key is already in table, just overwrite value. 12573 if (entry != kNotFound) { 12574 set(EntryToIndex(entry) + 1, value); 12575 return this; 12576 } 12577 12578 // Check whether the hash table should be extended. 12579 Object* obj; 12580 { MaybeObject* maybe_obj = EnsureCapacity(1, key); 12581 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 12582 } 12583 ObjectHashTable* table = ObjectHashTable::cast(obj); 12584 table->AddEntry(table->FindInsertionEntry(hash), key, value); 12585 return table; 12586} 12587 12588 12589void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) { 12590 set(EntryToIndex(entry), key); 12591 set(EntryToIndex(entry) + 1, value); 12592 ElementAdded(); 12593} 12594 12595 12596void ObjectHashTable::RemoveEntry(int entry) { 12597 set_the_hole(EntryToIndex(entry)); 12598 set_the_hole(EntryToIndex(entry) + 1); 12599 ElementRemoved(); 12600} 12601 12602 12603#ifdef ENABLE_DEBUGGER_SUPPORT 12604// Check if there is a break point at this code position. 12605bool DebugInfo::HasBreakPoint(int code_position) { 12606 // Get the break point info object for this code position. 12607 Object* break_point_info = GetBreakPointInfo(code_position); 12608 12609 // If there is no break point info object or no break points in the break 12610 // point info object there is no break point at this code position. 12611 if (break_point_info->IsUndefined()) return false; 12612 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0; 12613} 12614 12615 12616// Get the break point info object for this code position. 12617Object* DebugInfo::GetBreakPointInfo(int code_position) { 12618 // Find the index of the break point info object for this code position. 12619 int index = GetBreakPointInfoIndex(code_position); 12620 12621 // Return the break point info object if any. 12622 if (index == kNoBreakPointInfo) return GetHeap()->undefined_value(); 12623 return BreakPointInfo::cast(break_points()->get(index)); 12624} 12625 12626 12627// Clear a break point at the specified code position. 12628void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info, 12629 int code_position, 12630 Handle<Object> break_point_object) { 12631 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position)); 12632 if (break_point_info->IsUndefined()) return; 12633 BreakPointInfo::ClearBreakPoint( 12634 Handle<BreakPointInfo>::cast(break_point_info), 12635 break_point_object); 12636} 12637 12638 12639void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, 12640 int code_position, 12641 int source_position, 12642 int statement_position, 12643 Handle<Object> break_point_object) { 12644 Isolate* isolate = Isolate::Current(); 12645 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position)); 12646 if (!break_point_info->IsUndefined()) { 12647 BreakPointInfo::SetBreakPoint( 12648 Handle<BreakPointInfo>::cast(break_point_info), 12649 break_point_object); 12650 return; 12651 } 12652 12653 // Adding a new break point for a code position which did not have any 12654 // break points before. Try to find a free slot. 12655 int index = kNoBreakPointInfo; 12656 for (int i = 0; i < debug_info->break_points()->length(); i++) { 12657 if (debug_info->break_points()->get(i)->IsUndefined()) { 12658 index = i; 12659 break; 12660 } 12661 } 12662 if (index == kNoBreakPointInfo) { 12663 // No free slot - extend break point info array. 12664 Handle<FixedArray> old_break_points = 12665 Handle<FixedArray>(FixedArray::cast(debug_info->break_points())); 12666 Handle<FixedArray> new_break_points = 12667 isolate->factory()->NewFixedArray( 12668 old_break_points->length() + 12669 Debug::kEstimatedNofBreakPointsInFunction); 12670 12671 debug_info->set_break_points(*new_break_points); 12672 for (int i = 0; i < old_break_points->length(); i++) { 12673 new_break_points->set(i, old_break_points->get(i)); 12674 } 12675 index = old_break_points->length(); 12676 } 12677 ASSERT(index != kNoBreakPointInfo); 12678 12679 // Allocate new BreakPointInfo object and set the break point. 12680 Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast( 12681 isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE)); 12682 new_break_point_info->set_code_position(Smi::FromInt(code_position)); 12683 new_break_point_info->set_source_position(Smi::FromInt(source_position)); 12684 new_break_point_info-> 12685 set_statement_position(Smi::FromInt(statement_position)); 12686 new_break_point_info->set_break_point_objects( 12687 isolate->heap()->undefined_value()); 12688 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object); 12689 debug_info->break_points()->set(index, *new_break_point_info); 12690} 12691 12692 12693// Get the break point objects for a code position. 12694Object* DebugInfo::GetBreakPointObjects(int code_position) { 12695 Object* break_point_info = GetBreakPointInfo(code_position); 12696 if (break_point_info->IsUndefined()) { 12697 return GetHeap()->undefined_value(); 12698 } 12699 return BreakPointInfo::cast(break_point_info)->break_point_objects(); 12700} 12701 12702 12703// Get the total number of break points. 12704int DebugInfo::GetBreakPointCount() { 12705 if (break_points()->IsUndefined()) return 0; 12706 int count = 0; 12707 for (int i = 0; i < break_points()->length(); i++) { 12708 if (!break_points()->get(i)->IsUndefined()) { 12709 BreakPointInfo* break_point_info = 12710 BreakPointInfo::cast(break_points()->get(i)); 12711 count += break_point_info->GetBreakPointCount(); 12712 } 12713 } 12714 return count; 12715} 12716 12717 12718Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info, 12719 Handle<Object> break_point_object) { 12720 Heap* heap = debug_info->GetHeap(); 12721 if (debug_info->break_points()->IsUndefined()) return heap->undefined_value(); 12722 for (int i = 0; i < debug_info->break_points()->length(); i++) { 12723 if (!debug_info->break_points()->get(i)->IsUndefined()) { 12724 Handle<BreakPointInfo> break_point_info = 12725 Handle<BreakPointInfo>(BreakPointInfo::cast( 12726 debug_info->break_points()->get(i))); 12727 if (BreakPointInfo::HasBreakPointObject(break_point_info, 12728 break_point_object)) { 12729 return *break_point_info; 12730 } 12731 } 12732 } 12733 return heap->undefined_value(); 12734} 12735 12736 12737// Find the index of the break point info object for the specified code 12738// position. 12739int DebugInfo::GetBreakPointInfoIndex(int code_position) { 12740 if (break_points()->IsUndefined()) return kNoBreakPointInfo; 12741 for (int i = 0; i < break_points()->length(); i++) { 12742 if (!break_points()->get(i)->IsUndefined()) { 12743 BreakPointInfo* break_point_info = 12744 BreakPointInfo::cast(break_points()->get(i)); 12745 if (break_point_info->code_position()->value() == code_position) { 12746 return i; 12747 } 12748 } 12749 } 12750 return kNoBreakPointInfo; 12751} 12752 12753 12754// Remove the specified break point object. 12755void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info, 12756 Handle<Object> break_point_object) { 12757 Isolate* isolate = Isolate::Current(); 12758 // If there are no break points just ignore. 12759 if (break_point_info->break_point_objects()->IsUndefined()) return; 12760 // If there is a single break point clear it if it is the same. 12761 if (!break_point_info->break_point_objects()->IsFixedArray()) { 12762 if (break_point_info->break_point_objects() == *break_point_object) { 12763 break_point_info->set_break_point_objects( 12764 isolate->heap()->undefined_value()); 12765 } 12766 return; 12767 } 12768 // If there are multiple break points shrink the array 12769 ASSERT(break_point_info->break_point_objects()->IsFixedArray()); 12770 Handle<FixedArray> old_array = 12771 Handle<FixedArray>( 12772 FixedArray::cast(break_point_info->break_point_objects())); 12773 Handle<FixedArray> new_array = 12774 isolate->factory()->NewFixedArray(old_array->length() - 1); 12775 int found_count = 0; 12776 for (int i = 0; i < old_array->length(); i++) { 12777 if (old_array->get(i) == *break_point_object) { 12778 ASSERT(found_count == 0); 12779 found_count++; 12780 } else { 12781 new_array->set(i - found_count, old_array->get(i)); 12782 } 12783 } 12784 // If the break point was found in the list change it. 12785 if (found_count > 0) break_point_info->set_break_point_objects(*new_array); 12786} 12787 12788 12789// Add the specified break point object. 12790void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info, 12791 Handle<Object> break_point_object) { 12792 // If there was no break point objects before just set it. 12793 if (break_point_info->break_point_objects()->IsUndefined()) { 12794 break_point_info->set_break_point_objects(*break_point_object); 12795 return; 12796 } 12797 // If the break point object is the same as before just ignore. 12798 if (break_point_info->break_point_objects() == *break_point_object) return; 12799 // If there was one break point object before replace with array. 12800 if (!break_point_info->break_point_objects()->IsFixedArray()) { 12801 Handle<FixedArray> array = FACTORY->NewFixedArray(2); 12802 array->set(0, break_point_info->break_point_objects()); 12803 array->set(1, *break_point_object); 12804 break_point_info->set_break_point_objects(*array); 12805 return; 12806 } 12807 // If there was more than one break point before extend array. 12808 Handle<FixedArray> old_array = 12809 Handle<FixedArray>( 12810 FixedArray::cast(break_point_info->break_point_objects())); 12811 Handle<FixedArray> new_array = 12812 FACTORY->NewFixedArray(old_array->length() + 1); 12813 for (int i = 0; i < old_array->length(); i++) { 12814 // If the break point was there before just ignore. 12815 if (old_array->get(i) == *break_point_object) return; 12816 new_array->set(i, old_array->get(i)); 12817 } 12818 // Add the new break point. 12819 new_array->set(old_array->length(), *break_point_object); 12820 break_point_info->set_break_point_objects(*new_array); 12821} 12822 12823 12824bool BreakPointInfo::HasBreakPointObject( 12825 Handle<BreakPointInfo> break_point_info, 12826 Handle<Object> break_point_object) { 12827 // No break point. 12828 if (break_point_info->break_point_objects()->IsUndefined()) return false; 12829 // Single break point. 12830 if (!break_point_info->break_point_objects()->IsFixedArray()) { 12831 return break_point_info->break_point_objects() == *break_point_object; 12832 } 12833 // Multiple break points. 12834 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects()); 12835 for (int i = 0; i < array->length(); i++) { 12836 if (array->get(i) == *break_point_object) { 12837 return true; 12838 } 12839 } 12840 return false; 12841} 12842 12843 12844// Get the number of break points. 12845int BreakPointInfo::GetBreakPointCount() { 12846 // No break point. 12847 if (break_point_objects()->IsUndefined()) return 0; 12848 // Single break point. 12849 if (!break_point_objects()->IsFixedArray()) return 1; 12850 // Multiple break points. 12851 return FixedArray::cast(break_point_objects())->length(); 12852} 12853#endif // ENABLE_DEBUGGER_SUPPORT 12854 12855 12856MaybeObject* JSDate::GetField(Object* object, Smi* index) { 12857 return JSDate::cast(object)->DoGetField( 12858 static_cast<FieldIndex>(index->value())); 12859} 12860 12861 12862Object* JSDate::DoGetField(FieldIndex index) { 12863 ASSERT(index != kDateValue); 12864 12865 DateCache* date_cache = GetIsolate()->date_cache(); 12866 12867 if (index < kFirstUncachedField) { 12868 Object* stamp = cache_stamp(); 12869 if (stamp != date_cache->stamp() && stamp->IsSmi()) { 12870 // Since the stamp is not NaN, the value is also not NaN. 12871 int64_t local_time_ms = 12872 date_cache->ToLocal(static_cast<int64_t>(value()->Number())); 12873 SetLocalFields(local_time_ms, date_cache); 12874 } 12875 switch (index) { 12876 case kYear: return year(); 12877 case kMonth: return month(); 12878 case kDay: return day(); 12879 case kWeekday: return weekday(); 12880 case kHour: return hour(); 12881 case kMinute: return min(); 12882 case kSecond: return sec(); 12883 default: UNREACHABLE(); 12884 } 12885 } 12886 12887 if (index >= kFirstUTCField) { 12888 return GetUTCField(index, value()->Number(), date_cache); 12889 } 12890 12891 double time = value()->Number(); 12892 if (isnan(time)) return GetIsolate()->heap()->nan_value(); 12893 12894 int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time)); 12895 int days = DateCache::DaysFromTime(local_time_ms); 12896 12897 if (index == kDays) return Smi::FromInt(days); 12898 12899 int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); 12900 if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000); 12901 ASSERT(index == kTimeInDay); 12902 return Smi::FromInt(time_in_day_ms); 12903} 12904 12905 12906Object* JSDate::GetUTCField(FieldIndex index, 12907 double value, 12908 DateCache* date_cache) { 12909 ASSERT(index >= kFirstUTCField); 12910 12911 if (isnan(value)) return GetIsolate()->heap()->nan_value(); 12912 12913 int64_t time_ms = static_cast<int64_t>(value); 12914 12915 if (index == kTimezoneOffset) { 12916 return Smi::FromInt(date_cache->TimezoneOffset(time_ms)); 12917 } 12918 12919 int days = DateCache::DaysFromTime(time_ms); 12920 12921 if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days)); 12922 12923 if (index <= kDayUTC) { 12924 int year, month, day; 12925 date_cache->YearMonthDayFromDays(days, &year, &month, &day); 12926 if (index == kYearUTC) return Smi::FromInt(year); 12927 if (index == kMonthUTC) return Smi::FromInt(month); 12928 ASSERT(index == kDayUTC); 12929 return Smi::FromInt(day); 12930 } 12931 12932 int time_in_day_ms = DateCache::TimeInDay(time_ms, days); 12933 switch (index) { 12934 case kHourUTC: return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000)); 12935 case kMinuteUTC: return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60); 12936 case kSecondUTC: return Smi::FromInt((time_in_day_ms / 1000) % 60); 12937 case kMillisecondUTC: return Smi::FromInt(time_in_day_ms % 1000); 12938 case kDaysUTC: return Smi::FromInt(days); 12939 case kTimeInDayUTC: return Smi::FromInt(time_in_day_ms); 12940 default: UNREACHABLE(); 12941 } 12942 12943 UNREACHABLE(); 12944 return NULL; 12945} 12946 12947 12948void JSDate::SetValue(Object* value, bool is_value_nan) { 12949 set_value(value); 12950 if (is_value_nan) { 12951 HeapNumber* nan = GetIsolate()->heap()->nan_value(); 12952 set_cache_stamp(nan, SKIP_WRITE_BARRIER); 12953 set_year(nan, SKIP_WRITE_BARRIER); 12954 set_month(nan, SKIP_WRITE_BARRIER); 12955 set_day(nan, SKIP_WRITE_BARRIER); 12956 set_hour(nan, SKIP_WRITE_BARRIER); 12957 set_min(nan, SKIP_WRITE_BARRIER); 12958 set_sec(nan, SKIP_WRITE_BARRIER); 12959 set_weekday(nan, SKIP_WRITE_BARRIER); 12960 } else { 12961 set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER); 12962 } 12963} 12964 12965 12966void JSDate::SetLocalFields(int64_t local_time_ms, DateCache* date_cache) { 12967 int days = DateCache::DaysFromTime(local_time_ms); 12968 int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); 12969 int year, month, day; 12970 date_cache->YearMonthDayFromDays(days, &year, &month, &day); 12971 int weekday = date_cache->Weekday(days); 12972 int hour = time_in_day_ms / (60 * 60 * 1000); 12973 int min = (time_in_day_ms / (60 * 1000)) % 60; 12974 int sec = (time_in_day_ms / 1000) % 60; 12975 set_cache_stamp(date_cache->stamp()); 12976 set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER); 12977 set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER); 12978 set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER); 12979 set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER); 12980 set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER); 12981 set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER); 12982 set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER); 12983} 12984 12985} } // namespace v8::internal 12986