| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 __ Ret(); | 416 __ Ret(); |
| 417 } | 417 } |
| 418 | 418 |
| 419 | 419 |
| 420 // Generate StoreField code, value is passed in r0 register. | 420 // Generate StoreField code, value is passed in r0 register. |
| 421 // When leaving generated code after success, the receiver_reg and name_reg | 421 // When leaving generated code after success, the receiver_reg and name_reg |
| 422 // may be clobbered. Upon branch to miss_label, the receiver and name | 422 // may be clobbered. Upon branch to miss_label, the receiver and name |
| 423 // registers have their original values. | 423 // registers have their original values. |
| 424 void StubCompiler::GenerateStoreField(MacroAssembler* masm, | 424 void StubCompiler::GenerateStoreField(MacroAssembler* masm, |
| 425 Handle<JSObject> object, | 425 Handle<JSObject> object, |
| 426 int index, | 426 LookupResult* lookup, |
| 427 Handle<Map> transition, | 427 Handle<Map> transition, |
| 428 Handle<Name> name, | 428 Handle<Name> name, |
| 429 Register receiver_reg, | 429 Register receiver_reg, |
| 430 Register name_reg, | 430 Register name_reg, |
| 431 Register value_reg, | 431 Register value_reg, |
| 432 Register scratch1, | 432 Register scratch1, |
| 433 Register scratch2, | 433 Register scratch2, |
| 434 Label* miss_label, | 434 Label* miss_label, |
| 435 Label* miss_restore_name) { | 435 Label* miss_restore_name) { |
| 436 // r0 : value | 436 // r0 : value |
| 437 Label exit; | 437 Label exit; |
| 438 | 438 |
| 439 LookupResult lookup(masm->isolate()); | |
| 440 object->Lookup(*name, &lookup); | |
| 441 if (lookup.IsFound() && (lookup.IsReadOnly() || !lookup.IsCacheable())) { | |
| 442 // In sloppy mode, we could just return the value and be done. However, we | |
| 443 // might be in strict mode, where we have to throw. Since we cannot tell, | |
| 444 // go into slow case unconditionally. | |
| 445 __ jmp(miss_label); | |
| 446 return; | |
| 447 } | |
| 448 | |
| 449 // Check that the map of the object hasn't changed. | 439 // Check that the map of the object hasn't changed. |
| 450 CompareMapMode mode = transition.is_null() ? ALLOW_ELEMENT_TRANSITION_MAPS | 440 CompareMapMode mode = transition.is_null() ? ALLOW_ELEMENT_TRANSITION_MAPS |
| 451 : REQUIRE_EXACT_MAP; | 441 : REQUIRE_EXACT_MAP; |
| 452 __ CheckMap(receiver_reg, scratch1, Handle<Map>(object->map()), miss_label, | 442 __ CheckMap(receiver_reg, scratch1, Handle<Map>(object->map()), miss_label, |
| 453 DO_SMI_CHECK, mode); | 443 DO_SMI_CHECK, mode); |
| 454 | 444 |
| 455 // Perform global security token check if needed. | 445 // Perform global security token check if needed. |
| 456 if (object->IsJSGlobalProxy()) { | 446 if (object->IsJSGlobalProxy()) { |
| 457 __ CheckAccessGlobalProxy(receiver_reg, scratch1, miss_label); | 447 __ CheckAccessGlobalProxy(receiver_reg, scratch1, miss_label); |
| 458 } | 448 } |
| 459 | 449 |
| 460 // Check that we are allowed to write this. | 450 // Check that we are allowed to write this. |
| 461 if (!transition.is_null() && object->GetPrototype()->IsJSObject()) { | 451 if (!transition.is_null() && object->GetPrototype()->IsJSObject()) { |
| 462 JSObject* holder; | 452 JSObject* holder; |
| 463 if (lookup.IsFound()) { | 453 // holder == object indicates that no property was found. |
| 464 holder = lookup.holder(); | 454 if (lookup->holder() != *object) { |
| 455 holder = lookup->holder(); |
| 465 } else { | 456 } else { |
| 466 // Find the top object. | 457 // Find the top object. |
| 467 holder = *object; | 458 holder = *object; |
| 468 do { | 459 do { |
| 469 holder = JSObject::cast(holder->GetPrototype()); | 460 holder = JSObject::cast(holder->GetPrototype()); |
| 470 } while (holder->GetPrototype()->IsJSObject()); | 461 } while (holder->GetPrototype()->IsJSObject()); |
| 471 } | 462 } |
| 472 CheckPrototypes(object, receiver_reg, Handle<JSObject>(holder), name_reg, | 463 Register holder_reg = CheckPrototypes( |
| 473 scratch1, scratch2, name, miss_restore_name); | 464 object, receiver_reg, Handle<JSObject>(holder), name_reg, |
| 465 scratch1, scratch2, name, miss_restore_name); |
| 466 // If no property was found, and the holder (the last object in the |
| 467 // prototype chain) is in slow mode, we need to do a negative lookup on the |
| 468 // holder. |
| 469 if (lookup->holder() == *object && |
| 470 !holder->HasFastProperties() && |
| 471 !holder->IsJSGlobalProxy() && |
| 472 !holder->IsJSGlobalObject()) { |
| 473 GenerateDictionaryNegativeLookup( |
| 474 masm, miss_restore_name, holder_reg, name, scratch1, scratch2); |
| 475 } |
| 474 } | 476 } |
| 475 | 477 |
| 476 // Stub never generated for non-global objects that require access | 478 // Stub never generated for non-global objects that require access |
| 477 // checks. | 479 // checks. |
| 478 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); | 480 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); |
| 479 | 481 |
| 480 // Perform map transition for the receiver if necessary. | 482 // Perform map transition for the receiver if necessary. |
| 481 if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) { | 483 if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) { |
| 482 // The properties must be extended before we can store the value. | 484 // The properties must be extended before we can store the value. |
| 483 // We jump to a runtime call that extends the properties array. | 485 // We jump to a runtime call that extends the properties array. |
| 484 __ push(receiver_reg); | 486 __ push(receiver_reg); |
| 485 __ mov(r2, Operand(transition)); | 487 __ mov(r2, Operand(transition)); |
| 486 __ Push(r2, r0); | 488 __ Push(r2, r0); |
| 487 __ TailCallExternalReference( | 489 __ TailCallExternalReference( |
| 488 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), | 490 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), |
| 489 masm->isolate()), | 491 masm->isolate()), |
| 490 3, | 492 3, |
| 491 1); | 493 1); |
| 492 return; | 494 return; |
| 493 } | 495 } |
| 494 | 496 |
| 497 int index; |
| 495 if (!transition.is_null()) { | 498 if (!transition.is_null()) { |
| 496 // Update the map of the object. | 499 // Update the map of the object. |
| 497 __ mov(scratch1, Operand(transition)); | 500 __ mov(scratch1, Operand(transition)); |
| 498 __ str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); | 501 __ str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); |
| 499 | 502 |
| 500 // Update the write barrier for the map field and pass the now unused | 503 // Update the write barrier for the map field and pass the now unused |
| 501 // name_reg as scratch register. | 504 // name_reg as scratch register. |
| 502 __ RecordWriteField(receiver_reg, | 505 __ RecordWriteField(receiver_reg, |
| 503 HeapObject::kMapOffset, | 506 HeapObject::kMapOffset, |
| 504 scratch1, | 507 scratch1, |
| 505 name_reg, | 508 name_reg, |
| 506 kLRHasNotBeenSaved, | 509 kLRHasNotBeenSaved, |
| 507 kDontSaveFPRegs, | 510 kDontSaveFPRegs, |
| 508 OMIT_REMEMBERED_SET, | 511 OMIT_REMEMBERED_SET, |
| 509 OMIT_SMI_CHECK); | 512 OMIT_SMI_CHECK); |
| 513 index = transition->instance_descriptors()->GetFieldIndex( |
| 514 transition->LastAdded()); |
| 515 } else { |
| 516 index = lookup->GetFieldIndex().field_index(); |
| 510 } | 517 } |
| 511 | 518 |
| 512 // Adjust for the number of properties stored in the object. Even in the | 519 // Adjust for the number of properties stored in the object. Even in the |
| 513 // face of a transition we can use the old map here because the size of the | 520 // face of a transition we can use the old map here because the size of the |
| 514 // object and the number of in-object properties is not going to change. | 521 // object and the number of in-object properties is not going to change. |
| 515 index -= object->map()->inobject_properties(); | 522 index -= object->map()->inobject_properties(); |
| 516 | 523 |
| 517 if (index < 0) { | 524 if (index < 0) { |
| 518 // Set the property straight into the object. | 525 // Set the property straight into the object. |
| 519 int offset = object->map()->instance_size() + (index * kPointerSize); | 526 int offset = object->map()->instance_size() + (index * kPointerSize); |
| (...skipping 3322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3842 TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow); | 3849 TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow); |
| 3843 } | 3850 } |
| 3844 } | 3851 } |
| 3845 | 3852 |
| 3846 | 3853 |
| 3847 #undef __ | 3854 #undef __ |
| 3848 | 3855 |
| 3849 } } // namespace v8::internal | 3856 } } // namespace v8::internal |
| 3850 | 3857 |
| 3851 #endif // V8_TARGET_ARCH_ARM | 3858 #endif // V8_TARGET_ARCH_ARM |
| OLD | NEW |