v8/test/mjsunit/strong/mutually-recursive-classes.js
marja ddd3f318c7 [strong] Stricter check for referring to other classes inside methods.
Add the restriction that both classes must be declared inside the same
consectutive class declaration batch.

Dependency analysis not implemented yet.

BUG=v8:3956
LOG=N

Review URL: https://codereview.chromium.org/1060913005

Cr-Commit-Position: refs/heads/master@{#28032}
2015-04-23 14:04:47 +00:00

230 lines
4.8 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-arrow-functions
"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 { }
})();