v8/test/mjsunit/strong/declaration-after-use.js
marja 4a709dd658 [strong] Fix scoping related errors for methods.
Methods can refer to the class name.

BUG=v8:3927
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#27075}
2015-03-09 14:30:40 +00:00

270 lines
8.7 KiB
JavaScript

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --strong-mode --harmony_rest_parameters --harmony_arrow_functions --harmony_classes --harmony-computed-property-names
// Note that it's essential for these tests that the reference is inside dead
// code (because we already produce ReferenceErrors for run-time unresolved
// variables and don't want to confuse those with strong mode errors). But the
// errors should *not* be inside lazy, unexecuted functions, since lazy parsing
// doesn't produce strong mode scoping errors).
// In addition, assertThrows will call eval and that changes variable binding
// types (see e.g., UNBOUND_EVAL_SHADOWED). We can avoid unwanted side effects
// by wrapping the code to be tested inside an outer function.
function assertThrowsHelper(code, error) {
"use strict";
let prologue = "(function outer() { ";
let epilogue = " })();";
assertThrows(prologue + code + epilogue, error);
}
(function DeclarationAfterUse() {
// Note that these tests only test cases where the declaration is found but is
// after the use. In particular, we cannot yet detect cases where the use can
// possibly bind to a global variable.
assertThrowsHelper("'use strong'; if (false) { x; let x = 0; }",
ReferenceError);
assertThrowsHelper(
"function f() { 'use strong'; if (false) { x; let x = 0; } } f();",
ReferenceError);
assertThrowsHelper(
"'use strong'; function f() { if (false) { x; } } let x = 0; f();",
ReferenceError);
assertThrowsHelper(
"function f() { 'use strong'; if (false) { x; } } var x = 0; f();",
ReferenceError);
assertThrowsHelper(
"function f() { 'use strong'; if (false) { x; } } var x; f();",
ReferenceError);
// Errors are also detected when the declaration and the use are in the same
// eval scope.
assertThrowsHelper("'use strong'; eval('x; let x = 0;')", ReferenceError);
// Use occurring in the initializer of the declaration:
assertThrowsHelper("'use strong'; if (false) { let x = x + 1; }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { let x = x; }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { let x = y, y = 4; }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { let x = function() { x; } }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { let x = a => { x; } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { function f() {}; let x = f(x); }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { const x = x; }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { const x = function() { x; } }",
ReferenceError);
assertThrowsHelper("'use strong'; if (false) { const x = a => { x; } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { function f() {}; const x = f(x); }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { for (let x = x; ; ) { } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { for (const x = x; ; ) { } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { for (let x = y, y; ; ) { } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { for (const x = y, y = 0; ; ) { } }",
ReferenceError);
// Computed property names
assertThrowsHelper(
"'use strong'; if (false) { let o = { 'a': 'b', [o.a]: 'c'}; }",
ReferenceError);
})();
(function DeclarationAfterUseInClasses() {
assertThrowsHelper("'use strong'; if (false) { class C extends C { } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 extends C { } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 extends C2 { } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { constructor() { C; } } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { method() { C; } } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"static a() { return 'A'; } [C.a()]() { return 'B'; } }; }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"static a() { return 'A'; } [C2.a()]() { return 'B'; } }; }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"[(function() { C; return 'A';})()]() { return 'B'; } }; }",
ReferenceError);
// The reference to C or C2 is inside a function, but not a method.
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"[(function() { C2; return 'A';})()]() { return 'B'; } }; }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"[(function() { C; return 'A';})()]() { return 'B'; } }; }",
ReferenceError);
// The reference to C or C2 is inside a method, but it's not a method of the
// relevant class (C2).
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"[(new (class D { m() { C2; return 'A'; } })).m()]() " +
"{ return 'B'; } } }",
ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"[(new (class D { m() { C; return 'A'; } })).m()]() " +
"{ return 'B'; } } }",
ReferenceError);
// Methods inside object literals are not sufficiently distinguished from
// methods inside classes.
// https://code.google.com/p/v8/issues/detail?id=3948
// assertThrowsHelper(
// "'use strong'; if (false) { let C = class C2 { " +
// "[({m() { C2; return 'A'; }}).m()]() " +
// "{ return 'B'; } } }",
// ReferenceError);
assertThrowsHelper(
"'use strong'; if (false) { let C = class C2 { " +
"[({m() { C; return 'A'; }}).m()]() " +
"{ return 'B'; } } }",
ReferenceError);
// assertThrowsHelper(
// "'use strong';\n" +
// "if (false) {\n" +
// " class COuter {\n" +
// " m() {\n" +
// " class CInner {\n" +
// " [({ m() { CInner; return 'A'; } }).m()]() {\n" +
// " return 'B';\n" +
// " }\n" +
// " }\n" +
// " }\n" +
// " }\n" +
// "}",
// ReferenceError);
})();
(function UsesWhichAreFine() {
"use strong";
let var1 = 0;
var1;
let var2a = 0, var2b = var2a + 1, var2c = 2 + var2b;
for (let var3 = 0; var3 < 1; var3++) {
var3;
}
for (let var4a = 0, var4b = var4a; var4a + var4b < 4; var4a++, var4b++) {
var4a;
var4b;
}
let var5 = 5;
for (; var5 < 10; ++var5) { }
let arr = [1, 2];
for (let i of arr) {
i;
}
let var6 = [1, 2];
// The second var6 resolves to outside (not to the first var6).
for (let var6 of var6) { var6; }
try {
throw "error";
} catch (e) {
e;
}
function func1() { func1; this; }
func1();
func1;
function * func2() { func2; this; }
func2();
func2;
function func4(p, ...rest) { p; rest; this; func2; }
func4();
let func5 = (p1, p2) => { p1; p2; };
func5();
function func6() {
var1, var2a, var2b, var2c;
}
(function eval1() {
let var7 = 0; // Declaration position will be something large.
// But use position will be something small, however, this is not an error,
// since the use is inside an eval scope.
eval("var7;");
})();
class C1 { constructor() { C1; } }; new C1();
let C2 = class C3 { constructor() { C3; } }; new C2();
class C4 { method() { C4; } }; new C4();
let C5 = class C6 { method() { C6; } }; new C5();
class C7 { static method() { C7; } }; new C7();
let C8 = class C9 { static method() { C9; } }; new C8();
class C10 { get x() { C10; } }; new C10();
let C11 = class C12 { get x() { C12; } }; new C11();
// Regression test for unnamed classes.
let C12 = class { m() { var1; } };
class COuter {
m() {
class CInner {
// Here we can refer to COuter but not to CInner (see corresponding
// assertion test):
[({ m() { COuter; return 'A'; } }).m()]() { return 'B'; }
// And here we can refer to both:
n() { COuter; CInner; }
}
return new CInner();
}
}
(new COuter()).m().n();
})();