Index: test/mjsunit/strong/declaration-after-use.js |
diff --git a/test/mjsunit/strong/declaration-after-use.js b/test/mjsunit/strong/declaration-after-use.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0087357000ad37fbd2cdd2792e6c99be62e28ce9 |
--- /dev/null |
+++ b/test/mjsunit/strong/declaration-after-use.js |
@@ -0,0 +1,188 @@ |
+// 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); |
+ |
+ // TODO(marja, rossberg): More tests related to computed property names in |
+ // classes + recognize more errors. This one is not recognized as an error |
+ // yet: |
+ // let C = class C2 { |
+ // static a() { return 'A'; } |
+ // [C2.a()]() { return 'B'; } << C2 should not be allowed here |
+ // }; |
+})(); |
+ |
+ |
+(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; method; } }; new C4(); |
+ let C5 = class C6 { method() { C6; method; } }; new C5(); |
+})(); |