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