1 // Copyright 2006-2009 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #include "api.h" 31 #include "arguments.h" 32 #include "ic-inl.h" 33 #include "stub-cache.h" 34 35 namespace v8 { 36 namespace internal { 37 38 // ----------------------------------------------------------------------- 39 // StubCache implementation. 40 41 42 StubCache::Entry StubCache::primary_[StubCache::kPrimaryTableSize]; 43 StubCache::Entry StubCache::secondary_[StubCache::kSecondaryTableSize]; 44 45 void StubCache::Initialize(bool create_heap_objects) { 46 ASSERT(IsPowerOf2(kPrimaryTableSize)); 47 ASSERT(IsPowerOf2(kSecondaryTableSize)); 48 if (create_heap_objects) { 49 HandleScope scope; 50 Clear(); 51 } 52 } 53 54 55 Code* StubCache::Set(String* name, Map* map, Code* code) { 56 // Get the flags from the code. 57 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); 58 59 // Validate that the name does not move on scavenge, and that we 60 // can use identity checks instead of string equality checks. 61 ASSERT(!Heap::InNewSpace(name)); 62 ASSERT(name->IsSymbol()); 63 64 // The state bits are not important to the hash function because 65 // the stub cache only contains monomorphic stubs. Make sure that 66 // the bits are the least significant so they will be the ones 67 // masked out. 68 ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC); 69 ASSERT(Code::kFlagsICStateShift == 0); 70 71 // Make sure that the code type is not included in the hash. 72 ASSERT(Code::ExtractTypeFromFlags(flags) == 0); 73 74 // Compute the primary entry. 75 int primary_offset = PrimaryOffset(name, flags, map); 76 Entry* primary = entry(primary_, primary_offset); 77 Code* hit = primary->value; 78 79 // If the primary entry has useful data in it, we retire it to the 80 // secondary cache before overwriting it. 81 if (hit != Builtins::builtin(Builtins::Illegal)) { 82 Code::Flags primary_flags = Code::RemoveTypeFromFlags(hit->flags()); 83 int secondary_offset = 84 SecondaryOffset(primary->key, primary_flags, primary_offset); 85 Entry* secondary = entry(secondary_, secondary_offset); 86 *secondary = *primary; 87 } 88 89 // Update primary cache. 90 primary->key = name; 91 primary->value = code; 92 return code; 93 } 94 95 96 Object* StubCache::ComputeLoadField(String* name, 97 JSObject* receiver, 98 JSObject* holder, 99 int field_index) { 100 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); 101 Object* code = receiver->map()->FindInCodeCache(name, flags); 102 if (code->IsUndefined()) { 103 LoadStubCompiler compiler; 104 code = compiler.CompileLoadField(receiver, holder, field_index, name); 105 if (code->IsFailure()) return code; 106 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); 107 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 108 if (result->IsFailure()) return result; 109 } 110 return Set(name, receiver->map(), Code::cast(code)); 111 } 112 113 114 Object* StubCache::ComputeLoadCallback(String* name, 115 JSObject* receiver, 116 JSObject* holder, 117 AccessorInfo* callback) { 118 ASSERT(v8::ToCData<Address>(callback->getter()) != 0); 119 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); 120 Object* code = receiver->map()->FindInCodeCache(name, flags); 121 if (code->IsUndefined()) { 122 LoadStubCompiler compiler; 123 code = compiler.CompileLoadCallback(name, receiver, holder, callback); 124 if (code->IsFailure()) return code; 125 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); 126 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 127 if (result->IsFailure()) return result; 128 } 129 return Set(name, receiver->map(), Code::cast(code)); 130 } 131 132 133 Object* StubCache::ComputeLoadConstant(String* name, 134 JSObject* receiver, 135 JSObject* holder, 136 Object* value) { 137 Code::Flags flags = 138 Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); 139 Object* code = receiver->map()->FindInCodeCache(name, flags); 140 if (code->IsUndefined()) { 141 LoadStubCompiler compiler; 142 code = compiler.CompileLoadConstant(receiver, holder, value, name); 143 if (code->IsFailure()) return code; 144 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); 145 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 146 if (result->IsFailure()) return result; 147 } 148 return Set(name, receiver->map(), Code::cast(code)); 149 } 150 151 152 Object* StubCache::ComputeLoadInterceptor(String* name, 153 JSObject* receiver, 154 JSObject* holder) { 155 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); 156 Object* code = receiver->map()->FindInCodeCache(name, flags); 157 if (code->IsUndefined()) { 158 LoadStubCompiler compiler; 159 code = compiler.CompileLoadInterceptor(receiver, holder, name); 160 if (code->IsFailure()) return code; 161 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); 162 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 163 if (result->IsFailure()) return result; 164 } 165 return Set(name, receiver->map(), Code::cast(code)); 166 } 167 168 169 Object* StubCache::ComputeLoadNormal(String* name, JSObject* receiver) { 170 Code* code = Builtins::builtin(Builtins::LoadIC_Normal); 171 return Set(name, receiver->map(), code); 172 } 173 174 175 Object* StubCache::ComputeLoadGlobal(String* name, 176 JSObject* receiver, 177 GlobalObject* holder, 178 JSGlobalPropertyCell* cell, 179 bool is_dont_delete) { 180 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); 181 Object* code = receiver->map()->FindInCodeCache(name, flags); 182 if (code->IsUndefined()) { 183 LoadStubCompiler compiler; 184 code = compiler.CompileLoadGlobal(receiver, 185 holder, 186 cell, 187 name, 188 is_dont_delete); 189 if (code->IsFailure()) return code; 190 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); 191 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 192 if (result->IsFailure()) return result; 193 } 194 return Set(name, receiver->map(), Code::cast(code)); 195 } 196 197 198 Object* StubCache::ComputeKeyedLoadField(String* name, 199 JSObject* receiver, 200 JSObject* holder, 201 int field_index) { 202 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); 203 Object* code = receiver->map()->FindInCodeCache(name, flags); 204 if (code->IsUndefined()) { 205 KeyedLoadStubCompiler compiler; 206 code = compiler.CompileLoadField(name, receiver, holder, field_index); 207 if (code->IsFailure()) return code; 208 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 209 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 210 if (result->IsFailure()) return result; 211 } 212 return code; 213 } 214 215 216 Object* StubCache::ComputeKeyedLoadConstant(String* name, 217 JSObject* receiver, 218 JSObject* holder, 219 Object* value) { 220 Code::Flags flags = 221 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); 222 Object* code = receiver->map()->FindInCodeCache(name, flags); 223 if (code->IsUndefined()) { 224 KeyedLoadStubCompiler compiler; 225 code = compiler.CompileLoadConstant(name, receiver, holder, value); 226 if (code->IsFailure()) return code; 227 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 228 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 229 if (result->IsFailure()) return result; 230 } 231 return code; 232 } 233 234 235 Object* StubCache::ComputeKeyedLoadInterceptor(String* name, 236 JSObject* receiver, 237 JSObject* holder) { 238 Code::Flags flags = 239 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); 240 Object* code = receiver->map()->FindInCodeCache(name, flags); 241 if (code->IsUndefined()) { 242 KeyedLoadStubCompiler compiler; 243 code = compiler.CompileLoadInterceptor(receiver, holder, name); 244 if (code->IsFailure()) return code; 245 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 246 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 247 if (result->IsFailure()) return result; 248 } 249 return code; 250 } 251 252 253 Object* StubCache::ComputeKeyedLoadCallback(String* name, 254 JSObject* receiver, 255 JSObject* holder, 256 AccessorInfo* callback) { 257 Code::Flags flags = 258 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); 259 Object* code = receiver->map()->FindInCodeCache(name, flags); 260 if (code->IsUndefined()) { 261 KeyedLoadStubCompiler compiler; 262 code = compiler.CompileLoadCallback(name, receiver, holder, callback); 263 if (code->IsFailure()) return code; 264 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 265 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 266 if (result->IsFailure()) return result; 267 } 268 return code; 269 } 270 271 272 273 Object* StubCache::ComputeKeyedLoadArrayLength(String* name, 274 JSArray* receiver) { 275 Code::Flags flags = 276 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); 277 Object* code = receiver->map()->FindInCodeCache(name, flags); 278 if (code->IsUndefined()) { 279 KeyedLoadStubCompiler compiler; 280 code = compiler.CompileLoadArrayLength(name); 281 if (code->IsFailure()) return code; 282 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 283 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 284 if (result->IsFailure()) return result; 285 } 286 return code; 287 } 288 289 290 Object* StubCache::ComputeKeyedLoadStringLength(String* name, 291 String* receiver) { 292 Code::Flags flags = 293 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); 294 Object* code = receiver->map()->FindInCodeCache(name, flags); 295 if (code->IsUndefined()) { 296 KeyedLoadStubCompiler compiler; 297 code = compiler.CompileLoadStringLength(name); 298 if (code->IsFailure()) return code; 299 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 300 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 301 if (result->IsFailure()) return result; 302 } 303 return code; 304 } 305 306 307 Object* StubCache::ComputeKeyedLoadFunctionPrototype(String* name, 308 JSFunction* receiver) { 309 Code::Flags flags = 310 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); 311 Object* code = receiver->map()->FindInCodeCache(name, flags); 312 if (code->IsUndefined()) { 313 KeyedLoadStubCompiler compiler; 314 code = compiler.CompileLoadFunctionPrototype(name); 315 if (code->IsFailure()) return code; 316 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); 317 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 318 if (result->IsFailure()) return result; 319 } 320 return code; 321 } 322 323 324 Object* StubCache::ComputeStoreField(String* name, 325 JSObject* receiver, 326 int field_index, 327 Map* transition) { 328 PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; 329 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type); 330 Object* code = receiver->map()->FindInCodeCache(name, flags); 331 if (code->IsUndefined()) { 332 StoreStubCompiler compiler; 333 code = compiler.CompileStoreField(receiver, field_index, transition, name); 334 if (code->IsFailure()) return code; 335 LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); 336 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 337 if (result->IsFailure()) return result; 338 } 339 return Set(name, receiver->map(), Code::cast(code)); 340 } 341 342 343 Object* StubCache::ComputeStoreGlobal(String* name, 344 GlobalObject* receiver, 345 JSGlobalPropertyCell* cell) { 346 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL); 347 Object* code = receiver->map()->FindInCodeCache(name, flags); 348 if (code->IsUndefined()) { 349 StoreStubCompiler compiler; 350 code = compiler.CompileStoreGlobal(receiver, cell, name); 351 if (code->IsFailure()) return code; 352 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); 353 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 354 if (result->IsFailure()) return result; 355 } 356 return Set(name, receiver->map(), Code::cast(code)); 357 } 358 359 360 Object* StubCache::ComputeStoreCallback(String* name, 361 JSObject* receiver, 362 AccessorInfo* callback) { 363 ASSERT(v8::ToCData<Address>(callback->setter()) != 0); 364 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, CALLBACKS); 365 Object* code = receiver->map()->FindInCodeCache(name, flags); 366 if (code->IsUndefined()) { 367 StoreStubCompiler compiler; 368 code = compiler.CompileStoreCallback(receiver, callback, name); 369 if (code->IsFailure()) return code; 370 LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); 371 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 372 if (result->IsFailure()) return result; 373 } 374 return Set(name, receiver->map(), Code::cast(code)); 375 } 376 377 378 Object* StubCache::ComputeStoreInterceptor(String* name, 379 JSObject* receiver) { 380 Code::Flags flags = 381 Code::ComputeMonomorphicFlags(Code::STORE_IC, INTERCEPTOR); 382 Object* code = receiver->map()->FindInCodeCache(name, flags); 383 if (code->IsUndefined()) { 384 StoreStubCompiler compiler; 385 code = compiler.CompileStoreInterceptor(receiver, name); 386 if (code->IsFailure()) return code; 387 LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); 388 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 389 if (result->IsFailure()) return result; 390 } 391 return Set(name, receiver->map(), Code::cast(code)); 392 } 393 394 395 Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver, 396 int field_index, Map* transition) { 397 PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; 398 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type); 399 Object* code = receiver->map()->FindInCodeCache(name, flags); 400 if (code->IsUndefined()) { 401 KeyedStoreStubCompiler compiler; 402 code = compiler.CompileStoreField(receiver, field_index, transition, name); 403 if (code->IsFailure()) return code; 404 LOG(CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), name)); 405 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 406 if (result->IsFailure()) return result; 407 } 408 return code; 409 } 410 411 412 Object* StubCache::ComputeCallConstant(int argc, 413 InLoopFlag in_loop, 414 String* name, 415 Object* object, 416 JSObject* holder, 417 JSFunction* function) { 418 // Compute the check type and the map. 419 Map* map = IC::GetCodeCacheMapForObject(object); 420 421 // Compute check type based on receiver/holder. 422 StubCompiler::CheckType check = StubCompiler::RECEIVER_MAP_CHECK; 423 if (object->IsString()) { 424 check = StubCompiler::STRING_CHECK; 425 } else if (object->IsNumber()) { 426 check = StubCompiler::NUMBER_CHECK; 427 } else if (object->IsBoolean()) { 428 check = StubCompiler::BOOLEAN_CHECK; 429 } 430 431 Code::Flags flags = 432 Code::ComputeMonomorphicFlags(Code::CALL_IC, 433 CONSTANT_FUNCTION, 434 in_loop, 435 argc); 436 Object* code = map->FindInCodeCache(name, flags); 437 if (code->IsUndefined()) { 438 if (object->IsJSObject()) { 439 Object* opt = 440 Top::LookupSpecialFunction(JSObject::cast(object), holder, function); 441 if (opt->IsJSFunction()) { 442 check = StubCompiler::JSARRAY_HAS_FAST_ELEMENTS_CHECK; 443 function = JSFunction::cast(opt); 444 } 445 } 446 // If the function hasn't been compiled yet, we cannot do it now 447 // because it may cause GC. To avoid this issue, we return an 448 // internal error which will make sure we do not update any 449 // caches. 450 if (!function->is_compiled()) return Failure::InternalError(); 451 // Compile the stub - only create stubs for fully compiled functions. 452 CallStubCompiler compiler(argc, in_loop); 453 code = compiler.CompileCallConstant(object, holder, function, name, check); 454 if (code->IsFailure()) return code; 455 ASSERT_EQ(flags, Code::cast(code)->flags()); 456 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); 457 Object* result = map->UpdateCodeCache(name, Code::cast(code)); 458 if (result->IsFailure()) return result; 459 } 460 return Set(name, map, Code::cast(code)); 461 } 462 463 464 Object* StubCache::ComputeCallField(int argc, 465 InLoopFlag in_loop, 466 String* name, 467 Object* object, 468 JSObject* holder, 469 int index) { 470 // Compute the check type and the map. 471 Map* map = IC::GetCodeCacheMapForObject(object); 472 473 // TODO(1233596): We cannot do receiver map check for non-JS objects 474 // because they may be represented as immediates without a 475 // map. Instead, we check against the map in the holder. 476 if (object->IsNumber() || object->IsBoolean() || object->IsString()) { 477 object = holder; 478 } 479 480 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, 481 FIELD, 482 in_loop, 483 argc); 484 Object* code = map->FindInCodeCache(name, flags); 485 if (code->IsUndefined()) { 486 CallStubCompiler compiler(argc, in_loop); 487 code = compiler.CompileCallField(JSObject::cast(object), 488 holder, 489 index, 490 name); 491 if (code->IsFailure()) return code; 492 ASSERT_EQ(flags, Code::cast(code)->flags()); 493 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); 494 Object* result = map->UpdateCodeCache(name, Code::cast(code)); 495 if (result->IsFailure()) return result; 496 } 497 return Set(name, map, Code::cast(code)); 498 } 499 500 501 Object* StubCache::ComputeCallInterceptor(int argc, 502 String* name, 503 Object* object, 504 JSObject* holder) { 505 // Compute the check type and the map. 506 // If the object is a value, we use the prototype map for the cache. 507 Map* map = IC::GetCodeCacheMapForObject(object); 508 509 // TODO(1233596): We cannot do receiver map check for non-JS objects 510 // because they may be represented as immediates without a 511 // map. Instead, we check against the map in the holder. 512 if (object->IsNumber() || object->IsBoolean() || object->IsString()) { 513 object = holder; 514 } 515 516 Code::Flags flags = 517 Code::ComputeMonomorphicFlags(Code::CALL_IC, 518 INTERCEPTOR, 519 NOT_IN_LOOP, 520 argc); 521 Object* code = map->FindInCodeCache(name, flags); 522 if (code->IsUndefined()) { 523 CallStubCompiler compiler(argc, NOT_IN_LOOP); 524 code = compiler.CompileCallInterceptor(JSObject::cast(object), 525 holder, 526 name); 527 if (code->IsFailure()) return code; 528 ASSERT_EQ(flags, Code::cast(code)->flags()); 529 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); 530 Object* result = map->UpdateCodeCache(name, Code::cast(code)); 531 if (result->IsFailure()) return result; 532 } 533 return Set(name, map, Code::cast(code)); 534 } 535 536 537 Object* StubCache::ComputeCallNormal(int argc, 538 InLoopFlag in_loop, 539 String* name, 540 JSObject* receiver) { 541 Object* code = ComputeCallNormal(argc, in_loop); 542 if (code->IsFailure()) return code; 543 return Set(name, receiver->map(), Code::cast(code)); 544 } 545 546 547 Object* StubCache::ComputeCallGlobal(int argc, 548 InLoopFlag in_loop, 549 String* name, 550 JSObject* receiver, 551 GlobalObject* holder, 552 JSGlobalPropertyCell* cell, 553 JSFunction* function) { 554 Code::Flags flags = 555 Code::ComputeMonomorphicFlags(Code::CALL_IC, NORMAL, in_loop, argc); 556 Object* code = receiver->map()->FindInCodeCache(name, flags); 557 if (code->IsUndefined()) { 558 // If the function hasn't been compiled yet, we cannot do it now 559 // because it may cause GC. To avoid this issue, we return an 560 // internal error which will make sure we do not update any 561 // caches. 562 if (!function->is_compiled()) return Failure::InternalError(); 563 CallStubCompiler compiler(argc, in_loop); 564 code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); 565 if (code->IsFailure()) return code; 566 ASSERT_EQ(flags, Code::cast(code)->flags()); 567 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); 568 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); 569 if (result->IsFailure()) return result; 570 } 571 return Set(name, receiver->map(), Code::cast(code)); 572 } 573 574 575 static Object* GetProbeValue(Code::Flags flags) { 576 // Use raw_unchecked... so we don't get assert failures during GC. 577 NumberDictionary* dictionary = Heap::raw_unchecked_non_monomorphic_cache(); 578 int entry = dictionary->FindEntry(flags); 579 if (entry != -1) return dictionary->ValueAt(entry); 580 return Heap::raw_unchecked_undefined_value(); 581 } 582 583 584 static Object* ProbeCache(Code::Flags flags) { 585 Object* probe = GetProbeValue(flags); 586 if (probe != Heap::undefined_value()) return probe; 587 // Seed the cache with an undefined value to make sure that any 588 // generated code object can always be inserted into the cache 589 // without causing allocation failures. 590 Object* result = 591 Heap::non_monomorphic_cache()->AtNumberPut(flags, 592 Heap::undefined_value()); 593 if (result->IsFailure()) return result; 594 Heap::public_set_non_monomorphic_cache(NumberDictionary::cast(result)); 595 return probe; 596 } 597 598 599 static Object* FillCache(Object* code) { 600 if (code->IsCode()) { 601 int entry = 602 Heap::non_monomorphic_cache()->FindEntry( 603 Code::cast(code)->flags()); 604 // The entry must be present see comment in ProbeCache. 605 ASSERT(entry != -1); 606 ASSERT(Heap::non_monomorphic_cache()->ValueAt(entry) == 607 Heap::undefined_value()); 608 Heap::non_monomorphic_cache()->ValueAtPut(entry, code); 609 CHECK(GetProbeValue(Code::cast(code)->flags()) == code); 610 } 611 return code; 612 } 613 614 615 Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) { 616 Code::Flags flags = 617 Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc); 618 Object* result = ProbeCache(flags); 619 ASSERT(!result->IsUndefined()); 620 // This might be called during the marking phase of the collector 621 // hence the unchecked cast. 622 return reinterpret_cast<Code*>(result); 623 } 624 625 626 Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) { 627 Code::Flags flags = 628 Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc); 629 Object* probe = ProbeCache(flags); 630 if (!probe->IsUndefined()) return probe; 631 StubCompiler compiler; 632 return FillCache(compiler.CompileCallInitialize(flags)); 633 } 634 635 636 Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) { 637 Code::Flags flags = 638 Code::ComputeFlags(Code::CALL_IC, in_loop, PREMONOMORPHIC, NORMAL, argc); 639 Object* probe = ProbeCache(flags); 640 if (!probe->IsUndefined()) return probe; 641 StubCompiler compiler; 642 return FillCache(compiler.CompileCallPreMonomorphic(flags)); 643 } 644 645 646 Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) { 647 Code::Flags flags = 648 Code::ComputeFlags(Code::CALL_IC, in_loop, MONOMORPHIC, NORMAL, argc); 649 Object* probe = ProbeCache(flags); 650 if (!probe->IsUndefined()) return probe; 651 StubCompiler compiler; 652 return FillCache(compiler.CompileCallNormal(flags)); 653 } 654 655 656 Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) { 657 Code::Flags flags = 658 Code::ComputeFlags(Code::CALL_IC, in_loop, MEGAMORPHIC, NORMAL, argc); 659 Object* probe = ProbeCache(flags); 660 if (!probe->IsUndefined()) return probe; 661 StubCompiler compiler; 662 return FillCache(compiler.CompileCallMegamorphic(flags)); 663 } 664 665 666 Object* StubCache::ComputeCallMiss(int argc) { 667 Code::Flags flags = 668 Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, MEGAMORPHIC, NORMAL, argc); 669 Object* probe = ProbeCache(flags); 670 if (!probe->IsUndefined()) return probe; 671 StubCompiler compiler; 672 return FillCache(compiler.CompileCallMiss(flags)); 673 } 674 675 676 #ifdef ENABLE_DEBUGGER_SUPPORT 677 Object* StubCache::ComputeCallDebugBreak(int argc) { 678 Code::Flags flags = 679 Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc); 680 Object* probe = ProbeCache(flags); 681 if (!probe->IsUndefined()) return probe; 682 StubCompiler compiler; 683 return FillCache(compiler.CompileCallDebugBreak(flags)); 684 } 685 686 687 Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) { 688 Code::Flags flags = 689 Code::ComputeFlags(Code::CALL_IC, 690 NOT_IN_LOOP, 691 DEBUG_PREPARE_STEP_IN, 692 NORMAL, 693 argc); 694 Object* probe = ProbeCache(flags); 695 if (!probe->IsUndefined()) return probe; 696 StubCompiler compiler; 697 return FillCache(compiler.CompileCallDebugPrepareStepIn(flags)); 698 } 699 #endif 700 701 702 Object* StubCache::ComputeLazyCompile(int argc) { 703 Code::Flags flags = 704 Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, UNINITIALIZED, NORMAL, argc); 705 Object* probe = ProbeCache(flags); 706 if (!probe->IsUndefined()) return probe; 707 StubCompiler compiler; 708 Object* result = FillCache(compiler.CompileLazyCompile(flags)); 709 if (result->IsCode()) { 710 Code* code = Code::cast(result); 711 USE(code); 712 LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, 713 code, code->arguments_count())); 714 } 715 return result; 716 } 717 718 719 void StubCache::Clear() { 720 for (int i = 0; i < kPrimaryTableSize; i++) { 721 primary_[i].key = Heap::empty_string(); 722 primary_[i].value = Builtins::builtin(Builtins::Illegal); 723 } 724 for (int j = 0; j < kSecondaryTableSize; j++) { 725 secondary_[j].key = Heap::empty_string(); 726 secondary_[j].value = Builtins::builtin(Builtins::Illegal); 727 } 728 } 729 730 731 // ------------------------------------------------------------------------ 732 // StubCompiler implementation. 733 734 735 // Support function for computing call IC miss stubs. 736 Handle<Code> ComputeCallMiss(int argc) { 737 CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc), Code); 738 } 739 740 741 742 Object* LoadCallbackProperty(Arguments args) { 743 ASSERT(args[0]->IsJSObject()); 744 ASSERT(args[1]->IsJSObject()); 745 AccessorInfo* callback = AccessorInfo::cast(args[2]); 746 Address getter_address = v8::ToCData<Address>(callback->getter()); 747 v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address); 748 ASSERT(fun != NULL); 749 CustomArguments custom_args(callback->data(), 750 JSObject::cast(args[0]), 751 JSObject::cast(args[1])); 752 v8::AccessorInfo info(custom_args.end()); 753 HandleScope scope; 754 v8::Handle<v8::Value> result; 755 { 756 // Leaving JavaScript. 757 VMState state(EXTERNAL); 758 #ifdef ENABLE_LOGGING_AND_PROFILING 759 state.set_external_callback(getter_address); 760 #endif 761 result = fun(v8::Utils::ToLocal(args.at<String>(4)), info); 762 } 763 RETURN_IF_SCHEDULED_EXCEPTION(); 764 if (result.IsEmpty()) return Heap::undefined_value(); 765 return *v8::Utils::OpenHandle(*result); 766 } 767 768 769 Object* StoreCallbackProperty(Arguments args) { 770 JSObject* recv = JSObject::cast(args[0]); 771 AccessorInfo* callback = AccessorInfo::cast(args[1]); 772 Address setter_address = v8::ToCData<Address>(callback->setter()); 773 v8::AccessorSetter fun = FUNCTION_CAST<v8::AccessorSetter>(setter_address); 774 ASSERT(fun != NULL); 775 Handle<String> name = args.at<String>(2); 776 Handle<Object> value = args.at<Object>(3); 777 HandleScope scope; 778 LOG(ApiNamedPropertyAccess("store", recv, *name)); 779 CustomArguments custom_args(callback->data(), recv, recv); 780 v8::AccessorInfo info(custom_args.end()); 781 { 782 // Leaving JavaScript. 783 VMState state(EXTERNAL); 784 #ifdef ENABLE_LOGGING_AND_PROFILING 785 state.set_external_callback(setter_address); 786 #endif 787 fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); 788 } 789 RETURN_IF_SCHEDULED_EXCEPTION(); 790 return *value; 791 } 792 793 /** 794 * Attempts to load a property with an interceptor (which must be present), 795 * but doesn't search the prototype chain. 796 * 797 * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't 798 * provide any value for the given name. 799 */ 800 Object* LoadPropertyWithInterceptorOnly(Arguments args) { 801 JSObject* receiver_handle = JSObject::cast(args[0]); 802 JSObject* holder_handle = JSObject::cast(args[1]); 803 Handle<String> name_handle = args.at<String>(2); 804 Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>(3); 805 Object* data_handle = args[4]; 806 807 Address getter_address = v8::ToCData<Address>(interceptor_info->getter()); 808 v8::NamedPropertyGetter getter = 809 FUNCTION_CAST<v8::NamedPropertyGetter>(getter_address); 810 ASSERT(getter != NULL); 811 812 { 813 // Use the interceptor getter. 814 CustomArguments args(data_handle, receiver_handle, holder_handle); 815 v8::AccessorInfo info(args.end()); 816 HandleScope scope; 817 v8::Handle<v8::Value> r; 818 { 819 // Leaving JavaScript. 820 VMState state(EXTERNAL); 821 r = getter(v8::Utils::ToLocal(name_handle), info); 822 } 823 RETURN_IF_SCHEDULED_EXCEPTION(); 824 if (!r.IsEmpty()) { 825 return *v8::Utils::OpenHandle(*r); 826 } 827 } 828 829 return Heap::no_interceptor_result_sentinel(); 830 } 831 832 833 static Object* ThrowReferenceError(String* name) { 834 // If the load is non-contextual, just return the undefined result. 835 // Note that both keyed and non-keyed loads may end up here, so we 836 // can't use either LoadIC or KeyedLoadIC constructors. 837 IC ic(IC::NO_EXTRA_FRAME); 838 ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); 839 if (!ic.SlowIsContextual()) return Heap::undefined_value(); 840 841 // Throw a reference error. 842 HandleScope scope; 843 Handle<String> name_handle(name); 844 Handle<Object> error = 845 Factory::NewReferenceError("not_defined", 846 HandleVector(&name_handle, 1)); 847 return Top::Throw(*error); 848 } 849 850 851 static Object* LoadWithInterceptor(Arguments* args, 852 PropertyAttributes* attrs) { 853 Handle<JSObject> receiver_handle = args->at<JSObject>(0); 854 Handle<JSObject> holder_handle = args->at<JSObject>(1); 855 Handle<String> name_handle = args->at<String>(2); 856 Handle<InterceptorInfo> interceptor_info = args->at<InterceptorInfo>(3); 857 Handle<Object> data_handle = args->at<Object>(4); 858 859 Address getter_address = v8::ToCData<Address>(interceptor_info->getter()); 860 v8::NamedPropertyGetter getter = 861 FUNCTION_CAST<v8::NamedPropertyGetter>(getter_address); 862 ASSERT(getter != NULL); 863 864 { 865 // Use the interceptor getter. 866 CustomArguments args(*data_handle, *receiver_handle, *holder_handle); 867 v8::AccessorInfo info(args.end()); 868 HandleScope scope; 869 v8::Handle<v8::Value> r; 870 { 871 // Leaving JavaScript. 872 VMState state(EXTERNAL); 873 r = getter(v8::Utils::ToLocal(name_handle), info); 874 } 875 RETURN_IF_SCHEDULED_EXCEPTION(); 876 if (!r.IsEmpty()) { 877 *attrs = NONE; 878 return *v8::Utils::OpenHandle(*r); 879 } 880 } 881 882 Object* result = holder_handle->GetPropertyPostInterceptor( 883 *receiver_handle, 884 *name_handle, 885 attrs); 886 RETURN_IF_SCHEDULED_EXCEPTION(); 887 return result; 888 } 889 890 891 /** 892 * Loads a property with an interceptor performing post interceptor 893 * lookup if interceptor failed. 894 */ 895 Object* LoadPropertyWithInterceptorForLoad(Arguments args) { 896 PropertyAttributes attr = NONE; 897 Object* result = LoadWithInterceptor(&args, &attr); 898 if (result->IsFailure()) return result; 899 900 // If the property is present, return it. 901 if (attr != ABSENT) return result; 902 return ThrowReferenceError(String::cast(args[2])); 903 } 904 905 906 Object* LoadPropertyWithInterceptorForCall(Arguments args) { 907 PropertyAttributes attr; 908 Object* result = LoadWithInterceptor(&args, &attr); 909 RETURN_IF_SCHEDULED_EXCEPTION(); 910 // This is call IC. In this case, we simply return the undefined result which 911 // will lead to an exception when trying to invoke the result as a 912 // function. 913 return result; 914 } 915 916 917 Object* StoreInterceptorProperty(Arguments args) { 918 JSObject* recv = JSObject::cast(args[0]); 919 String* name = String::cast(args[1]); 920 Object* value = args[2]; 921 ASSERT(recv->HasNamedInterceptor()); 922 PropertyAttributes attr = NONE; 923 Object* result = recv->SetPropertyWithInterceptor(name, value, attr); 924 return result; 925 } 926 927 928 Object* KeyedLoadPropertyWithInterceptor(Arguments args) { 929 JSObject* receiver = JSObject::cast(args[0]); 930 uint32_t index = Smi::cast(args[1])->value(); 931 return receiver->GetElementWithInterceptor(receiver, index); 932 } 933 934 935 Object* StubCompiler::CompileCallInitialize(Code::Flags flags) { 936 HandleScope scope; 937 int argc = Code::ExtractArgumentsCountFromFlags(flags); 938 CallIC::GenerateInitialize(masm(), argc); 939 Object* result = GetCodeWithFlags(flags, "CompileCallInitialize"); 940 if (!result->IsFailure()) { 941 Counters::call_initialize_stubs.Increment(); 942 Code* code = Code::cast(result); 943 USE(code); 944 LOG(CodeCreateEvent(Logger::CALL_INITIALIZE_TAG, 945 code, code->arguments_count())); 946 } 947 return result; 948 } 949 950 951 Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { 952 HandleScope scope; 953 int argc = Code::ExtractArgumentsCountFromFlags(flags); 954 // The code of the PreMonomorphic stub is the same as the code 955 // of the Initialized stub. They just differ on the code object flags. 956 CallIC::GenerateInitialize(masm(), argc); 957 Object* result = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); 958 if (!result->IsFailure()) { 959 Counters::call_premonomorphic_stubs.Increment(); 960 Code* code = Code::cast(result); 961 USE(code); 962 LOG(CodeCreateEvent(Logger::CALL_PRE_MONOMORPHIC_TAG, 963 code, code->arguments_count())); 964 } 965 return result; 966 } 967 968 969 Object* StubCompiler::CompileCallNormal(Code::Flags flags) { 970 HandleScope scope; 971 int argc = Code::ExtractArgumentsCountFromFlags(flags); 972 CallIC::GenerateNormal(masm(), argc); 973 Object* result = GetCodeWithFlags(flags, "CompileCallNormal"); 974 if (!result->IsFailure()) { 975 Counters::call_normal_stubs.Increment(); 976 Code* code = Code::cast(result); 977 USE(code); 978 LOG(CodeCreateEvent(Logger::CALL_NORMAL_TAG, 979 code, code->arguments_count())); 980 } 981 return result; 982 } 983 984 985 Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) { 986 HandleScope scope; 987 int argc = Code::ExtractArgumentsCountFromFlags(flags); 988 CallIC::GenerateMegamorphic(masm(), argc); 989 Object* result = GetCodeWithFlags(flags, "CompileCallMegamorphic"); 990 if (!result->IsFailure()) { 991 Counters::call_megamorphic_stubs.Increment(); 992 Code* code = Code::cast(result); 993 USE(code); 994 LOG(CodeCreateEvent(Logger::CALL_MEGAMORPHIC_TAG, 995 code, code->arguments_count())); 996 } 997 return result; 998 } 999 1000 1001 Object* StubCompiler::CompileCallMiss(Code::Flags flags) { 1002 HandleScope scope; 1003 int argc = Code::ExtractArgumentsCountFromFlags(flags); 1004 CallIC::GenerateMiss(masm(), argc); 1005 Object* result = GetCodeWithFlags(flags, "CompileCallMiss"); 1006 if (!result->IsFailure()) { 1007 Counters::call_megamorphic_stubs.Increment(); 1008 Code* code = Code::cast(result); 1009 USE(code); 1010 LOG(CodeCreateEvent(Logger::CALL_MISS_TAG, code, code->arguments_count())); 1011 } 1012 return result; 1013 } 1014 1015 1016 #ifdef ENABLE_DEBUGGER_SUPPORT 1017 Object* StubCompiler::CompileCallDebugBreak(Code::Flags flags) { 1018 HandleScope scope; 1019 Debug::GenerateCallICDebugBreak(masm()); 1020 Object* result = GetCodeWithFlags(flags, "CompileCallDebugBreak"); 1021 if (!result->IsFailure()) { 1022 Code* code = Code::cast(result); 1023 USE(code); 1024 LOG(CodeCreateEvent(Logger::CALL_DEBUG_BREAK_TAG, 1025 code, code->arguments_count())); 1026 } 1027 return result; 1028 } 1029 1030 1031 Object* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { 1032 HandleScope scope; 1033 // Use the same code for the the step in preparations as we do for 1034 // the miss case. 1035 int argc = Code::ExtractArgumentsCountFromFlags(flags); 1036 CallIC::GenerateMiss(masm(), argc); 1037 Object* result = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); 1038 if (!result->IsFailure()) { 1039 Code* code = Code::cast(result); 1040 USE(code); 1041 LOG(CodeCreateEvent(Logger::CALL_DEBUG_PREPARE_STEP_IN_TAG, 1042 code, code->arguments_count())); 1043 } 1044 return result; 1045 } 1046 #endif 1047 1048 1049 Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { 1050 // Check for allocation failures during stub compilation. 1051 if (failure_->IsFailure()) return failure_; 1052 1053 // Create code object in the heap. 1054 CodeDesc desc; 1055 masm_.GetCode(&desc); 1056 Object* result = Heap::CreateCode(desc, NULL, flags, masm_.CodeObject()); 1057 #ifdef ENABLE_DISASSEMBLER 1058 if (FLAG_print_code_stubs && !result->IsFailure()) { 1059 Code::cast(result)->Disassemble(name); 1060 } 1061 #endif 1062 return result; 1063 } 1064 1065 1066 Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, String* name) { 1067 if (FLAG_print_code_stubs && (name != NULL)) { 1068 return GetCodeWithFlags(flags, *name->ToCString()); 1069 } 1070 return GetCodeWithFlags(flags, reinterpret_cast<char*>(NULL)); 1071 } 1072 1073 1074 void StubCompiler::LookupPostInterceptor(JSObject* holder, 1075 String* name, 1076 LookupResult* lookup) { 1077 holder->LocalLookupRealNamedProperty(name, lookup); 1078 if (!lookup->IsProperty()) { 1079 lookup->NotFound(); 1080 Object* proto = holder->GetPrototype(); 1081 if (proto != Heap::null_value()) { 1082 proto->Lookup(name, lookup); 1083 } 1084 } 1085 } 1086 1087 1088 1089 Object* LoadStubCompiler::GetCode(PropertyType type, String* name) { 1090 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, type); 1091 return GetCodeWithFlags(flags, name); 1092 } 1093 1094 1095 Object* KeyedLoadStubCompiler::GetCode(PropertyType type, String* name) { 1096 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, type); 1097 return GetCodeWithFlags(flags, name); 1098 } 1099 1100 1101 Object* StoreStubCompiler::GetCode(PropertyType type, String* name) { 1102 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type); 1103 return GetCodeWithFlags(flags, name); 1104 } 1105 1106 1107 Object* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) { 1108 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type); 1109 return GetCodeWithFlags(flags, name); 1110 } 1111 1112 1113 Object* CallStubCompiler::GetCode(PropertyType type, String* name) { 1114 int argc = arguments_.immediate(); 1115 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, 1116 type, 1117 in_loop_, 1118 argc); 1119 return GetCodeWithFlags(flags, name); 1120 } 1121 1122 1123 Object* ConstructStubCompiler::GetCode() { 1124 Code::Flags flags = Code::ComputeFlags(Code::STUB); 1125 Object* result = GetCodeWithFlags(flags, "ConstructStub"); 1126 if (!result->IsFailure()) { 1127 Code* code = Code::cast(result); 1128 USE(code); 1129 LOG(CodeCreateEvent(Logger::STUB_TAG, code, "ConstructStub")); 1130 } 1131 return result; 1132 } 1133 1134 1135 } } // namespace v8::internal 1136