handler-compiler-x87.cc revision 109988c7ccb6f3fd1a58574fa3dfb88beaef6632
1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#if V8_TARGET_ARCH_X87 6 7#include "src/ic/handler-compiler.h" 8 9#include "src/field-type.h" 10#include "src/ic/call-optimization.h" 11#include "src/ic/ic.h" 12#include "src/isolate-inl.h" 13 14namespace v8 { 15namespace internal { 16 17#define __ ACCESS_MASM(masm) 18 19 20void NamedLoadHandlerCompiler::GenerateLoadViaGetter( 21 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder, 22 int accessor_index, int expected_arguments, Register scratch) { 23 { 24 FrameScope scope(masm, StackFrame::INTERNAL); 25 26 if (accessor_index >= 0) { 27 DCHECK(!holder.is(scratch)); 28 DCHECK(!receiver.is(scratch)); 29 // Call the JavaScript getter with the receiver on the stack. 30 if (map->IsJSGlobalObjectMap()) { 31 // Swap in the global receiver. 32 __ mov(scratch, 33 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 34 receiver = scratch; 35 } 36 __ push(receiver); 37 ParameterCount actual(0); 38 ParameterCount expected(expected_arguments); 39 __ LoadAccessor(edi, holder, accessor_index, ACCESSOR_GETTER); 40 __ InvokeFunction(edi, expected, actual, CALL_FUNCTION, 41 CheckDebugStepCallWrapper()); 42 } else { 43 // If we generate a global code snippet for deoptimization only, remember 44 // the place to continue after deoptimization. 45 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 46 } 47 48 // Restore context register. 49 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 50 } 51 __ ret(0); 52} 53 54 55void PropertyHandlerCompiler::PushVectorAndSlot(Register vector, 56 Register slot) { 57 MacroAssembler* masm = this->masm(); 58 __ push(vector); 59 __ push(slot); 60} 61 62 63void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) { 64 MacroAssembler* masm = this->masm(); 65 __ pop(slot); 66 __ pop(vector); 67} 68 69 70void PropertyHandlerCompiler::DiscardVectorAndSlot() { 71 MacroAssembler* masm = this->masm(); 72 // Remove vector and slot. 73 __ add(esp, Immediate(2 * kPointerSize)); 74} 75 76 77void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup( 78 MacroAssembler* masm, Label* miss_label, Register receiver, 79 Handle<Name> name, Register scratch0, Register scratch1) { 80 DCHECK(name->IsUniqueName()); 81 DCHECK(!receiver.is(scratch0)); 82 Counters* counters = masm->isolate()->counters(); 83 __ IncrementCounter(counters->negative_lookups(), 1); 84 __ IncrementCounter(counters->negative_lookups_miss(), 1); 85 86 __ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset)); 87 88 const int kInterceptorOrAccessCheckNeededMask = 89 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); 90 91 // Bail out if the receiver has a named interceptor or requires access checks. 92 __ test_b(FieldOperand(scratch0, Map::kBitFieldOffset), 93 kInterceptorOrAccessCheckNeededMask); 94 __ j(not_zero, miss_label); 95 96 // Check that receiver is a JSObject. 97 __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE); 98 __ j(below, miss_label); 99 100 // Load properties array. 101 Register properties = scratch0; 102 __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); 103 104 // Check that the properties array is a dictionary. 105 __ cmp(FieldOperand(properties, HeapObject::kMapOffset), 106 Immediate(masm->isolate()->factory()->hash_table_map())); 107 __ j(not_equal, miss_label); 108 109 Label done; 110 NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done, 111 properties, name, scratch1); 112 __ bind(&done); 113 __ DecrementCounter(counters->negative_lookups_miss(), 1); 114} 115 116 117void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype( 118 MacroAssembler* masm, int index, Register result, Label* miss) { 119 __ LoadGlobalFunction(index, result); 120 // Load its initial map. The global functions all have initial maps. 121 __ mov(result, 122 FieldOperand(result, JSFunction::kPrototypeOrInitialMapOffset)); 123 // Load the prototype from the initial map. 124 __ mov(result, FieldOperand(result, Map::kPrototypeOffset)); 125} 126 127 128void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype( 129 MacroAssembler* masm, Register receiver, Register scratch1, 130 Register scratch2, Label* miss_label) { 131 // TODO(mvstanton): This isn't used on ia32. Move all the other 132 // platform implementations into a code stub so this method can be removed. 133 UNREACHABLE(); 134} 135 136 137// Generate call to api function. 138// This function uses push() to generate smaller, faster code than 139// the version above. It is an optimization that should will be removed 140// when api call ICs are generated in hydrogen. 141void PropertyHandlerCompiler::GenerateApiAccessorCall( 142 MacroAssembler* masm, const CallOptimization& optimization, 143 Handle<Map> receiver_map, Register receiver, Register scratch, 144 bool is_store, Register store_parameter, Register accessor_holder, 145 int accessor_index) { 146 DCHECK(!accessor_holder.is(scratch)); 147 // Copy return value. 148 __ pop(scratch); 149 // receiver 150 __ push(receiver); 151 // Write the arguments to stack frame. 152 if (is_store) { 153 DCHECK(!receiver.is(store_parameter)); 154 DCHECK(!scratch.is(store_parameter)); 155 __ push(store_parameter); 156 } 157 __ push(scratch); 158 // Stack now matches JSFunction abi. 159 DCHECK(optimization.is_simple_api_call()); 160 161 // Abi for CallApiFunctionStub. 162 Register callee = edi; 163 Register data = ebx; 164 Register holder = ecx; 165 Register api_function_address = edx; 166 scratch = no_reg; 167 168 // Put callee in place. 169 __ LoadAccessor(callee, accessor_holder, accessor_index, 170 is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER); 171 172 // Put holder in place. 173 CallOptimization::HolderLookup holder_lookup; 174 int holder_depth = 0; 175 optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup, 176 &holder_depth); 177 switch (holder_lookup) { 178 case CallOptimization::kHolderIsReceiver: 179 __ Move(holder, receiver); 180 break; 181 case CallOptimization::kHolderFound: 182 __ mov(holder, FieldOperand(receiver, HeapObject::kMapOffset)); 183 __ mov(holder, FieldOperand(holder, Map::kPrototypeOffset)); 184 for (int i = 1; i < holder_depth; i++) { 185 __ mov(holder, FieldOperand(holder, HeapObject::kMapOffset)); 186 __ mov(holder, FieldOperand(holder, Map::kPrototypeOffset)); 187 } 188 break; 189 case CallOptimization::kHolderNotFound: 190 UNREACHABLE(); 191 break; 192 } 193 194 Isolate* isolate = masm->isolate(); 195 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); 196 bool call_data_undefined = false; 197 // Put call data in place. 198 if (api_call_info->data()->IsUndefined()) { 199 call_data_undefined = true; 200 __ mov(data, Immediate(isolate->factory()->undefined_value())); 201 } else { 202 if (optimization.is_constant_call()) { 203 __ mov(data, FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset)); 204 __ mov(data, FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset)); 205 __ mov(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset)); 206 } else { 207 __ mov(data, FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset)); 208 } 209 __ mov(data, FieldOperand(data, CallHandlerInfo::kDataOffset)); 210 } 211 212 if (api_call_info->fast_handler()->IsCode()) { 213 // Just tail call into the code. 214 __ Jump(handle(Code::cast(api_call_info->fast_handler())), 215 RelocInfo::CODE_TARGET); 216 return; 217 } 218 // Put api_function_address in place. 219 Address function_address = v8::ToCData<Address>(api_call_info->callback()); 220 __ mov(api_function_address, Immediate(function_address)); 221 222 // Jump to stub. 223 CallApiAccessorStub stub(isolate, is_store, call_data_undefined, 224 !optimization.is_constant_call()); 225 __ TailCallStub(&stub); 226} 227 228 229// Generate code to check that a global property cell is empty. Create 230// the property cell at compilation time if no cell exists for the 231// property. 232void PropertyHandlerCompiler::GenerateCheckPropertyCell( 233 MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name, 234 Register scratch, Label* miss) { 235 Handle<PropertyCell> cell = JSGlobalObject::EnsurePropertyCell(global, name); 236 DCHECK(cell->value()->IsTheHole()); 237 Factory* factory = masm->isolate()->factory(); 238 Handle<WeakCell> weak_cell = factory->NewWeakCell(cell); 239 __ LoadWeakValue(scratch, weak_cell, miss); 240 __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), 241 Immediate(factory->the_hole_value())); 242 __ j(not_equal, miss); 243} 244 245 246void NamedStoreHandlerCompiler::GenerateStoreViaSetter( 247 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder, 248 int accessor_index, int expected_arguments, Register scratch) { 249 // ----------- S t a t e ------------- 250 // -- esp[0] : return address 251 // ----------------------------------- 252 { 253 FrameScope scope(masm, StackFrame::INTERNAL); 254 255 // Save value register, so we can restore it later. 256 __ push(value()); 257 258 if (accessor_index >= 0) { 259 DCHECK(!holder.is(scratch)); 260 DCHECK(!receiver.is(scratch)); 261 DCHECK(!value().is(scratch)); 262 // Call the JavaScript setter with receiver and value on the stack. 263 if (map->IsJSGlobalObjectMap()) { 264 __ mov(scratch, 265 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 266 receiver = scratch; 267 } 268 __ push(receiver); 269 __ push(value()); 270 ParameterCount actual(1); 271 ParameterCount expected(expected_arguments); 272 __ LoadAccessor(edi, holder, accessor_index, ACCESSOR_SETTER); 273 __ InvokeFunction(edi, expected, actual, CALL_FUNCTION, 274 CheckDebugStepCallWrapper()); 275 } else { 276 // If we generate a global code snippet for deoptimization only, remember 277 // the place to continue after deoptimization. 278 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 279 } 280 281 // We have to return the passed value, not the return value of the setter. 282 __ pop(eax); 283 284 // Restore context register. 285 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 286 } 287 __ ret(0); 288} 289 290 291static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, 292 Register holder, Register name, 293 Handle<JSObject> holder_obj) { 294 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0); 295 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1); 296 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2); 297 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3); 298 __ push(name); 299 __ push(receiver); 300 __ push(holder); 301} 302 303 304static void CompileCallLoadPropertyWithInterceptor( 305 MacroAssembler* masm, Register receiver, Register holder, Register name, 306 Handle<JSObject> holder_obj, Runtime::FunctionId id) { 307 DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength == 308 Runtime::FunctionForId(id)->nargs); 309 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 310 __ CallRuntime(id); 311} 312 313 314static void StoreIC_PushArgs(MacroAssembler* masm) { 315 Register receiver = StoreDescriptor::ReceiverRegister(); 316 Register name = StoreDescriptor::NameRegister(); 317 Register value = StoreDescriptor::ValueRegister(); 318 Register slot = VectorStoreICDescriptor::SlotRegister(); 319 Register vector = VectorStoreICDescriptor::VectorRegister(); 320 321 __ xchg(receiver, Operand(esp, 0)); 322 __ push(name); 323 __ push(value); 324 __ push(slot); 325 __ push(vector); 326 __ push(receiver); // which contains the return address. 327} 328 329 330void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) { 331 // Return address is on the stack. 332 StoreIC_PushArgs(masm); 333 334 // Do tail-call to runtime routine. 335 __ TailCallRuntime(Runtime::kStoreIC_Slow); 336} 337 338 339void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) { 340 // Return address is on the stack. 341 StoreIC_PushArgs(masm); 342 343 // Do tail-call to runtime routine. 344 __ TailCallRuntime(Runtime::kKeyedStoreIC_Slow); 345} 346 347 348#undef __ 349#define __ ACCESS_MASM(masm()) 350 351 352void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label, 353 Handle<Name> name) { 354 if (!label->is_unused()) { 355 __ bind(label); 356 __ mov(this->name(), Immediate(name)); 357 } 358} 359 360 361void NamedStoreHandlerCompiler::GenerateRestoreName(Handle<Name> name) { 362 __ mov(this->name(), Immediate(name)); 363} 364 365 366void NamedStoreHandlerCompiler::RearrangeVectorAndSlot( 367 Register current_map, Register destination_map) { 368 DCHECK(destination_map.is(StoreTransitionHelper::MapRegister())); 369 DCHECK(current_map.is(StoreTransitionHelper::VectorRegister())); 370 ExternalReference virtual_slot = 371 ExternalReference::virtual_slot_register(isolate()); 372 __ mov(destination_map, current_map); 373 __ pop(current_map); 374 __ mov(Operand::StaticVariable(virtual_slot), current_map); 375 __ pop(current_map); // put vector in place. 376} 377 378 379void NamedStoreHandlerCompiler::GenerateRestoreMap(Handle<Map> transition, 380 Register map_reg, 381 Register scratch, 382 Label* miss) { 383 Handle<WeakCell> cell = Map::WeakCellForMap(transition); 384 DCHECK(!map_reg.is(scratch)); 385 __ LoadWeakValue(map_reg, cell, miss); 386 if (transition->CanBeDeprecated()) { 387 __ mov(scratch, FieldOperand(map_reg, Map::kBitField3Offset)); 388 __ and_(scratch, Immediate(Map::Deprecated::kMask)); 389 __ j(not_zero, miss); 390 } 391} 392 393 394void NamedStoreHandlerCompiler::GenerateConstantCheck(Register map_reg, 395 int descriptor, 396 Register value_reg, 397 Register scratch, 398 Label* miss_label) { 399 DCHECK(!map_reg.is(scratch)); 400 DCHECK(!map_reg.is(value_reg)); 401 DCHECK(!value_reg.is(scratch)); 402 __ LoadInstanceDescriptors(map_reg, scratch); 403 __ mov(scratch, 404 FieldOperand(scratch, DescriptorArray::GetValueOffset(descriptor))); 405 __ cmp(value_reg, scratch); 406 __ j(not_equal, miss_label); 407} 408 409void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(FieldType* field_type, 410 Register value_reg, 411 Label* miss_label) { 412 Register map_reg = scratch1(); 413 Register scratch = scratch2(); 414 DCHECK(!value_reg.is(map_reg)); 415 DCHECK(!value_reg.is(scratch)); 416 __ JumpIfSmi(value_reg, miss_label); 417 if (field_type->IsClass()) { 418 __ mov(map_reg, FieldOperand(value_reg, HeapObject::kMapOffset)); 419 __ CmpWeakValue(map_reg, Map::WeakCellForMap(field_type->AsClass()), 420 scratch); 421 __ j(not_equal, miss_label); 422 } 423} 424 425 426Register PropertyHandlerCompiler::CheckPrototypes( 427 Register object_reg, Register holder_reg, Register scratch1, 428 Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check, 429 ReturnHolder return_what) { 430 Handle<Map> receiver_map = map(); 431 432 // Make sure there's no overlap between holder and object registers. 433 DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 434 DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && 435 !scratch2.is(scratch1)); 436 437 if (FLAG_eliminate_prototype_chain_checks) { 438 Handle<Cell> validity_cell = 439 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); 440 if (!validity_cell.is_null()) { 441 DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), 442 validity_cell->value()); 443 // Operand::ForCell(...) points to the cell's payload! 444 __ cmp(Operand::ForCell(validity_cell), 445 Immediate(Smi::FromInt(Map::kPrototypeChainValid))); 446 __ j(not_equal, miss); 447 } 448 449 // The prototype chain of primitives (and their JSValue wrappers) depends 450 // on the native context, which can't be guarded by validity cells. 451 // |object_reg| holds the native context specific prototype in this case; 452 // we need to check its map. 453 if (check == CHECK_ALL_MAPS) { 454 __ mov(scratch1, FieldOperand(object_reg, HeapObject::kMapOffset)); 455 Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map); 456 __ CmpWeakValue(scratch1, cell, scratch2); 457 __ j(not_equal, miss); 458 } 459 } 460 461 // Keep track of the current object in register reg. 462 Register reg = object_reg; 463 int depth = 0; 464 465 Handle<JSObject> current = Handle<JSObject>::null(); 466 if (receiver_map->IsJSGlobalObjectMap()) { 467 current = isolate()->global_object(); 468 } 469 470 // Check access rights to the global object. This has to happen after 471 // the map check so that we know that the object is actually a global 472 // object. 473 // This allows us to install generated handlers for accesses to the 474 // global proxy (as opposed to using slow ICs). See corresponding code 475 // in LookupForRead(). 476 if (receiver_map->IsJSGlobalProxyMap()) { 477 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 478 } 479 480 Handle<JSObject> prototype = Handle<JSObject>::null(); 481 Handle<Map> current_map = receiver_map; 482 Handle<Map> holder_map(holder()->map()); 483 // Traverse the prototype chain and check the maps in the prototype chain for 484 // fast and global objects or do negative lookup for normal objects. 485 while (!current_map.is_identical_to(holder_map)) { 486 ++depth; 487 488 // Only global objects and objects that do not require access 489 // checks are allowed in stubs. 490 DCHECK(current_map->IsJSGlobalProxyMap() || 491 !current_map->is_access_check_needed()); 492 493 prototype = handle(JSObject::cast(current_map->prototype())); 494 if (current_map->is_dictionary_map() && 495 !current_map->IsJSGlobalObjectMap()) { 496 DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. 497 if (!name->IsUniqueName()) { 498 DCHECK(name->IsString()); 499 name = factory()->InternalizeString(Handle<String>::cast(name)); 500 } 501 DCHECK(current.is_null() || 502 current->property_dictionary()->FindEntry(name) == 503 NameDictionary::kNotFound); 504 505 if (FLAG_eliminate_prototype_chain_checks && depth > 1) { 506 // TODO(jkummerow): Cache and re-use weak cell. 507 __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss); 508 } 509 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, 510 scratch2); 511 512 if (!FLAG_eliminate_prototype_chain_checks) { 513 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 514 __ mov(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 515 } 516 } else { 517 Register map_reg = scratch1; 518 if (!FLAG_eliminate_prototype_chain_checks) { 519 __ mov(map_reg, FieldOperand(reg, HeapObject::kMapOffset)); 520 } 521 if (current_map->IsJSGlobalObjectMap()) { 522 GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), 523 name, scratch2, miss); 524 } else if (!FLAG_eliminate_prototype_chain_checks && 525 (depth != 1 || check == CHECK_ALL_MAPS)) { 526 Handle<WeakCell> cell = Map::WeakCellForMap(current_map); 527 __ CmpWeakValue(map_reg, cell, scratch2); 528 __ j(not_equal, miss); 529 } 530 if (!FLAG_eliminate_prototype_chain_checks) { 531 __ mov(holder_reg, FieldOperand(map_reg, Map::kPrototypeOffset)); 532 } 533 } 534 535 reg = holder_reg; // From now on the object will be in holder_reg. 536 // Go to the next object in the prototype chain. 537 current = prototype; 538 current_map = handle(current->map()); 539 } 540 541 DCHECK(!current_map->IsJSGlobalProxyMap()); 542 543 // Log the check depth. 544 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 545 546 if (!FLAG_eliminate_prototype_chain_checks && 547 (depth != 0 || check == CHECK_ALL_MAPS)) { 548 // Check the holder map. 549 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 550 Handle<WeakCell> cell = Map::WeakCellForMap(current_map); 551 __ CmpWeakValue(scratch1, cell, scratch2); 552 __ j(not_equal, miss); 553 } 554 555 bool return_holder = return_what == RETURN_HOLDER; 556 if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) { 557 __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss); 558 } 559 560 // Return the register containing the holder. 561 return return_holder ? reg : no_reg; 562} 563 564 565void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 566 if (!miss->is_unused()) { 567 Label success; 568 __ jmp(&success); 569 __ bind(miss); 570 if (IC::ICUseVector(kind())) { 571 DCHECK(kind() == Code::LOAD_IC); 572 PopVectorAndSlot(); 573 } 574 TailCallBuiltin(masm(), MissBuiltin(kind())); 575 __ bind(&success); 576 } 577} 578 579 580void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 581 if (!miss->is_unused()) { 582 Label success; 583 __ jmp(&success); 584 GenerateRestoreName(miss, name); 585 if (IC::ICUseVector(kind())) PopVectorAndSlot(); 586 TailCallBuiltin(masm(), MissBuiltin(kind())); 587 __ bind(&success); 588 } 589} 590 591 592void NamedLoadHandlerCompiler::GenerateLoadCallback( 593 Register reg, Handle<AccessorInfo> callback) { 594 DCHECK(!AreAliased(scratch2(), scratch3(), receiver())); 595 DCHECK(!AreAliased(scratch2(), scratch3(), reg)); 596 597 // Insert additional parameters into the stack frame above return address. 598 __ pop(scratch3()); // Get return address to place it below. 599 600 // Build v8::PropertyCallbackInfo::args_ array on the stack and push property 601 // name below the exit frame to make GC aware of them. 602 STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0); 603 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1); 604 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2); 605 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3); 606 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4); 607 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5); 608 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6); 609 STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7); 610 611 __ push(receiver()); // receiver 612 // Push data from AccessorInfo. 613 Handle<Object> data(callback->data(), isolate()); 614 if (data->IsUndefined() || data->IsSmi()) { 615 __ push(Immediate(data)); 616 } else { 617 Handle<WeakCell> cell = 618 isolate()->factory()->NewWeakCell(Handle<HeapObject>::cast(data)); 619 // The callback is alive if this instruction is executed, 620 // so the weak cell is not cleared and points to data. 621 __ GetWeakValue(scratch2(), cell); 622 __ push(scratch2()); 623 } 624 __ push(Immediate(isolate()->factory()->undefined_value())); // ReturnValue 625 // ReturnValue default value 626 __ push(Immediate(isolate()->factory()->undefined_value())); 627 __ push(Immediate(reinterpret_cast<int>(isolate()))); 628 __ push(reg); // holder 629 __ push(Immediate(Smi::FromInt(0))); // should_throw_on_error -> false 630 631 __ push(name()); // name 632 __ push(scratch3()); // Restore return address. 633 634 // Abi for CallApiGetter 635 Register getter_address = ApiGetterDescriptor::function_address(); 636 Address function_address = v8::ToCData<Address>(callback->getter()); 637 __ mov(getter_address, Immediate(function_address)); 638 639 CallApiGetterStub stub(isolate()); 640 __ TailCallStub(&stub); 641} 642 643 644void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) { 645 // Return the constant value. 646 __ LoadObject(eax, value); 647 __ ret(0); 648} 649 650 651void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup( 652 LookupIterator* it, Register holder_reg) { 653 DCHECK(holder()->HasNamedInterceptor()); 654 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined()); 655 656 // Compile the interceptor call, followed by inline code to load the 657 // property from further up the prototype chain if the call fails. 658 // Check that the maps haven't changed. 659 DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 660 661 // Preserve the receiver register explicitly whenever it is different from the 662 // holder and it is needed should the interceptor return without any result. 663 // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD 664 // case might cause a miss during the prototype check. 665 bool must_perform_prototype_check = 666 !holder().is_identical_to(it->GetHolder<JSObject>()); 667 bool must_preserve_receiver_reg = 668 !receiver().is(holder_reg) && 669 (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check); 670 671 // Save necessary data before invoking an interceptor. 672 // Requires a frame to make GC aware of pushed pointers. 673 { 674 FrameScope frame_scope(masm(), StackFrame::INTERNAL); 675 676 if (must_preserve_receiver_reg) { 677 __ push(receiver()); 678 } 679 __ push(holder_reg); 680 __ push(this->name()); 681 InterceptorVectorSlotPush(holder_reg); 682 // Invoke an interceptor. Note: map checks from receiver to 683 // interceptor's holder has been compiled before (see a caller 684 // of this method.) 685 CompileCallLoadPropertyWithInterceptor( 686 masm(), receiver(), holder_reg, this->name(), holder(), 687 Runtime::kLoadPropertyWithInterceptorOnly); 688 689 // Check if interceptor provided a value for property. If it's 690 // the case, return immediately. 691 Label interceptor_failed; 692 __ cmp(eax, factory()->no_interceptor_result_sentinel()); 693 __ j(equal, &interceptor_failed); 694 frame_scope.GenerateLeaveFrame(); 695 __ ret(0); 696 697 // Clobber registers when generating debug-code to provoke errors. 698 __ bind(&interceptor_failed); 699 if (FLAG_debug_code) { 700 __ mov(receiver(), Immediate(bit_cast<int32_t>(kZapValue))); 701 __ mov(holder_reg, Immediate(bit_cast<int32_t>(kZapValue))); 702 __ mov(this->name(), Immediate(bit_cast<int32_t>(kZapValue))); 703 } 704 705 InterceptorVectorSlotPop(holder_reg); 706 __ pop(this->name()); 707 __ pop(holder_reg); 708 if (must_preserve_receiver_reg) { 709 __ pop(receiver()); 710 } 711 712 // Leave the internal frame. 713 } 714 715 GenerateLoadPostInterceptor(it, holder_reg); 716} 717 718 719void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) { 720 DCHECK(holder()->HasNamedInterceptor()); 721 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined()); 722 // Call the runtime system to load the interceptor. 723 __ pop(scratch2()); // save old return address 724 PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(), 725 holder()); 726 __ push(scratch2()); // restore old return address 727 728 __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor); 729} 730 731 732Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( 733 Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback, 734 LanguageMode language_mode) { 735 Register holder_reg = Frontend(name); 736 737 __ pop(scratch1()); // remove the return address 738 __ push(receiver()); 739 __ push(holder_reg); 740 // If the callback cannot leak, then push the callback directly, 741 // otherwise wrap it in a weak cell. 742 if (callback->data()->IsUndefined() || callback->data()->IsSmi()) { 743 __ Push(callback); 744 } else { 745 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback); 746 __ Push(cell); 747 } 748 __ Push(name); 749 __ push(value()); 750 __ push(Immediate(Smi::FromInt(language_mode))); 751 __ push(scratch1()); // restore return address 752 753 // Do tail-call to the runtime system. 754 __ TailCallRuntime(Runtime::kStoreCallbackProperty); 755 756 // Return the generated code. 757 return GetCode(kind(), Code::FAST, name); 758} 759 760 761Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor( 762 Handle<Name> name) { 763 __ pop(scratch1()); // remove the return address 764 __ push(receiver()); 765 __ push(this->name()); 766 __ push(value()); 767 __ push(scratch1()); // restore return address 768 769 // Do tail-call to the runtime system. 770 __ TailCallRuntime(Runtime::kStorePropertyWithInterceptor); 771 772 // Return the generated code. 773 return GetCode(kind(), Code::FAST, name); 774} 775 776 777Register NamedStoreHandlerCompiler::value() { 778 return StoreDescriptor::ValueRegister(); 779} 780 781 782Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( 783 Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) { 784 Label miss; 785 if (IC::ICUseVector(kind())) { 786 PushVectorAndSlot(); 787 } 788 FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING); 789 // Get the value from the cell. 790 Register result = StoreDescriptor::ValueRegister(); 791 Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell); 792 __ LoadWeakValue(result, weak_cell, &miss); 793 __ mov(result, FieldOperand(result, PropertyCell::kValueOffset)); 794 795 // Check for deleted property if property can actually be deleted. 796 if (is_configurable) { 797 __ cmp(result, factory()->the_hole_value()); 798 __ j(equal, &miss); 799 } else if (FLAG_debug_code) { 800 __ cmp(result, factory()->the_hole_value()); 801 __ Check(not_equal, kDontDeleteCellsCannotContainTheHole); 802 } 803 804 Counters* counters = isolate()->counters(); 805 __ IncrementCounter(counters->ic_named_load_global_stub(), 1); 806 // The code above already loads the result into the return register. 807 if (IC::ICUseVector(kind())) { 808 DiscardVectorAndSlot(); 809 } 810 __ ret(0); 811 812 FrontendFooter(name, &miss); 813 814 // Return the generated code. 815 return GetCode(kind(), Code::NORMAL, name); 816} 817 818 819#undef __ 820} // namespace internal 821} // namespace v8 822 823#endif // V8_TARGET_ARCH_X87 824