Index: src/codegen-ia32.cc |
=================================================================== |
--- src/codegen-ia32.cc (revision 448) |
+++ src/codegen-ia32.cc (working copy) |
@@ -58,20 +58,25 @@ |
class Reference BASE_EMBEDDED { |
public: |
- enum Type { ILLEGAL = -1, EMPTY = 0, NAMED = 1, KEYED = 2 }; |
+ // The values of the types is important, see size(). |
+ enum Type { ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 }; |
Reference(Ia32CodeGenerator* cgen, Expression* expression); |
~Reference(); |
- Expression* expression() const { return expression_; } |
- Type type() const { return type_; } |
+ Expression* expression() const { return expression_; } |
+ Type type() const { return type_; } |
void set_type(Type value) { |
ASSERT(type_ == ILLEGAL); |
type_ = value; |
} |
- int size() const { return type_; } |
- bool is_illegal() const { return type_ == ILLEGAL; } |
+ // The size of the reference or -1 if the reference is illegal. |
+ int size() const { return type_; } |
+ bool is_illegal() const { return type_ == ILLEGAL; } |
+ bool is_slot() const { return type_ == SLOT; } |
+ bool is_property() const { return type_ == NAMED || type_ == KEYED; } |
+ |
private: |
Ia32CodeGenerator* cgen_; |
Expression* expression_; |
@@ -653,18 +658,18 @@ |
ASSERT(scope->arguments_shadow() != NULL); |
Comment cmnt(masm_, "[ store arguments object"); |
{ Reference shadow_ref(this, scope->arguments_shadow()); |
+ ASSERT(shadow_ref.is_slot()); |
{ Reference arguments_ref(this, scope->arguments()); |
+ ASSERT(arguments_ref.is_slot()); |
// If the newly-allocated arguments object is already on the |
- // stack, we make use of the property that references representing |
- // variables take up no space on the expression stack (ie, it |
- // doesn't matter that the stored value is actually below the |
- // reference). |
- ASSERT(arguments_ref.size() == 0); |
- ASSERT(shadow_ref.size() == 0); |
- |
- // If the newly-allocated argument object is not already on the |
- // stack, we rely on the property that loading a |
- // (zero-sized) reference will not clobber the ecx register. |
+ // stack, we make use of the convenient property that references |
+ // representing slots take up no space on the expression stack |
+ // (ie, it doesn't matter that the stored value is actually below |
+ // the reference). |
+ // |
+ // If the newly-allocated argument object is not already on |
+ // the stack, we rely on the property that loading a |
+ // zero-sized reference will not clobber the ecx register. |
if (!arguments_object_saved) { |
__ push(ecx); |
} |
@@ -845,33 +850,37 @@ |
Variable* var = e->AsVariableProxy()->AsVariable(); |
if (property != NULL) { |
+ // The expression is either a property or a variable proxy that rewrites |
+ // to a property. |
Load(property->obj()); |
- // Used a named reference if the key is a literal symbol. |
- // We don't use a named reference if they key is a string that can be |
- // legally parsed as an integer. This is because, otherwise we don't |
- // get into the slow case code that handles [] on String objects. |
+ // We use a named reference if the key is a literal symbol, unless it is |
+ // a string that can be legally parsed as an integer. This is because |
+ // otherwise we will not get into the slow case code that handles [] on |
+ // String objects. |
Literal* literal = property->key()->AsLiteral(); |
uint32_t dummy; |
- if (literal != NULL && literal->handle()->IsSymbol() && |
- !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) { |
+ if (literal != NULL && |
+ literal->handle()->IsSymbol() && |
+ !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) { |
ref->set_type(Reference::NAMED); |
} else { |
Load(property->key()); |
ref->set_type(Reference::KEYED); |
} |
} else if (var != NULL) { |
+ // The expression is a variable proxy that does not rewrite to a |
+ // property. Global variables are treated as named property references. |
if (var->is_global()) { |
- // global variable |
LoadGlobal(); |
ref->set_type(Reference::NAMED); |
} else { |
- // local variable |
- ref->set_type(Reference::EMPTY); |
+ ASSERT(var->slot() != NULL); |
+ ref->set_type(Reference::SLOT); |
} |
} else { |
+ // Anything else is a runtime error. |
Load(e); |
__ CallRuntime(Runtime::kThrowReferenceError, 1); |
- __ push(eax); |
} |
} |
@@ -959,14 +968,13 @@ |
void Ia32CodeGenerator::GetReferenceProperty(Expression* key) { |
ASSERT(!ref()->is_illegal()); |
- Reference::Type type = ref()->type(); |
// TODO(1241834): Make sure that this it is safe to ignore the distinction |
// between access types LOAD and LOAD_TYPEOF_EXPR. If there is a chance |
// that reference errors can be thrown below, we must distinguish between |
// the two kinds of loads (typeof expression loads must not throw a |
// reference error). |
- if (type == Reference::NAMED) { |
+ if (ref()->type() == Reference::NAMED) { |
// Compute the name of the property. |
Literal* literal = key->AsLiteral(); |
Handle<String> name(String::cast(*literal->handle())); |
@@ -983,7 +991,7 @@ |
} |
} else { |
// Access keyed property. |
- ASSERT(type == Reference::KEYED); |
+ ASSERT(ref()->type() == Reference::KEYED); |
// Call IC code. |
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
@@ -1753,9 +1761,13 @@ |
if (val != NULL) { |
// Set initial value. |
Reference target(this, node->proxy()); |
+ ASSERT(target.is_slot()); |
Load(val); |
SetValue(&target); |
- // Get rid of the assigned value (declarations are statements). |
+ // Get rid of the assigned value (declarations are statements). It's |
+ // safe to pop the value lying on top of the reference before unloading |
+ // the reference itself (which preserves the top of stack) because we |
+ // know that it is a zero-sized reference. |
__ pop(eax); // Pop(no_reg); |
} |
} |
@@ -2269,19 +2281,29 @@ |
// Store the entry in the 'each' expression and take another spin in the loop. |
// edx: i'th entry of the enum cache (or string there of) |
- __ push(Operand(ebx)); |
+ __ push(ebx); |
{ Reference each(this, node->each()); |
if (!each.is_illegal()) { |
if (each.size() > 0) { |
__ push(Operand(esp, kPointerSize * each.size())); |
} |
+ // If the reference was to a slot we rely on the convenient property |
+ // that it doesn't matter whether a value (eg, ebx pushed above) is |
+ // right on top of or right underneath a zero-sized reference. |
SetValue(&each); |
if (each.size() > 0) { |
+ // It's safe to pop the value lying on top of the reference before |
+ // unloading the reference itself (which preserves the top of stack, |
+ // ie, now the topmost value of the non-zero sized reference), since |
+ // we will discard the top of stack after unloading the reference |
+ // anyway. |
__ pop(eax); |
} |
} |
} |
- __ pop(eax); // pop the i'th entry pushed above |
+ // Discard the i'th entry pushed above or else the remainder of the |
+ // reference, whichever is currently on top of the stack. |
+ __ pop(eax); |
CheckStack(); // TODO(1222600): ignore if body contains calls. |
__ jmp(&loop); |
@@ -2308,10 +2330,11 @@ |
// Store the caught exception in the catch variable. |
{ Reference ref(this, node->catch_var()); |
- // Load the exception to the top of the stack. |
- __ push(Operand(esp, ref.size() * kPointerSize)); |
+ ASSERT(ref.is_slot()); |
+ // Load the exception to the top of the stack. Here we make use of the |
+ // convenient property that it doesn't matter whether a value is |
+ // immediately on top of or underneath a zero-sized reference. |
SetValue(&ref); |
- __ pop(eax); // pop the pushed exception |
} |
// Remove the exception from the stack. |
@@ -3051,6 +3074,7 @@ |
GetValue(&ref); |
// Pass receiver to called function. |
+ // The reference's size is non-negative. |
__ push(Operand(esp, ref.size() * kPointerSize)); |
// Call the function. |