Revision: 3587
Author: [email protected]
Date: Tue Jan 12 09:22:57 2010
Log: Fix a problem with const initialization in the top-level code generator.

When initializing the special local variable containing the reference to the enclosing function in named functions we now (correctly) emit an INIT_CONST instead of INIT_VAR,
and we correctly bail out in the top-level code generator.

Also part of this change is adding missing statement position information
for some statements in the top-level code generator.

Review URL: http://codereview.chromium.org/536029
http://code.google.com/p/v8/source/detail?r=3587

Modified:
 /branches/bleeding_edge/src/arm/codegen-arm.cc
 /branches/bleeding_edge/src/arm/codegen-arm.h
 /branches/bleeding_edge/src/compiler.cc
 /branches/bleeding_edge/src/fast-codegen.cc
 /branches/bleeding_edge/src/ia32/codegen-ia32.cc
 /branches/bleeding_edge/src/parser.cc
 /branches/bleeding_edge/src/x64/codegen-x64.cc

=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Jan 12 03:54:19 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Jan 12 09:22:57 2010
@@ -262,6 +262,13 @@
       }
       frame_->Drop();  // Value is no longer needed.
     }
+
+    // Initialize ThisFunction reference if present.
+    if (scope_->is_function_scope() && scope_->function() != NULL) {
+      __ mov(ip, Operand(Factory::the_hole_value()));
+      frame_->EmitPush(ip);
+      StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT);
+    }

     // Generate code to 'execute' declarations and initialize functions
     // (source elements). In case of an illegal redeclaration we need to
@@ -2444,6 +2451,87 @@
     }
   }
 }
+
+
+void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
+  ASSERT(slot != NULL);
+  if (slot->type() == Slot::LOOKUP) {
+    ASSERT(slot->var()->is_dynamic());
+
+    // For now, just do a runtime call.
+    frame_->EmitPush(cp);
+    __ mov(r0, Operand(slot->var()->name()));
+    frame_->EmitPush(r0);
+
+    if (init_state == CONST_INIT) {
+      // Same as the case for a normal store, but ignores attribute
+      // (e.g. READ_ONLY) of context slot so that we can initialize
+      // const properties (introduced via eval("const foo = (some
+      // expr);")). Also, uses the current function context instead of
+      // the top context.
+      //
+      // Note that we must declare the foo upon entry of eval(), via a
+      // context slot declaration, but we cannot initialize it at the
+      // same time, because the const declaration may be at the end of
+      // the eval code (sigh...) and the const variable may have been
+      // used before (where its value is 'undefined'). Thus, we can only
+      // do the initialization when we actually encounter the expression
+      // and when the expression operands are defined and valid, and
+      // thus we need the split into 2 operations: declaration of the
+      // context slot followed by initialization.
+      frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+    } else {
+      frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
+    }
+    // Storing a variable must keep the (new) value on the expression
+    // stack. This is necessary for compiling assignment expressions.
+    frame_->EmitPush(r0);
+
+  } else {
+    ASSERT(!slot->var()->is_dynamic());
+
+    JumpTarget exit;
+    if (init_state == CONST_INIT) {
+      ASSERT(slot->var()->mode() == Variable::CONST);
+      // Only the first const initialization must be executed (the slot
+      // still contains 'the hole' value). When the assignment is
+      // executed, the code is identical to a normal store (see below).
+      Comment cmnt(masm_, "[ Init const");
+      __ ldr(r2, SlotOperand(slot, r2));
+      __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+      __ cmp(r2, ip);
+      exit.Branch(ne);
+    }
+
+    // We must execute the store.  Storing a variable must keep the
+    // (new) value on the stack. This is necessary for compiling
+    // assignment expressions.
+    //
+    // Note: We will reach here even with slot->var()->mode() ==
+    // Variable::CONST because of const declarations which will
+    // initialize consts to 'the hole' value and by doing so, end up
+    // calling this code.  r2 may be loaded with context; used below in
+    // RecordWrite.
+    frame_->EmitPop(r0);
+    __ str(r0, SlotOperand(slot, r2));
+    frame_->EmitPush(r0);
+    if (slot->type() == Slot::CONTEXT) {
+      // Skip write barrier if the written value is a smi.
+      __ tst(r0, Operand(kSmiTagMask));
+      exit.Branch(eq);
+      // r2 is loaded with context when calling SlotOperand above.
+      int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+      __ mov(r3, Operand(offset));
+      __ RecordWrite(r2, r3, r1);
+    }
+    // If we definitely did not jump over the assignment, we do not need
+    // to bind the exit label.  Doing so can defeat peephole
+    // optimization.
+    if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
+      exit.Bind();
+    }
+  }
+}


 void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
@@ -4262,83 +4350,7 @@
     case SLOT: {
       Comment cmnt(masm, "[ Store to Slot");
       Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
-      ASSERT(slot != NULL);
-      if (slot->type() == Slot::LOOKUP) {
-        ASSERT(slot->var()->is_dynamic());
-
-        // For now, just do a runtime call.
-        frame->EmitPush(cp);
-        __ mov(r0, Operand(slot->var()->name()));
-        frame->EmitPush(r0);
-
-        if (init_state == CONST_INIT) {
-          // Same as the case for a normal store, but ignores attribute
-          // (e.g. READ_ONLY) of context slot so that we can initialize
-          // const properties (introduced via eval("const foo = (some
-          // expr);")). Also, uses the current function context instead of
-          // the top context.
-          //
-          // Note that we must declare the foo upon entry of eval(), via a
-          // context slot declaration, but we cannot initialize it at the
-          // same time, because the const declaration may be at the end of
-          // the eval code (sigh...) and the const variable may have been
- // used before (where its value is 'undefined'). Thus, we can only - // do the initialization when we actually encounter the expression
-          // and when the expression operands are defined and valid, and
-          // thus we need the split into 2 operations: declaration of the
-          // context slot followed by initialization.
-          frame->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
-        } else {
-          frame->CallRuntime(Runtime::kStoreContextSlot, 3);
-        }
-        // Storing a variable must keep the (new) value on the expression
-        // stack. This is necessary for compiling assignment expressions.
-        frame->EmitPush(r0);
-
-      } else {
-        ASSERT(!slot->var()->is_dynamic());
-
-        JumpTarget exit;
-        if (init_state == CONST_INIT) {
-          ASSERT(slot->var()->mode() == Variable::CONST);
-          // Only the first const initialization must be executed (the slot
-          // still contains 'the hole' value). When the assignment is
-          // executed, the code is identical to a normal store (see below).
-          Comment cmnt(masm, "[ Init const");
-          __ ldr(r2, cgen_->SlotOperand(slot, r2));
-          __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
-          __ cmp(r2, ip);
-          exit.Branch(ne);
-        }
-
-        // We must execute the store.  Storing a variable must keep the
-        // (new) value on the stack. This is necessary for compiling
-        // assignment expressions.
-        //
-        // Note: We will reach here even with slot->var()->mode() ==
-        // Variable::CONST because of const declarations which will
-        // initialize consts to 'the hole' value and by doing so, end up
-        // calling this code.  r2 may be loaded with context; used below in
-        // RecordWrite.
-        frame->EmitPop(r0);
-        __ str(r0, cgen_->SlotOperand(slot, r2));
-        frame->EmitPush(r0);
-        if (slot->type() == Slot::CONTEXT) {
-          // Skip write barrier if the written value is a smi.
-          __ tst(r0, Operand(kSmiTagMask));
-          exit.Branch(eq);
-          // r2 is loaded with context when calling SlotOperand above.
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
-          __ mov(r3, Operand(offset));
-          __ RecordWrite(r2, r3, r1);
-        }
- // If we definitely did not jump over the assignment, we do not need
-        // to bind the exit label.  Doing so can defeat peephole
-        // optimization.
-        if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
-          exit.Bind();
-        }
-      }
+      cgen_->StoreToSlot(slot, init_state);
       break;
     }

=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.h       Fri Jan  8 03:58:15 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.h       Tue Jan 12 09:22:57 2010
@@ -272,6 +272,9 @@

   // Read a value from a slot and leave it on top of the expression stack.
   void LoadFromSlot(Slot* slot, TypeofState typeof_state);
+  // Store the value on top of the stack to a slot.
+  void StoreToSlot(Slot* slot, InitState init_state);
+
   void LoadFromGlobalSlotCheckExtensions(Slot* slot,
                                          TypeofState typeof_state,
                                          Register tmp,
=======================================
--- /branches/bleeding_edge/src/compiler.cc     Tue Jan 12 00:48:26 2010
+++ /branches/bleeding_edge/src/compiler.cc     Tue Jan 12 09:22:57 2010
@@ -888,6 +888,9 @@
   Property* prop = expr->target()->AsProperty();
   ASSERT(var == NULL || prop == NULL);
   if (var != NULL) {
+    if (var->mode() == Variable::CONST) {
+      BAILOUT("Assignment to const");
+    }
     // All global variables are supported.
     if (!var->is_global()) {
       ASSERT(var->slot() != NULL);
=======================================
--- /branches/bleeding_edge/src/fast-codegen.cc Tue Jan 12 00:48:26 2010
+++ /branches/bleeding_edge/src/fast-codegen.cc Tue Jan 12 09:22:57 2010
@@ -288,6 +288,7 @@

 void FastCodeGenerator::VisitIfStatement(IfStatement* stmt) {
   Comment cmnt(masm_, "[ IfStatement");
+  SetStatementPosition(stmt);
// Expressions cannot recursively enter statements, there are no labels in
   // the state.
   ASSERT_EQ(NULL, true_label_);
@@ -315,6 +316,7 @@

 void FastCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
   Comment cmnt(masm_,  "[ ContinueStatement");
+  SetStatementPosition(stmt);
   NestedStatement* current = nesting_stack_;
   int stack_depth = 0;
   while (!current->IsContinueTarget(stmt->target())) {
@@ -330,6 +332,7 @@

 void FastCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
   Comment cmnt(masm_,  "[ BreakStatement");
+  SetStatementPosition(stmt);
   NestedStatement* current = nesting_stack_;
   int stack_depth = 0;
   while (!current->IsBreakTarget(stmt->target())) {
@@ -345,6 +348,7 @@

 void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
   Comment cmnt(masm_, "[ ReturnStatement");
+  SetStatementPosition(stmt);
   Expression* expr = stmt->expression();
   // Complete the statement based on the type of the subexpression.
   if (expr->AsLiteral() != NULL) {
@@ -406,6 +410,7 @@

 void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
   Comment cmnt(masm_, "[ DoWhileStatement");
+  SetStatementPosition(stmt);
   Label body, stack_limit_hit, stack_check_success;

   Iteration loop_statement(this, stmt);
@@ -443,6 +448,7 @@

 void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
   Comment cmnt(masm_, "[ WhileStatement");
+  SetStatementPosition(stmt);
   Label body, stack_limit_hit, stack_check_success;

   Iteration loop_statement(this, stmt);
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Jan 12 00:48:26 2010 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Jan 12 09:22:57 2010
@@ -250,6 +250,12 @@
     if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
       StoreArgumentsObject(true);
     }
+
+    // Initialize ThisFunction reference if present.
+    if (scope_->is_function_scope() && scope_->function() != NULL) {
+      frame_->Push(Factory::the_hole_value());
+      StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT);
+    }

     // Generate code to 'execute' declarations and initialize functions
     // (source elements). In case of an illegal redeclaration we need to
=======================================
--- /branches/bleeding_edge/src/parser.cc       Mon Jan 11 04:13:24 2010
+++ /branches/bleeding_edge/src/parser.cc       Tue Jan 12 09:22:57 2010
@@ -3598,7 +3598,7 @@
           top_scope_->NewUnresolved(function_name, inside_with());
       fproxy->BindTo(fvar);
       body.Add(new ExpressionStatement(
-                   new Assignment(Token::INIT_VAR, fproxy,
+                   new Assignment(Token::INIT_CONST, fproxy,
                                   NEW(ThisFunction()),
                                   RelocInfo::kNoPosition)));
     }
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc      Tue Jan 12 00:48:26 2010
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc      Tue Jan 12 09:22:57 2010
@@ -392,6 +392,12 @@
     if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
       StoreArgumentsObject(true);
     }
+
+    // Initialize ThisFunction reference if present.
+    if (scope_->is_function_scope() && scope_->function() != NULL) {
+      frame_->Push(Factory::the_hole_value());
+      StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT);
+    }

     // Generate code to 'execute' declarations and initialize functions
     // (source elements). In case of an illegal redeclaration we need to
-- 
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to