Test.hx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. class A {
  2. }
  3. class B extends A {
  4. function new() {
  5. init();
  6. Test.calls.push("B");
  7. }
  8. function init() {}
  9. }
  10. class C extends B {}
  11. class D extends C {
  12. var n = "D";
  13. override function init() Test.calls.push(n);
  14. }
  15. class E extends D {
  16. function new() {
  17. Test.use(this);
  18. Test.calls.push("E");
  19. super();
  20. }
  21. }
  22. class F extends E {
  23. public function new() {
  24. super();
  25. Test.calls.push("F");
  26. }
  27. }
  28. extern class ExtNoCtor {
  29. static function __init__():Void haxe.macro.Compiler.includeFile("./extern.js", "top");
  30. }
  31. class Base extends ExtNoCtor {
  32. function new() {
  33. Test.calls.push("BASE");
  34. }
  35. }
  36. class Child extends Base {
  37. public function new() {
  38. Test.use(this);
  39. Test.calls.push("CHILD");
  40. super();
  41. }
  42. }
  43. class RootNoArgs {
  44. public function new() {}
  45. }
  46. class ChildOneArg extends RootNoArgs {
  47. public var x:Int;
  48. public function new(x) {
  49. super();
  50. this.x = x;
  51. }
  52. }
  53. class GrandChildNoArgs extends ChildOneArg {
  54. public function new() {
  55. Test.use(this);
  56. super(42);
  57. }
  58. }
  59. class Test {
  60. public static var calls:Array<String>;
  61. @:pure(false) public static function use(v:Any) {}
  62. static inline final hxCtor = "_hx_constructor";
  63. static inline final hxSkipCtor = "_hx_skip_constructor";
  64. static function hasCtorMethod(c:Dynamic):Bool {
  65. return c.prototype.hasOwnProperty(hxCtor);
  66. }
  67. static function hasSkip(c:Dynamic) {
  68. return c.hasOwnProperty(hxSkipCtor);
  69. }
  70. static var failures = 0;
  71. static function assert(bool:Bool, ?pos:haxe.PosInfos) {
  72. if (!bool) {
  73. (untyped process).stderr.write(haxe.Log.formatOutput("assertion failed\n", pos));
  74. failures++;
  75. }
  76. }
  77. static function main() {
  78. // A has no constructor, so it's safe to just call `super` first
  79. // even if we skip ctor in the subclasses
  80. assert(!hasCtorMethod(A));
  81. assert(!hasSkip(A));
  82. // B has an extracted ctor and also the static skip flag,
  83. // as it is the first in the hierarchy who's ctor needs to be skipped
  84. assert(hasCtorMethod(B));
  85. assert(hasSkip(B));
  86. // C and others down the chain should NOT have the static skip flag,
  87. // but it can have a hxctor (although in this case it could be optimized away)
  88. assert(!hasSkip(C));
  89. // D must have a hxctor because it accesses this before super (for field init)
  90. // it also supports skipping because it has children who want to skip
  91. assert(hasCtorMethod(D));
  92. assert(!hasSkip(D));
  93. // E also must have a hxctor because it accesses this before super
  94. // but it doesn't support skipping, because noone down the inheritance requires it
  95. assert(hasCtorMethod(E));
  96. assert(!hasSkip(E));
  97. // F should NOT have hxctor because it doesn't access this before super and
  98. // it has no children that require skipping
  99. assert(!hasCtorMethod(F));
  100. assert(!hasSkip(F));
  101. // now for the call order
  102. calls = [];
  103. new F();
  104. // E is first, because it's called before `super` in the parent class, and we call `super` before anything else
  105. // D is second, because we call `init` before pushing `B` in the first constructor. here it's important that the field `n` is set
  106. // before we call super(), so it's initialized at the point we call `init`
  107. // B is the last "super" ctor in the hierarchy
  108. // and finally F is pushed after a super call as usual
  109. assert(calls.join("") == "EDBF");
  110. // we also support skipping when inheriting from extern without constructors
  111. assert(!hasCtorMethod(ExtNoCtor));
  112. assert(!hasSkip(ExtNoCtor));
  113. assert(hasCtorMethod(Base));
  114. assert(hasSkip(Base));
  115. assert(hasCtorMethod(Child));
  116. assert(!hasSkip(Child));
  117. calls = [];
  118. new Child();
  119. assert(calls.join("|") == "CHILD|BASE");
  120. assert(new ChildOneArg(42).x == 42); // #7988
  121. // ---
  122. (untyped process).exit(if (failures == 0) 0 else 1);
  123. }
  124. }