Skip to content

Commit

Permalink
rewrite void-returning statements in constructors that capture result…
Browse files Browse the repository at this point in the history
… of super call (#11868)

* rewrite void-returning statements in constructors that capture result of super call

* linter
  • Loading branch information
vladima authored and mhegazy committed Oct 28, 2016
1 parent a163641 commit 86138e3
Show file tree
Hide file tree
Showing 5 changed files with 410 additions and 4 deletions.
30 changes: 26 additions & 4 deletions src/compiler/transformers/es2015.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ namespace ts {
let enclosingFunction: FunctionLikeDeclaration;
let enclosingNonArrowFunction: FunctionLikeDeclaration;
let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement;
let isInConstructorWithCapturedSuper: boolean;

/**
* Used to track if we are emitting body of the converted loop
Expand Down Expand Up @@ -231,14 +232,17 @@ namespace ts {
const savedCurrentParent = currentParent;
const savedCurrentNode = currentNode;
const savedConvertedLoopState = convertedLoopState;
const savedIsInConstructorWithCapturedSuper = isInConstructorWithCapturedSuper;
if (nodeStartsNewLexicalEnvironment(node)) {
// don't treat content of nodes that start new lexical environment as part of converted loop copy
// don't treat content of nodes that start new lexical environment as part of converted loop copy or constructor body
isInConstructorWithCapturedSuper = false;
convertedLoopState = undefined;
}

onBeforeVisitNode(node);
const visited = f(node);

isInConstructorWithCapturedSuper = savedIsInConstructorWithCapturedSuper;
convertedLoopState = savedConvertedLoopState;
enclosingFunction = savedEnclosingFunction;
enclosingNonArrowFunction = savedEnclosingNonArrowFunction;
Expand All @@ -251,17 +255,31 @@ namespace ts {
return visited;
}

function returnCapturedThis(node: Node): Node {
return setOriginalNode(createReturn(createIdentifier("_this")), node);
}

function isReturnVoidStatementInConstructorWithCapturedSuper(node: Node): boolean {
return isInConstructorWithCapturedSuper && node.kind === SyntaxKind.ReturnStatement && !(<ReturnStatement>node).expression;
}

function shouldCheckNode(node: Node): boolean {
return (node.transformFlags & TransformFlags.ES2015) !== 0 ||
node.kind === SyntaxKind.LabeledStatement ||
(isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node));
}

function visitorWorker(node: Node): VisitResult<Node> {
if (shouldCheckNode(node)) {
if (isReturnVoidStatementInConstructorWithCapturedSuper(node)) {
return returnCapturedThis(<ReturnStatement>node);
}
else if (shouldCheckNode(node)) {
return visitJavaScript(node);
}
else if (node.transformFlags & TransformFlags.ContainsES2015) {
else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) {
// we want to dive in this branch either if node has children with ES2015 specific syntax
// or we are inside constructor that captures result of the super call so all returns without expression should be
// rewritten. Note: we skip expressions since returns should never appear there
return visitEachChild(node, visitor, context);
}
else {
Expand All @@ -283,6 +301,7 @@ namespace ts {
function visitNodesInConvertedLoop(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.ReturnStatement:
node = isReturnVoidStatementInConstructorWithCapturedSuper(node) ? returnCapturedThis(node) : node;
return visitReturnStatement(<ReturnStatement>node);

case SyntaxKind.VariableStatement:
Expand Down Expand Up @@ -864,7 +883,10 @@ namespace ts {
}

if (constructor) {
const body = saveStateAndInvoke(constructor, constructor => visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset));
const body = saveStateAndInvoke(constructor, constructor => {
isInConstructorWithCapturedSuper = superCaptureStatus === SuperCaptureResult.ReplaceSuperCapture;
return visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset);
});
addRange(statements, body);
}

Expand Down
123 changes: 123 additions & 0 deletions tests/baselines/reference/constructorWithCapturedSuper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//// [constructorWithCapturedSuper.ts]
let oneA: A;

class A {
constructor() {
return oneA;
}
}

class B extends A {
constructor(x: number) {
super();
if (x === 1) {
return;
}
while (x < 2) {
return;
}
try {
return
}
catch (e) {
return;
}
finally {
return;
}
}
}

class C extends A {
constructor(x: number) {
super();
for (let i = 0; i < 10; ++i) {
() => i + x;
if (x === 1) {
return;
}
}
}
}

class D extends A {
constructor(x: number) {
super();
() => {
return;
}
function foo() {
return;
}
}
}

//// [constructorWithCapturedSuper.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var oneA;
var A = (function () {
function A() {
return oneA;
}
return A;
}());
var B = (function (_super) {
__extends(B, _super);
function B(x) {
var _this = _super.call(this) || this;
if (x === 1) {
return _this;
}
while (x < 2) {
return _this;
}
try {
return _this;
}
catch (e) {
return _this;
}
finally {
return _this;
}
return _this;
}
return B;
}(A));
var C = (function (_super) {
__extends(C, _super);
function C(x) {
var _this = _super.call(this) || this;
var _loop_1 = function (i) {
(function () { return i + x; });
if (x === 1) {
return { value: _this };
}
};
for (var i = 0; i < 10; ++i) {
var state_1 = _loop_1(i);
if (typeof state_1 === "object")
return state_1.value;
}
return _this;
}
return C;
}(A));
var D = (function (_super) {
__extends(D, _super);
function D(x) {
var _this = _super.call(this) || this;
(function () {
return;
});
function foo() {
return;
}
return _this;
}
return D;
}(A));
96 changes: 96 additions & 0 deletions tests/baselines/reference/constructorWithCapturedSuper.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
=== tests/cases/compiler/constructorWithCapturedSuper.ts ===
let oneA: A;
>oneA : Symbol(oneA, Decl(constructorWithCapturedSuper.ts, 0, 3))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

class A {
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

constructor() {
return oneA;
>oneA : Symbol(oneA, Decl(constructorWithCapturedSuper.ts, 0, 3))
}
}

class B extends A {
>B : Symbol(B, Decl(constructorWithCapturedSuper.ts, 6, 1))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

constructor(x: number) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))

super();
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

if (x === 1) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))

return;
}
while (x < 2) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))

return;
}
try {
return
}
catch (e) {
>e : Symbol(e, Decl(constructorWithCapturedSuper.ts, 20, 15))

return;
}
finally {
return;
}
}
}

class C extends A {
>C : Symbol(C, Decl(constructorWithCapturedSuper.ts, 27, 1))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

constructor(x: number) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))

super();
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

for (let i = 0; i < 10; ++i) {
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))

() => i + x;
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))

if (x === 1) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))

return;
}
}
}
}

class D extends A {
>D : Symbol(D, Decl(constructorWithCapturedSuper.ts, 39, 1))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

constructor(x: number) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 42, 16))

super();
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))

() => {
return;
}
function foo() {
>foo : Symbol(foo, Decl(constructorWithCapturedSuper.ts, 46, 9))

return;
}
}
}
Loading

0 comments on commit 86138e3

Please sign in to comment.