Remove 'this'-related tx flag usage from transformConstructorBody

This commit is contained in:
Ron Buckton 2019-02-27 18:11:01 -08:00
parent 510bef6856
commit b02dab4e00
14 changed files with 317 additions and 263 deletions

View File

@ -3839,6 +3839,7 @@ namespace ts {
case SyntaxKind.ThisKeyword:
// Mark this node and its ancestors as containing a lexical `this` keyword.
transformFlags |= TransformFlags.AssertES2015;
transformFlags |= TransformFlags.ContainsLexicalThis;
break;

View File

@ -145,30 +145,6 @@ namespace ts {
loopOutParameters: LoopOutParameter[];
}
const enum SuperCaptureResult {
/**
* A capture may have been added for calls to 'super', but
* the caller should emit subsequent statements normally.
*/
NoReplacement,
/**
* A call to 'super()' got replaced with a capturing statement like:
*
* var _this = _super.call(...) || this;
*
* Callers should skip the current statement.
*/
ReplaceSuperCapture,
/**
* A call to 'super()' got replaced with a capturing statement like:
*
* return _super.call(...) || this;
*
* Callers should skip the current statement and avoid any returns of '_this'.
*/
ReplaceWithReturn,
}
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined) => Statement;
// Facts we track as we traverse the tree
@ -262,7 +238,6 @@ namespace ts {
SubtreeFactsMask = ~AncestorFactsMask,
PropagateNewTargetMask = NewTarget,
ArrowFunctionSubtreeExcludes = None,
FunctionSubtreeExcludes = NewTarget | LexicalThis | CapturedLexicalThis,
}
@ -528,22 +503,23 @@ namespace ts {
function visitSourceFile(node: SourceFile): SourceFile {
const ancestorFacts = enterSubtree(HierarchyFacts.SourceFileExcludes, HierarchyFacts.SourceFileIncludes);
const prologue: Statement[] = [];
const statements: Statement[] = [];
startLexicalEnvironment();
let statementOffset: number | undefined = addStandardPrologue(statements, node.statements, /*ensureUseStrict*/ false);
addCaptureThisForNodeIfNeeded(statements, node);
statementOffset = addCustomPrologue(statements, node.statements, statementOffset, visitor);
let statementOffset = addStandardPrologue(prologue, node.statements, /*ensureUseStrict*/ false);
insertCaptureThisForNodeIfNeeded(prologue, node);
statementOffset = addCustomPrologue(prologue, node.statements, statementOffset, visitor);
addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset));
if (taggedTemplateStringDeclarations) {
statements.push(
createVariableStatement(/*modifiers*/ undefined,
createVariableDeclarationList(taggedTemplateStringDeclarations)));
}
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(prologue, endLexicalEnvironment());
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return updateSourceFileNode(
node,
setTextRange(createNodeArray(statements), node.statements)
setTextRange(createNodeArray(concatenate(prologue, statements)), node.statements)
);
}
@ -597,6 +573,9 @@ namespace ts {
function visitThisKeyword(node: Node): Node {
hierarchyFacts |= HierarchyFacts.LexicalThis;
if (hierarchyFacts & HierarchyFacts.ArrowFunction) {
hierarchyFacts |= HierarchyFacts.CapturedLexicalThis;
}
if (convertedLoopState) {
if (hierarchyFacts & HierarchyFacts.ArrowFunction) {
// if the enclosing function is an ArrowFunction then we use the captured 'this' keyword.
@ -848,7 +827,7 @@ namespace ts {
setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps);
statements.push(statement);
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const block = createBlock(setTextRange(createNodeArray(statements), /*location*/ node.members), /*multiLine*/ true);
setEmitFlags(block, EmitFlags.NoComments);
@ -926,6 +905,28 @@ namespace ts {
|| <ParameterDeclaration[]>[];
}
function createDefaultConstructorBody(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
// We must be here because the user didn't write a constructor
// but we needed to call 'super(...args)' anyway as per 14.5.14 of the ES2016 spec.
// If that's the case we can just immediately return the result of a 'super()' call.
const statements: Statement[] = [];
resumeLexicalEnvironment();
mergeLexicalEnvironment(statements, endLexicalEnvironment());
if (isDerivedClass) {
// return _super !== null && _super.apply(this, arguments) || this;
statements.push(createReturn(createDefaultSuperCallOrThis()));
}
const statementsArray = createNodeArray(statements);
setTextRange(statementsArray, node.members);
const block = createBlock(statementsArray, /*multiLine*/ true);
setTextRange(block, node);
setEmitFlags(block, EmitFlags.NoComments);
return block;
}
/**
* Transforms the body of a constructor declaration of a class.
*
@ -935,82 +936,153 @@ namespace ts {
* @param hasSynthesizedSuper A value indicating whether the constructor starts with a
* synthesized `super` call.
*/
function transformConstructorBody(constructor: ConstructorDeclaration | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments | undefined, hasSynthesizedSuper: boolean) {
const statements: Statement[] = [];
resumeLexicalEnvironment();
let statementOffset = -1;
if (hasSynthesizedSuper) {
// If a super call has already been synthesized,
// we're going to assume that we should just transform everything after that.
// The assumption is that no prior step in the pipeline has added any prologue directives.
statementOffset = 0;
}
else if (constructor) {
statementOffset = addStandardPrologue(statements, constructor.body!.statements, /*ensureUseStrict*/ false);
}
if (constructor) {
addDefaultValueAssignmentsIfNeeded(statements, constructor);
addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper);
if (!hasSynthesizedSuper) {
// If no super call has been synthesized, emit custom prologue directives.
statementOffset = addCustomPrologue(statements, constructor.body!.statements, statementOffset, visitor);
}
Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!");
}
function transformConstructorBody(constructor: ConstructorDeclaration & { body: FunctionBody } | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments | undefined, hasSynthesizedSuper: boolean) {
// determine whether the class is known syntactically to be a derived class (e.g. a
// class that extends a value that is not syntactically known to be `null`).
const isDerivedClass = !!extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword;
const superCaptureStatus = declareOrCaptureOrReturnThisForConstructorIfNeeded(statements, constructor, isDerivedClass, hasSynthesizedSuper, statementOffset);
// The last statement expression was replaced. Skip it.
if (superCaptureStatus === SuperCaptureResult.ReplaceSuperCapture || superCaptureStatus === SuperCaptureResult.ReplaceWithReturn) {
statementOffset++;
// When the subclass does not have a constructor, we synthesize a *default* constructor using the following
// representation:
//
// ```
// // es2015 (source)
// class C extends Base { }
//
// // es5 (transformed)
// var C = (function (_super) {
// function C() {
// return _super.apply(this, arguments) || this;
// }
// return C;
// })(Base);
// ```
if (!constructor) return createDefaultConstructorBody(node, isDerivedClass);
// The prologue will contain all leading standard and custom prologue statements added by this transform
const prologue: Statement[] = [];
const statements: Statement[] = [];
resumeLexicalEnvironment();
// If a super call has already been synthesized,
// we're going to assume that we should just transform everything after that.
// The assumption is that no prior step in the pipeline has added any prologue directives.
let statementOffset = 0;
if (!hasSynthesizedSuper) statementOffset = addStandardPrologue(prologue, constructor.body.statements, /*ensureUseStrict*/ false);
addDefaultValueAssignmentsIfNeeded(statements, constructor);
addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper);
if (!hasSynthesizedSuper) statementOffset = addCustomPrologue(statements, constructor.body.statements, statementOffset, visitor);
// If the first statement is a call to `super()`, visit the statement directly
let superCallExpression: Expression | undefined;
if (hasSynthesizedSuper) {
superCallExpression = createDefaultSuperCallOrThis();
}
if (constructor) {
if (superCaptureStatus === SuperCaptureResult.ReplaceSuperCapture) {
hierarchyFacts |= HierarchyFacts.ConstructorWithCapturedSuper;
else if (isDerivedClass && statementOffset < constructor.body.statements.length) {
const firstStatement = constructor.body.statements[statementOffset];
if (isExpressionStatement(firstStatement) && isSuperCall(firstStatement.expression)) {
superCallExpression = visitImmediateSuperCallInBody(firstStatement.expression);
}
addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, /*start*/ statementOffset));
}
// Return `_this` unless we're sure enough that it would be pointless to add a return statement.
// If there's a constructor that we can tell returns in enough places, then we *do not* want to add a return.
if (isDerivedClass
&& superCaptureStatus !== SuperCaptureResult.ReplaceWithReturn
&& !(constructor && isSufficientlyCoveredByReturnStatements(constructor.body!))) {
statements.push(
createReturn(
createFileLevelUniqueName("_this")
)
);
if (superCallExpression) {
hierarchyFacts |= HierarchyFacts.ConstructorWithCapturedSuper;
statementOffset++; // skip this statement, we will add it after visiting the rest of the body.
}
addStatementsAfterPrologue(statements, endLexicalEnvironment());
// visit the remaining statements
addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset));
if (constructor) {
prependCaptureNewTargetIfNeeded(statements, constructor, /*copyOnWrite*/ false);
mergeLexicalEnvironment(prologue, endLexicalEnvironment());
insertCaptureNewTargetIfNeeded(prologue, constructor, /*copyOnWrite*/ false);
if (isDerivedClass) {
if (superCallExpression && statementOffset === constructor.body.statements.length && !(hierarchyFacts & HierarchyFacts.LexicalThis)) {
// If the subclass constructor does *not* contain `this` and *ends* with a `super()` call, we will use the
// following representation:
//
// ```
// // es2015 (source)
// class C extends Base {
// constructor() {
// super("foo");
// }
// }
//
// // es5 (transformed)
// var C = (function (_super) {
// function C() {
// return _super.call(this, "foo") || this;
// }
// return C;
// })(Base);
// ```
const superCall = cast(cast(superCallExpression, isBinaryExpression).left, isCallExpression);
const returnStatement = createReturn(superCallExpression);
setCommentRange(returnStatement, getCommentRange(superCall));
setEmitFlags(superCall, EmitFlags.NoComments);
statements.push(returnStatement);
}
else {
// Otherwise, we will use the following transformed representation for calls to `super()` in a constructor:
//
// ```
// // es2015 (source)
// class C extends Base {
// constructor() {
// super("foo");
// this.x = 1;
// }
// }
//
// // es5 (transformed)
// var C = (function (_super) {
// function C() {
// var _this = _super.call(this, "foo") || this;
// _this.x = 1;
// return _this;
// }
// return C;
// })(Base);
// ```
// Since the `super()` call was the first statement, we insert the `this` capturing call to
// `super()` at the top of the list of `statements` (after any pre-existing custom prologues).
insertCaptureThisForNode(statements, constructor, superCallExpression || createActualThis());
if (!isSufficientlyCoveredByReturnStatements(constructor.body)) {
statements.push(createReturn(createFileLevelUniqueName("_this")));
}
}
}
else {
// If a class is not derived from a base class or does not have a call to `super()`, `this` is only
// captured when necessitated by an arrow function capturing the lexical `this`:
//
// ```
// // es2015
// class C {}
//
// // es5
// var C = (function () {
// function C() {
// }
// return C;
// })();
// ```
insertCaptureThisForNodeIfNeeded(prologue, constructor);
}
const block = createBlock(
setTextRange(
createNodeArray(
statements
concatenate(prologue, statements)
),
/*location*/ constructor ? constructor.body!.statements : node.members
/*location*/ constructor.body.statements
),
/*multiLine*/ true
);
setTextRange(block, constructor ? constructor.body : node);
if (!constructor) {
setEmitFlags(block, EmitFlags.NoComments);
}
setTextRange(block, constructor.body);
return block;
}
@ -1044,104 +1116,6 @@ namespace ts {
return false;
}
/**
* Declares a `_this` variable for derived classes and for when arrow functions capture `this`.
*
* @returns The new statement offset into the `statements` array.
*/
function declareOrCaptureOrReturnThisForConstructorIfNeeded(
statements: Statement[],
ctor: ConstructorDeclaration | undefined,
isDerivedClass: boolean,
hasSynthesizedSuper: boolean,
statementOffset: number) {
// If this isn't a derived class, just capture 'this' for arrow functions if necessary.
if (!isDerivedClass) {
if (ctor) {
addCaptureThisForNodeIfNeeded(statements, ctor);
}
return SuperCaptureResult.NoReplacement;
}
// We must be here because the user didn't write a constructor
// but we needed to call 'super(...args)' anyway as per 14.5.14 of the ES2016 spec.
// If that's the case we can just immediately return the result of a 'super()' call.
if (!ctor) {
statements.push(createReturn(createDefaultSuperCallOrThis()));
return SuperCaptureResult.ReplaceWithReturn;
}
// The constructor exists, but it and the 'super()' call it contains were generated
// for something like property initializers.
// Create a captured '_this' variable and assume it will subsequently be used.
if (hasSynthesizedSuper) {
captureThisForNode(statements, ctor, createDefaultSuperCallOrThis());
enableSubstitutionsForCapturedThis();
return SuperCaptureResult.ReplaceSuperCapture;
}
// Most of the time, a 'super' call will be the first real statement in a constructor body.
// In these cases, we'd like to transform these into a *single* statement instead of a declaration
// followed by an assignment statement for '_this'. For instance, if we emitted without an initializer,
// we'd get:
//
// var _this;
// _this = _super.call(...) || this;
//
// instead of
//
// var _this = _super.call(...) || this;
//
// Additionally, if the 'super()' call is the last statement, we should just avoid capturing
// entirely and immediately return the result like so:
//
// return _super.call(...) || this;
//
let firstStatement: Statement | undefined;
let superCallExpression: Expression | undefined;
const ctorStatements = ctor.body!.statements;
if (statementOffset < ctorStatements.length) {
firstStatement = ctorStatements[statementOffset];
if (firstStatement.kind === SyntaxKind.ExpressionStatement && isSuperCall((firstStatement as ExpressionStatement).expression)) {
superCallExpression = visitImmediateSuperCallInBody((firstStatement as ExpressionStatement).expression as CallExpression);
}
}
// Return the result if we have an immediate super() call on the last statement,
// but only if the constructor itself doesn't use 'this' elsewhere.
if (superCallExpression
&& statementOffset === ctorStatements.length - 1
&& !(ctor.transformFlags & (TransformFlags.ContainsLexicalThis | TransformFlags.ContainsCapturedLexicalThis))) {
const returnStatement = createReturn(superCallExpression);
if (superCallExpression.kind !== SyntaxKind.BinaryExpression
|| (superCallExpression as BinaryExpression).left.kind !== SyntaxKind.CallExpression) {
Debug.fail("Assumed generated super call would have form 'super.call(...) || this'.");
}
// Shift comments from the original super call to the return statement.
setCommentRange(returnStatement, getCommentRange(
setEmitFlags(
(superCallExpression as BinaryExpression).left,
EmitFlags.NoComments)));
statements.push(returnStatement);
return SuperCaptureResult.ReplaceWithReturn;
}
// Perform the capture.
captureThisForNode(statements, ctor, superCallExpression || createActualThis());
// If we're actually replacing the original statement, we need to signal this to the caller.
if (superCallExpression) {
return SuperCaptureResult.ReplaceSuperCapture;
}
return SuperCaptureResult.NoReplacement;
}
function createActualThis() {
return setEmitFlags(createThis(), EmitFlags.NoSubstitution);
}
@ -1232,11 +1206,12 @@ namespace ts {
* @param statements The statements for the new function body.
* @param node A function-like node.
*/
function addDefaultValueAssignmentsIfNeeded(statements: Statement[], node: FunctionLikeDeclaration): void {
function addDefaultValueAssignmentsIfNeeded(statements: Statement[], node: FunctionLikeDeclaration): boolean {
if (!some(node.parameters, hasDefaultValueOrBindingPattern)) {
return;
return false;
}
let added = false;
for (const parameter of node.parameters) {
const { name, initializer, dotDotDotToken } = parameter;
@ -1247,12 +1222,14 @@ namespace ts {
}
if (isBindingPattern(name)) {
addDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer);
added = insertDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer) || added;
}
else if (initializer) {
addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer);
insertDefaultValueAssignmentForInitializer(statements, parameter, name, initializer);
added = true;
}
}
return added;
}
/**
@ -1263,14 +1240,13 @@ namespace ts {
* @param name The name of the parameter.
* @param initializer The initializer for the parameter.
*/
function addDefaultValueAssignmentForBindingPattern(statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression | undefined): void {
const temp = getGeneratedNameForNode(parameter);
function insertDefaultValueAssignmentForBindingPattern(statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression | undefined): boolean {
// In cases where a binding pattern is simply '[]' or '{}',
// we usually don't want to emit a var declaration; however, in the presence
// of an initializer, we must emit that expression to preserve side effects.
if (name.elements.length > 0) {
statements.push(
insertStatementAfterCustomPrologue(
statements,
setEmitFlags(
createVariableStatement(
/*modifiers*/ undefined,
@ -1280,27 +1256,31 @@ namespace ts {
visitor,
context,
FlattenLevel.All,
temp
getGeneratedNameForNode(parameter)
)
)
),
EmitFlags.CustomPrologue
)
);
return true;
}
else if (initializer) {
statements.push(
insertStatementAfterCustomPrologue(
statements,
setEmitFlags(
createExpressionStatement(
createAssignment(
temp,
getGeneratedNameForNode(parameter),
visitNode(initializer, visitor, isExpression)
)
),
EmitFlags.CustomPrologue
)
);
return true;
}
return false;
}
/**
@ -1311,7 +1291,7 @@ namespace ts {
* @param name The name of the parameter.
* @param initializer The initializer for the parameter.
*/
function addDefaultValueAssignmentForInitializer(statements: Statement[], parameter: ParameterDeclaration, name: Identifier, initializer: Expression): void {
function insertDefaultValueAssignmentForInitializer(statements: Statement[], parameter: ParameterDeclaration, name: Identifier, initializer: Expression): void {
initializer = visitNode(initializer, visitor, isExpression);
const statement = createIf(
createTypeCheck(getSynthesizedClone(name), "undefined"),
@ -1340,7 +1320,7 @@ namespace ts {
startOnNewLine(statement);
setTextRange(statement, parameter);
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue | EmitFlags.NoComments);
statements.push(statement);
insertStatementAfterCustomPrologue(statements, statement);
}
/**
@ -1364,10 +1344,11 @@ namespace ts {
* part of a constructor declaration with a
* synthesized call to `super`
*/
function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void {
function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): boolean {
const prologueStatements: Statement[] = [];
const parameter = lastOrUndefined(node.parameters);
if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) {
return;
return false;
}
// `declarationName` is the name of the local declaration for the parameter.
@ -1380,7 +1361,7 @@ namespace ts {
const temp = createLoopVariable();
// var param = [];
statements.push(
prologueStatements.push(
setEmitFlags(
setTextRange(
createVariableStatement(
@ -1439,11 +1420,11 @@ namespace ts {
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
startOnNewLine(forStatement);
statements.push(forStatement);
prologueStatements.push(forStatement);
if (parameter.name.kind !== SyntaxKind.Identifier) {
// do the actual destructuring of the rest parameter if necessary
statements.push(
prologueStatements.push(
setEmitFlags(
setTextRange(
createVariableStatement(
@ -1458,6 +1439,9 @@ namespace ts {
)
);
}
insertStatementsAfterCustomPrologue(statements, prologueStatements);
return true;
}
/**
@ -1466,13 +1450,15 @@ namespace ts {
* @param statements The statements for the new function body.
* @param node A node.
*/
function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): void {
function insertCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): boolean {
if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) {
captureThisForNode(statements, node, createThis());
insertCaptureThisForNode(statements, node, createThis());
return true;
}
return false;
}
function captureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined): void {
function insertCaptureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined): void {
enableSubstitutionsForCapturedThis();
const captureThisStatement = createVariableStatement(
/*modifiers*/ undefined,
@ -1486,10 +1472,10 @@ namespace ts {
);
setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue);
setSourceMapRange(captureThisStatement, node);
statements.push(captureThisStatement);
insertStatementAfterCustomPrologue(statements, captureThisStatement);
}
function prependCaptureNewTargetIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, copyOnWrite: boolean): Statement[] {
function insertCaptureNewTargetIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, copyOnWrite: boolean): Statement[] {
if (hierarchyFacts & HierarchyFacts.NewTarget) {
let newTarget: Expression;
switch (node.kind) {
@ -1549,11 +1535,13 @@ namespace ts {
])
);
setEmitFlags(captureNewTargetStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue);
if (copyOnWrite) {
return [captureNewTargetStatement, ...statements];
statements = statements.slice();
}
statements.unshift(captureNewTargetStatement);
insertStatementAfterCustomPrologue(statements, captureNewTargetStatement);
}
return statements;
@ -1716,9 +1704,6 @@ namespace ts {
* @param node An ArrowFunction node.
*/
function visitArrowFunction(node: ArrowFunction) {
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
enableSubstitutionsForCapturedThis();
}
const savedConvertedLoopState = convertedLoopState;
convertedLoopState = undefined;
const ancestorFacts = enterSubtree(HierarchyFacts.ArrowFunctionExcludes, HierarchyFacts.ArrowFunctionIncludes);
@ -1734,7 +1719,14 @@ namespace ts {
setTextRange(func, node);
setOriginalNode(func, node);
setEmitFlags(func, EmitFlags.CapturesThis);
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
if (hierarchyFacts & HierarchyFacts.CapturedLexicalThis) {
enableSubstitutionsForCapturedThis();
}
// If an arrow function contains
exitSubtree(ancestorFacts, HierarchyFacts.ArrowFunctionSubtreeExcludes, HierarchyFacts.None);
convertedLoopState = savedConvertedLoopState;
return func;
}
@ -1862,7 +1854,7 @@ namespace ts {
let statementsLocation: TextRange;
let closeBraceLocation: TextRange | undefined;
const leadingStatements: Statement[] = [];
const prologue: Statement[] = [];
const statements: Statement[] = [];
const body = node.body!;
let statementOffset: number | undefined;
@ -1871,16 +1863,15 @@ namespace ts {
if (isBlock(body)) {
// ensureUseStrict is false because no new prologue-directive should be added.
// addStandardPrologue will put already-existing directives at the beginning of the target statement-array
statementOffset = addStandardPrologue(leadingStatements, body.statements, /*ensureUseStrict*/ false);
statementOffset = addStandardPrologue(prologue, body.statements, /*ensureUseStrict*/ false);
}
addCaptureThisForNodeIfNeeded(leadingStatements, node);
addDefaultValueAssignmentsIfNeeded(leadingStatements, node);
addRestParameterIfNeeded(leadingStatements, node, /*inConstructorWithSynthesizedSuper*/ false);
multiLine = addDefaultValueAssignmentsIfNeeded(statements, node) || multiLine;
multiLine = addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false) || multiLine;
if (isBlock(body)) {
// addCustomPrologue puts already-existing directives at the beginning of the target statement-array
statementOffset = addCustomPrologue(leadingStatements, body.statements, statementOffset, visitor);
statementOffset = addCustomPrologue(statements, body.statements, statementOffset, visitor);
statementsLocation = body.statements;
addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset));
@ -1921,16 +1912,16 @@ namespace ts {
closeBraceLocation = body;
}
const lexicalEnvironment = context.endLexicalEnvironment();
addStatementsAfterPrologue(statements, lexicalEnvironment);
prependCaptureNewTargetIfNeeded(statements, node, /*copyOnWrite*/ false);
mergeLexicalEnvironment(prologue, endLexicalEnvironment());
insertCaptureNewTargetIfNeeded(prologue, node, /*copyOnWrite*/ false);
insertCaptureThisForNodeIfNeeded(prologue, node);
// If we added any final generated statements, this must be a multi-line block
if (some(leadingStatements) || some(lexicalEnvironment)) {
if (some(prologue)) {
multiLine = true;
}
const block = createBlock(setTextRange(createNodeArray([...leadingStatements, ...statements]), statementsLocation), multiLine);
const block = createBlock(setTextRange(createNodeArray(concatenate(prologue, statements)), statementsLocation), multiLine);
setTextRange(block, node.body);
if (!multiLine && singleLine) {
setEmitFlags(block, EmitFlags.SingleLine);
@ -1950,7 +1941,7 @@ namespace ts {
updated,
setTextRange(
createNodeArray(
prependCaptureNewTargetIfNeeded(updated.statements as MutableNodeArray<Statement>, node, /*copyOnWrite*/ true)
insertCaptureNewTargetIfNeeded(updated.statements as MutableNodeArray<Statement>, node, /*copyOnWrite*/ true)
),
/*location*/ updated.statements
)
@ -3107,7 +3098,7 @@ namespace ts {
}
copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOutParameter, statements);
addStatementsAfterPrologue(statements, lexicalEnvironment);
insertStatementsAfterStandardPrologue(statements, lexicalEnvironment);
const loopBody = createBlock(statements, /*multiLine*/ true);
if (isBlock(statement)) setOriginalNode(loopBody, statement);
@ -3775,7 +3766,7 @@ namespace ts {
resultingCall = createFunctionApply(
visitNode(target, callExpressionVisitor, isExpression),
visitNode(thisArg, visitor, isExpression),
node.expression.kind === SyntaxKind.SuperKeyword ? thisArg : visitNode(thisArg, visitor, isExpression),
transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, /*hasTrailingComma*/ false)
);
}
@ -3791,19 +3782,17 @@ namespace ts {
// _super.prototype.m.call(this, a)
resultingCall = createFunctionCall(
visitNode(target, callExpressionVisitor, isExpression),
visitNode(thisArg, visitor, isExpression),
node.expression.kind === SyntaxKind.SuperKeyword ? thisArg : visitNode(thisArg, visitor, isExpression),
visitNodes(node.arguments, visitor, isExpression),
/*location*/ node
);
}
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const actualThis = createThis();
setEmitFlags(actualThis, EmitFlags.NoSubstitution);
const initializer =
createLogicalOr(
resultingCall,
actualThis
createActualThis()
);
resultingCall = assignToCapturedThis
? createAssignment(createFileLevelUniqueName("_this"), initializer)

View File

@ -438,7 +438,7 @@ namespace ts {
)
);
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
// This step isn't needed if we eventually transform this to ES5.
@ -448,7 +448,7 @@ namespace ts {
enableSubstitutionForAsyncMethodsWithSuper();
const variableStatement = createSuperAccessVariableStatement(resolver, node, capturedSuperProperties);
substitutedSuperAccessors[getNodeId(variableStatement)] = true;
addStatementsAfterPrologue(statements, [variableStatement]);
insertStatementsAfterStandardPrologue(statements, [variableStatement]);
}
const block = createBlock(statements, /*multiLine*/ true);

View File

@ -689,12 +689,12 @@ namespace ts {
enableSubstitutionForAsyncMethodsWithSuper();
const variableStatement = createSuperAccessVariableStatement(resolver, node, capturedSuperProperties);
substitutedSuperAccessors[getNodeId(variableStatement)] = true;
addStatementsAfterPrologue(statements, [variableStatement]);
insertStatementsAfterStandardPrologue(statements, [variableStatement]);
}
statements.push(returnStatement);
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const block = updateBlock(node.body!, statements);
if (emitSuperHelpers && hasSuperElementAccess) {
@ -726,7 +726,7 @@ namespace ts {
const leadingStatements = endLexicalEnvironment();
if (statementOffset > 0 || some(statements) || some(leadingStatements)) {
const block = convertToFunctionBody(body, /*multiLine*/ true);
addStatementsAfterPrologue(statements, leadingStatements);
insertStatementsAfterStandardPrologue(statements, leadingStatements);
addRange(statements, block.statements.slice(statementOffset));
return updateBlock(block, setTextRange(createNodeArray(statements), block.statements));
}

View File

@ -587,7 +587,7 @@ namespace ts {
transformAndEmitStatements(body.statements, statementOffset);
const buildResult = build();
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
statements.push(createReturn(buildResult));
// Restore previous generator state

View File

@ -100,7 +100,7 @@ namespace ts {
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement));
addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset));
addExportEqualsIfNeeded(statements, /*emitAsReturn*/ false);
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const updated = updateSourceFileNode(node, setTextRange(createNodeArray(statements), node.statements));
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
@ -432,7 +432,7 @@ namespace ts {
// End the lexical environment for the module body
// and merge any new lexical declarations.
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const body = createBlock(statements, /*multiLine*/ true);
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {

View File

@ -257,7 +257,7 @@ namespace ts {
// We emit hoisted variables early to align roughly with our previous emit output.
// Two key differences in this approach are:
// - Temporary variables will appear at the top rather than at the bottom of the file
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const exportStarFunction = addExportStarIfNeeded(statements)!; // TODO: GH#18217
const moduleObject = createObjectLiteral([

View File

@ -675,7 +675,7 @@ namespace ts {
setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps);
statements.push(statement);
addStatementsAfterPrologue(statements, context.endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, context.endLexicalEnvironment());
const iife = createImmediatelyInvokedArrowFunction(statements);
setEmitFlags(iife, EmitFlags.TypeScriptClassWrapper);
@ -2672,7 +2672,7 @@ namespace ts {
const statements: Statement[] = [];
startLexicalEnvironment();
const members = map(node.members, transformEnumMember);
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
addRange(statements, members);
currentNamespaceContainerName = savedCurrentNamespaceLocalName;
@ -2993,7 +2993,7 @@ namespace ts {
statementsLocation = moveRangePos(moduleBlock.statements, -1);
}
addStatementsAfterPrologue(statements, endLexicalEnvironment());
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
currentNamespaceContainerName = savedCurrentNamespaceContainerName;
currentNamespace = savedCurrentNamespace;
currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName;

View File

@ -401,10 +401,7 @@ namespace ts {
return !nodeIsMissing(node);
}
/**
* Prepends statements to an array while taking care of prologue directives.
*/
export function addStatementsAfterPrologue<T extends Statement>(to: T[], from: ReadonlyArray<T> | undefined): T[] {
function insertStatementsAfterPrologue<T extends Statement>(to: T[], from: ReadonlyArray<T> | undefined, isPrologueDirective: (node: Node) => boolean): T[] {
if (from === undefined || from.length === 0) return to;
let statementIndex = 0;
// skip all prologue directives to insert at the correct position
@ -417,6 +414,46 @@ namespace ts {
return to;
}
function insertStatementAfterPrologue<T extends Statement>(to: T[], statement: T | undefined, isPrologueDirective: (node: Node) => boolean): T[] {
if (statement === undefined) return to;
let statementIndex = 0;
// skip all prologue directives to insert at the correct position
for (; statementIndex < to.length; ++statementIndex) {
if (!isPrologueDirective(to[statementIndex])) {
break;
}
}
to.splice(statementIndex, 0, statement);
return to;
}
function isAnyPrologueDirective(node: Node) {
return isPrologueDirective(node) || !!(getEmitFlags(node) & EmitFlags.CustomPrologue);
}
/**
* Prepends statements to an array while taking care of prologue directives.
*/
export function insertStatementsAfterStandardPrologue<T extends Statement>(to: T[], from: ReadonlyArray<T> | undefined): T[] {
return insertStatementsAfterPrologue(to, from, isPrologueDirective);
}
export function insertStatementsAfterCustomPrologue<T extends Statement>(to: T[], from: ReadonlyArray<T> | undefined): T[] {
return insertStatementsAfterPrologue(to, from, isAnyPrologueDirective);
}
/**
* Prepends statements to an array while taking care of prologue directives.
*/
export function insertStatementAfterStandardPrologue<T extends Statement>(to: T[], statement: T | undefined): T[] {
return insertStatementAfterPrologue(to, statement, isPrologueDirective);
}
export function insertStatementAfterCustomPrologue<T extends Statement>(to: T[], statement: T | undefined): T[] {
return insertStatementAfterPrologue(to, statement, isAnyPrologueDirective);
}
/**
* Determine if the given comment is a triple-slash
*
@ -3418,8 +3455,8 @@ namespace ts {
return computeLineAndCharacterOfPosition(lineMap, pos).line;
}
export function getFirstConstructorWithBody(node: ClassLikeDeclaration): ConstructorDeclaration | undefined {
return find(node.members, (member): member is ConstructorDeclaration => isConstructorDeclaration(member) && nodeIsPresent(member.body));
export function getFirstConstructorWithBody(node: ClassLikeDeclaration): ConstructorDeclaration & { body: FunctionBody } | undefined {
return find(node.members, (member): member is ConstructorDeclaration & { body: FunctionBody } => isConstructorDeclaration(member) && nodeIsPresent(member.body));
}
function getSetAccessorValueParameter(accessor: SetAccessorDeclaration): ParameterDeclaration | undefined {

View File

@ -1478,8 +1478,8 @@ namespace ts {
}
return isNodeArray(statements)
? setTextRange(createNodeArray(addStatementsAfterPrologue(statements.slice(), declarations)), statements)
: addStatementsAfterPrologue(statements, declarations);
? setTextRange(createNodeArray(insertStatementsAfterStandardPrologue(statements.slice(), declarations)), statements)
: insertStatementsAfterStandardPrologue(statements, declarations);
}
/**

View File

@ -34,26 +34,44 @@ var C = /** @class */ (function () {
this.f = function () { return _newTarget; };
}
C.prototype[_newTarget] = function () { };
C.prototype.c = function () { var _newTarget = void 0; return _newTarget; };
C.prototype.c = function () {
var _newTarget = void 0;
return _newTarget;
};
Object.defineProperty(C.prototype, "d", {
get: function () { var _newTarget = void 0; return _newTarget; },
get: function () {
var _newTarget = void 0;
return _newTarget;
},
enumerable: true,
configurable: true
});
Object.defineProperty(C.prototype, "e", {
set: function (_) { var _newTarget = void 0; _ = _newTarget; },
set: function (_) {
var _newTarget = void 0;
_ = _newTarget;
},
enumerable: true,
configurable: true
});
C[_newTarget] = function () { };
C.g = function () { var _newTarget = void 0; return _newTarget; };
C.g = function () {
var _newTarget = void 0;
return _newTarget;
};
Object.defineProperty(C, "h", {
get: function () { var _newTarget = void 0; return _newTarget; },
get: function () {
var _newTarget = void 0;
return _newTarget;
},
enumerable: true,
configurable: true
});
Object.defineProperty(C, "i", {
set: function (_) { var _newTarget = void 0; _ = _newTarget; },
set: function (_) {
var _newTarget = void 0;
_ = _newTarget;
},
enumerable: true,
configurable: true
});
@ -62,14 +80,23 @@ var C = /** @class */ (function () {
}());
var O = (_a = {},
_a[_newTarget] = undefined,
_a.k = function () { var _newTarget = void 0; return _newTarget; },
_a.k = function () {
var _newTarget = void 0;
return _newTarget;
},
Object.defineProperty(_a, "l", {
get: function () { var _newTarget = void 0; return _newTarget; },
get: function () {
var _newTarget = void 0;
return _newTarget;
},
enumerable: true,
configurable: true
}),
Object.defineProperty(_a, "m", {
set: function (_) { var _newTarget = void 0; _ = _newTarget; },
set: function (_) {
var _newTarget = void 0;
_ = _newTarget;
},
enumerable: true,
configurable: true
}),

View File

@ -25,9 +25,9 @@ function f2(_: ReadonlyArray<number>): void {}
//// [noUnusedLocals_writeOnly.js]
"use strict";
function f(x, b) {
var _a, _b;
if (x === void 0) { x = 0; }
if (b === void 0) { b = false; }
var _a, _b;
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;

View File

@ -123,8 +123,8 @@ var Bs = /** @class */ (function (_super) {
var Cs = /** @class */ (function (_super) {
__extends(Cs, _super);
function Cs() {
var _this = _super.call(this) || this;
"use strict";
var _this = _super.call(this) || this;
return _this;
}
Cs.s = 9;

View File

@ -15,13 +15,13 @@ class P {
var _this = this;
var P = /** @class */ (function () {
function P(z, zz, zzz) {
var _this = this;
if (z === void 0) { z = this; }
if (zz === void 0) { zz = this; }
if (zzz === void 0) { zzz = function (p) {
if (p === void 0) { p = _this; }
return _this;
}; }
var _this = this;
this.z = z;
this.x = this;
zzz = function (p) {