Fix exception for assignment to uninitialised const
authorrossberg <rossberg@chromium.org>
Thu, 5 Mar 2015 12:43:55 +0000 (04:43 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 5 Mar 2015 12:44:01 +0000 (12:44 +0000)
R=dslomov@chromium.org, mstarzinger@chromium.org
BUG=

Review URL: https://codereview.chromium.org/976053002

Cr-Commit-Position: refs/heads/master@{#27014}

src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/compiler/ast-graph-builder.cc
src/full-codegen.h
src/ia32/full-codegen-ia32.cc
src/ic/ic.cc
src/messages.js
src/runtime/runtime-scopes.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/block-const-assign.js

index 96766b42e83dd466735e5e0db4fe7fa10acc6fe8..78161fb019cc5d3c98b08e43952e9254466f7514 100644 (file)
@@ -2726,25 +2726,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
     __ ldr(StoreDescriptor::ReceiverRegister(), GlobalObjectOperand());
     CallStoreIC();
 
-  } else if (op == Token::INIT_CONST_LEGACY) {
-    // Const initializers need a write barrier.
-    DCHECK(!var->IsParameter());  // No const parameters.
-    if (var->IsLookupSlot()) {
-      __ push(r0);
-      __ mov(r0, Operand(var->name()));
-      __ Push(cp, r0);  // Context and name.
-      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
-    } else {
-      DCHECK(var->IsStackAllocated() || var->IsContextSlot());
-      Label skip;
-      MemOperand location = VarOperand(var, r1);
-      __ ldr(r2, location);
-      __ CompareRoot(r2, Heap::kTheHoleValueRootIndex);
-      __ b(ne, &skip);
-      EmitStoreToStackLocalOrContextSlot(var, location);
-      __ bind(&skip);
-    }
-
   } else if (var->mode() == LET && op != Token::INIT_LET) {
     // Non-initializing assignment to let variable needs a write barrier.
     DCHECK(!var->IsLookupSlot());
@@ -2761,6 +2742,21 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
     __ bind(&assign);
     EmitStoreToStackLocalOrContextSlot(var, location);
 
+  } else if (var->mode() == CONST && op != Token::INIT_CONST) {
+    // Assignment to const variable needs a write barrier.
+    DCHECK(!var->IsLookupSlot());
+    DCHECK(var->IsStackAllocated() || var->IsContextSlot());
+    Label const_error;
+    MemOperand location = VarOperand(var, r1);
+    __ ldr(r3, location);
+    __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
+    __ b(ne, &const_error);
+    __ mov(r3, Operand(var->name()));
+    __ push(r3);
+    __ CallRuntime(Runtime::kThrowReferenceError, 1);
+    __ bind(&const_error);
+    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
   } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
     if (var->IsLookupSlot()) {
       // Assignment to var.
@@ -2782,8 +2778,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
       }
       EmitStoreToStackLocalOrContextSlot(var, location);
     }
-  } else if (IsSignallingAssignmentToConst(var, op, language_mode())) {
-    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
+  } else if (op == Token::INIT_CONST_LEGACY) {
+    // Const initializers need a write barrier.
+    DCHECK(var->mode() == CONST_LEGACY);
+    DCHECK(!var->IsParameter());  // No const parameters.
+    if (var->IsLookupSlot()) {
+      __ push(r0);
+      __ mov(r0, Operand(var->name()));
+      __ Push(cp, r0);  // Context and name.
+      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
+    } else {
+      DCHECK(var->IsStackAllocated() || var->IsContextSlot());
+      Label skip;
+      MemOperand location = VarOperand(var, r1);
+      __ ldr(r2, location);
+      __ CompareRoot(r2, Heap::kTheHoleValueRootIndex);
+      __ b(ne, &skip);
+      EmitStoreToStackLocalOrContextSlot(var, location);
+      __ bind(&skip);
+    }
+
+  } else {
+    DCHECK(var->mode() == CONST_LEGACY && op != Token::INIT_CONST_LEGACY);
+    if (is_strict(language_mode())) {
+      __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+    }
+    // Silently ignore store in sloppy mode.
   }
 }
 
index 572b33063bd29775f34a809e2c35c3ff2c9d9a2c..95f7fae00761132a8e1394864c23cd624f2ab949 100644 (file)
@@ -2415,23 +2415,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     __ Ldr(StoreDescriptor::ReceiverRegister(), GlobalObjectMemOperand());
     CallStoreIC();
 
-  } else if (op == Token::INIT_CONST_LEGACY) {
-    // Const initializers need a write barrier.
-    DCHECK(!var->IsParameter());  // No const parameters.
-    if (var->IsLookupSlot()) {
-      __ Mov(x1, Operand(var->name()));
-      __ Push(x0, cp, x1);
-      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
-    } else {
-      DCHECK(var->IsStackLocal() || var->IsContextSlot());
-      Label skip;
-      MemOperand location = VarOperand(var, x1);
-      __ Ldr(x10, location);
-      __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &skip);
-      EmitStoreToStackLocalOrContextSlot(var, location);
-      __ Bind(&skip);
-    }
-
   } else if (var->mode() == LET && op != Token::INIT_LET) {
     // Non-initializing assignment to let variable needs a write barrier.
     DCHECK(!var->IsLookupSlot());
@@ -2447,6 +2430,20 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     __ Bind(&assign);
     EmitStoreToStackLocalOrContextSlot(var, location);
 
+  } else if (var->mode() == CONST && op != Token::INIT_CONST) {
+    // Assignment to const variable needs a write barrier.
+    DCHECK(!var->IsLookupSlot());
+    DCHECK(var->IsStackAllocated() || var->IsContextSlot());
+    Label const_error;
+    MemOperand location = VarOperand(var, x1);
+    __ Ldr(x10, location);
+    __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &const_error);
+    __ Mov(x10, Operand(var->name()));
+    __ Push(x10);
+    __ CallRuntime(Runtime::kThrowReferenceError, 1);
+    __ Bind(&const_error);
+    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
   } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
     if (var->IsLookupSlot()) {
       // Assignment to var.
@@ -2470,8 +2467,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
       }
       EmitStoreToStackLocalOrContextSlot(var, location);
     }
-  } else if (IsSignallingAssignmentToConst(var, op, language_mode())) {
-    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
+  } else if (op == Token::INIT_CONST_LEGACY) {
+    // Const initializers need a write barrier.
+    DCHECK(var->mode() == CONST_LEGACY);
+    DCHECK(!var->IsParameter());  // No const parameters.
+    if (var->IsLookupSlot()) {
+      __ Mov(x1, Operand(var->name()));
+      __ Push(x0, cp, x1);
+      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
+    } else {
+      DCHECK(var->IsStackLocal() || var->IsContextSlot());
+      Label skip;
+      MemOperand location = VarOperand(var, x1);
+      __ Ldr(x10, location);
+      __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &skip);
+      EmitStoreToStackLocalOrContextSlot(var, location);
+      __ Bind(&skip);
+    }
+
+  } else {
+    DCHECK(var->mode() == CONST_LEGACY && op != Token::INIT_CONST_LEGACY);
+    if (is_strict(language_mode())) {
+      __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+    }
+    // Silently ignore store in sloppy mode.
   }
 }
 
index f4b8676730d0b7d80b1101bdf201551bf64ba5cd..d744f7c7d1f9344de9c8e482c32e8c826199e8f0 100644 (file)
@@ -2701,7 +2701,7 @@ Node* AstGraphBuilder::BuildVariableAssignment(
           value = BuildHoleCheckSilent(current, value, current);
         }
       } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) {
-        // Non-initializing assignments to legacy const is
+        // Non-initializing assignment to legacy const is
         // - exception in strict mode.
         // - ignored in sloppy mode.
         if (is_strict(language_mode())) {
@@ -2720,7 +2720,13 @@ Node* AstGraphBuilder::BuildVariableAssignment(
           value = BuildHoleCheckThrow(current, variable, value, bailout_id);
         }
       } else if (mode == CONST && op != Token::INIT_CONST) {
-        // Non-initializing assignments to const is exception in all modes.
+        // Assignment to const is exception in all modes.
+        Node* current = environment()->Lookup(variable);
+        if (current->op() == the_hole->op()) {
+          return BuildThrowReferenceError(variable, bailout_id);
+        } else if (value->opcode() == IrOpcode::kPhi) {
+          BuildHoleCheckThrow(current, variable, value, bailout_id);
+        }
         return BuildThrowConstAssignError(bailout_id);
       }
       environment()->Bind(variable, value);
@@ -2735,7 +2741,7 @@ Node* AstGraphBuilder::BuildVariableAssignment(
         Node* current = NewNode(op, current_context());
         value = BuildHoleCheckSilent(current, value, current);
       } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) {
-        // Non-initializing assignments to legacy const is
+        // Non-initializing assignment to legacy const is
         // - exception in strict mode.
         // - ignored in sloppy mode.
         if (is_strict(language_mode())) {
@@ -2749,7 +2755,11 @@ Node* AstGraphBuilder::BuildVariableAssignment(
         Node* current = NewNode(op, current_context());
         value = BuildHoleCheckThrow(current, variable, value, bailout_id);
       } else if (mode == CONST && op != Token::INIT_CONST) {
-        // Non-initializing assignments to const is exception in all modes.
+        // Assignment to const is exception in all modes.
+        const Operator* op =
+            javascript()->LoadContext(depth, variable->index(), false);
+        Node* current = NewNode(op, current_context());
+        BuildHoleCheckThrow(current, variable, value, bailout_id);
         return BuildThrowConstAssignError(bailout_id);
       }
       const Operator* op = javascript()->StoreContext(depth, variable->index());
index 84eb02c94c95b86f00200b1f9d3ef2bd16466c60..42f2b2c9233561c7054551b73205b37f97c12eba 100644 (file)
@@ -589,19 +589,6 @@ class FullCodeGenerator: public AstVisitor {
   // is expected in the accumulator.
   void EmitAssignment(Expression* expr);
 
-  // Shall an error be thrown if assignment with 'op' operation is perfomed
-  // on this variable in given language mode?
-  static bool IsSignallingAssignmentToConst(Variable* var, Token::Value op,
-                                            LanguageMode language_mode) {
-    if (var->mode() == CONST) return op != Token::INIT_CONST;
-
-    if (var->mode() == CONST_LEGACY) {
-      return is_strict(language_mode) && op != Token::INIT_CONST_LEGACY;
-    }
-
-    return false;
-  }
-
   // Complete a variable assignment.  The right-hand-side value is expected
   // in the accumulator.
   void EmitVariableAssignment(Variable* var,
index d74e9ef87b21d0ecf5f7da4bad2c25e199f960e6..8ef6ff3e46ba6e4fd48c170a313663219da78c25 100644 (file)
@@ -2632,25 +2632,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     __ mov(StoreDescriptor::ReceiverRegister(), GlobalObjectOperand());
     CallStoreIC();
 
-  } else if (op == Token::INIT_CONST_LEGACY) {
-    // Const initializers need a write barrier.
-    DCHECK(!var->IsParameter());  // No const parameters.
-    if (var->IsLookupSlot()) {
-      __ push(eax);
-      __ push(esi);
-      __ push(Immediate(var->name()));
-      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
-    } else {
-      DCHECK(var->IsStackLocal() || var->IsContextSlot());
-      Label skip;
-      MemOperand location = VarOperand(var, ecx);
-      __ mov(edx, location);
-      __ cmp(edx, isolate()->factory()->the_hole_value());
-      __ j(not_equal, &skip, Label::kNear);
-      EmitStoreToStackLocalOrContextSlot(var, location);
-      __ bind(&skip);
-    }
-
   } else if (var->mode() == LET && op != Token::INIT_LET) {
     // Non-initializing assignment to let variable needs a write barrier.
     DCHECK(!var->IsLookupSlot());
@@ -2664,6 +2645,21 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     __ CallRuntime(Runtime::kThrowReferenceError, 1);
     __ bind(&assign);
     EmitStoreToStackLocalOrContextSlot(var, location);
+
+  } else if (var->mode() == CONST && op != Token::INIT_CONST) {
+    // Assignment to const variable needs a write barrier.
+    DCHECK(!var->IsLookupSlot());
+    DCHECK(var->IsStackAllocated() || var->IsContextSlot());
+    Label const_error;
+    MemOperand location = VarOperand(var, ecx);
+    __ mov(edx, location);
+    __ cmp(edx, isolate()->factory()->the_hole_value());
+    __ j(not_equal, &const_error, Label::kNear);
+    __ push(Immediate(var->name()));
+    __ CallRuntime(Runtime::kThrowReferenceError, 1);
+    __ bind(&const_error);
+    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
   } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
     if (var->IsLookupSlot()) {
       // Assignment to var.
@@ -2685,8 +2681,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
       }
       EmitStoreToStackLocalOrContextSlot(var, location);
     }
-  } else if (IsSignallingAssignmentToConst(var, op, language_mode())) {
-    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
+  } else if (op == Token::INIT_CONST_LEGACY) {
+    // Const initializers need a write barrier.
+    DCHECK(var->mode() == CONST_LEGACY);
+    DCHECK(!var->IsParameter());  // No const parameters.
+    if (var->IsLookupSlot()) {
+      __ push(eax);
+      __ push(esi);
+      __ push(Immediate(var->name()));
+      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
+    } else {
+      DCHECK(var->IsStackLocal() || var->IsContextSlot());
+      Label skip;
+      MemOperand location = VarOperand(var, ecx);
+      __ mov(edx, location);
+      __ cmp(edx, isolate()->factory()->the_hole_value());
+      __ j(not_equal, &skip, Label::kNear);
+      EmitStoreToStackLocalOrContextSlot(var, location);
+      __ bind(&skip);
+    }
+
+  } else {
+    DCHECK(var->mode() == CONST_LEGACY && op != Token::INIT_CONST_LEGACY);
+    if (is_strict(language_mode())) {
+      __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+    }
+    // Silently ignore store in sloppy mode.
   }
 }
 
index 651eb2cac61ac24f93d232e1ecded9c892383bf6..f896d16cabf2ca055a8e9d63bd60e538817fc830 100644 (file)
@@ -1551,7 +1551,7 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
       Handle<Context> script_context = ScriptContextTable::GetContext(
           script_contexts, lookup_result.context_index);
       if (lookup_result.mode == CONST) {
-        return TypeError("harmony_const_assign", object, name);
+        return TypeError("const_assign", object, name);
       }
 
       if (FLAG_use_ic &&
index 37182414ec11802a939030e7cfe75f1a5e1d8df2..ba739987897417c7917901659d68f892cc8fe0ac 100644 (file)
@@ -174,7 +174,7 @@ var kMessages = {
   generator_poison_pill:         ["'caller' and 'arguments' properties may not be accessed on generator functions."],
   cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
   redef_external_array_element:  ["Cannot redefine a property of an object with external array elements"],
-  harmony_const_assign:          ["Assignment to constant variable."],
+  const_assign:                  ["Assignment to constant variable."],
   symbol_to_string:              ["Cannot convert a Symbol value to a string"],
   symbol_to_primitive:           ["Cannot convert a Symbol wrapper object to a primitive value"],
   symbol_to_number:              ["Cannot convert a Symbol value to a number"],
index 10160a116e98ce370ebd7eb7c8053d6fb03c8012..2a2ab16d45ef357e928d49387042a8214caed154 100644 (file)
@@ -26,7 +26,7 @@ RUNTIME_FUNCTION(Runtime_ThrowConstAssignError) {
   HandleScope scope(isolate);
   THROW_NEW_ERROR_RETURN_FAILURE(
       isolate,
-      NewTypeError("harmony_const_assign", HandleVector<Object>(NULL, 0)));
+      NewTypeError("const_assign", HandleVector<Object>(NULL, 0)));
 }
 
 
index 9bc0766e93d6b748b695311f6a191ab590f3797f..1da93d6f948714c91fb55d54dee166973c3b31a0 100644 (file)
@@ -2635,25 +2635,6 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     __ movp(StoreDescriptor::ReceiverRegister(), GlobalObjectOperand());
     CallStoreIC();
 
-  } else if (op == Token::INIT_CONST_LEGACY) {
-    // Const initializers need a write barrier.
-    DCHECK(!var->IsParameter());  // No const parameters.
-    if (var->IsLookupSlot()) {
-      __ Push(rax);
-      __ Push(rsi);
-      __ Push(var->name());
-      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
-    } else {
-      DCHECK(var->IsStackLocal() || var->IsContextSlot());
-      Label skip;
-      MemOperand location = VarOperand(var, rcx);
-      __ movp(rdx, location);
-      __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
-      __ j(not_equal, &skip);
-      EmitStoreToStackLocalOrContextSlot(var, location);
-      __ bind(&skip);
-    }
-
   } else if (var->mode() == LET && op != Token::INIT_LET) {
     // Non-initializing assignment to let variable needs a write barrier.
     DCHECK(!var->IsLookupSlot());
@@ -2668,6 +2649,20 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     __ bind(&assign);
     EmitStoreToStackLocalOrContextSlot(var, location);
 
+  } else if (var->mode() == CONST && op != Token::INIT_CONST) {
+    // Assignment to const variable needs a write barrier.
+    DCHECK(!var->IsLookupSlot());
+    DCHECK(var->IsStackAllocated() || var->IsContextSlot());
+    Label const_error;
+    MemOperand location = VarOperand(var, rcx);
+    __ movp(rdx, location);
+    __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+    __ j(not_equal, &const_error, Label::kNear);
+    __ Push(var->name());
+    __ CallRuntime(Runtime::kThrowReferenceError, 1);
+    __ bind(&const_error);
+    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
   } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
     if (var->IsLookupSlot()) {
       // Assignment to var.
@@ -2689,8 +2684,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
       }
       EmitStoreToStackLocalOrContextSlot(var, location);
     }
-  } else if (IsSignallingAssignmentToConst(var, op, language_mode())) {
-    __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+
+  } else if (op == Token::INIT_CONST_LEGACY) {
+    // Const initializers need a write barrier.
+    DCHECK(var->mode() == CONST_LEGACY);
+    DCHECK(!var->IsParameter());  // No const parameters.
+    if (var->IsLookupSlot()) {
+      __ Push(rax);
+      __ Push(rsi);
+      __ Push(var->name());
+      __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
+    } else {
+      DCHECK(var->IsStackLocal() || var->IsContextSlot());
+      Label skip;
+      MemOperand location = VarOperand(var, rcx);
+      __ movp(rdx, location);
+      __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+      __ j(not_equal, &skip);
+      EmitStoreToStackLocalOrContextSlot(var, location);
+      __ bind(&skip);
+    }
+
+  } else {
+    DCHECK(var->mode() == CONST_LEGACY && op != Token::INIT_CONST_LEGACY);
+    if (is_strict(language_mode())) {
+      __ CallRuntime(Runtime::kThrowConstAssignError, 0);
+    }
+    // Silently ignore store in sloppy mode.
   }
 }
 
index 397695f86aecd1e2a5b8637e6c74147269e10d89..5efe42c56bbc39603be40b89f933afa455ba859d 100644 (file)
@@ -25,7 +25,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// Flags: --harmony-scoping
+// Flags: --harmony-scoping --harmony-computed-property-names
 
 // Test that we throw early syntax errors in harmony mode
 // when using an immutable binding in an assigment or with
 
 "use strict";
 
-// Function local const.
-function constDecl0(use) {
-  return "(function() { const constvar = 1; " + use + "; })();";
-}
-
-
-function constDecl1(use) {
-  return "(function() { " + use + "; const constvar = 1; })();";
-}
-
-
-// Function local const, assign from eval.
-function constDecl2(use) {
-  use = "eval('(function() { " + use + " })')()";
-  return "(function() { const constvar = 1; " + use + "; })();";
-}
-
-
-function constDecl3(use) {
-  use = "eval('(function() { " + use + " })')()";
-  return "(function() { " + use + "; const constvar = 1; })();";
-}
-
-
-// Block local const.
-function constDecl4(use) {
-  return "(function() { { const constvar = 1; " + use + "; } })();";
-}
-
-
-function constDecl5(use) {
-  return "(function() { { " + use + "; const constvar = 1; } })();";
-}
-
-
-// Block local const, assign from eval.
-function constDecl6(use) {
-  use = "eval('(function() {" + use + "})')()";
-  return "(function() { { const constvar = 1; " + use + "; } })();";
-}
-
-
-function constDecl7(use) {
-  use = "eval('(function() {" + use + "})')()";
-  return "(function() { { " + use + "; const constvar = 1; } })();";
-}
-
-
-// Function expression name.
-function constDecl8(use) {
-  return "(function constvar() { " + use + "; })();";
-}
-
-
-// Function expression name, assign from eval.
-function constDecl9(use) {
-  use = "eval('(function(){" + use + "})')()";
-  return "(function constvar() { " + use + "; })();";
-}
-
-// For loop variable.
-function constDecl10(use) {
-  return "(function() { for (const constvar = 0; ;) { " + use + "; } })();";
-}
-
-// For-in loop variable.
-function constDecl11(use) {
-  return "(function() { for (const constvar in {a: 1}) { " + use + "; } })();";
-}
-
-// For-of loop variable.
-function constDecl12(use) {
-  return "(function() { for (const constvar of [1]) { " + use + "; } })();";
-}
-
-
-let decls = [ constDecl0,
-              constDecl1,
-              constDecl2,
-              constDecl3,
-              constDecl4,
-              constDecl5,
-              constDecl6,
-              constDecl7,
-              constDecl8,
-              constDecl9,
-              constDecl10,
-              constDecl11,
-              constDecl12
-              ];
-let declsForTDZ = new Set([constDecl1, constDecl3, constDecl5, constDecl7]);
-let uses = [ 'constvar = 1;',
-             'constvar += 1;',
-             '++constvar;',
-             'constvar++;'
-             ];
-
-function Test(d,u) {
-  'use strict';
+const decls = [
+  // Const declaration.
+  function(use) { return "const c = 1; " + use + ";" }, TypeError,
+  function(use) { return "const x = 0, c = 1; " + use + ";" }, TypeError,
+  function(use) { return "const c = 1, x = (" + use + ");" }, TypeError,
+  function(use) { return use + "; const c = 1;" }, ReferenceError,
+  function(use) { return use + "; const x = 0, c = 1;" }, ReferenceError,
+  function(use) { return "const x = (" + use + "), c = 1;" }, ReferenceError,
+  function(use) { return "const c = (" + use + ");" }, ReferenceError,
+
+  // Function expression.
+  function(use) { return "(function c() { " + use + "; })();"; }, TypeError,
+  // TODO(rossberg): Once we have default parameters, test using 'c' there.
+
+  // Class expression.
+  function(use) {
+    return "new class c { constructor() { " + use + " } };";
+  }, TypeError,
+  function(use) {
+    return "(new class c { m() { " + use + " } }).m();";
+  }, TypeError,
+  function(use) {
+    return "(new class c { get a() { " + use + " } }).a;";
+  }, TypeError,
+  function(use) {
+    return "(new class c { set a(x) { " + use + " } }).a = 0;";
+  }, TypeError,
+  function(use) {
+    return "(class c { static m() { " + use + " } }).s();";
+  }, TypeError,
+  function(use) {
+    return "(class c extends (" + use + ") {});";
+  }, ReferenceError,
+  function(use) {
+    return "(class c { [" + use + "]() {} });";
+  }, ReferenceError,
+  function(use) {
+    return "(class c { get [" + use + "]() {} });";
+  }, ReferenceError,
+  function(use) {
+    return "(class c { set [" + use + "](x) {} });";
+  }, ReferenceError,
+  function(use) {
+    return "(class c { static [" + use + "]() {} });";
+  }, ReferenceError,
+
+  // For loop.
+  function(use) {
+    return "for (const c = 0; " + use + ";) {}"
+  }, TypeError,
+  function(use) {
+    return "for (const x = 0, c = 0; " + use + ";) {}"
+  }, TypeError,
+  function(use) {
+    return "for (const c = 0; ; " + use + ") {}"
+  }, TypeError,
+  function(use) {
+    return "for (const x = 0, c = 0; ; " + use + ") {}"
+  }, TypeError,
+  function(use) {
+    return "for (const c = 0; ;) { " + use + "; }"
+  }, TypeError,
+  function(use) {
+    return "for (const x = 0, c = 0; ;) { " + use + "; }"
+  }, TypeError,
+  function(use) {
+    return "for (const c in {a: 1}) { " + use + "; }"
+  }, TypeError,
+  function(use) {
+    return "for (const c of [1]) { " + use + "; }"
+  }, TypeError,
+  function(use) {
+    return "for (const x = (" + use + "), c = 0; ;) {}"
+  }, ReferenceError,
+  function(use) {
+    return "for (const c = (" + use + "); ;) {}"
+  }, ReferenceError,
+]
+
+let uses = [
+  'c = 1',
+  'c += 1',
+  '++c',
+  'c--',
+];
+
+let declcontexts = [
+  function(decl) { return decl; },
+  function(decl) { return "eval(\'" + decl + "\')"; },
+  function(decl) { return "{ " + decl + " }"; },
+  function(decl) { return "(function() { " + decl + " })()"; },
+];
+
+let usecontexts = [
+  function(use) { return use; },
+  function(use) { return "eval(\"" + use + "\")"; },
+  function(use) { return "(function() { " + use + " })()"; },
+  function(use) { return "(function() { eval(\"" + use + "\"); })()"; },
+  function(use) { return "eval(\"(function() { " + use + "; })\")()"; },
+];
+
+function Test(program, error) {
+  program = "'use strict'; " + program;
   try {
-    print(d(u));
-    eval(d(u));
+    print(program, "  // throw " + error.name);
+    eval(program);
   } catch (e) {
-    if (declsForTDZ.has(d) && u !== uses[0]) {
-      // In these cases, read of a const variable occurs
-      // before a write to it, so TDZ kicks in before const check.
-      assertInstanceof(e, ReferenceError);
-      return;
+    assertInstanceof(e, error);
+    if (e === TypeError) {
+      assertTrue(e.toString().indexOf("Assignment to constant variable") >= 0);
     }
-    assertInstanceof(e, TypeError);
-    assertTrue(e.toString().indexOf("Assignment to constant variable") >= 0);
     return;
   }
   assertUnreachable();
 }
 
-for (var d = 0; d < decls.length; ++d) {
+for (var d = 0; d < decls.length; d += 2) {
   for (var u = 0; u < uses.length; ++u) {
-    Test(decls[d], uses[u]);
+    for (var o = 0; o < declcontexts.length; ++o) {
+      for (var i = 0; i < usecontexts.length; ++i) {
+        Test(declcontexts[o](decls[d](usecontexts[i](uses[u]))), decls[d + 1]);
+      }
+    }
   }
 }