Merge pull request #18027 from Microsoft/fix16924
Switch to arrow for ts class wrapper IIFE
This commit is contained in:
commit
05402b8596
|
@ -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);
|
||||
}
|
||||
|
@ -4036,8 +4054,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)
|
||||
|
|
|
@ -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> {
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
])
|
||||
);
|
||||
|
|
|
@ -4185,6 +4185,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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
21
tests/baselines/reference/asyncArrowInClassES5.js
Normal file
21
tests/baselines/reference/asyncArrowInClassES5.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
//// [asyncArrowInClassES5.ts]
|
||||
// https://github.com/Microsoft/TypeScript/issues/16924
|
||||
// Should capture `this`
|
||||
|
||||
class Test {
|
||||
static member = async (x: string) => { };
|
||||
}
|
||||
|
||||
|
||||
//// [asyncArrowInClassES5.js]
|
||||
// https://github.com/Microsoft/TypeScript/issues/16924
|
||||
// Should capture `this`
|
||||
var _this = this;
|
||||
var Test = /** @class */ (function () {
|
||||
function Test() {
|
||||
}
|
||||
Test.member = function (x) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||
return [2 /*return*/];
|
||||
}); }); };
|
||||
return Test;
|
||||
}());
|
12
tests/baselines/reference/asyncArrowInClassES5.symbols
Normal file
12
tests/baselines/reference/asyncArrowInClassES5.symbols
Normal file
|
@ -0,0 +1,12 @@
|
|||
=== tests/cases/compiler/asyncArrowInClassES5.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/16924
|
||||
// Should capture `this`
|
||||
|
||||
class Test {
|
||||
>Test : Symbol(Test, Decl(asyncArrowInClassES5.ts, 0, 0))
|
||||
|
||||
static member = async (x: string) => { };
|
||||
>member : Symbol(Test.member, Decl(asyncArrowInClassES5.ts, 3, 12))
|
||||
>x : Symbol(x, Decl(asyncArrowInClassES5.ts, 4, 27))
|
||||
}
|
||||
|
13
tests/baselines/reference/asyncArrowInClassES5.types
Normal file
13
tests/baselines/reference/asyncArrowInClassES5.types
Normal file
|
@ -0,0 +1,13 @@
|
|||
=== tests/cases/compiler/asyncArrowInClassES5.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/16924
|
||||
// Should capture `this`
|
||||
|
||||
class Test {
|
||||
>Test : Test
|
||||
|
||||
static member = async (x: string) => { };
|
||||
>member : (x: string) => Promise<void>
|
||||
>async (x: string) => { } : (x: string) => Promise<void>
|
||||
>x : string
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ var stringOrNumberOrUndefined = C.inStaticNestedArrowFunction;
|
|||
|
||||
|
||||
//// [output.js]
|
||||
var _this = this;
|
||||
var C = /** @class */ (function () {
|
||||
function C() {
|
||||
var _this = this;
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ class Vector {
|
|||
}
|
||||
|
||||
//// [thisInArrowFunctionInStaticInitializer1.js]
|
||||
var _this = this;
|
||||
function log(a) { }
|
||||
var Vector = /** @class */ (function () {
|
||||
function Vector() {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -21,6 +21,7 @@ class Foo {
|
|||
}
|
||||
|
||||
//// [thisInOuterClassBody.js]
|
||||
var _this = this;
|
||||
var Foo = /** @class */ (function () {
|
||||
function Foo() {
|
||||
this.x = this;
|
||||
|
|
|
@ -8,6 +8,7 @@ class C2<T> {
|
|||
}
|
||||
|
||||
//// [typeOfThisInStaticMembers2.js]
|
||||
var _this = this;
|
||||
var C = /** @class */ (function () {
|
||||
function C() {
|
||||
}
|
||||
|
|
9
tests/cases/compiler/asyncArrowInClassES5.ts
Normal file
9
tests/cases/compiler/asyncArrowInClassES5.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
// @noEmitHelpers: true
|
||||
// @lib: es2015
|
||||
// @target: es5
|
||||
// https://github.com/Microsoft/TypeScript/issues/16924
|
||||
// Should capture `this`
|
||||
|
||||
class Test {
|
||||
static member = async (x: string) => { };
|
||||
}
|
Loading…
Reference in a new issue