44c8c1bcfe
Cleanup: Better helpers, unified line splitting. Also asserting that without strong mode, the snippets don't produce errors. Fix: The eval related tests were wrong (we didn't make sure that the error thrown comes from strong scoping and is not a normal runtime error). BUG=v8:3956 LOG=N Review URL: https://codereview.chromium.org/993393002 Cr-Commit-Position: refs/heads/master@{#27153}
256 lines
7.5 KiB
JavaScript
256 lines
7.5 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 --harmony_templates
|
|
|
|
// 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) {
|
|
"use strict";
|
|
let prologue = "(function outer() { if (false) { ";
|
|
let epilogue = " } })();";
|
|
|
|
assertThrows("'use strong'; " + prologue + code + epilogue, ReferenceError);
|
|
|
|
// Make sure the error happens only in strong mode (note that we need strict
|
|
// mode here because of let).
|
|
assertDoesNotThrow("'use strict'; " + prologue + code + epilogue);
|
|
}
|
|
|
|
(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("x; let x = 0;");
|
|
assertThrowsHelper("function f() { x; let x = 0; }");
|
|
assertThrowsHelper("function f() { x; } let x = 0;");
|
|
|
|
// These tests needs to be done a bit more manually, since var is not allowed
|
|
// in strong mode:
|
|
assertThrows(
|
|
`(function outer() {
|
|
function f() { 'use strong'; if (false) { x; } } var x = 0; f();
|
|
})()`,
|
|
ReferenceError);
|
|
assertDoesNotThrow(
|
|
"(function outer() {\n" +
|
|
" function f() { if (false) { x; } } var x = 0; f(); \n" +
|
|
"})()");
|
|
|
|
assertThrows(
|
|
"(function outer() {\n" +
|
|
" function f() { 'use strong'; if (false) { x; } } var x; f(); \n" +
|
|
"})()",
|
|
ReferenceError);
|
|
assertDoesNotThrow(
|
|
"(function outer() {\n" +
|
|
" function f() { if (false) { x; } } var x; f(); \n" +
|
|
"})()");
|
|
|
|
// Errors are also detected when the declaration and the use are in the same
|
|
// eval scope.
|
|
assertThrows("'use strong'; eval('if (false) { x; let x = 0;}')",
|
|
ReferenceError);
|
|
assertDoesNotThrow("'use strict'; eval('if (false) { x; let x = 0; }')");
|
|
|
|
// Use occurring in the initializer of the declaration:
|
|
assertThrowsHelper("let x = x + 1;");
|
|
assertThrowsHelper("let x = x;");
|
|
assertThrowsHelper("let x = y, y = 4;");
|
|
assertThrowsHelper("let x = function() { x; }");
|
|
assertThrowsHelper("let x = a => { x; }");
|
|
assertThrowsHelper("function f(x) { return x; }; let x = f(x);");
|
|
assertThrowsHelper("const x = x;");
|
|
assertThrowsHelper("const x = function() { x; }");
|
|
assertThrowsHelper("const x = a => { x; }");
|
|
assertThrowsHelper("function f(x) {return x}; const x = f(x);");
|
|
|
|
assertThrowsHelper("for (let x = x; ; ) { }");
|
|
assertThrowsHelper("for (const x = x; ; ) { }");
|
|
assertThrowsHelper("for (let x = y, y; ; ) { }");
|
|
assertThrowsHelper("for (const x = y, y = 0; ; ) { }");
|
|
|
|
// Computed property names
|
|
assertThrowsHelper("let o = { 'a': 'b', [o.a]: 'c'};");
|
|
})();
|
|
|
|
|
|
(function DeclarationAfterUseInClasses() {
|
|
assertThrowsHelper("class C extends C { }");
|
|
assertThrowsHelper("let C = class C2 extends C { }");
|
|
assertThrowsHelper("let C = class C2 extends C2 { }");
|
|
|
|
assertThrowsHelper("let C = class C2 { constructor() { C; } }");
|
|
assertThrowsHelper("let C = class C2 { method() { C; } }");
|
|
assertThrowsHelper("let C = class C2 { *generator_method() { C; } }");
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
static a() { return 'A'; }
|
|
[C.a()]() { return 'B'; }
|
|
};`);
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
static a() { return 'A'; }
|
|
[C2.a()]() { return 'B'; }
|
|
};`);
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[(function() { C; return 'A';})()]() { return 'B'; }
|
|
};`);
|
|
|
|
// The reference to C or C2 is inside a function, but not a method.
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[(function() { C2; return 'A';})()]() { return 'B'; }
|
|
};`);
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[(function() { C; return 'A';})()]() { return 'B'; }
|
|
};`);
|
|
|
|
// The reference to C or C2 is inside a method, but it's not a method of the
|
|
// relevant class (C2).
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[(new (class D { m() { C2; return 'A'; } })).m()]() {
|
|
return 'B';
|
|
}
|
|
}`);
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[(new (class D { m() { C; return 'A'; } })).m()]() {
|
|
return 'B';
|
|
}
|
|
}`);
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[({m() { C2; return 'A'; }}).m()]() { return 'B'; }
|
|
}`);
|
|
|
|
assertThrowsHelper(
|
|
`let C = class C2 {
|
|
[({m() { C; return 'A'; }}).m()]() { return 'B'; }
|
|
}`);
|
|
|
|
assertThrowsHelper(
|
|
`class COuter {
|
|
m() {
|
|
class CInner {
|
|
[({ m() { CInner; return 'A'; } }).m()]() {
|
|
return 'B';
|
|
}
|
|
}
|
|
}
|
|
}`);
|
|
})();
|
|
|
|
|
|
(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; } *generator_method() { C4; } }; new C4();
|
|
let C5 = class C6 { method() { C6; } *generator_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 C13 = 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();
|
|
|
|
// Making sure the check which is supposed to prevent "object literal inside
|
|
// computed property name references the class name" is not too generic:
|
|
class C14 { m() { let obj = { n() { C14 } }; obj.n(); } }; (new C14()).m();
|
|
})();
|