[decorators] Correctly insert _initialize(this) after super(). (#8970)

* [decorators] Correctly insert `_initialize(this)` after `super()`.

This commit fixes to problem:
1) After `super();` statements, `_initialize(this)` was inserted without
   a trailing semicolon.
2) `(0, super())` causes an infinite recursion.

* Fix tests

* Add test
This commit is contained in:
Nicolò Ribaudo 2018-11-07 06:58:09 +01:00 committed by Daniel Tschinder
parent 5d5cd8612f
commit b706e34fc8
13 changed files with 167 additions and 3 deletions

View File

@ -113,6 +113,10 @@ const bareSupersVisitor = {
CallExpression(path, { initializeInstanceElements }) {
if (path.get("callee").isSuper()) {
path.insertAfter(t.cloneNode(initializeInstanceElements));
// Sometimes this path gets requeued (e.g. in (super(), foo)), and
// it leads to infinite recursion.
path.skip();
}
},
Function(path) {

View File

@ -0,0 +1,6 @@
@decorator(parameter)
class Sub extends Super {
constructor() {
super().method();
}
}

View File

@ -0,0 +1,17 @@
let Sub = babelHelpers.decorate([decorator(parameter)], function (_initialize, _Super) {
"use strict";
class Sub extends _Super {
constructor() {
var _temp;
(_temp = super(), _initialize(this), _temp).method();
}
}
return {
F: Sub,
d: []
};
}, Super);

View File

@ -0,0 +1,7 @@
@dec
class B extends A {
constructor() {
super();
[];
}
}

View File

@ -0,0 +1,19 @@
let B = babelHelpers.decorate([dec], function (_initialize, _A) {
"use strict";
class B extends _A {
constructor() {
super();
_initialize(this);
[];
}
}
return {
F: B,
d: []
};
}, A);

View File

@ -0,0 +1,6 @@
@dec
class B extends A {
constructor() {
(0, super());
}
}

View File

@ -0,0 +1,17 @@
let B = babelHelpers.decorate([dec], function (_initialize, _A) {
"use strict";
class B extends _A {
constructor() {
var _temp;
0, (_temp = super(), _initialize(this), _temp);
}
}
return {
F: B,
d: []
};
}, A);

View File

@ -0,0 +1,14 @@
@dec
class B extends A {
constructor() {
const foo = () => { super(); };
if (a) { super(); }
else { foo(); }
while (0) { super(); }
super();
}
}

View File

@ -0,0 +1,37 @@
let B = babelHelpers.decorate([dec], function (_initialize, _A) {
"use strict";
class B extends _A {
constructor() {
const foo = () => {
super();
_initialize(this);
};
if (a) {
super();
_initialize(this);
} else {
foo();
}
while (0) {
super();
_initialize(this);
}
super();
_initialize(this);
}
}
return {
F: B,
d: []
};
}, A);

View File

@ -0,0 +1,6 @@
@dec
class B extends A {
constructor() {
super();
}
}

View File

@ -0,0 +1,17 @@
let B = babelHelpers.decorate([dec], function (_initialize, _A) {
"use strict";
class B extends _A {
constructor() {
super();
_initialize(this);
}
}
return {
F: B,
d: []
};
}, A);

View File

@ -105,7 +105,17 @@ export function insertAfter(nodes) {
parentPath.isExportNamedDeclaration() ||
(parentPath.isExportDefaultDeclaration() && this.isDeclaration())
) {
return parentPath.insertAfter(nodes);
return parentPath.insertAfter(
nodes.map(node => {
// Usually after an expression we can safely insert another expression:
// A.insertAfter(B)
// foo = A; -> foo = (A, B);
// If A is an expression statement, it isn't safe anymore so we need to
// convert B to an expression statement
// A; -> A; B // No semicolon! It could break if followed by [!
return t.isExpression(node) ? t.expressionStatement(node) : node;
}),
);
} else if (
(this.isNodeType("Expression") && !this.isJSXElement()) ||
(parentPath.isForStatement() && this.key === "init")

View File

@ -235,7 +235,9 @@ describe("modification", function() {
fnPath.insertAfter(t.identifier("x"));
expect(bodyPath.get("body")).toHaveLength(2);
expect(bodyPath.get("body.1").node).toEqual(t.identifier("x"));
expect(bodyPath.get("body.1").node).toEqual(
t.expressionStatement(t.identifier("x")),
);
});
it("the ExportDefaultDeclaration, if a declaration is exported", function() {
@ -246,7 +248,9 @@ describe("modification", function() {
fnPath.insertAfter(t.identifier("x"));
expect(bodyPath.get("body")).toHaveLength(2);
expect(bodyPath.get("body.1").node).toEqual(t.identifier("x"));
expect(bodyPath.get("body.1").node).toEqual(
t.expressionStatement(t.identifier("x")),
);
});
it("the exported expression", function() {