|
@@ -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);
|
|
|
+ }
|
|
|
+}
|