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:
ager@chromium.org 2011-01-27 08:35:39 +00:00
parent 592089419d
commit dc61921bbf
12 changed files with 106 additions and 23 deletions

View File

@ -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);
}
}

View File

@ -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")

View File

@ -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));
}

View File

@ -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();

View File

@ -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_;
};

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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'));