| // 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 |
| "use strict" |
| |
| let prologue_dead = "(function outer() { if (false) { "; |
| let epilogue_dead = " } })();"; |
| |
| let prologue_live = "(function outer() { "; |
| let epilogue_live = "})();"; |
| |
| // For code which already throws a run-time error in non-strong mode; we assert |
| // that we now get the error already compilation time. |
| function assertLateErrorsBecomeEarly(code) { |
| assertThrows("'use strong'; " + prologue_dead + code + epilogue_dead, |
| ReferenceError); |
| |
| // Make sure the error happens only in strong mode (note that we need strict |
| // mode here because of let). |
| assertDoesNotThrow("'use strict'; " + prologue_dead + code + epilogue_dead); |
| |
| // But if we don't put the references inside a dead code, it throws a run-time |
| // error (also in strict mode). |
| assertThrows("'use strong'; " + prologue_live + code + epilogue_live, |
| ReferenceError); |
| assertThrows("'use strict'; " + prologue_live + code + epilogue_live, |
| ReferenceError); |
| } |
| |
| // For code which doesn't throw an error at all in non-strong mode. |
| function assertNonErrorsBecomeEarly(code) { |
| assertThrows("'use strong'; " + prologue_dead + code + epilogue_dead, |
| ReferenceError); |
| assertDoesNotThrow("'use strict'; " + prologue_dead + code + epilogue_dead); |
| |
| assertThrows("'use strong'; " + prologue_live + code + epilogue_live, |
| ReferenceError); |
| assertDoesNotThrow("'use strict'; " + prologue_live + code + epilogue_live, |
| ReferenceError); |
| } |
| |
| (function InitTimeReferenceForward() { |
| // It's never OK to have an init time reference to a class which hasn't been |
| // declared. |
| assertLateErrorsBecomeEarly( |
| `class A extends B { } |
| class B {}`); |
| |
| assertLateErrorsBecomeEarly( |
| `class A { |
| [B.sm()]() { } |
| } |
| class B { |
| static sm() { return 0; } |
| }`); |
| })(); |
| |
| (function InitTimeReferenceBackward() { |
| // Backwards is of course fine. |
| "use strong"; |
| class A { |
| static sm() { return 0; } |
| } |
| let i = "making these classes non-consecutive"; |
| class B extends A {}; |
| "by inserting statements and declarations in between"; |
| class C { |
| [A.sm()]() { } |
| }; |
| })(); |
| |
| (function BasicMutualRecursion() { |
| "use strong"; |
| class A { |
| m() { B; } |
| static sm() { B; } |
| } |
| // No statements or declarations between the classes. |
| class B { |
| m() { A; } |
| static sm() { A; } |
| } |
| })(); |
| |
| (function MutualRecursionWithMoreClasses() { |
| "use strong"; |
| class A { |
| m() { B; C; } |
| static sm() { B; C; } |
| } |
| class B { |
| m() { A; C; } |
| static sm() { A; C; } |
| } |
| class C { |
| m() { A; B; } |
| static sm() { A; B; } |
| } |
| })(); |
| |
| (function ReferringForwardInDeeperScopes() { |
| "use strong"; |
| |
| function foo() { |
| class A1 { |
| m() { B1; } |
| } |
| class B1 { } |
| } |
| |
| class Outer { |
| m() { |
| class A2 { |
| m() { B2; } |
| } |
| class B2 { } |
| } |
| } |
| |
| for (let i = 0; i < 1; ++i) { |
| class A3 { |
| m() { B3; } |
| } |
| class B3 { } |
| } |
| |
| (a, b) => { |
| class A4 { |
| m() { B4; } |
| } |
| class B4 { } |
| } |
| })(); |
| |
| (function ReferringForwardButClassesNotConsecutive() { |
| assertNonErrorsBecomeEarly( |
| `class A { |
| m() { B; } |
| } |
| ; |
| class B {}`); |
| |
| assertNonErrorsBecomeEarly( |
| `let A = class { |
| m() { B; } |
| } |
| class B {}`); |
| |
| assertNonErrorsBecomeEarly( |
| `class A { |
| m() { B1; } // Just a normal use-before-declaration. |
| } |
| let B1 = class B2 {}`); |
| |
| assertNonErrorsBecomeEarly( |
| `class A { |
| m() { B; } |
| } |
| let i = 0; |
| class B {}`); |
| |
| assertNonErrorsBecomeEarly( |
| `class A { |
| m() { B; } |
| } |
| function foo() {} |
| class B {}`); |
| |
| assertNonErrorsBecomeEarly( |
| `function foo() { |
| class A { |
| m() { B; } |
| } |
| } |
| class B {}`); |
| |
| assertNonErrorsBecomeEarly( |
| `class A extends class B { m() { C; } } { |
| } |
| class C { }`); |
| |
| assertLateErrorsBecomeEarly( |
| `class A extends class B { [C.sm()]() { } } { |
| } |
| class C { static sm() { return 'a';} }`); |
| |
| assertLateErrorsBecomeEarly( |
| `class A extends class B extends C { } { |
| } |
| class C { }`); |
| })(); |
| |
| |
| (function RegressionForClassResolution() { |
| assertNonErrorsBecomeEarly( |
| `let A = class B { |
| m() { C; } |
| } |
| ;;;; |
| class C {} |
| class B {}`); |
| })(); |
| |
| |
| (function TestMultipleMethodScopes() { |
| "use strong"; |
| |
| // Test cases where the reference is inside multiple method scopes. |
| class A1 { |
| m() { |
| class C1 { |
| m() { B1; } |
| } |
| } |
| } |
| class B1 { } |
| |
| ; |
| |
| class A2 { |
| m() { |
| class C2 extends B2 { |
| } |
| } |
| } |
| class B2 { } |
| })(); |