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#include "src/v8.h" 6 7#if V8_TARGET_ARCH_X87 8 9#include "src/ic/call-optimization.h" 10#include "src/ic/handler-compiler.h" 11#include "src/ic/ic.h" 12 13namespace v8 { 14namespace internal { 15 16#define __ ACCESS_MASM(masm) 17 18 19void NamedLoadHandlerCompiler::GenerateLoadViaGetter( 20 MacroAssembler* masm, Handle<HeapType> type, Register receiver, 21 Handle<JSFunction> getter) { 22 { 23 FrameScope scope(masm, StackFrame::INTERNAL); 24 25 if (!getter.is_null()) { 26 // Call the JavaScript getter with the receiver on the stack. 27 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { 28 // Swap in the global receiver. 29 __ mov(receiver, 30 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 31 } 32 __ push(receiver); 33 ParameterCount actual(0); 34 ParameterCount expected(getter); 35 __ InvokeFunction(getter, expected, actual, CALL_FUNCTION, 36 NullCallWrapper()); 37 } else { 38 // If we generate a global code snippet for deoptimization only, remember 39 // the place to continue after deoptimization. 40 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 41 } 42 43 // Restore context register. 44 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 45 } 46 __ ret(0); 47} 48 49 50void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup( 51 MacroAssembler* masm, Label* miss_label, Register receiver, 52 Handle<Name> name, Register scratch0, Register scratch1) { 53 DCHECK(name->IsUniqueName()); 54 DCHECK(!receiver.is(scratch0)); 55 Counters* counters = masm->isolate()->counters(); 56 __ IncrementCounter(counters->negative_lookups(), 1); 57 __ IncrementCounter(counters->negative_lookups_miss(), 1); 58 59 __ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset)); 60 61 const int kInterceptorOrAccessCheckNeededMask = 62 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); 63 64 // Bail out if the receiver has a named interceptor or requires access checks. 65 __ test_b(FieldOperand(scratch0, Map::kBitFieldOffset), 66 kInterceptorOrAccessCheckNeededMask); 67 __ j(not_zero, miss_label); 68 69 // Check that receiver is a JSObject. 70 __ CmpInstanceType(scratch0, FIRST_SPEC_OBJECT_TYPE); 71 __ j(below, miss_label); 72 73 // Load properties array. 74 Register properties = scratch0; 75 __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); 76 77 // Check that the properties array is a dictionary. 78 __ cmp(FieldOperand(properties, HeapObject::kMapOffset), 79 Immediate(masm->isolate()->factory()->hash_table_map())); 80 __ j(not_equal, miss_label); 81 82 Label done; 83 NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done, 84 properties, name, scratch1); 85 __ bind(&done); 86 __ DecrementCounter(counters->negative_lookups_miss(), 1); 87} 88 89 90void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype( 91 MacroAssembler* masm, int index, Register prototype, Label* miss) { 92 // Get the global function with the given index. 93 Handle<JSFunction> function( 94 JSFunction::cast(masm->isolate()->native_context()->get(index))); 95 // Check we're still in the same context. 96 Register scratch = prototype; 97 const int offset = Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX); 98 __ mov(scratch, Operand(esi, offset)); 99 __ mov(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset)); 100 __ cmp(Operand(scratch, Context::SlotOffset(index)), function); 101 __ j(not_equal, miss); 102 103 // Load its initial map. The global functions all have initial maps. 104 __ Move(prototype, Immediate(Handle<Map>(function->initial_map()))); 105 // Load the prototype from the initial map. 106 __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); 107} 108 109 110void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype( 111 MacroAssembler* masm, Register receiver, Register scratch1, 112 Register scratch2, Label* miss_label) { 113 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); 114 __ mov(eax, scratch1); 115 __ ret(0); 116} 117 118 119// Generate call to api function. 120// This function uses push() to generate smaller, faster code than 121// the version above. It is an optimization that should will be removed 122// when api call ICs are generated in hydrogen. 123void PropertyHandlerCompiler::GenerateFastApiCall( 124 MacroAssembler* masm, const CallOptimization& optimization, 125 Handle<Map> receiver_map, Register receiver, Register scratch_in, 126 bool is_store, int argc, Register* values) { 127 // Copy return value. 128 __ pop(scratch_in); 129 // receiver 130 __ push(receiver); 131 // Write the arguments to stack frame. 132 for (int i = 0; i < argc; i++) { 133 Register arg = values[argc - 1 - i]; 134 DCHECK(!receiver.is(arg)); 135 DCHECK(!scratch_in.is(arg)); 136 __ push(arg); 137 } 138 __ push(scratch_in); 139 // Stack now matches JSFunction abi. 140 DCHECK(optimization.is_simple_api_call()); 141 142 // Abi for CallApiFunctionStub. 143 Register callee = eax; 144 Register call_data = ebx; 145 Register holder = ecx; 146 Register api_function_address = edx; 147 Register scratch = edi; // scratch_in is no longer valid. 148 149 // Put holder in place. 150 CallOptimization::HolderLookup holder_lookup; 151 Handle<JSObject> api_holder = 152 optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup); 153 switch (holder_lookup) { 154 case CallOptimization::kHolderIsReceiver: 155 __ Move(holder, receiver); 156 break; 157 case CallOptimization::kHolderFound: 158 __ LoadHeapObject(holder, api_holder); 159 break; 160 case CallOptimization::kHolderNotFound: 161 UNREACHABLE(); 162 break; 163 } 164 165 Isolate* isolate = masm->isolate(); 166 Handle<JSFunction> function = optimization.constant_function(); 167 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); 168 Handle<Object> call_data_obj(api_call_info->data(), isolate); 169 170 // Put callee in place. 171 __ LoadHeapObject(callee, function); 172 173 bool call_data_undefined = false; 174 // Put call_data in place. 175 if (isolate->heap()->InNewSpace(*call_data_obj)) { 176 __ mov(scratch, api_call_info); 177 __ mov(call_data, FieldOperand(scratch, CallHandlerInfo::kDataOffset)); 178 } else if (call_data_obj->IsUndefined()) { 179 call_data_undefined = true; 180 __ mov(call_data, Immediate(isolate->factory()->undefined_value())); 181 } else { 182 __ mov(call_data, call_data_obj); 183 } 184 185 // Put api_function_address in place. 186 Address function_address = v8::ToCData<Address>(api_call_info->callback()); 187 __ mov(api_function_address, Immediate(function_address)); 188 189 // Jump to stub. 190 CallApiFunctionStub stub(isolate, is_store, call_data_undefined, argc); 191 __ TailCallStub(&stub); 192} 193 194 195// Generate code to check that a global property cell is empty. Create 196// the property cell at compilation time if no cell exists for the 197// property. 198void PropertyHandlerCompiler::GenerateCheckPropertyCell( 199 MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name, 200 Register scratch, Label* miss) { 201 Handle<PropertyCell> cell = JSGlobalObject::EnsurePropertyCell(global, name); 202 DCHECK(cell->value()->IsTheHole()); 203 Handle<Oddball> the_hole = masm->isolate()->factory()->the_hole_value(); 204 if (masm->serializer_enabled()) { 205 __ mov(scratch, Immediate(cell)); 206 __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), 207 Immediate(the_hole)); 208 } else { 209 __ cmp(Operand::ForCell(cell), Immediate(the_hole)); 210 } 211 __ j(not_equal, miss); 212} 213 214 215void NamedStoreHandlerCompiler::GenerateStoreViaSetter( 216 MacroAssembler* masm, Handle<HeapType> type, Register receiver, 217 Handle<JSFunction> setter) { 218 // ----------- S t a t e ------------- 219 // -- esp[0] : return address 220 // ----------------------------------- 221 { 222 FrameScope scope(masm, StackFrame::INTERNAL); 223 224 // Save value register, so we can restore it later. 225 __ push(value()); 226 227 if (!setter.is_null()) { 228 // Call the JavaScript setter with receiver and value on the stack. 229 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { 230 // Swap in the global receiver. 231 __ mov(receiver, 232 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 233 } 234 __ push(receiver); 235 __ push(value()); 236 ParameterCount actual(1); 237 ParameterCount expected(setter); 238 __ InvokeFunction(setter, expected, actual, CALL_FUNCTION, 239 NullCallWrapper()); 240 } else { 241 // If we generate a global code snippet for deoptimization only, remember 242 // the place to continue after deoptimization. 243 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 244 } 245 246 // We have to return the passed value, not the return value of the setter. 247 __ pop(eax); 248 249 // Restore context register. 250 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 251 } 252 __ ret(0); 253} 254 255 256static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, 257 Register holder, Register name, 258 Handle<JSObject> holder_obj) { 259 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0); 260 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsInfoIndex == 1); 261 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 2); 262 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 3); 263 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 4); 264 __ push(name); 265 Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); 266 DCHECK(!masm->isolate()->heap()->InNewSpace(*interceptor)); 267 Register scratch = name; 268 __ mov(scratch, Immediate(interceptor)); 269 __ push(scratch); 270 __ push(receiver); 271 __ push(holder); 272} 273 274 275static void CompileCallLoadPropertyWithInterceptor( 276 MacroAssembler* masm, Register receiver, Register holder, Register name, 277 Handle<JSObject> holder_obj, IC::UtilityId id) { 278 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 279 __ CallExternalReference(ExternalReference(IC_Utility(id), masm->isolate()), 280 NamedLoadHandlerCompiler::kInterceptorArgsLength); 281} 282 283 284static void StoreIC_PushArgs(MacroAssembler* masm) { 285 Register receiver = StoreDescriptor::ReceiverRegister(); 286 Register name = StoreDescriptor::NameRegister(); 287 Register value = StoreDescriptor::ValueRegister(); 288 289 DCHECK(!ebx.is(receiver) && !ebx.is(name) && !ebx.is(value)); 290 291 __ pop(ebx); 292 __ push(receiver); 293 __ push(name); 294 __ push(value); 295 __ push(ebx); 296} 297 298 299void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) { 300 // Return address is on the stack. 301 StoreIC_PushArgs(masm); 302 303 // Do tail-call to runtime routine. 304 ExternalReference ref(IC_Utility(IC::kStoreIC_Slow), masm->isolate()); 305 __ TailCallExternalReference(ref, 3, 1); 306} 307 308 309void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) { 310 // Return address is on the stack. 311 StoreIC_PushArgs(masm); 312 313 // Do tail-call to runtime routine. 314 ExternalReference ref(IC_Utility(IC::kKeyedStoreIC_Slow), masm->isolate()); 315 __ TailCallExternalReference(ref, 3, 1); 316} 317 318 319#undef __ 320#define __ ACCESS_MASM(masm()) 321 322 323void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label, 324 Handle<Name> name) { 325 if (!label->is_unused()) { 326 __ bind(label); 327 __ mov(this->name(), Immediate(name)); 328 } 329} 330 331 332// Receiver_reg is preserved on jumps to miss_label, but may be destroyed if 333// store is successful. 334void NamedStoreHandlerCompiler::GenerateStoreTransition( 335 Handle<Map> transition, Handle<Name> name, Register receiver_reg, 336 Register storage_reg, Register value_reg, Register scratch1, 337 Register scratch2, Register unused, Label* miss_label, Label* slow) { 338 int descriptor = transition->LastAdded(); 339 DescriptorArray* descriptors = transition->instance_descriptors(); 340 PropertyDetails details = descriptors->GetDetails(descriptor); 341 Representation representation = details.representation(); 342 DCHECK(!representation.IsNone()); 343 344 if (details.type() == CONSTANT) { 345 Handle<Object> constant(descriptors->GetValue(descriptor), isolate()); 346 __ CmpObject(value_reg, constant); 347 __ j(not_equal, miss_label); 348 } else if (representation.IsSmi()) { 349 __ JumpIfNotSmi(value_reg, miss_label); 350 } else if (representation.IsHeapObject()) { 351 __ JumpIfSmi(value_reg, miss_label); 352 HeapType* field_type = descriptors->GetFieldType(descriptor); 353 HeapType::Iterator<Map> it = field_type->Classes(); 354 if (!it.Done()) { 355 Label do_store; 356 while (true) { 357 __ CompareMap(value_reg, it.Current()); 358 it.Advance(); 359 if (it.Done()) { 360 __ j(not_equal, miss_label); 361 break; 362 } 363 __ j(equal, &do_store, Label::kNear); 364 } 365 __ bind(&do_store); 366 } 367 } else if (representation.IsDouble()) { 368 Label do_store, heap_number; 369 __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow, MUTABLE); 370 371 __ JumpIfNotSmi(value_reg, &heap_number); 372 __ SmiUntag(value_reg); 373 __ push(value_reg); 374 __ fild_s(Operand(esp, 0)); 375 __ pop(value_reg); 376 __ SmiTag(value_reg); 377 __ jmp(&do_store); 378 379 __ bind(&heap_number); 380 __ CheckMap(value_reg, isolate()->factory()->heap_number_map(), miss_label, 381 DONT_DO_SMI_CHECK); 382 __ fld_d(FieldOperand(value_reg, HeapNumber::kValueOffset)); 383 384 __ bind(&do_store); 385 __ fstp_d(FieldOperand(storage_reg, HeapNumber::kValueOffset)); 386 } 387 388 // Stub never generated for objects that require access checks. 389 DCHECK(!transition->is_access_check_needed()); 390 391 // Perform map transition for the receiver if necessary. 392 if (details.type() == FIELD && 393 Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) { 394 // The properties must be extended before we can store the value. 395 // We jump to a runtime call that extends the properties array. 396 __ pop(scratch1); // Return address. 397 __ push(receiver_reg); 398 __ push(Immediate(transition)); 399 __ push(value_reg); 400 __ push(scratch1); 401 __ TailCallExternalReference( 402 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), 403 isolate()), 404 3, 1); 405 return; 406 } 407 408 // Update the map of the object. 409 __ mov(scratch1, Immediate(transition)); 410 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch1); 411 412 // Update the write barrier for the map field. 413 __ RecordWriteField(receiver_reg, HeapObject::kMapOffset, scratch1, scratch2, 414 kDontSaveFPRegs, OMIT_REMEMBERED_SET, OMIT_SMI_CHECK); 415 416 if (details.type() == CONSTANT) { 417 DCHECK(value_reg.is(eax)); 418 __ ret(0); 419 return; 420 } 421 422 int index = transition->instance_descriptors()->GetFieldIndex( 423 transition->LastAdded()); 424 425 // Adjust for the number of properties stored in the object. Even in the 426 // face of a transition we can use the old map here because the size of the 427 // object and the number of in-object properties is not going to change. 428 index -= transition->inobject_properties(); 429 430 SmiCheck smi_check = 431 representation.IsTagged() ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 432 // TODO(verwaest): Share this code as a code stub. 433 if (index < 0) { 434 // Set the property straight into the object. 435 int offset = transition->instance_size() + (index * kPointerSize); 436 if (representation.IsDouble()) { 437 __ mov(FieldOperand(receiver_reg, offset), storage_reg); 438 } else { 439 __ mov(FieldOperand(receiver_reg, offset), value_reg); 440 } 441 442 if (!representation.IsSmi()) { 443 // Update the write barrier for the array address. 444 if (!representation.IsDouble()) { 445 __ mov(storage_reg, value_reg); 446 } 447 __ RecordWriteField(receiver_reg, offset, storage_reg, scratch1, 448 kDontSaveFPRegs, EMIT_REMEMBERED_SET, smi_check); 449 } 450 } else { 451 // Write to the properties array. 452 int offset = index * kPointerSize + FixedArray::kHeaderSize; 453 // Get the properties array (optimistically). 454 __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); 455 if (representation.IsDouble()) { 456 __ mov(FieldOperand(scratch1, offset), storage_reg); 457 } else { 458 __ mov(FieldOperand(scratch1, offset), value_reg); 459 } 460 461 if (!representation.IsSmi()) { 462 // Update the write barrier for the array address. 463 if (!representation.IsDouble()) { 464 __ mov(storage_reg, value_reg); 465 } 466 __ RecordWriteField(scratch1, offset, storage_reg, receiver_reg, 467 kDontSaveFPRegs, EMIT_REMEMBERED_SET, smi_check); 468 } 469 } 470 471 // Return the value (register eax). 472 DCHECK(value_reg.is(eax)); 473 __ ret(0); 474} 475 476 477void NamedStoreHandlerCompiler::GenerateStoreField(LookupIterator* lookup, 478 Register value_reg, 479 Label* miss_label) { 480 DCHECK(lookup->representation().IsHeapObject()); 481 __ JumpIfSmi(value_reg, miss_label); 482 HeapType::Iterator<Map> it = lookup->GetFieldType()->Classes(); 483 Label do_store; 484 while (true) { 485 __ CompareMap(value_reg, it.Current()); 486 it.Advance(); 487 if (it.Done()) { 488 __ j(not_equal, miss_label); 489 break; 490 } 491 __ j(equal, &do_store, Label::kNear); 492 } 493 __ bind(&do_store); 494 495 StoreFieldStub stub(isolate(), lookup->GetFieldIndex(), 496 lookup->representation()); 497 GenerateTailCall(masm(), stub.GetCode()); 498} 499 500 501Register PropertyHandlerCompiler::CheckPrototypes( 502 Register object_reg, Register holder_reg, Register scratch1, 503 Register scratch2, Handle<Name> name, Label* miss, 504 PrototypeCheckType check) { 505 Handle<Map> receiver_map(IC::TypeToMap(*type(), isolate())); 506 507 // Make sure there's no overlap between holder and object registers. 508 DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 509 DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && 510 !scratch2.is(scratch1)); 511 512 // Keep track of the current object in register reg. 513 Register reg = object_reg; 514 int depth = 0; 515 516 Handle<JSObject> current = Handle<JSObject>::null(); 517 if (type()->IsConstant()) 518 current = Handle<JSObject>::cast(type()->AsConstant()->Value()); 519 Handle<JSObject> prototype = Handle<JSObject>::null(); 520 Handle<Map> current_map = receiver_map; 521 Handle<Map> holder_map(holder()->map()); 522 // Traverse the prototype chain and check the maps in the prototype chain for 523 // fast and global objects or do negative lookup for normal objects. 524 while (!current_map.is_identical_to(holder_map)) { 525 ++depth; 526 527 // Only global objects and objects that do not require access 528 // checks are allowed in stubs. 529 DCHECK(current_map->IsJSGlobalProxyMap() || 530 !current_map->is_access_check_needed()); 531 532 prototype = handle(JSObject::cast(current_map->prototype())); 533 if (current_map->is_dictionary_map() && 534 !current_map->IsJSGlobalObjectMap()) { 535 DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. 536 if (!name->IsUniqueName()) { 537 DCHECK(name->IsString()); 538 name = factory()->InternalizeString(Handle<String>::cast(name)); 539 } 540 DCHECK(current.is_null() || 541 current->property_dictionary()->FindEntry(name) == 542 NameDictionary::kNotFound); 543 544 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, 545 scratch2); 546 547 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 548 reg = holder_reg; // From now on the object will be in holder_reg. 549 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 550 } else { 551 bool in_new_space = heap()->InNewSpace(*prototype); 552 // Two possible reasons for loading the prototype from the map: 553 // (1) Can't store references to new space in code. 554 // (2) Handler is shared for all receivers with the same prototype 555 // map (but not necessarily the same prototype instance). 556 bool load_prototype_from_map = in_new_space || depth == 1; 557 if (depth != 1 || check == CHECK_ALL_MAPS) { 558 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); 559 } 560 561 // Check access rights to the global object. This has to happen after 562 // the map check so that we know that the object is actually a global 563 // object. 564 // This allows us to install generated handlers for accesses to the 565 // global proxy (as opposed to using slow ICs). See corresponding code 566 // in LookupForRead(). 567 if (current_map->IsJSGlobalProxyMap()) { 568 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 569 } else if (current_map->IsJSGlobalObjectMap()) { 570 GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), 571 name, scratch2, miss); 572 } 573 574 if (load_prototype_from_map) { 575 // Save the map in scratch1 for later. 576 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 577 } 578 579 reg = holder_reg; // From now on the object will be in holder_reg. 580 581 if (load_prototype_from_map) { 582 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 583 } else { 584 __ mov(reg, prototype); 585 } 586 } 587 588 // Go to the next object in the prototype chain. 589 current = prototype; 590 current_map = handle(current->map()); 591 } 592 593 // Log the check depth. 594 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 595 596 if (depth != 0 || check == CHECK_ALL_MAPS) { 597 // Check the holder map. 598 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); 599 } 600 601 // Perform security check for access to the global object. 602 DCHECK(current_map->IsJSGlobalProxyMap() || 603 !current_map->is_access_check_needed()); 604 if (current_map->IsJSGlobalProxyMap()) { 605 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 606 } 607 608 // Return the register containing the holder. 609 return reg; 610} 611 612 613void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 614 if (!miss->is_unused()) { 615 Label success; 616 __ jmp(&success); 617 __ bind(miss); 618 TailCallBuiltin(masm(), MissBuiltin(kind())); 619 __ bind(&success); 620 } 621} 622 623 624void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 625 if (!miss->is_unused()) { 626 Label success; 627 __ jmp(&success); 628 GenerateRestoreName(miss, name); 629 TailCallBuiltin(masm(), MissBuiltin(kind())); 630 __ bind(&success); 631 } 632} 633 634 635void NamedLoadHandlerCompiler::GenerateLoadCallback( 636 Register reg, Handle<ExecutableAccessorInfo> callback) { 637 // Insert additional parameters into the stack frame above return address. 638 DCHECK(!scratch3().is(reg)); 639 __ pop(scratch3()); // Get return address to place it below. 640 641 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); 642 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); 643 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); 644 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); 645 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); 646 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); 647 __ push(receiver()); // receiver 648 // Push data from ExecutableAccessorInfo. 649 if (isolate()->heap()->InNewSpace(callback->data())) { 650 DCHECK(!scratch2().is(reg)); 651 __ mov(scratch2(), Immediate(callback)); 652 __ push(FieldOperand(scratch2(), ExecutableAccessorInfo::kDataOffset)); 653 } else { 654 __ push(Immediate(Handle<Object>(callback->data(), isolate()))); 655 } 656 __ push(Immediate(isolate()->factory()->undefined_value())); // ReturnValue 657 // ReturnValue default value 658 __ push(Immediate(isolate()->factory()->undefined_value())); 659 __ push(Immediate(reinterpret_cast<int>(isolate()))); 660 __ push(reg); // holder 661 662 // Save a pointer to where we pushed the arguments. This will be 663 // passed as the const PropertyAccessorInfo& to the C++ callback. 664 __ push(esp); 665 666 __ push(name()); // name 667 668 __ push(scratch3()); // Restore return address. 669 670 // Abi for CallApiGetter 671 Register getter_address = ApiGetterDescriptor::function_address(); 672 Address function_address = v8::ToCData<Address>(callback->getter()); 673 __ mov(getter_address, Immediate(function_address)); 674 675 CallApiGetterStub stub(isolate()); 676 __ TailCallStub(&stub); 677} 678 679 680void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) { 681 // Return the constant value. 682 __ LoadObject(eax, value); 683 __ ret(0); 684} 685 686 687void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup( 688 LookupIterator* it, Register holder_reg) { 689 DCHECK(holder()->HasNamedInterceptor()); 690 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined()); 691 692 // Compile the interceptor call, followed by inline code to load the 693 // property from further up the prototype chain if the call fails. 694 // Check that the maps haven't changed. 695 DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 696 697 // Preserve the receiver register explicitly whenever it is different from the 698 // holder and it is needed should the interceptor return without any result. 699 // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD 700 // case might cause a miss during the prototype check. 701 bool must_perform_prototype_check = 702 !holder().is_identical_to(it->GetHolder<JSObject>()); 703 bool must_preserve_receiver_reg = 704 !receiver().is(holder_reg) && 705 (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check); 706 707 // Save necessary data before invoking an interceptor. 708 // Requires a frame to make GC aware of pushed pointers. 709 { 710 FrameScope frame_scope(masm(), StackFrame::INTERNAL); 711 712 if (must_preserve_receiver_reg) { 713 __ push(receiver()); 714 } 715 __ push(holder_reg); 716 __ push(this->name()); 717 718 // Invoke an interceptor. Note: map checks from receiver to 719 // interceptor's holder has been compiled before (see a caller 720 // of this method.) 721 CompileCallLoadPropertyWithInterceptor( 722 masm(), receiver(), holder_reg, this->name(), holder(), 723 IC::kLoadPropertyWithInterceptorOnly); 724 725 // Check if interceptor provided a value for property. If it's 726 // the case, return immediately. 727 Label interceptor_failed; 728 __ cmp(eax, factory()->no_interceptor_result_sentinel()); 729 __ j(equal, &interceptor_failed); 730 frame_scope.GenerateLeaveFrame(); 731 __ ret(0); 732 733 // Clobber registers when generating debug-code to provoke errors. 734 __ bind(&interceptor_failed); 735 if (FLAG_debug_code) { 736 __ mov(receiver(), Immediate(bit_cast<int32_t>(kZapValue))); 737 __ mov(holder_reg, Immediate(bit_cast<int32_t>(kZapValue))); 738 __ mov(this->name(), Immediate(bit_cast<int32_t>(kZapValue))); 739 } 740 741 __ pop(this->name()); 742 __ pop(holder_reg); 743 if (must_preserve_receiver_reg) { 744 __ pop(receiver()); 745 } 746 747 // Leave the internal frame. 748 } 749 750 GenerateLoadPostInterceptor(it, holder_reg); 751} 752 753 754void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) { 755 DCHECK(holder()->HasNamedInterceptor()); 756 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined()); 757 // Call the runtime system to load the interceptor. 758 __ pop(scratch2()); // save old return address 759 PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(), 760 holder()); 761 __ push(scratch2()); // restore old return address 762 763 ExternalReference ref = ExternalReference( 764 IC_Utility(IC::kLoadPropertyWithInterceptor), isolate()); 765 __ TailCallExternalReference( 766 ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1); 767} 768 769 770Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( 771 Handle<JSObject> object, Handle<Name> name, 772 Handle<ExecutableAccessorInfo> callback) { 773 Register holder_reg = Frontend(receiver(), name); 774 775 __ pop(scratch1()); // remove the return address 776 __ push(receiver()); 777 __ push(holder_reg); 778 __ Push(callback); 779 __ Push(name); 780 __ push(value()); 781 __ push(scratch1()); // restore return address 782 783 // Do tail-call to the runtime system. 784 ExternalReference store_callback_property = 785 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); 786 __ TailCallExternalReference(store_callback_property, 5, 1); 787 788 // Return the generated code. 789 return GetCode(kind(), Code::FAST, name); 790} 791 792 793Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor( 794 Handle<Name> name) { 795 __ pop(scratch1()); // remove the return address 796 __ push(receiver()); 797 __ push(this->name()); 798 __ push(value()); 799 __ push(scratch1()); // restore return address 800 801 // Do tail-call to the runtime system. 802 ExternalReference store_ic_property = ExternalReference( 803 IC_Utility(IC::kStorePropertyWithInterceptor), isolate()); 804 __ TailCallExternalReference(store_ic_property, 3, 1); 805 806 // Return the generated code. 807 return GetCode(kind(), Code::FAST, name); 808} 809 810 811Register NamedStoreHandlerCompiler::value() { 812 return StoreDescriptor::ValueRegister(); 813} 814 815 816Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( 817 Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) { 818 Label miss; 819 820 FrontendHeader(receiver(), name, &miss); 821 // Get the value from the cell. 822 Register result = StoreDescriptor::ValueRegister(); 823 if (masm()->serializer_enabled()) { 824 __ mov(result, Immediate(cell)); 825 __ mov(result, FieldOperand(result, PropertyCell::kValueOffset)); 826 } else { 827 __ mov(result, Operand::ForCell(cell)); 828 } 829 830 // Check for deleted property if property can actually be deleted. 831 if (is_configurable) { 832 __ cmp(result, factory()->the_hole_value()); 833 __ j(equal, &miss); 834 } else if (FLAG_debug_code) { 835 __ cmp(result, factory()->the_hole_value()); 836 __ Check(not_equal, kDontDeleteCellsCannotContainTheHole); 837 } 838 839 Counters* counters = isolate()->counters(); 840 __ IncrementCounter(counters->named_load_global_stub(), 1); 841 // The code above already loads the result into the return register. 842 __ ret(0); 843 844 FrontendFooter(name, &miss); 845 846 // Return the generated code. 847 return GetCode(kind(), Code::NORMAL, name); 848} 849 850 851#undef __ 852} 853} // namespace v8::internal 854 855#endif // V8_TARGET_ARCH_X87 856