Pārlūkot izejas kodu

Test for ES6 class generation (#7835)

* [js] add some tests on es6 class generation edge cases

* [js] run es6 tests before saucelabs

because they are slowwww

* [js] test that inheriting no-ctor extern class and then using ctor skipping works

* [js] test compiler error when accessing this before super for classes derived from externs with constructors

* fix sauce tests

* [js] test error when trying to skip ctor for a child class of an extern without ctor that inherits an extern with ctors

* [js] test single extern inheritance + >1 normal inheritance instead of >1 extern inheritance until we figure out #7836 and #7837
Dan Korostelev 6 gadi atpakaļ
vecāks
revīzija
c951675fae

+ 1 - 0
tests/misc/es6/.gitignore

@@ -0,0 +1 @@
+/test.js

+ 135 - 0
tests/misc/es6/Test.hx

@@ -0,0 +1,135 @@
+class A {
+}
+
+class B extends A {
+	function new() {
+		init();
+		Test.calls.push("B");
+	}
+	function init() {}
+}
+
+class C extends B {}
+
+class D extends C {
+	var n = "D";
+	override function init() Test.calls.push(n);
+}
+
+class E extends D {
+	function new() {
+		Test.use(this);
+		Test.calls.push("E");
+		super();
+	}
+}
+
+class F extends E {
+	public function new() {
+		super();
+		Test.calls.push("F");
+	}
+}
+
+extern class ExtNoCtor {
+	static function __init__():Void haxe.macro.Compiler.includeFile("./extern.js", "top");
+}
+
+class Base extends ExtNoCtor {
+	function new() {
+		Test.calls.push("BASE");
+	}
+}
+
+class Child extends Base {
+	public function new() {
+		Test.use(this);
+		Test.calls.push("CHILD");
+		super();
+	}
+}
+
+class Test {
+	public static var calls:Array<String>;
+	@:pure(false) public static function use(v:Any) {}
+
+	static inline final hxCtor = "_hx_constructor";
+	static inline final hxSkipCtor = "_hx_skip_constructor";
+
+	static function hasCtorMethod(c:Dynamic):Bool {
+		return c.prototype.hasOwnProperty(hxCtor);
+	}
+
+	static function hasSkip(c:Dynamic) {
+		return c.hasOwnProperty(hxSkipCtor);
+	}
+
+	static var failures = 0;
+
+	static function assert(bool:Bool, ?pos:haxe.PosInfos) {
+		if (!bool) {
+			(untyped process).stderr.write(haxe.Log.formatOutput("assertion failed\n", pos));
+			failures++;
+		}
+	}
+
+	static function main() {
+		// A has no constructor, so it's safe to just call `super` first
+		// even if we skip ctor in the subclasses
+		assert(!hasCtorMethod(A));
+		assert(!hasSkip(A));
+
+		// B has an extracted ctor and also the static skip flag,
+		// as it is the first in the hierarchy who's ctor needs to be skipped
+		assert(hasCtorMethod(B));
+		assert(hasSkip(B));
+
+		// C and others down the chain should NOT have the static skip flag,
+		// but it can have a hxctor (although in this case it could be optimized away)
+		assert(!hasSkip(C));
+
+		// D must have a hxctor because it accesses this before super (for field init)
+		// it also supports skipping because it has children who want to skip
+		assert(hasCtorMethod(D));
+		assert(!hasSkip(D));
+
+		// E also must have a hxctor because it accesses this before super
+		// but it doesn't support skipping, because noone down the inheritance requires it
+		assert(hasCtorMethod(E));
+		assert(!hasSkip(E));
+
+		// F should NOT have hxctor because it doesn't access this before super and
+		// it has no children that require skipping
+		assert(!hasCtorMethod(F));
+		assert(!hasSkip(F));
+
+		// now for the call order
+		calls = [];
+		new F();
+		// E is first, because it's called before `super` in the parent class, and we call `super` before anything else
+		// D is second, because we call `init` before pushing `B` in the first constructor. here it's important that the field `n` is set
+		// before we call super(), so it's initialized at the point we call `init`
+		// B is the last "super" ctor in the hierarchy
+		// and finally F is pushed after a super call as usual
+		assert(calls.join("") == "EDBF");
+
+
+		// we also support skipping when inheriting from extern without constructors
+		assert(!hasCtorMethod(ExtNoCtor));
+		assert(!hasSkip(ExtNoCtor));
+
+		assert(hasCtorMethod(Base));
+		assert(hasSkip(Base));
+
+		assert(hasCtorMethod(Child));
+		assert(!hasSkip(Child));
+
+		calls = [];
+		new Child();
+		assert(calls.join("|") == "CHILD|BASE");
+
+		// ---
+
+		(untyped process).exit(if (failures == 0) 0 else 1);
+	}
+}

+ 1 - 0
tests/misc/es6/extern.js

@@ -0,0 +1 @@
+class ExtNoCtor {}

+ 4 - 0
tests/misc/es6/run.hxml

@@ -0,0 +1,4 @@
+-main Test
+-js test.js
+-D js-es=6
+-cmd node test.js

+ 9 - 0
tests/misc/projects/es6/A.hx

@@ -0,0 +1,9 @@
+extern class A {
+	function new(v:Any);
+}
+
+class B extends A {
+	function new() {
+		super(this);
+	}
+}

+ 10 - 0
tests/misc/projects/es6/B.hx

@@ -0,0 +1,10 @@
+extern class A {
+	function new();
+}
+
+class B extends A {
+	function new() {
+		trace(this);
+		super();
+	}
+}

+ 7 - 0
tests/misc/projects/es6/C.hx

@@ -0,0 +1,7 @@
+extern class A {
+	function new();
+}
+
+class B extends A {
+	var i = 5;
+}

+ 9 - 0
tests/misc/projects/es6/D.hx

@@ -0,0 +1,9 @@
+extern class A {
+	function new();
+}
+
+class B extends A {}
+
+class C extends B {
+	var i = 5;
+}

+ 3 - 0
tests/misc/projects/es6/compile-a-fail.hxml

@@ -0,0 +1,3 @@
+A
+-js a.js
+-D js-es=6

+ 1 - 0
tests/misc/projects/es6/compile-a-fail.hxml.stderr

@@ -0,0 +1 @@
+A.hx:7: characters 9-13 : Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor

+ 3 - 0
tests/misc/projects/es6/compile-b-fail.hxml

@@ -0,0 +1,3 @@
+B
+-js b.js
+-D js-es=6

+ 1 - 0
tests/misc/projects/es6/compile-b-fail.hxml.stderr

@@ -0,0 +1 @@
+B.hx:7: characters 9-13 : Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor

+ 3 - 0
tests/misc/projects/es6/compile-c-fail.hxml

@@ -0,0 +1,3 @@
+C
+-js c.js
+-D js-es=6

+ 1 - 0
tests/misc/projects/es6/compile-c-fail.hxml.stderr

@@ -0,0 +1 @@
+C.hx:6: characters 2-12 : Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor

+ 3 - 0
tests/misc/projects/es6/compile-d-fail.hxml

@@ -0,0 +1,3 @@
+D
+-js d.js
+-D js-es=6

+ 1 - 0
tests/misc/projects/es6/compile-d-fail.hxml.stderr

@@ -0,0 +1 @@
+D.hx:8: characters 2-12 : Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor

+ 5 - 0
tests/runci/targets/Js.hx

@@ -53,6 +53,10 @@ class Js {
 			}
 			}
 		];
 		];
 
 
+		infoMsg("Test ES6:");
+		changeDirectory(miscDir + "es6");
+		runCommand("haxe", ["run.hxml"]);
+
 		haxelibInstall("hxnodejs");
 		haxelibInstall("hxnodejs");
 		var env = Sys.environment();
 		var env = Sys.environment();
 		if (
 		if (
@@ -76,6 +80,7 @@ class Js {
 			// 	Sys.sleep(0.5);
 			// 	Sys.sleep(0.5);
 			// }
 			// }
 
 
+			changeDirectory(unitDir);
 			runCommand("npm", ["install", "wd", "q"], true);
 			runCommand("npm", ["install", "wd", "q"], true);
 			runCommand("haxe", ["compile-saucelabs-runner.hxml"]);
 			runCommand("haxe", ["compile-saucelabs-runner.hxml"]);
 			var server = new Process("nekotools", ["server"]);
 			var server = new Process("nekotools", ["server"]);