Fix reintroduction of global variables that have been deleted.
Deletion of global properties puts 'the hole' in the global property cell and updates the property details in the property dictionary with the information that the property has been deleted. When setting global properties that have been deleted in generated code we just store the new value in the global property cell. This does not update the property details in the property dictionary. Therefore, it looks like the property is not there eventhough it was just reintroduced. Perform 'the hole' checks in generated code for global property stores and bail out of ICs and optimized code if storing to a property cell that contains 'the hole'. Review URL: http://codereview.chromium.org/6306014 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6508 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
592089419d
commit
dc61921bbf
@ -1602,7 +1602,14 @@ LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
|
||||
return new LStoreGlobal(UseRegisterAtStart(instr->value()));
|
||||
if (instr->check_hole_value()) {
|
||||
LOperand* temp = TempRegister();
|
||||
LOperand* value = UseRegister(instr->value());
|
||||
return AssignEnvironment(new LStoreGlobal(value, temp));
|
||||
} else {
|
||||
LOperand* value = UseRegisterAtStart(instr->value());
|
||||
return new LStoreGlobal(value, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1254,10 +1254,11 @@ class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStoreGlobal: public LTemplateInstruction<0, 1, 0> {
|
||||
class LStoreGlobal: public LTemplateInstruction<0, 1, 1> {
|
||||
public:
|
||||
explicit LStoreGlobal(LOperand* value) {
|
||||
LStoreGlobal(LOperand* value, LOperand* temp) {
|
||||
inputs_[0] = value;
|
||||
temps_[0] = temp;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store-global")
|
||||
|
@ -2195,8 +2195,26 @@ void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
|
||||
void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
|
||||
Register value = ToRegister(instr->InputAt(0));
|
||||
__ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
|
||||
__ str(value, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
|
||||
Register scratch = scratch0();
|
||||
|
||||
// Load the cell.
|
||||
__ mov(scratch, Operand(Handle<Object>(instr->hydrogen()->cell())));
|
||||
|
||||
// If the cell we are storing to contains the hole it could have
|
||||
// been deleted from the property dictionary. In that case, we need
|
||||
// to update the property details in the property dictionary to mark
|
||||
// it as no longer deleted.
|
||||
if (instr->hydrogen()->check_hole_value()) {
|
||||
Register scratch2 = ToRegister(instr->TempAt(0));
|
||||
__ ldr(scratch2,
|
||||
FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset));
|
||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||
__ cmp(scratch2, ip);
|
||||
DeoptimizeIf(eq, instr->environment());
|
||||
}
|
||||
|
||||
// Store the value.
|
||||
__ str(value, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset));
|
||||
}
|
||||
|
||||
|
||||
|
@ -2649,9 +2649,18 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
|
||||
__ cmp(r3, Operand(Handle<Map>(object->map())));
|
||||
__ b(ne, &miss);
|
||||
|
||||
// Check that the value in the cell is not the hole. If it is, this
|
||||
// cell could have been deleted and reintroducing the global needs
|
||||
// to update the property details in the property dictionary of the
|
||||
// global object. We bail out to the runtime system to do that.
|
||||
__ mov(r4, Operand(Handle<JSGlobalPropertyCell>(cell)));
|
||||
__ LoadRoot(r5, Heap::kTheHoleValueRootIndex);
|
||||
__ ldr(r6, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset));
|
||||
__ cmp(r5, r6);
|
||||
__ b(eq, &miss);
|
||||
|
||||
// Store the value in the cell.
|
||||
__ mov(r2, Operand(Handle<JSGlobalPropertyCell>(cell)));
|
||||
__ str(r0, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset));
|
||||
__ str(r0, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset));
|
||||
|
||||
__ IncrementCounter(&Counters::named_store_global_inline, 1, r4, r3);
|
||||
__ Ret();
|
||||
|
@ -2575,12 +2575,17 @@ class HLoadGlobal: public HInstruction {
|
||||
|
||||
class HStoreGlobal: public HUnaryOperation {
|
||||
public:
|
||||
HStoreGlobal(HValue* value, Handle<JSGlobalPropertyCell> cell)
|
||||
: HUnaryOperation(value), cell_(cell) {
|
||||
HStoreGlobal(HValue* value,
|
||||
Handle<JSGlobalPropertyCell> cell,
|
||||
bool check_hole_value)
|
||||
: HUnaryOperation(value),
|
||||
cell_(cell),
|
||||
check_hole_value_(check_hole_value) {
|
||||
SetFlag(kChangesGlobalVars);
|
||||
}
|
||||
|
||||
Handle<JSGlobalPropertyCell> cell() const { return cell_; }
|
||||
bool check_hole_value() const { return check_hole_value_; }
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) const {
|
||||
return Representation::Tagged();
|
||||
@ -2591,6 +2596,7 @@ class HStoreGlobal: public HUnaryOperation {
|
||||
|
||||
private:
|
||||
Handle<JSGlobalPropertyCell> cell_;
|
||||
bool check_hole_value_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -3370,9 +3370,10 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
|
||||
LookupGlobalPropertyCell(var, &lookup, true);
|
||||
CHECK_BAILOUT;
|
||||
|
||||
bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
|
||||
Handle<GlobalObject> global(graph()->info()->global_object());
|
||||
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
|
||||
HInstruction* instr = new HStoreGlobal(value, cell);
|
||||
HInstruction* instr = new HStoreGlobal(value, cell, check_hole);
|
||||
instr->set_position(position);
|
||||
AddInstruction(instr);
|
||||
if (instr->HasSideEffects()) AddSimulate(ast_id);
|
||||
|
@ -1911,7 +1911,19 @@ void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
|
||||
|
||||
void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
|
||||
Register value = ToRegister(instr->InputAt(0));
|
||||
__ mov(Operand::Cell(instr->hydrogen()->cell()), value);
|
||||
Operand cell_operand = Operand::Cell(instr->hydrogen()->cell());
|
||||
|
||||
// If the cell we are storing to contains the hole it could have
|
||||
// been deleted from the property dictionary. In that case, we need
|
||||
// to update the property details in the property dictionary to mark
|
||||
// it as no longer deleted. We deoptimize in that case.
|
||||
if (instr->hydrogen()->check_hole_value()) {
|
||||
__ cmp(cell_operand, Factory::the_hole_value());
|
||||
DeoptimizeIf(equal, instr->environment());
|
||||
}
|
||||
|
||||
// Store the value.
|
||||
__ mov(cell_operand, value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1645,7 +1645,8 @@ LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
|
||||
return new LStoreGlobal(UseRegisterAtStart(instr->value()));
|
||||
LStoreGlobal* result = new LStoreGlobal(UseRegisterAtStart(instr->value()));
|
||||
return instr->check_hole_value() ? AssignEnvironment(result) : result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2582,14 +2582,24 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
|
||||
Immediate(Handle<Map>(object->map())));
|
||||
__ j(not_equal, &miss, not_taken);
|
||||
|
||||
// Store the value in the cell.
|
||||
|
||||
// Compute the cell operand to use.
|
||||
Operand cell_operand = Operand::Cell(Handle<JSGlobalPropertyCell>(cell));
|
||||
if (Serializer::enabled()) {
|
||||
__ mov(ecx, Immediate(Handle<JSGlobalPropertyCell>(cell)));
|
||||
__ mov(FieldOperand(ecx, JSGlobalPropertyCell::kValueOffset), eax);
|
||||
} else {
|
||||
__ mov(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)), eax);
|
||||
cell_operand = FieldOperand(ecx, JSGlobalPropertyCell::kValueOffset);
|
||||
}
|
||||
|
||||
// Check that the value in the cell is not the hole. If it is, this
|
||||
// cell could have been deleted and reintroducing the global needs
|
||||
// to update the property details in the property dictionary of the
|
||||
// global object. We bail out to the runtime system to do that.
|
||||
__ cmp(cell_operand, Factory::the_hole_value());
|
||||
__ j(equal, &miss);
|
||||
|
||||
// Store the value in the cell.
|
||||
__ mov(cell_operand, eax);
|
||||
|
||||
// Return the value (register eax).
|
||||
__ IncrementCounter(&Counters::named_store_global_inline, 1);
|
||||
__ ret(0);
|
||||
|
@ -57,8 +57,7 @@ Smi* PropertyDetails::AsSmi() {
|
||||
|
||||
|
||||
PropertyDetails PropertyDetails::AsDeleted() {
|
||||
PropertyDetails d(DONT_ENUM, NORMAL);
|
||||
Smi* smi = Smi::FromInt(AsSmi()->value() | DeletedField::encode(1));
|
||||
Smi* smi = Smi::FromInt(value_ | DeletedField::encode(1));
|
||||
return PropertyDetails(smi);
|
||||
}
|
||||
|
||||
|
@ -2438,9 +2438,17 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
|
||||
Handle<Map>(object->map()));
|
||||
__ j(not_equal, &miss);
|
||||
|
||||
// Check that the value in the cell is not the hole. If it is, this
|
||||
// cell could have been deleted and reintroducing the global needs
|
||||
// to update the property details in the property dictionary of the
|
||||
// global object. We bail out to the runtime system to do that.
|
||||
__ Move(rbx, Handle<JSGlobalPropertyCell>(cell));
|
||||
__ CompareRoot(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset),
|
||||
Heap::kTheHoleValueRootIndex);
|
||||
__ j(equal, &miss);
|
||||
|
||||
// Store the value in the cell.
|
||||
__ Move(rcx, Handle<JSGlobalPropertyCell>(cell));
|
||||
__ movq(FieldOperand(rcx, JSGlobalPropertyCell::kValueOffset), rax);
|
||||
__ movq(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), rax);
|
||||
|
||||
// Return the value (register rax).
|
||||
__ IncrementCounter(&Counters::named_store_global_inline, 1);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -32,6 +32,17 @@ assertFalse(delete tmp); // should be DONT_DELETE
|
||||
assertTrue("tmp" in this);
|
||||
function f() { return 1; }
|
||||
assertFalse(delete f); // should be DONT_DELETE
|
||||
assertEquals(1, f());
|
||||
assertEquals(1, f());
|
||||
|
||||
/* Perhaps related to bugs/11? */
|
||||
// Check that deleting and reintroducing global variables works.
|
||||
// Get into the IC case for storing to a deletable global property.
|
||||
function introduce_x() { x = 42; }
|
||||
for (var i = 0; i < 10; i++) introduce_x();
|
||||
// Check that the property has been introduced.
|
||||
assertTrue(this.hasOwnProperty('x'));
|
||||
// Check that deletion works.
|
||||
delete x;
|
||||
assertFalse(this.hasOwnProperty('x'));
|
||||
// Check that reintroduction works.
|
||||
introduce_x();
|
||||
assertTrue(this.hasOwnProperty('x'));
|
||||
|
Loading…
Reference in New Issue
Block a user