ic.cc revision 8defd9ff6930b4e24729971a61cf7469daf119be
1// Copyright 2006-2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include "v8.h" 29 30#include "accessors.h" 31#include "api.h" 32#include "arguments.h" 33#include "execution.h" 34#include "ic-inl.h" 35#include "runtime.h" 36#include "stub-cache.h" 37 38namespace v8 { 39namespace internal { 40 41#ifdef DEBUG 42static char TransitionMarkFromState(IC::State state) { 43 switch (state) { 44 case UNINITIALIZED: return '0'; 45 case PREMONOMORPHIC: return 'P'; 46 case MONOMORPHIC: return '1'; 47 case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; 48 case MEGAMORPHIC: return 'N'; 49 50 // We never see the debugger states here, because the state is 51 // computed from the original code - not the patched code. Let 52 // these cases fall through to the unreachable code below. 53 case DEBUG_BREAK: break; 54 case DEBUG_PREPARE_STEP_IN: break; 55 } 56 UNREACHABLE(); 57 return 0; 58} 59 60void IC::TraceIC(const char* type, 61 Handle<Object> name, 62 State old_state, 63 Code* new_target, 64 const char* extra_info) { 65 if (FLAG_trace_ic) { 66 State new_state = StateFrom(new_target, 67 Heap::undefined_value(), 68 Heap::undefined_value()); 69 PrintF("[%s (%c->%c)%s", type, 70 TransitionMarkFromState(old_state), 71 TransitionMarkFromState(new_state), 72 extra_info); 73 name->Print(); 74 PrintF("]\n"); 75 } 76} 77#endif 78 79 80IC::IC(FrameDepth depth) { 81 // To improve the performance of the (much used) IC code, we unfold 82 // a few levels of the stack frame iteration code. This yields a 83 // ~35% speedup when running DeltaBlue with the '--nouse-ic' flag. 84 const Address entry = Top::c_entry_fp(Top::GetCurrentThread()); 85 Address* pc_address = 86 reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset); 87 Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset); 88 // If there's another JavaScript frame on the stack, we need to look 89 // one frame further down the stack to find the frame pointer and 90 // the return address stack slot. 91 if (depth == EXTRA_CALL_FRAME) { 92 const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset; 93 pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset); 94 fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset); 95 } 96#ifdef DEBUG 97 StackFrameIterator it; 98 for (int i = 0; i < depth + 1; i++) it.Advance(); 99 StackFrame* frame = it.frame(); 100 ASSERT(fp == frame->fp() && pc_address == frame->pc_address()); 101#endif 102 fp_ = fp; 103 pc_address_ = pc_address; 104} 105 106 107#ifdef ENABLE_DEBUGGER_SUPPORT 108Address IC::OriginalCodeAddress() { 109 HandleScope scope; 110 // Compute the JavaScript frame for the frame pointer of this IC 111 // structure. We need this to be able to find the function 112 // corresponding to the frame. 113 StackFrameIterator it; 114 while (it.frame()->fp() != this->fp()) it.Advance(); 115 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame()); 116 // Find the function on the stack and both the active code for the 117 // function and the original code. 118 JSFunction* function = JSFunction::cast(frame->function()); 119 Handle<SharedFunctionInfo> shared(function->shared()); 120 Code* code = shared->code(); 121 ASSERT(Debug::HasDebugInfo(shared)); 122 Code* original_code = Debug::GetDebugInfo(shared)->original_code(); 123 ASSERT(original_code->IsCode()); 124 // Get the address of the call site in the active code. This is the 125 // place where the call to DebugBreakXXX is and where the IC 126 // normally would be. 127 Address addr = pc() - Assembler::kCallTargetAddressOffset; 128 // Return the address in the original code. This is the place where 129 // the call which has been overwritten by the DebugBreakXXX resides 130 // and the place where the inline cache system should look. 131 intptr_t delta = 132 original_code->instruction_start() - code->instruction_start(); 133 return addr + delta; 134} 135#endif 136 137 138static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup, 139 Object* receiver) { 140 Object* end = lookup->IsProperty() ? lookup->holder() : Heap::null_value(); 141 for (Object* current = receiver; 142 current != end; 143 current = current->GetPrototype()) { 144 if (current->IsJSObject() && 145 !JSObject::cast(current)->HasFastProperties() && 146 !current->IsJSGlobalProxy() && 147 !current->IsJSGlobalObject()) { 148 return true; 149 } 150 } 151 152 return false; 153} 154 155 156IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) { 157 IC::State state = target->ic_state(); 158 159 if (state != MONOMORPHIC) return state; 160 if (receiver->IsUndefined() || receiver->IsNull()) return state; 161 162 InlineCacheHolderFlag cache_holder = 163 Code::ExtractCacheHolderFromFlags(target->flags()); 164 165 166 if (cache_holder == OWN_MAP && !receiver->IsJSObject()) { 167 // The stub was generated for JSObject but called for non-JSObject. 168 // IC::GetCodeCacheMap is not applicable. 169 return MONOMORPHIC; 170 } else if (cache_holder == PROTOTYPE_MAP && 171 receiver->GetPrototype()->IsNull()) { 172 // IC::GetCodeCacheMap is not applicable. 173 return MONOMORPHIC; 174 } 175 Map* map = IC::GetCodeCacheMap(receiver, cache_holder); 176 177 // Decide whether the inline cache failed because of changes to the 178 // receiver itself or changes to one of its prototypes. 179 // 180 // If there are changes to the receiver itself, the map of the 181 // receiver will have changed and the current target will not be in 182 // the receiver map's code cache. Therefore, if the current target 183 // is in the receiver map's code cache, the inline cache failed due 184 // to prototype check failure. 185 int index = map->IndexInCodeCache(name, target); 186 if (index >= 0) { 187 // For keyed load/store/call, the most likely cause of cache failure is 188 // that the key has changed. We do not distinguish between 189 // prototype and non-prototype failures for keyed access. 190 Code::Kind kind = target->kind(); 191 if (kind == Code::KEYED_LOAD_IC || 192 kind == Code::KEYED_STORE_IC || 193 kind == Code::KEYED_CALL_IC) { 194 return MONOMORPHIC; 195 } 196 197 // Remove the target from the code cache to avoid hitting the same 198 // invalid stub again. 199 map->RemoveFromCodeCache(String::cast(name), target, index); 200 201 return MONOMORPHIC_PROTOTYPE_FAILURE; 202 } 203 204 // The builtins object is special. It only changes when JavaScript 205 // builtins are loaded lazily. It is important to keep inline 206 // caches for the builtins object monomorphic. Therefore, if we get 207 // an inline cache miss for the builtins object after lazily loading 208 // JavaScript builtins, we return uninitialized as the state to 209 // force the inline cache back to monomorphic state. 210 if (receiver->IsJSBuiltinsObject()) { 211 return UNINITIALIZED; 212 } 213 214 return MONOMORPHIC; 215} 216 217 218RelocInfo::Mode IC::ComputeMode() { 219 Address addr = address(); 220 Code* code = Code::cast(Heap::FindCodeObject(addr)); 221 for (RelocIterator it(code, RelocInfo::kCodeTargetMask); 222 !it.done(); it.next()) { 223 RelocInfo* info = it.rinfo(); 224 if (info->pc() == addr) return info->rmode(); 225 } 226 UNREACHABLE(); 227 return RelocInfo::NONE; 228} 229 230 231Failure* IC::TypeError(const char* type, 232 Handle<Object> object, 233 Handle<Object> key) { 234 HandleScope scope; 235 Handle<Object> args[2] = { key, object }; 236 Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2)); 237 return Top::Throw(*error); 238} 239 240 241Failure* IC::ReferenceError(const char* type, Handle<String> name) { 242 HandleScope scope; 243 Handle<Object> error = 244 Factory::NewReferenceError(type, HandleVector(&name, 1)); 245 return Top::Throw(*error); 246} 247 248 249void IC::Clear(Address address) { 250 Code* target = GetTargetAtAddress(address); 251 252 // Don't clear debug break inline cache as it will remove the break point. 253 if (target->ic_state() == DEBUG_BREAK) return; 254 255 switch (target->kind()) { 256 case Code::LOAD_IC: return LoadIC::Clear(address, target); 257 case Code::KEYED_LOAD_IC: return KeyedLoadIC::Clear(address, target); 258 case Code::STORE_IC: return StoreIC::Clear(address, target); 259 case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target); 260 case Code::CALL_IC: return CallIC::Clear(address, target); 261 case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target); 262 case Code::BINARY_OP_IC: return; // Clearing these is tricky and does not 263 // make any performance difference. 264 default: UNREACHABLE(); 265 } 266} 267 268 269void CallICBase::Clear(Address address, Code* target) { 270 State state = target->ic_state(); 271 if (state == UNINITIALIZED) return; 272 Code* code = 273 StubCache::FindCallInitialize(target->arguments_count(), 274 target->ic_in_loop(), 275 target->kind()); 276 SetTargetAtAddress(address, code); 277} 278 279 280void KeyedLoadIC::Clear(Address address, Code* target) { 281 if (target->ic_state() == UNINITIALIZED) return; 282 // Make sure to also clear the map used in inline fast cases. If we 283 // do not clear these maps, cached code can keep objects alive 284 // through the embedded maps. 285 ClearInlinedVersion(address); 286 SetTargetAtAddress(address, initialize_stub()); 287} 288 289 290void LoadIC::Clear(Address address, Code* target) { 291 if (target->ic_state() == UNINITIALIZED) return; 292 ClearInlinedVersion(address); 293 SetTargetAtAddress(address, initialize_stub()); 294} 295 296 297void StoreIC::Clear(Address address, Code* target) { 298 if (target->ic_state() == UNINITIALIZED) return; 299 SetTargetAtAddress(address, initialize_stub()); 300} 301 302 303void KeyedStoreIC::Clear(Address address, Code* target) { 304 if (target->ic_state() == UNINITIALIZED) return; 305 SetTargetAtAddress(address, initialize_stub()); 306} 307 308 309Code* KeyedLoadIC::external_array_stub(JSObject::ElementsKind elements_kind) { 310 switch (elements_kind) { 311 case JSObject::EXTERNAL_BYTE_ELEMENTS: 312 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalByteArray); 313 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 314 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedByteArray); 315 case JSObject::EXTERNAL_SHORT_ELEMENTS: 316 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalShortArray); 317 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 318 return Builtins::builtin( 319 Builtins::KeyedLoadIC_ExternalUnsignedShortArray); 320 case JSObject::EXTERNAL_INT_ELEMENTS: 321 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalIntArray); 322 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: 323 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedIntArray); 324 case JSObject::EXTERNAL_FLOAT_ELEMENTS: 325 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalFloatArray); 326 default: 327 UNREACHABLE(); 328 return NULL; 329 } 330} 331 332 333Code* KeyedStoreIC::external_array_stub(JSObject::ElementsKind elements_kind) { 334 switch (elements_kind) { 335 case JSObject::EXTERNAL_BYTE_ELEMENTS: 336 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalByteArray); 337 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 338 return Builtins::builtin( 339 Builtins::KeyedStoreIC_ExternalUnsignedByteArray); 340 case JSObject::EXTERNAL_SHORT_ELEMENTS: 341 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalShortArray); 342 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 343 return Builtins::builtin( 344 Builtins::KeyedStoreIC_ExternalUnsignedShortArray); 345 case JSObject::EXTERNAL_INT_ELEMENTS: 346 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalIntArray); 347 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: 348 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalUnsignedIntArray); 349 case JSObject::EXTERNAL_FLOAT_ELEMENTS: 350 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalFloatArray); 351 default: 352 UNREACHABLE(); 353 return NULL; 354 } 355} 356 357 358static bool HasInterceptorGetter(JSObject* object) { 359 return !object->GetNamedInterceptor()->getter()->IsUndefined(); 360} 361 362 363static void LookupForRead(Object* object, 364 String* name, 365 LookupResult* lookup) { 366 AssertNoAllocation no_gc; // pointers must stay valid 367 368 // Skip all the objects with named interceptors, but 369 // without actual getter. 370 while (true) { 371 object->Lookup(name, lookup); 372 // Besides normal conditions (property not found or it's not 373 // an interceptor), bail out if lookup is not cacheable: we won't 374 // be able to IC it anyway and regular lookup should work fine. 375 if (!lookup->IsFound() 376 || (lookup->type() != INTERCEPTOR) 377 || !lookup->IsCacheable()) { 378 return; 379 } 380 381 JSObject* holder = lookup->holder(); 382 if (HasInterceptorGetter(holder)) { 383 return; 384 } 385 386 holder->LocalLookupRealNamedProperty(name, lookup); 387 if (lookup->IsProperty()) { 388 ASSERT(lookup->type() != INTERCEPTOR); 389 return; 390 } 391 392 Object* proto = holder->GetPrototype(); 393 if (proto->IsNull()) { 394 lookup->NotFound(); 395 return; 396 } 397 398 object = proto; 399 } 400} 401 402 403Object* CallICBase::TryCallAsFunction(Object* object) { 404 HandleScope scope; 405 Handle<Object> target(object); 406 Handle<Object> delegate = Execution::GetFunctionDelegate(target); 407 408 if (delegate->IsJSFunction()) { 409 // Patch the receiver and use the delegate as the function to 410 // invoke. This is used for invoking objects as if they were 411 // functions. 412 const int argc = this->target()->arguments_count(); 413 StackFrameLocator locator; 414 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); 415 int index = frame->ComputeExpressionsCount() - (argc + 1); 416 frame->SetExpression(index, *target); 417 } 418 419 return *delegate; 420} 421 422 423void CallICBase::ReceiverToObject(Handle<Object> object) { 424 HandleScope scope; 425 Handle<Object> receiver(object); 426 427 // Change the receiver to the result of calling ToObject on it. 428 const int argc = this->target()->arguments_count(); 429 StackFrameLocator locator; 430 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); 431 int index = frame->ComputeExpressionsCount() - (argc + 1); 432 frame->SetExpression(index, *Factory::ToObject(object)); 433} 434 435 436Object* CallICBase::LoadFunction(State state, 437 Handle<Object> object, 438 Handle<String> name) { 439 // If the object is undefined or null it's illegal to try to get any 440 // of its properties; throw a TypeError in that case. 441 if (object->IsUndefined() || object->IsNull()) { 442 return TypeError("non_object_property_call", object, name); 443 } 444 445 if (object->IsString() || object->IsNumber() || object->IsBoolean()) { 446 ReceiverToObject(object); 447 } 448 449 // Check if the name is trivially convertible to an index and get 450 // the element if so. 451 uint32_t index; 452 if (name->AsArrayIndex(&index)) { 453 Object* result = object->GetElement(index); 454 if (result->IsJSFunction()) return result; 455 456 // Try to find a suitable function delegate for the object at hand. 457 result = TryCallAsFunction(result); 458 if (result->IsJSFunction()) return result; 459 460 // Otherwise, it will fail in the lookup step. 461 } 462 463 // Lookup the property in the object. 464 LookupResult lookup; 465 LookupForRead(*object, *name, &lookup); 466 467 if (!lookup.IsProperty()) { 468 // If the object does not have the requested property, check which 469 // exception we need to throw. 470 if (IsContextual(object)) { 471 return ReferenceError("not_defined", name); 472 } 473 return TypeError("undefined_method", object, name); 474 } 475 476 // Lookup is valid: Update inline cache and stub cache. 477 if (FLAG_use_ic) { 478 UpdateCaches(&lookup, state, object, name); 479 } 480 481 // Get the property. 482 PropertyAttributes attr; 483 Object* result = object->GetProperty(*object, &lookup, *name, &attr); 484 if (result->IsFailure()) return result; 485 if (lookup.type() == INTERCEPTOR) { 486 // If the object does not have the requested property, check which 487 // exception we need to throw. 488 if (attr == ABSENT) { 489 if (IsContextual(object)) { 490 return ReferenceError("not_defined", name); 491 } 492 return TypeError("undefined_method", object, name); 493 } 494 } 495 496 ASSERT(result != Heap::the_hole_value()); 497 498 if (result->IsJSFunction()) { 499#ifdef ENABLE_DEBUGGER_SUPPORT 500 // Handle stepping into a function if step into is active. 501 if (Debug::StepInActive()) { 502 // Protect the result in a handle as the debugger can allocate and might 503 // cause GC. 504 HandleScope scope; 505 Handle<JSFunction> function(JSFunction::cast(result)); 506 Debug::HandleStepIn(function, object, fp(), false); 507 return *function; 508 } 509#endif 510 511 return result; 512 } 513 514 // Try to find a suitable function delegate for the object at hand. 515 result = TryCallAsFunction(result); 516 return result->IsJSFunction() ? 517 result : TypeError("property_not_function", object, name); 518} 519 520 521void CallICBase::UpdateCaches(LookupResult* lookup, 522 State state, 523 Handle<Object> object, 524 Handle<String> name) { 525 // Bail out if we didn't find a result. 526 if (!lookup->IsProperty() || !lookup->IsCacheable()) return; 527 528#ifndef V8_TARGET_ARCH_IA32 529 // Normal objects only implemented for IA32 by now. 530 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return; 531#else 532 if (lookup->holder() != *object && 533 HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) { 534 // Suppress optimization for prototype chains with slow properties objects 535 // in the middle. 536 return; 537 } 538#endif 539 540 // Compute the number of arguments. 541 int argc = target()->arguments_count(); 542 InLoopFlag in_loop = target()->ic_in_loop(); 543 Object* code = NULL; 544 545 if (state == UNINITIALIZED) { 546 // This is the first time we execute this inline cache. 547 // Set the target to the pre monomorphic stub to delay 548 // setting the monomorphic state. 549 code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_); 550 } else if (state == MONOMORPHIC) { 551 code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_); 552 } else { 553 // Compute monomorphic stub. 554 switch (lookup->type()) { 555 case FIELD: { 556 int index = lookup->GetFieldIndex(); 557 code = StubCache::ComputeCallField(argc, 558 in_loop, 559 kind_, 560 *name, 561 *object, 562 lookup->holder(), 563 index); 564 break; 565 } 566 case CONSTANT_FUNCTION: { 567 // Get the constant function and compute the code stub for this 568 // call; used for rewriting to monomorphic state and making sure 569 // that the code stub is in the stub cache. 570 JSFunction* function = lookup->GetConstantFunction(); 571 code = StubCache::ComputeCallConstant(argc, 572 in_loop, 573 kind_, 574 *name, 575 *object, 576 lookup->holder(), 577 function); 578 break; 579 } 580 case NORMAL: { 581 if (!object->IsJSObject()) return; 582 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 583 584 if (lookup->holder()->IsGlobalObject()) { 585 GlobalObject* global = GlobalObject::cast(lookup->holder()); 586 JSGlobalPropertyCell* cell = 587 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); 588 if (!cell->value()->IsJSFunction()) return; 589 JSFunction* function = JSFunction::cast(cell->value()); 590 code = StubCache::ComputeCallGlobal(argc, 591 in_loop, 592 kind_, 593 *name, 594 *receiver, 595 global, 596 cell, 597 function); 598 } else { 599 // There is only one shared stub for calling normalized 600 // properties. It does not traverse the prototype chain, so the 601 // property must be found in the receiver for the stub to be 602 // applicable. 603 if (lookup->holder() != *receiver) return; 604 code = StubCache::ComputeCallNormal(argc, 605 in_loop, 606 kind_, 607 *name, 608 *receiver); 609 } 610 break; 611 } 612 case INTERCEPTOR: { 613 ASSERT(HasInterceptorGetter(lookup->holder())); 614 code = StubCache::ComputeCallInterceptor(argc, 615 kind_, 616 *name, 617 *object, 618 lookup->holder()); 619 break; 620 } 621 default: 622 return; 623 } 624 } 625 626 // If we're unable to compute the stub (not enough memory left), we 627 // simply avoid updating the caches. 628 if (code == NULL || code->IsFailure()) return; 629 630 // Patch the call site depending on the state of the cache. 631 if (state == UNINITIALIZED || 632 state == PREMONOMORPHIC || 633 state == MONOMORPHIC || 634 state == MONOMORPHIC_PROTOTYPE_FAILURE) { 635 set_target(Code::cast(code)); 636 } else if (state == MEGAMORPHIC) { 637 // Cache code holding map should be consistent with 638 // GenerateMonomorphicCacheProbe. It is not the map which holds the stub. 639 Map* map = JSObject::cast(object->IsJSObject() ? *object : 640 object->GetPrototype())->map(); 641 642 // Update the stub cache. 643 StubCache::Set(*name, map, Code::cast(code)); 644 } 645 646#ifdef DEBUG 647 TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC", 648 name, state, target(), in_loop ? " (in-loop)" : ""); 649#endif 650} 651 652 653Object* KeyedCallIC::LoadFunction(State state, 654 Handle<Object> object, 655 Handle<Object> key) { 656 if (key->IsSymbol()) { 657 return CallICBase::LoadFunction(state, object, Handle<String>::cast(key)); 658 } 659 660 if (object->IsUndefined() || object->IsNull()) { 661 return TypeError("non_object_property_call", object, key); 662 } 663 664 if (object->IsString() || object->IsNumber() || object->IsBoolean()) { 665 ReceiverToObject(object); 666 } 667 668 if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) { 669 int argc = target()->arguments_count(); 670 InLoopFlag in_loop = target()->ic_in_loop(); 671 Object* code = StubCache::ComputeCallMegamorphic( 672 argc, in_loop, Code::KEYED_CALL_IC); 673 if (!code->IsFailure()) { 674 set_target(Code::cast(code)); 675#ifdef DEBUG 676 TraceIC( 677 "KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : ""); 678#endif 679 } 680 } 681 Object* result = Runtime::GetObjectProperty(object, key); 682 if (result->IsJSFunction()) return result; 683 result = TryCallAsFunction(result); 684 return result->IsJSFunction() ? 685 result : TypeError("property_not_function", object, key); 686} 687 688 689Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { 690 // If the object is undefined or null it's illegal to try to get any 691 // of its properties; throw a TypeError in that case. 692 if (object->IsUndefined() || object->IsNull()) { 693 return TypeError("non_object_property_load", object, name); 694 } 695 696 if (FLAG_use_ic) { 697 // Use specialized code for getting the length of strings and 698 // string wrapper objects. The length property of string wrapper 699 // objects is read-only and therefore always returns the length of 700 // the underlying string value. See ECMA-262 15.5.5.1. 701 if ((object->IsString() || object->IsStringWrapper()) && 702 name->Equals(Heap::length_symbol())) { 703 HandleScope scope; 704 // Get the string if we have a string wrapper object. 705 if (object->IsJSValue()) { 706 object = Handle<Object>(Handle<JSValue>::cast(object)->value()); 707 } 708#ifdef DEBUG 709 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n"); 710#endif 711 Map* map = HeapObject::cast(*object)->map(); 712 if (object->IsString()) { 713 const int offset = String::kLengthOffset; 714 PatchInlinedLoad(address(), map, offset); 715 } 716 717 Code* target = NULL; 718 target = Builtins::builtin(Builtins::LoadIC_StringLength); 719 set_target(target); 720 return Smi::FromInt(String::cast(*object)->length()); 721 } 722 723 // Use specialized code for getting the length of arrays. 724 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) { 725#ifdef DEBUG 726 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n"); 727#endif 728 Map* map = HeapObject::cast(*object)->map(); 729 const int offset = JSArray::kLengthOffset; 730 PatchInlinedLoad(address(), map, offset); 731 732 Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength); 733 set_target(target); 734 return JSArray::cast(*object)->length(); 735 } 736 737 // Use specialized code for getting prototype of functions. 738 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) && 739 JSFunction::cast(*object)->should_have_prototype()) { 740#ifdef DEBUG 741 if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n"); 742#endif 743 Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype); 744 set_target(target); 745 return Accessors::FunctionGetPrototype(*object, 0); 746 } 747 } 748 749 // Check if the name is trivially convertible to an index and get 750 // the element if so. 751 uint32_t index; 752 if (name->AsArrayIndex(&index)) return object->GetElement(index); 753 754 // Named lookup in the object. 755 LookupResult lookup; 756 LookupForRead(*object, *name, &lookup); 757 758 // If we did not find a property, check if we need to throw an exception. 759 if (!lookup.IsProperty()) { 760 if (FLAG_strict || IsContextual(object)) { 761 return ReferenceError("not_defined", name); 762 } 763 LOG(SuspectReadEvent(*name, *object)); 764 } 765 766 bool can_be_inlined = 767 FLAG_use_ic && 768 state == PREMONOMORPHIC && 769 lookup.IsProperty() && 770 lookup.IsCacheable() && 771 lookup.holder() == *object && 772 lookup.type() == FIELD && 773 !object->IsAccessCheckNeeded(); 774 775 if (can_be_inlined) { 776 Map* map = lookup.holder()->map(); 777 // Property's index in the properties array. If negative we have 778 // an inobject property. 779 int index = lookup.GetFieldIndex() - map->inobject_properties(); 780 if (index < 0) { 781 // Index is an offset from the end of the object. 782 int offset = map->instance_size() + (index * kPointerSize); 783 if (PatchInlinedLoad(address(), map, offset)) { 784 set_target(megamorphic_stub()); 785 return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex()); 786#ifdef DEBUG 787 if (FLAG_trace_ic) { 788 PrintF("[LoadIC : inline patch %s]\n", *name->ToCString()); 789 } 790 } else { 791 if (FLAG_trace_ic) { 792 PrintF("[LoadIC : no inline patch %s (patching failed)]\n", 793 *name->ToCString()); 794 } 795 } 796 } else { 797 if (FLAG_trace_ic) { 798 PrintF("[LoadIC : no inline patch %s (not inobject)]\n", 799 *name->ToCString()); 800 } 801 } 802 } else { 803 if (FLAG_use_ic && state == PREMONOMORPHIC) { 804 if (FLAG_trace_ic) { 805 PrintF("[LoadIC : no inline patch %s (not inlinable)]\n", 806 *name->ToCString()); 807#endif 808 } 809 } 810 } 811 812 // Update inline cache and stub cache. 813 if (FLAG_use_ic) { 814 UpdateCaches(&lookup, state, object, name); 815 } 816 817 PropertyAttributes attr; 818 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) { 819 // Get the property. 820 Object* result = object->GetProperty(*object, &lookup, *name, &attr); 821 if (result->IsFailure()) return result; 822 // If the property is not present, check if we need to throw an 823 // exception. 824 if (attr == ABSENT && IsContextual(object)) { 825 return ReferenceError("not_defined", name); 826 } 827 return result; 828 } 829 830 // Get the property. 831 return object->GetProperty(*object, &lookup, *name, &attr); 832} 833 834 835void LoadIC::UpdateCaches(LookupResult* lookup, 836 State state, 837 Handle<Object> object, 838 Handle<String> name) { 839 // Bail out if the result is not cacheable. 840 if (!lookup->IsCacheable()) return; 841 842 // Loading properties from values is not common, so don't try to 843 // deal with non-JS objects here. 844 if (!object->IsJSObject()) return; 845 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 846 847 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return; 848 849 // Compute the code stub for this load. 850 Object* code = NULL; 851 if (state == UNINITIALIZED) { 852 // This is the first time we execute this inline cache. 853 // Set the target to the pre monomorphic stub to delay 854 // setting the monomorphic state. 855 code = pre_monomorphic_stub(); 856 } else if (!lookup->IsProperty()) { 857 // Nonexistent property. The result is undefined. 858 code = StubCache::ComputeLoadNonexistent(*name, *receiver); 859 } else { 860 // Compute monomorphic stub. 861 switch (lookup->type()) { 862 case FIELD: { 863 code = StubCache::ComputeLoadField(*name, *receiver, 864 lookup->holder(), 865 lookup->GetFieldIndex()); 866 break; 867 } 868 case CONSTANT_FUNCTION: { 869 Object* constant = lookup->GetConstantFunction(); 870 code = StubCache::ComputeLoadConstant(*name, *receiver, 871 lookup->holder(), constant); 872 break; 873 } 874 case NORMAL: { 875 if (lookup->holder()->IsGlobalObject()) { 876 GlobalObject* global = GlobalObject::cast(lookup->holder()); 877 JSGlobalPropertyCell* cell = 878 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); 879 code = StubCache::ComputeLoadGlobal(*name, 880 *receiver, 881 global, 882 cell, 883 lookup->IsDontDelete()); 884 } else { 885 // There is only one shared stub for loading normalized 886 // properties. It does not traverse the prototype chain, so the 887 // property must be found in the receiver for the stub to be 888 // applicable. 889 if (lookup->holder() != *receiver) return; 890 code = StubCache::ComputeLoadNormal(); 891 } 892 break; 893 } 894 case CALLBACKS: { 895 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return; 896 AccessorInfo* callback = 897 AccessorInfo::cast(lookup->GetCallbackObject()); 898 if (v8::ToCData<Address>(callback->getter()) == 0) return; 899 code = StubCache::ComputeLoadCallback(*name, *receiver, 900 lookup->holder(), callback); 901 break; 902 } 903 case INTERCEPTOR: { 904 ASSERT(HasInterceptorGetter(lookup->holder())); 905 code = StubCache::ComputeLoadInterceptor(*name, *receiver, 906 lookup->holder()); 907 break; 908 } 909 default: 910 return; 911 } 912 } 913 914 // If we're unable to compute the stub (not enough memory left), we 915 // simply avoid updating the caches. 916 if (code == NULL || code->IsFailure()) return; 917 918 // Patch the call site depending on the state of the cache. 919 if (state == UNINITIALIZED || state == PREMONOMORPHIC || 920 state == MONOMORPHIC_PROTOTYPE_FAILURE) { 921 set_target(Code::cast(code)); 922 } else if (state == MONOMORPHIC) { 923 set_target(megamorphic_stub()); 924 } else if (state == MEGAMORPHIC) { 925 // Cache code holding map should be consistent with 926 // GenerateMonomorphicCacheProbe. 927 Map* map = JSObject::cast(object->IsJSObject() ? *object : 928 object->GetPrototype())->map(); 929 930 StubCache::Set(*name, map, Code::cast(code)); 931 } 932 933#ifdef DEBUG 934 TraceIC("LoadIC", name, state, target()); 935#endif 936} 937 938 939Object* KeyedLoadIC::Load(State state, 940 Handle<Object> object, 941 Handle<Object> key) { 942 if (key->IsSymbol()) { 943 Handle<String> name = Handle<String>::cast(key); 944 945 // If the object is undefined or null it's illegal to try to get any 946 // of its properties; throw a TypeError in that case. 947 if (object->IsUndefined() || object->IsNull()) { 948 return TypeError("non_object_property_load", object, name); 949 } 950 951 if (FLAG_use_ic) { 952 // Use specialized code for getting the length of strings. 953 if (object->IsString() && name->Equals(Heap::length_symbol())) { 954 Handle<String> string = Handle<String>::cast(object); 955 Object* code = NULL; 956 code = StubCache::ComputeKeyedLoadStringLength(*name, *string); 957 if (code->IsFailure()) return code; 958 set_target(Code::cast(code)); 959#ifdef DEBUG 960 TraceIC("KeyedLoadIC", name, state, target()); 961#endif // DEBUG 962 return Smi::FromInt(string->length()); 963 } 964 965 // Use specialized code for getting the length of arrays. 966 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) { 967 Handle<JSArray> array = Handle<JSArray>::cast(object); 968 Object* code = StubCache::ComputeKeyedLoadArrayLength(*name, *array); 969 if (code->IsFailure()) return code; 970 set_target(Code::cast(code)); 971#ifdef DEBUG 972 TraceIC("KeyedLoadIC", name, state, target()); 973#endif // DEBUG 974 return JSArray::cast(*object)->length(); 975 } 976 977 // Use specialized code for getting prototype of functions. 978 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) && 979 JSFunction::cast(*object)->should_have_prototype()) { 980 Handle<JSFunction> function = Handle<JSFunction>::cast(object); 981 Object* code = 982 StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function); 983 if (code->IsFailure()) return code; 984 set_target(Code::cast(code)); 985#ifdef DEBUG 986 TraceIC("KeyedLoadIC", name, state, target()); 987#endif // DEBUG 988 return Accessors::FunctionGetPrototype(*object, 0); 989 } 990 } 991 992 // Check if the name is trivially convertible to an index and get 993 // the element or char if so. 994 uint32_t index = 0; 995 if (name->AsArrayIndex(&index)) { 996 HandleScope scope; 997 // Rewrite to the generic keyed load stub. 998 if (FLAG_use_ic) set_target(generic_stub()); 999 return Runtime::GetElementOrCharAt(object, index); 1000 } 1001 1002 // Named lookup. 1003 LookupResult lookup; 1004 LookupForRead(*object, *name, &lookup); 1005 1006 // If we did not find a property, check if we need to throw an exception. 1007 if (!lookup.IsProperty()) { 1008 if (FLAG_strict || IsContextual(object)) { 1009 return ReferenceError("not_defined", name); 1010 } 1011 } 1012 1013 if (FLAG_use_ic) { 1014 UpdateCaches(&lookup, state, object, name); 1015 } 1016 1017 PropertyAttributes attr; 1018 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) { 1019 // Get the property. 1020 Object* result = object->GetProperty(*object, &lookup, *name, &attr); 1021 if (result->IsFailure()) return result; 1022 // If the property is not present, check if we need to throw an 1023 // exception. 1024 if (attr == ABSENT && IsContextual(object)) { 1025 return ReferenceError("not_defined", name); 1026 } 1027 return result; 1028 } 1029 1030 return object->GetProperty(*object, &lookup, *name, &attr); 1031 } 1032 1033 // Do not use ICs for objects that require access checks (including 1034 // the global object). 1035 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded(); 1036 1037 if (use_ic) { 1038 Code* stub = generic_stub(); 1039 if (object->IsString() && key->IsNumber()) { 1040 stub = string_stub(); 1041 } else if (object->IsJSObject()) { 1042 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 1043 if (receiver->HasExternalArrayElements()) { 1044 stub = external_array_stub(receiver->GetElementsKind()); 1045 } else if (receiver->HasIndexedInterceptor()) { 1046 stub = indexed_interceptor_stub(); 1047 } 1048 } 1049 set_target(stub); 1050 // For JSObjects with fast elements that are not value wrappers 1051 // and that do not have indexed interceptors, we initialize the 1052 // inlined fast case (if present) by patching the inlined map 1053 // check. 1054 if (object->IsJSObject() && 1055 !object->IsJSValue() && 1056 !JSObject::cast(*object)->HasIndexedInterceptor() && 1057 JSObject::cast(*object)->HasFastElements()) { 1058 Map* map = JSObject::cast(*object)->map(); 1059 PatchInlinedLoad(address(), map); 1060 } 1061 } 1062 1063 // Get the property. 1064 return Runtime::GetObjectProperty(object, key); 1065} 1066 1067 1068void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, 1069 Handle<Object> object, Handle<String> name) { 1070 // Bail out if we didn't find a result. 1071 if (!lookup->IsProperty() || !lookup->IsCacheable()) return; 1072 1073 if (!object->IsJSObject()) return; 1074 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 1075 1076 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return; 1077 1078 // Compute the code stub for this load. 1079 Object* code = NULL; 1080 1081 if (state == UNINITIALIZED) { 1082 // This is the first time we execute this inline cache. 1083 // Set the target to the pre monomorphic stub to delay 1084 // setting the monomorphic state. 1085 code = pre_monomorphic_stub(); 1086 } else { 1087 // Compute a monomorphic stub. 1088 switch (lookup->type()) { 1089 case FIELD: { 1090 code = StubCache::ComputeKeyedLoadField(*name, *receiver, 1091 lookup->holder(), 1092 lookup->GetFieldIndex()); 1093 break; 1094 } 1095 case CONSTANT_FUNCTION: { 1096 Object* constant = lookup->GetConstantFunction(); 1097 code = StubCache::ComputeKeyedLoadConstant(*name, *receiver, 1098 lookup->holder(), constant); 1099 break; 1100 } 1101 case CALLBACKS: { 1102 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return; 1103 AccessorInfo* callback = 1104 AccessorInfo::cast(lookup->GetCallbackObject()); 1105 if (v8::ToCData<Address>(callback->getter()) == 0) return; 1106 code = StubCache::ComputeKeyedLoadCallback(*name, *receiver, 1107 lookup->holder(), callback); 1108 break; 1109 } 1110 case INTERCEPTOR: { 1111 ASSERT(HasInterceptorGetter(lookup->holder())); 1112 code = StubCache::ComputeKeyedLoadInterceptor(*name, *receiver, 1113 lookup->holder()); 1114 break; 1115 } 1116 default: { 1117 // Always rewrite to the generic case so that we do not 1118 // repeatedly try to rewrite. 1119 code = generic_stub(); 1120 break; 1121 } 1122 } 1123 } 1124 1125 // If we're unable to compute the stub (not enough memory left), we 1126 // simply avoid updating the caches. 1127 if (code == NULL || code->IsFailure()) return; 1128 1129 // Patch the call site depending on the state of the cache. Make 1130 // sure to always rewrite from monomorphic to megamorphic. 1131 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE); 1132 if (state == UNINITIALIZED || state == PREMONOMORPHIC) { 1133 set_target(Code::cast(code)); 1134 } else if (state == MONOMORPHIC) { 1135 set_target(megamorphic_stub()); 1136 } 1137 1138#ifdef DEBUG 1139 TraceIC("KeyedLoadIC", name, state, target()); 1140#endif 1141} 1142 1143 1144static bool StoreICableLookup(LookupResult* lookup) { 1145 // Bail out if we didn't find a result. 1146 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false; 1147 1148 // If the property is read-only, we leave the IC in its current 1149 // state. 1150 if (lookup->IsReadOnly()) return false; 1151 1152 return true; 1153} 1154 1155 1156static bool LookupForWrite(JSObject* object, 1157 String* name, 1158 LookupResult* lookup) { 1159 object->LocalLookup(name, lookup); 1160 if (!StoreICableLookup(lookup)) { 1161 return false; 1162 } 1163 1164 if (lookup->type() == INTERCEPTOR) { 1165 if (object->GetNamedInterceptor()->setter()->IsUndefined()) { 1166 object->LocalLookupRealNamedProperty(name, lookup); 1167 return StoreICableLookup(lookup); 1168 } 1169 } 1170 1171 return true; 1172} 1173 1174 1175Object* StoreIC::Store(State state, 1176 Handle<Object> object, 1177 Handle<String> name, 1178 Handle<Object> value) { 1179 // If the object is undefined or null it's illegal to try to set any 1180 // properties on it; throw a TypeError in that case. 1181 if (object->IsUndefined() || object->IsNull()) { 1182 return TypeError("non_object_property_store", object, name); 1183 } 1184 1185 // Ignore stores where the receiver is not a JSObject. 1186 if (!object->IsJSObject()) return *value; 1187 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 1188 1189 // Check if the given name is an array index. 1190 uint32_t index; 1191 if (name->AsArrayIndex(&index)) { 1192 HandleScope scope; 1193 Handle<Object> result = SetElement(receiver, index, value); 1194 if (result.is_null()) return Failure::Exception(); 1195 return *value; 1196 } 1197 1198 // Use specialized code for setting the length of arrays. 1199 if (receiver->IsJSArray() 1200 && name->Equals(Heap::length_symbol()) 1201 && receiver->AllowsSetElementsLength()) { 1202#ifdef DEBUG 1203 if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n"); 1204#endif 1205 Code* target = Builtins::builtin(Builtins::StoreIC_ArrayLength); 1206 set_target(target); 1207 return receiver->SetProperty(*name, *value, NONE); 1208 } 1209 1210 // Lookup the property locally in the receiver. 1211 if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) { 1212 LookupResult lookup; 1213 if (LookupForWrite(*receiver, *name, &lookup)) { 1214 UpdateCaches(&lookup, state, receiver, name, value); 1215 } 1216 } 1217 1218 // Set the property. 1219 return receiver->SetProperty(*name, *value, NONE); 1220} 1221 1222 1223void StoreIC::UpdateCaches(LookupResult* lookup, 1224 State state, 1225 Handle<JSObject> receiver, 1226 Handle<String> name, 1227 Handle<Object> value) { 1228 // Skip JSGlobalProxy. 1229 ASSERT(!receiver->IsJSGlobalProxy()); 1230 1231 ASSERT(StoreICableLookup(lookup)); 1232 1233 // If the property has a non-field type allowing map transitions 1234 // where there is extra room in the object, we leave the IC in its 1235 // current state. 1236 PropertyType type = lookup->type(); 1237 1238 // Compute the code stub for this store; used for rewriting to 1239 // monomorphic state and making sure that the code stub is in the 1240 // stub cache. 1241 Object* code = NULL; 1242 switch (type) { 1243 case FIELD: { 1244 code = StubCache::ComputeStoreField(*name, *receiver, 1245 lookup->GetFieldIndex()); 1246 break; 1247 } 1248 case MAP_TRANSITION: { 1249 if (lookup->GetAttributes() != NONE) return; 1250 HandleScope scope; 1251 ASSERT(type == MAP_TRANSITION); 1252 Handle<Map> transition(lookup->GetTransitionMap()); 1253 int index = transition->PropertyIndexFor(*name); 1254 code = StubCache::ComputeStoreField(*name, *receiver, index, *transition); 1255 break; 1256 } 1257 case NORMAL: { 1258 if (receiver->IsGlobalObject()) { 1259 // The stub generated for the global object picks the value directly 1260 // from the property cell. So the property must be directly on the 1261 // global object. 1262 Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver); 1263 JSGlobalPropertyCell* cell = 1264 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); 1265 code = StubCache::ComputeStoreGlobal(*name, *global, cell); 1266 } else { 1267 if (lookup->holder() != *receiver) return; 1268 code = StubCache::ComputeStoreNormal(); 1269 } 1270 break; 1271 } 1272 case CALLBACKS: { 1273 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return; 1274 AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); 1275 if (v8::ToCData<Address>(callback->setter()) == 0) return; 1276 code = StubCache::ComputeStoreCallback(*name, *receiver, callback); 1277 break; 1278 } 1279 case INTERCEPTOR: { 1280 ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined()); 1281 code = StubCache::ComputeStoreInterceptor(*name, *receiver); 1282 break; 1283 } 1284 default: 1285 return; 1286 } 1287 1288 // If we're unable to compute the stub (not enough memory left), we 1289 // simply avoid updating the caches. 1290 if (code == NULL || code->IsFailure()) return; 1291 1292 // Patch the call site depending on the state of the cache. 1293 if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) { 1294 set_target(Code::cast(code)); 1295 } else if (state == MONOMORPHIC) { 1296 // Only move to megamorphic if the target changes. 1297 if (target() != Code::cast(code)) set_target(megamorphic_stub()); 1298 } else if (state == MEGAMORPHIC) { 1299 // Update the stub cache. 1300 StubCache::Set(*name, receiver->map(), Code::cast(code)); 1301 } 1302 1303#ifdef DEBUG 1304 TraceIC("StoreIC", name, state, target()); 1305#endif 1306} 1307 1308 1309Object* KeyedStoreIC::Store(State state, 1310 Handle<Object> object, 1311 Handle<Object> key, 1312 Handle<Object> value) { 1313 if (key->IsSymbol()) { 1314 Handle<String> name = Handle<String>::cast(key); 1315 1316 // If the object is undefined or null it's illegal to try to set any 1317 // properties on it; throw a TypeError in that case. 1318 if (object->IsUndefined() || object->IsNull()) { 1319 return TypeError("non_object_property_store", object, name); 1320 } 1321 1322 // Ignore stores where the receiver is not a JSObject. 1323 if (!object->IsJSObject()) return *value; 1324 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 1325 1326 // Check if the given name is an array index. 1327 uint32_t index; 1328 if (name->AsArrayIndex(&index)) { 1329 HandleScope scope; 1330 Handle<Object> result = SetElement(receiver, index, value); 1331 if (result.is_null()) return Failure::Exception(); 1332 return *value; 1333 } 1334 1335 // Lookup the property locally in the receiver. 1336 LookupResult lookup; 1337 receiver->LocalLookup(*name, &lookup); 1338 1339 // Update inline cache and stub cache. 1340 if (FLAG_use_ic) { 1341 UpdateCaches(&lookup, state, receiver, name, value); 1342 } 1343 1344 // Set the property. 1345 return receiver->SetProperty(*name, *value, NONE); 1346 } 1347 1348 // Do not use ICs for objects that require access checks (including 1349 // the global object). 1350 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded(); 1351 ASSERT(!(use_ic && object->IsJSGlobalProxy())); 1352 1353 if (use_ic) { 1354 Code* stub = generic_stub(); 1355 if (object->IsJSObject()) { 1356 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 1357 if (receiver->HasExternalArrayElements()) { 1358 stub = external_array_stub(receiver->GetElementsKind()); 1359 } 1360 } 1361 set_target(stub); 1362 } 1363 1364 // Set the property. 1365 return Runtime::SetObjectProperty(object, key, value, NONE); 1366} 1367 1368 1369void KeyedStoreIC::UpdateCaches(LookupResult* lookup, 1370 State state, 1371 Handle<JSObject> receiver, 1372 Handle<String> name, 1373 Handle<Object> value) { 1374 // Skip JSGlobalProxy. 1375 if (receiver->IsJSGlobalProxy()) return; 1376 1377 // Bail out if we didn't find a result. 1378 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return; 1379 1380 // If the property is read-only, we leave the IC in its current 1381 // state. 1382 if (lookup->IsReadOnly()) return; 1383 1384 // If the property has a non-field type allowing map transitions 1385 // where there is extra room in the object, we leave the IC in its 1386 // current state. 1387 PropertyType type = lookup->type(); 1388 1389 // Compute the code stub for this store; used for rewriting to 1390 // monomorphic state and making sure that the code stub is in the 1391 // stub cache. 1392 Object* code = NULL; 1393 1394 switch (type) { 1395 case FIELD: { 1396 code = StubCache::ComputeKeyedStoreField(*name, *receiver, 1397 lookup->GetFieldIndex()); 1398 break; 1399 } 1400 case MAP_TRANSITION: { 1401 if (lookup->GetAttributes() == NONE) { 1402 HandleScope scope; 1403 ASSERT(type == MAP_TRANSITION); 1404 Handle<Map> transition(lookup->GetTransitionMap()); 1405 int index = transition->PropertyIndexFor(*name); 1406 code = StubCache::ComputeKeyedStoreField(*name, *receiver, 1407 index, *transition); 1408 break; 1409 } 1410 // fall through. 1411 } 1412 default: { 1413 // Always rewrite to the generic case so that we do not 1414 // repeatedly try to rewrite. 1415 code = generic_stub(); 1416 break; 1417 } 1418 } 1419 1420 // If we're unable to compute the stub (not enough memory left), we 1421 // simply avoid updating the caches. 1422 if (code == NULL || code->IsFailure()) return; 1423 1424 // Patch the call site depending on the state of the cache. Make 1425 // sure to always rewrite from monomorphic to megamorphic. 1426 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE); 1427 if (state == UNINITIALIZED || state == PREMONOMORPHIC) { 1428 set_target(Code::cast(code)); 1429 } else if (state == MONOMORPHIC) { 1430 set_target(megamorphic_stub()); 1431 } 1432 1433#ifdef DEBUG 1434 TraceIC("KeyedStoreIC", name, state, target()); 1435#endif 1436} 1437 1438 1439// ---------------------------------------------------------------------------- 1440// Static IC stub generators. 1441// 1442 1443static Object* CompileFunction(Object* result, 1444 Handle<Object> object, 1445 InLoopFlag in_loop) { 1446 // Compile now with optimization. 1447 HandleScope scope; 1448 Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result)); 1449 if (in_loop == IN_LOOP) { 1450 CompileLazyInLoop(function, object, CLEAR_EXCEPTION); 1451 } else { 1452 CompileLazy(function, object, CLEAR_EXCEPTION); 1453 } 1454 return *function; 1455} 1456 1457 1458// Used from ic-<arch>.cc. 1459Object* CallIC_Miss(Arguments args) { 1460 NoHandleAllocation na; 1461 ASSERT(args.length() == 2); 1462 CallIC ic; 1463 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1464 Object* result = 1465 ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1)); 1466 1467 // The first time the inline cache is updated may be the first time the 1468 // function it references gets called. If the function was lazily compiled 1469 // then the first call will trigger a compilation. We check for this case 1470 // and we do the compilation immediately, instead of waiting for the stub 1471 // currently attached to the JSFunction object to trigger compilation. We 1472 // do this in the case where we know that the inline cache is inside a loop, 1473 // because then we know that we want to optimize the function. 1474 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { 1475 return result; 1476 } 1477 return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop()); 1478} 1479 1480 1481// Used from ic-<arch>.cc. 1482Object* KeyedCallIC_Miss(Arguments args) { 1483 NoHandleAllocation na; 1484 ASSERT(args.length() == 2); 1485 KeyedCallIC ic; 1486 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1487 Object* result = 1488 ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1)); 1489 1490 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { 1491 return result; 1492 } 1493 return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop()); 1494} 1495 1496 1497// Used from ic-<arch>.cc. 1498Object* LoadIC_Miss(Arguments args) { 1499 NoHandleAllocation na; 1500 ASSERT(args.length() == 2); 1501 LoadIC ic; 1502 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1503 return ic.Load(state, args.at<Object>(0), args.at<String>(1)); 1504} 1505 1506 1507// Used from ic-<arch>.cc 1508Object* KeyedLoadIC_Miss(Arguments args) { 1509 NoHandleAllocation na; 1510 ASSERT(args.length() == 2); 1511 KeyedLoadIC ic; 1512 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1513 return ic.Load(state, args.at<Object>(0), args.at<Object>(1)); 1514} 1515 1516 1517// Used from ic-<arch>.cc. 1518Object* StoreIC_Miss(Arguments args) { 1519 NoHandleAllocation na; 1520 ASSERT(args.length() == 3); 1521 StoreIC ic; 1522 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1523 return ic.Store(state, args.at<Object>(0), args.at<String>(1), 1524 args.at<Object>(2)); 1525} 1526 1527 1528Object* StoreIC_ArrayLength(Arguments args) { 1529 NoHandleAllocation nha; 1530 1531 ASSERT(args.length() == 2); 1532 JSObject* receiver = JSObject::cast(args[0]); 1533 Object* len = args[1]; 1534 1535 Object* result = receiver->SetElementsLength(len); 1536 if (result->IsFailure()) return result; 1537 return len; 1538} 1539 1540 1541// Extend storage is called in a store inline cache when 1542// it is necessary to extend the properties array of a 1543// JSObject. 1544Object* SharedStoreIC_ExtendStorage(Arguments args) { 1545 NoHandleAllocation na; 1546 ASSERT(args.length() == 3); 1547 1548 // Convert the parameters 1549 JSObject* object = JSObject::cast(args[0]); 1550 Map* transition = Map::cast(args[1]); 1551 Object* value = args[2]; 1552 1553 // Check the object has run out out property space. 1554 ASSERT(object->HasFastProperties()); 1555 ASSERT(object->map()->unused_property_fields() == 0); 1556 1557 // Expand the properties array. 1558 FixedArray* old_storage = object->properties(); 1559 int new_unused = transition->unused_property_fields(); 1560 int new_size = old_storage->length() + new_unused + 1; 1561 Object* result = old_storage->CopySize(new_size); 1562 if (result->IsFailure()) return result; 1563 FixedArray* new_storage = FixedArray::cast(result); 1564 new_storage->set(old_storage->length(), value); 1565 1566 // Set the new property value and do the map transition. 1567 object->set_properties(new_storage); 1568 object->set_map(transition); 1569 1570 // Return the stored value. 1571 return value; 1572} 1573 1574 1575// Used from ic-<arch>.cc. 1576Object* KeyedStoreIC_Miss(Arguments args) { 1577 NoHandleAllocation na; 1578 ASSERT(args.length() == 3); 1579 KeyedStoreIC ic; 1580 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1581 return ic.Store(state, args.at<Object>(0), args.at<Object>(1), 1582 args.at<Object>(2)); 1583} 1584 1585 1586void BinaryOpIC::patch(Code* code) { 1587 set_target(code); 1588} 1589 1590 1591const char* BinaryOpIC::GetName(TypeInfo type_info) { 1592 switch (type_info) { 1593 case DEFAULT: return "Default"; 1594 case GENERIC: return "Generic"; 1595 case HEAP_NUMBERS: return "HeapNumbers"; 1596 case STRINGS: return "Strings"; 1597 default: return "Invalid"; 1598 } 1599} 1600 1601 1602BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) { 1603 switch (type_info) { 1604 // DEFAULT is mapped to UNINITIALIZED so that calls to DEFAULT stubs 1605 // are not cleared at GC. 1606 case DEFAULT: return UNINITIALIZED; 1607 1608 // Could have mapped GENERIC to MONOMORPHIC just as well but MEGAMORPHIC is 1609 // conceptually closer. 1610 case GENERIC: return MEGAMORPHIC; 1611 1612 default: return MONOMORPHIC; 1613 } 1614} 1615 1616 1617BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left, 1618 Object* right) { 1619 if (left->IsSmi() && right->IsSmi()) { 1620 return GENERIC; 1621 } 1622 1623 if (left->IsNumber() && right->IsNumber()) { 1624 return HEAP_NUMBERS; 1625 } 1626 1627 if (left->IsString() || right->IsString()) { 1628 // Patching for fast string ADD makes sense even if only one of the 1629 // arguments is a string. 1630 return STRINGS; 1631 } 1632 1633 return GENERIC; 1634} 1635 1636 1637// defined in codegen-<arch>.cc 1638Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info); 1639 1640 1641Object* BinaryOp_Patch(Arguments args) { 1642 ASSERT(args.length() == 6); 1643 1644 Handle<Object> left = args.at<Object>(0); 1645 Handle<Object> right = args.at<Object>(1); 1646 Handle<Object> result = args.at<Object>(2); 1647 int key = Smi::cast(args[3])->value(); 1648#ifdef DEBUG 1649 Token::Value op = static_cast<Token::Value>(Smi::cast(args[4])->value()); 1650 BinaryOpIC::TypeInfo prev_type_info = 1651 static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[5])->value()); 1652#endif // DEBUG 1653 { HandleScope scope; 1654 BinaryOpIC::TypeInfo type_info = BinaryOpIC::GetTypeInfo(*left, *right); 1655 Handle<Code> code = GetBinaryOpStub(key, type_info); 1656 if (!code.is_null()) { 1657 BinaryOpIC ic; 1658 ic.patch(*code); 1659#ifdef DEBUG 1660 if (FLAG_trace_ic) { 1661 PrintF("[BinaryOpIC (%s->%s)#%s]\n", 1662 BinaryOpIC::GetName(prev_type_info), 1663 BinaryOpIC::GetName(type_info), 1664 Token::Name(op)); 1665 } 1666#endif // DEBUG 1667 } 1668 } 1669 1670 return *result; 1671} 1672 1673 1674static Address IC_utilities[] = { 1675#define ADDR(name) FUNCTION_ADDR(name), 1676 IC_UTIL_LIST(ADDR) 1677 NULL 1678#undef ADDR 1679}; 1680 1681 1682Address IC::AddressFromUtilityId(IC::UtilityId id) { 1683 return IC_utilities[id]; 1684} 1685 1686 1687} } // namespace v8::internal 1688