Skip to content

Commit

Permalink
Switch to arrow for ts class wrapper IIFE
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Aug 25, 2017
1 parent 9bb1915 commit 01b7df6
Show file tree
Hide file tree
Showing 15 changed files with 64 additions and 60 deletions.
43 changes: 42 additions & 1 deletion src/compiler/factory.ts
Expand Up @@ -2372,6 +2372,24 @@ namespace ts {
);
}

export function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression;
export function createImmediatelyInvokedArrowFunction(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression;
export function createImmediatelyInvokedArrowFunction(statements: Statement[], param?: ParameterDeclaration, paramValue?: Expression) {
return createCall(
createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ param ? [param] : [],
/*type*/ undefined,
/*equalsGreaterThanToken*/ undefined,
createBlock(statements, /*multiLine*/ true)
),
/*typeArguments*/ undefined,
/*argumentsArray*/ paramValue ? [paramValue] : []
);
}


export function createComma(left: Expression, right: Expression) {
return <Expression>createBinary(left, SyntaxKind.CommaToken, right);
}
Expand Down Expand Up @@ -4040,8 +4058,31 @@ namespace ts {
}
}

/**
* Determines whether a node is a parenthesized expression that can be ignored when recreating outer expressions.
*
* A parenthesized expression can be ignored when all of the following are true:
*
* - It's `pos` and `end` are not -1
* - It does not have a custom source map range
* - It does not have a custom comment range
* - It does not have synthetic leading or trailing comments
*
* If an outermost parenthesized expression is ignored, but the containing expression requires a parentheses around
* the expression to maintain precedence, a new parenthesized expression should be created automatically when
* the containing expression is created/updated.
*/
function isIgnorableParen(node: Expression) {
return node.kind === SyntaxKind.ParenthesizedExpression
&& nodeIsSynthesized(node)
&& nodeIsSynthesized(getSourceMapRange(node))
&& nodeIsSynthesized(getCommentRange(node))
&& !some(getSyntheticLeadingComments(node))
&& !some(getSyntheticTrailingComments(node));
}

export function recreateOuterExpressions(outerExpression: Expression | undefined, innerExpression: Expression, kinds = OuterExpressionKinds.All): Expression {
if (outerExpression && isOuterExpression(outerExpression, kinds)) {
if (outerExpression && isOuterExpression(outerExpression, kinds) && !isIgnorableParen(outerExpression)) {
return updateOuterExpression(
outerExpression,
recreateOuterExpressions(outerExpression.expression, innerExpression)
Expand Down
59 changes: 4 additions & 55 deletions src/compiler/transformers/es2015.ts
Expand Up @@ -339,64 +339,12 @@ namespace ts {
&& !(<ReturnStatement>node).expression;
}

function isClassLikeVariableStatement(node: Node) {
if (!isVariableStatement(node)) return false;
const variable = singleOrUndefined((<VariableStatement>node).declarationList.declarations);
return variable
&& variable.initializer
&& isIdentifier(variable.name)
&& (isClassLike(variable.initializer)
|| (isAssignmentExpression(variable.initializer)
&& isIdentifier(variable.initializer.left)
&& isClassLike(variable.initializer.right)));
}

function isTypeScriptClassWrapper(node: Node) {
const call = tryCast(node, isCallExpression);
if (!call || isParseTreeNode(call) ||
some(call.typeArguments) ||
some(call.arguments)) {
return false;
}

const func = tryCast(skipOuterExpressions(call.expression), isFunctionExpression);
if (!func || isParseTreeNode(func) ||
some(func.typeParameters) ||
some(func.parameters) ||
func.type ||
!func.body) {
return false;
}

const statements = func.body.statements;
if (statements.length < 2) {
return false;
}

const firstStatement = statements[0];
if (isParseTreeNode(firstStatement) ||
!isClassLike(firstStatement) &&
!isClassLikeVariableStatement(firstStatement)) {
return false;
}

const lastStatement = elementAt(statements, -1);
const returnStatement = tryCast(isVariableStatement(lastStatement) ? elementAt(statements, -2) : lastStatement, isReturnStatement);
if (!returnStatement ||
!returnStatement.expression ||
!isIdentifier(skipOuterExpressions(returnStatement.expression))) {
return false;
}

return true;
}

function shouldVisitNode(node: Node): boolean {
return (node.transformFlags & TransformFlags.ContainsES2015) !== 0
|| convertedLoopState !== undefined
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block)))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node))
|| isTypeScriptClassWrapper(node);
|| (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) !== 0;
}

function visitor(node: Node): VisitResult<Node> {
Expand Down Expand Up @@ -3308,13 +3256,14 @@ namespace ts {
* @param node a CallExpression.
*/
function visitCallExpression(node: CallExpression) {
if (isTypeScriptClassWrapper(node)) {
if (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) {
return visitTypeScriptClassWrapper(node);
}

if (node.transformFlags & TransformFlags.ES2015) {
return visitCallExpressionWithPotentialCapturedThisAssignment(node, /*assignToCapturedThis*/ true);
}

return updateCall(
node,
visitNode(node.expression, callExpressionVisitor, isExpression),
Expand Down Expand Up @@ -3357,7 +3306,7 @@ namespace ts {

// We skip any outer expressions in a number of places to get to the innermost
// expression, but we will restore them later to preserve comments and source maps.
const body = cast(skipOuterExpressions(node.expression), isFunctionExpression).body;
const body = cast(cast(skipOuterExpressions(node.expression), isArrowFunction).body, isBlock);

// The class statements are the statements generated by visiting the first statement of the
// body (1), while all other statements are added to remainingStatements (2)
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/transformers/ts.ts
Expand Up @@ -613,13 +613,16 @@ namespace ts {

addRange(statements, context.endLexicalEnvironment());

const iife = createImmediatelyInvokedArrowFunction(statements);
setEmitFlags(iife, EmitFlags.TypeScriptClassWrapper);

const varStatement = createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList([
createVariableDeclaration(
getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ false),
/*type*/ undefined,
createImmediatelyInvokedFunctionExpression(statements)
iife
)
])
);
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Expand Up @@ -4170,6 +4170,7 @@ namespace ts {
HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions.
/*@internal*/ TypeScriptClassWrapper = 1 << 25, // The node is an IIFE class wrapper created by the ts transform.
}

export interface EmitHelper {
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/utilities.ts
Expand Up @@ -2068,9 +2068,9 @@ namespace ts {
|| kind === SyntaxKind.SourceFile;
}

export function nodeIsSynthesized(node: TextRange): boolean {
return positionIsSynthesized(node.pos)
|| positionIsSynthesized(node.end);
export function nodeIsSynthesized(range: TextRange): boolean {
return positionIsSynthesized(range.pos)
|| positionIsSynthesized(range.end);
}

export function getOriginalSourceFile(sourceFile: SourceFile) {
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/decoratorOnClassMethod11.js
Expand Up @@ -17,6 +17,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
};
var M;
(function (M) {
var _this = this;
var C = /** @class */ (function () {
function C() {
}
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/decoratorOnClassMethod12.js
Expand Up @@ -28,6 +28,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
};
var M;
(function (M) {
var _this = this;
var S = /** @class */ (function () {
function S() {
}
Expand Down
Expand Up @@ -124,6 +124,7 @@ var stringOrNumberOrUndefined = C.inStaticNestedArrowFunction;


//// [output.js]
var _this = this;
var C = /** @class */ (function () {
function C() {
var _this = this;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/superAccess2.js
Expand Up @@ -35,6 +35,7 @@ var __extends = (this && this.__extends) || (function () {
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var _this = this;
var P = /** @class */ (function () {
function P() {
}
Expand Down
Expand Up @@ -9,6 +9,7 @@ class Vector {
}

//// [thisInArrowFunctionInStaticInitializer1.js]
var _this = this;
function log(a) { }
var Vector = /** @class */ (function () {
function Vector() {
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/thisInConstructorParameter2.js
Expand Up @@ -10,6 +10,7 @@ class P {
}

//// [thisInConstructorParameter2.js]
var _this = this;
var P = /** @class */ (function () {
function P(z, zz) {
if (z === void 0) { z = this; }
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/thisInInvalidContexts.js
Expand Up @@ -59,6 +59,7 @@ var __extends = (this && this.__extends) || (function () {
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var _this = this;
//'this' in static member initializer
var ErrClass1 = /** @class */ (function () {
function ErrClass1() {
Expand Down
Expand Up @@ -60,6 +60,7 @@ var __extends = (this && this.__extends) || (function () {
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var _this = this;
//'this' in static member initializer
var ErrClass1 = /** @class */ (function () {
function ErrClass1() {
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/thisInOuterClassBody.js
Expand Up @@ -21,6 +21,7 @@ class Foo {
}

//// [thisInOuterClassBody.js]
var _this = this;
var Foo = /** @class */ (function () {
function Foo() {
this.x = this;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/typeOfThisInStaticMembers2.js
Expand Up @@ -8,6 +8,7 @@ class C2<T> {
}

//// [typeOfThisInStaticMembers2.js]
var _this = this;
var C = /** @class */ (function () {
function C() {
}
Expand Down

0 comments on commit 01b7df6

Please sign in to comment.