Index: test/mjsunit/strong/mutually-recursive-classes.js |
diff --git a/test/mjsunit/strong/mutually-recursive-classes.js b/test/mjsunit/strong/mutually-recursive-classes.js |
index c8d6fad260fd9fad4cfc8bc7d2ca1868df8143e3..c8cf9788ba232db00e94f19fb897a22e513cc3a6 100644 |
--- a/test/mjsunit/strong/mutually-recursive-classes.js |
+++ b/test/mjsunit/strong/mutually-recursive-classes.js |
@@ -2,23 +2,20 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-// Flags: --strong-mode |
+// Flags: --strong-mode --harmony-arrow-functions |
+"use strict" |
-// 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). |
+let prologue_dead = "(function outer() { if (false) { "; |
+let epilogue_dead = " } })();"; |
-// 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_dead = "(function outer() { if (false) { "; |
- let epilogue_dead = " } })();"; |
+let prologue_live = "(function outer() { "; |
+let epilogue_live = "})();"; |
- assertThrows("'use strong'; " + prologue_dead + code + epilogue_dead, ReferenceError); |
+// 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). |
@@ -26,24 +23,35 @@ function assertThrowsHelper(code) { |
// But if we don't put the references inside a dead code, it throws a run-time |
// error (also in strict mode). |
- let prologue_live = "(function outer() { "; |
- let epilogue_live = "})();"; |
+ 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); |
- assertThrows("'use strict'; " + prologue_live + code + epilogue_live, ReferenceError); |
+ 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. |
- assertThrowsHelper( |
- `class A extends B { }; |
+ assertLateErrorsBecomeEarly( |
+ `class A extends B { } |
class B {}`); |
- assertThrowsHelper( |
+ assertLateErrorsBecomeEarly( |
`class A { |
[B.sm()]() { } |
- }; |
+ } |
class B { |
static sm() { return 0; } |
}`); |
@@ -54,7 +62,7 @@ function assertThrowsHelper(code) { |
"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"; |
@@ -68,10 +76,154 @@ function assertThrowsHelper(code) { |
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 { } |
})(); |