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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c8d6fad260fd9fad4cfc8bc7d2ca1868df8143e3 |
--- /dev/null |
+++ b/test/mjsunit/strong/mutually-recursive-classes.js |
@@ -0,0 +1,77 @@ |
+// 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 |
+ |
+// 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_dead = "(function outer() { if (false) { "; |
+ let epilogue_dead = " } })();"; |
+ |
+ 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). |
+ 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); |
+} |
+ |
+(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 { }; |
+ class B {}`); |
+ |
+ assertThrowsHelper( |
+ `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; } |
+ }; |
+})(); |