mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 11:35:42 -06:00
Move transformFunctionBody to factory
It is shared by es2015 and esNext transformers. This commit just adds a convertObjectRest flag to be passed on to flattenDestructuring functions, as well as adding necessary parameters to use the code outside a transformer.
This commit is contained in:
parent
4337369ed4
commit
4c365bd76a
@ -3056,6 +3056,351 @@ namespace ts {
|
||||
return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the body of a function-like node.
|
||||
*
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
export function transformFunctionBody(node: FunctionLikeDeclaration,
|
||||
visitor: (node: Node) => VisitResult<Node>,
|
||||
currentSourceFile: SourceFile,
|
||||
context: TransformationContext,
|
||||
enableSubstitutionsForCapturedThis: () => void,
|
||||
convertObjectRest?: boolean) {
|
||||
let multiLine = false; // indicates whether the block *must* be emitted as multiple lines
|
||||
let singleLine = false; // indicates whether the block *may* be emitted as a single line
|
||||
let statementsLocation: TextRange;
|
||||
let closeBraceLocation: TextRange;
|
||||
|
||||
const statements: Statement[] = [];
|
||||
const body = node.body;
|
||||
let statementOffset: number;
|
||||
|
||||
context.startLexicalEnvironment();
|
||||
if (isBlock(body)) {
|
||||
// ensureUseStrict is false because no new prologue-directive should be added.
|
||||
// addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array
|
||||
statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor);
|
||||
}
|
||||
|
||||
addCaptureThisForNodeIfNeeded(statements, node, enableSubstitutionsForCapturedThis);
|
||||
addDefaultValueAssignmentsIfNeeded(statements, node, visitor, convertObjectRest);
|
||||
addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false);
|
||||
|
||||
// If we added any generated statements, this must be a multi-line block.
|
||||
if (!multiLine && statements.length > 0) {
|
||||
multiLine = true;
|
||||
}
|
||||
|
||||
if (isBlock(body)) {
|
||||
statementsLocation = body.statements;
|
||||
addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset));
|
||||
|
||||
// If the original body was a multi-line block, this must be a multi-line block.
|
||||
if (!multiLine && body.multiLine) {
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(node.kind === SyntaxKind.ArrowFunction);
|
||||
|
||||
// To align with the old emitter, we use a synthetic end position on the location
|
||||
// for the statement list we synthesize when we down-level an arrow function with
|
||||
// an expression function body. This prevents both comments and source maps from
|
||||
// being emitted for the end position only.
|
||||
statementsLocation = moveRangeEnd(body, -1);
|
||||
|
||||
const equalsGreaterThanToken = (<ArrowFunction>node).equalsGreaterThanToken;
|
||||
if (!nodeIsSynthesized(equalsGreaterThanToken) && !nodeIsSynthesized(body)) {
|
||||
if (rangeEndIsOnSameLineAsRangeStart(equalsGreaterThanToken, body, currentSourceFile)) {
|
||||
singleLine = true;
|
||||
}
|
||||
else {
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
const expression = visitNode(body, visitor, isExpression);
|
||||
const returnStatement = createReturn(expression, /*location*/ body);
|
||||
setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments);
|
||||
statements.push(returnStatement);
|
||||
|
||||
// To align with the source map emit for the old emitter, we set a custom
|
||||
// source map location for the close brace.
|
||||
closeBraceLocation = body;
|
||||
}
|
||||
|
||||
const lexicalEnvironment = context.endLexicalEnvironment();
|
||||
addRange(statements, lexicalEnvironment);
|
||||
|
||||
// If we added any final generated statements, this must be a multi-line block
|
||||
if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) {
|
||||
multiLine = true;
|
||||
}
|
||||
|
||||
const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine);
|
||||
if (!multiLine && singleLine) {
|
||||
setEmitFlags(block, EmitFlags.SingleLine);
|
||||
}
|
||||
|
||||
if (closeBraceLocation) {
|
||||
setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation);
|
||||
}
|
||||
|
||||
setOriginalNode(block, node.body);
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement to capture the `this` of a function declaration if it is needed.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A node.
|
||||
*/
|
||||
export function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node, enableSubstitutionsForCapturedThis: () => void): void {
|
||||
if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) {
|
||||
captureThisForNode(statements, node, createThis(), enableSubstitutionsForCapturedThis);
|
||||
}
|
||||
}
|
||||
|
||||
export function captureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined, enableSubstitutionsForCapturedThis?: () => void, originalStatement?: Statement): void {
|
||||
enableSubstitutionsForCapturedThis();
|
||||
const captureThisStatement = createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
"_this",
|
||||
/*type*/ undefined,
|
||||
initializer
|
||||
)
|
||||
]),
|
||||
originalStatement
|
||||
);
|
||||
|
||||
setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue);
|
||||
setSourceMapRange(captureThisStatement, node);
|
||||
statements.push(captureThisStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether we need to add default value assignments for a
|
||||
* function-like node.
|
||||
*
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
function shouldAddDefaultValueAssignments(node: FunctionLikeDeclaration): boolean {
|
||||
return (node.transformFlags & TransformFlags.ContainsDefaultValueAssignments) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node if it contains parameters with
|
||||
* binding patterns or initializers.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
export function addDefaultValueAssignmentsIfNeeded(statements: Statement[],
|
||||
node: FunctionLikeDeclaration,
|
||||
visitor: (node: Node) => VisitResult<Node>,
|
||||
convertObjectRest: boolean): void {
|
||||
if (!shouldAddDefaultValueAssignments(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const parameter of node.parameters) {
|
||||
const { name, initializer, dotDotDotToken } = parameter;
|
||||
|
||||
// A rest parameter cannot have a binding pattern or an initializer,
|
||||
// so let's just ignore it.
|
||||
if (dotDotDotToken) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBindingPattern(name)) {
|
||||
addDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer, visitor, convertObjectRest);
|
||||
}
|
||||
else if (initializer) {
|
||||
addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node for parameters with binding patterns
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param parameter The parameter for the function.
|
||||
* @param name The name of the parameter.
|
||||
* @param initializer The initializer for the parameter.
|
||||
*/
|
||||
function addDefaultValueAssignmentForBindingPattern(statements: Statement[],
|
||||
parameter: ParameterDeclaration,
|
||||
name: BindingPattern, initializer: Expression,
|
||||
visitor: (node: Node) => VisitResult<Node>,
|
||||
convertObjectRest: boolean): void {
|
||||
const temp = getGeneratedNameForNode(parameter);
|
||||
|
||||
// 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(
|
||||
setEmitFlags(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList(
|
||||
flattenParameterDestructuring(parameter, temp, visitor, convertObjectRest)
|
||||
)
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (initializer) {
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
temp,
|
||||
visitNode(initializer, visitor, isExpression)
|
||||
)
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node for parameters with initializers.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param parameter The parameter for the function.
|
||||
* @param name The name of the parameter.
|
||||
* @param initializer The initializer for the parameter.
|
||||
*/
|
||||
function addDefaultValueAssignmentForInitializer(statements: Statement[],
|
||||
parameter: ParameterDeclaration,
|
||||
name: Identifier,
|
||||
initializer: Expression,
|
||||
visitor: (node: Node) => VisitResult<Node>): void {
|
||||
initializer = visitNode(initializer, visitor, isExpression);
|
||||
const statement = createIf(
|
||||
createStrictEquality(
|
||||
getSynthesizedClone(name),
|
||||
createVoidZero()
|
||||
),
|
||||
setEmitFlags(
|
||||
createBlock([
|
||||
createStatement(
|
||||
createAssignment(
|
||||
setEmitFlags(getMutableClone(name), EmitFlags.NoSourceMap),
|
||||
setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer)),
|
||||
/*location*/ parameter
|
||||
)
|
||||
)
|
||||
], /*location*/ parameter),
|
||||
EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps
|
||||
),
|
||||
/*elseStatement*/ undefined,
|
||||
/*location*/ parameter
|
||||
);
|
||||
statement.startsOnNewLine = true;
|
||||
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue);
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether we need to add statements to handle a rest parameter.
|
||||
*
|
||||
* @param node A ParameterDeclaration node.
|
||||
* @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is
|
||||
* part of a constructor declaration with a
|
||||
* synthesized call to `super`
|
||||
*/
|
||||
function shouldAddRestParameter(node: ParameterDeclaration, inConstructorWithSynthesizedSuper: boolean) {
|
||||
return node && node.dotDotDotToken && node.name.kind === SyntaxKind.Identifier && !inConstructorWithSynthesizedSuper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node if it contains a rest parameter.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A function-like node.
|
||||
* @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is
|
||||
* part of a constructor declaration with a
|
||||
* synthesized call to `super`
|
||||
*/
|
||||
export function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void {
|
||||
const parameter = lastOrUndefined(node.parameters);
|
||||
if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// `declarationName` is the name of the local declaration for the parameter.
|
||||
const declarationName = getMutableClone(<Identifier>parameter.name);
|
||||
setEmitFlags(declarationName, EmitFlags.NoSourceMap);
|
||||
|
||||
// `expressionName` is the name of the parameter used in expressions.
|
||||
const expressionName = getSynthesizedClone(<Identifier>parameter.name);
|
||||
const restIndex = node.parameters.length - 1;
|
||||
const temp = createLoopVariable();
|
||||
|
||||
// var param = [];
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
declarationName,
|
||||
/*type*/ undefined,
|
||||
createArrayLiteral([])
|
||||
)
|
||||
]),
|
||||
/*location*/ parameter
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
|
||||
// for (var _i = restIndex; _i < arguments.length; _i++) {
|
||||
// param[_i - restIndex] = arguments[_i];
|
||||
// }
|
||||
const forStatement = createFor(
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(temp, /*type*/ undefined, createLiteral(restIndex))
|
||||
], /*location*/ parameter),
|
||||
createLessThan(
|
||||
temp,
|
||||
createPropertyAccess(createIdentifier("arguments"), "length"),
|
||||
/*location*/ parameter
|
||||
),
|
||||
createPostfixIncrement(temp, /*location*/ parameter),
|
||||
createBlock([
|
||||
startOnNewLine(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
createElementAccess(
|
||||
expressionName,
|
||||
createSubtract(temp, createLiteral(restIndex))
|
||||
),
|
||||
createElementAccess(createIdentifier("arguments"), temp)
|
||||
),
|
||||
/*location*/ parameter
|
||||
)
|
||||
)
|
||||
])
|
||||
);
|
||||
|
||||
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
|
||||
startOnNewLine(forStatement);
|
||||
statements.push(forStatement);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export function convertForOf(node: ForOfStatement, convertedLoopBodyStatements: Statement[],
|
||||
visitor: (node: Node) => VisitResult<Node>,
|
||||
enableSubstitutionsForBlockScopedBindings: () => void,
|
||||
|
||||
@ -861,7 +861,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (constructor) {
|
||||
addDefaultValueAssignmentsIfNeeded(statements, constructor);
|
||||
addDefaultValueAssignmentsIfNeeded(statements, constructor, visitor, /*convertObjectRest*/ false);
|
||||
addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper);
|
||||
Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!");
|
||||
|
||||
@ -954,7 +954,7 @@ namespace ts {
|
||||
// If this isn't a derived class, just capture 'this' for arrow functions if necessary.
|
||||
if (!hasExtendsClause) {
|
||||
if (ctor) {
|
||||
addCaptureThisForNodeIfNeeded(statements, ctor);
|
||||
addCaptureThisForNodeIfNeeded(statements, ctor, enableSubstitutionsForCapturedThis);
|
||||
}
|
||||
return SuperCaptureResult.NoReplacement;
|
||||
}
|
||||
@ -1016,7 +1016,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Perform the capture.
|
||||
captureThisForNode(statements, ctor, superCallExpression, firstStatement);
|
||||
captureThisForNode(statements, ctor, superCallExpression, enableSubstitutionsForCapturedThis, firstStatement);
|
||||
|
||||
// If we're actually replacing the original statement, we need to signal this to the caller.
|
||||
if (superCallExpression) {
|
||||
@ -1085,242 +1085,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether we need to add default value assignments for a
|
||||
* function-like node.
|
||||
*
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
function shouldAddDefaultValueAssignments(node: FunctionLikeDeclaration): boolean {
|
||||
return (node.transformFlags & TransformFlags.ContainsDefaultValueAssignments) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node if it contains parameters with
|
||||
* binding patterns or initializers.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
function addDefaultValueAssignmentsIfNeeded(statements: Statement[], node: FunctionLikeDeclaration): void {
|
||||
if (!shouldAddDefaultValueAssignments(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const parameter of node.parameters) {
|
||||
const { name, initializer, dotDotDotToken } = parameter;
|
||||
|
||||
// A rest parameter cannot have a binding pattern or an initializer,
|
||||
// so let's just ignore it.
|
||||
if (dotDotDotToken) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBindingPattern(name)) {
|
||||
addDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer);
|
||||
}
|
||||
else if (initializer) {
|
||||
addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node for parameters with binding patterns
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param parameter The parameter for the function.
|
||||
* @param name The name of the parameter.
|
||||
* @param initializer The initializer for the parameter.
|
||||
*/
|
||||
function addDefaultValueAssignmentForBindingPattern(statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression): void {
|
||||
const temp = getGeneratedNameForNode(parameter);
|
||||
|
||||
// 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(
|
||||
setEmitFlags(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList(
|
||||
flattenParameterDestructuring(parameter, temp, visitor)
|
||||
)
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (initializer) {
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
temp,
|
||||
visitNode(initializer, visitor, isExpression)
|
||||
)
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node for parameters with initializers.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param parameter The parameter for the function.
|
||||
* @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 {
|
||||
initializer = visitNode(initializer, visitor, isExpression);
|
||||
const statement = createIf(
|
||||
createStrictEquality(
|
||||
getSynthesizedClone(name),
|
||||
createVoidZero()
|
||||
),
|
||||
setEmitFlags(
|
||||
createBlock([
|
||||
createStatement(
|
||||
createAssignment(
|
||||
setEmitFlags(getMutableClone(name), EmitFlags.NoSourceMap),
|
||||
setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer)),
|
||||
/*location*/ parameter
|
||||
)
|
||||
)
|
||||
], /*location*/ parameter),
|
||||
EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps
|
||||
),
|
||||
/*elseStatement*/ undefined,
|
||||
/*location*/ parameter
|
||||
);
|
||||
statement.startsOnNewLine = true;
|
||||
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue);
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether we need to add statements to handle a rest parameter.
|
||||
*
|
||||
* @param node A ParameterDeclaration node.
|
||||
* @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is
|
||||
* part of a constructor declaration with a
|
||||
* synthesized call to `super`
|
||||
*/
|
||||
function shouldAddRestParameter(node: ParameterDeclaration, inConstructorWithSynthesizedSuper: boolean) {
|
||||
return node && node.dotDotDotToken && node.name.kind === SyntaxKind.Identifier && !inConstructorWithSynthesizedSuper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node if it contains a rest parameter.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A function-like node.
|
||||
* @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is
|
||||
* part of a constructor declaration with a
|
||||
* synthesized call to `super`
|
||||
*/
|
||||
function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): void {
|
||||
const parameter = lastOrUndefined(node.parameters);
|
||||
if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// `declarationName` is the name of the local declaration for the parameter.
|
||||
const declarationName = getMutableClone(<Identifier>parameter.name);
|
||||
setEmitFlags(declarationName, EmitFlags.NoSourceMap);
|
||||
|
||||
// `expressionName` is the name of the parameter used in expressions.
|
||||
const expressionName = getSynthesizedClone(<Identifier>parameter.name);
|
||||
const restIndex = node.parameters.length - 1;
|
||||
const temp = createLoopVariable();
|
||||
|
||||
// var param = [];
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
declarationName,
|
||||
/*type*/ undefined,
|
||||
createArrayLiteral([])
|
||||
)
|
||||
]),
|
||||
/*location*/ parameter
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
|
||||
// for (var _i = restIndex; _i < arguments.length; _i++) {
|
||||
// param[_i - restIndex] = arguments[_i];
|
||||
// }
|
||||
const forStatement = createFor(
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(temp, /*type*/ undefined, createLiteral(restIndex))
|
||||
], /*location*/ parameter),
|
||||
createLessThan(
|
||||
temp,
|
||||
createPropertyAccess(createIdentifier("arguments"), "length"),
|
||||
/*location*/ parameter
|
||||
),
|
||||
createPostfixIncrement(temp, /*location*/ parameter),
|
||||
createBlock([
|
||||
startOnNewLine(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
createElementAccess(
|
||||
expressionName,
|
||||
createSubtract(temp, createLiteral(restIndex))
|
||||
),
|
||||
createElementAccess(createIdentifier("arguments"), temp)
|
||||
),
|
||||
/*location*/ parameter
|
||||
)
|
||||
)
|
||||
])
|
||||
);
|
||||
|
||||
setEmitFlags(forStatement, EmitFlags.CustomPrologue);
|
||||
startOnNewLine(forStatement);
|
||||
statements.push(forStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement to capture the `this` of a function declaration if it is needed.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A node.
|
||||
*/
|
||||
function addCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): void {
|
||||
if (node.transformFlags & TransformFlags.ContainsCapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) {
|
||||
captureThisForNode(statements, node, createThis());
|
||||
}
|
||||
}
|
||||
|
||||
function captureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined, originalStatement?: Statement): void {
|
||||
enableSubstitutionsForCapturedThis();
|
||||
const captureThisStatement = createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
"_this",
|
||||
/*type*/ undefined,
|
||||
initializer
|
||||
)
|
||||
]),
|
||||
originalStatement
|
||||
);
|
||||
|
||||
setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue);
|
||||
setSourceMapRange(captureThisStatement, node);
|
||||
statements.push(captureThisStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the class body function for a class to define the members of the
|
||||
* class.
|
||||
@ -1518,7 +1282,7 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.parameters, visitor, isParameter),
|
||||
/*type*/ undefined,
|
||||
transformFunctionBody(node),
|
||||
transformFunctionBody(node, visitor, currentSourceFile, context, enableSubstitutionsForCapturedThis),
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node);
|
||||
@ -1545,7 +1309,7 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.parameters, visitor, isParameter),
|
||||
/*type*/ undefined,
|
||||
saveStateAndInvoke(node, transformFunctionBody),
|
||||
saveStateAndInvoke(node, node => transformFunctionBody(node, visitor, currentSourceFile, context, enableSubstitutionsForCapturedThis)),
|
||||
location
|
||||
),
|
||||
/*original*/ node
|
||||
@ -1555,96 +1319,6 @@ namespace ts {
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the body of a function-like node.
|
||||
*
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
function transformFunctionBody(node: FunctionLikeDeclaration) {
|
||||
let multiLine = false; // indicates whether the block *must* be emitted as multiple lines
|
||||
let singleLine = false; // indicates whether the block *may* be emitted as a single line
|
||||
let statementsLocation: TextRange;
|
||||
let closeBraceLocation: TextRange;
|
||||
|
||||
const statements: Statement[] = [];
|
||||
const body = node.body;
|
||||
let statementOffset: number;
|
||||
|
||||
startLexicalEnvironment();
|
||||
if (isBlock(body)) {
|
||||
// ensureUseStrict is false because no new prologue-directive should be added.
|
||||
// addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array
|
||||
statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor);
|
||||
}
|
||||
|
||||
addCaptureThisForNodeIfNeeded(statements, node);
|
||||
addDefaultValueAssignmentsIfNeeded(statements, node);
|
||||
addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false);
|
||||
|
||||
// If we added any generated statements, this must be a multi-line block.
|
||||
if (!multiLine && statements.length > 0) {
|
||||
multiLine = true;
|
||||
}
|
||||
|
||||
if (isBlock(body)) {
|
||||
statementsLocation = body.statements;
|
||||
addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset));
|
||||
|
||||
// If the original body was a multi-line block, this must be a multi-line block.
|
||||
if (!multiLine && body.multiLine) {
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(node.kind === SyntaxKind.ArrowFunction);
|
||||
|
||||
// To align with the old emitter, we use a synthetic end position on the location
|
||||
// for the statement list we synthesize when we down-level an arrow function with
|
||||
// an expression function body. This prevents both comments and source maps from
|
||||
// being emitted for the end position only.
|
||||
statementsLocation = moveRangeEnd(body, -1);
|
||||
|
||||
const equalsGreaterThanToken = (<ArrowFunction>node).equalsGreaterThanToken;
|
||||
if (!nodeIsSynthesized(equalsGreaterThanToken) && !nodeIsSynthesized(body)) {
|
||||
if (rangeEndIsOnSameLineAsRangeStart(equalsGreaterThanToken, body, currentSourceFile)) {
|
||||
singleLine = true;
|
||||
}
|
||||
else {
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
const expression = visitNode(body, visitor, isExpression);
|
||||
const returnStatement = createReturn(expression, /*location*/ body);
|
||||
setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments);
|
||||
statements.push(returnStatement);
|
||||
|
||||
// To align with the source map emit for the old emitter, we set a custom
|
||||
// source map location for the close brace.
|
||||
closeBraceLocation = body;
|
||||
}
|
||||
|
||||
const lexicalEnvironment = endLexicalEnvironment();
|
||||
addRange(statements, lexicalEnvironment);
|
||||
|
||||
// If we added any final generated statements, this must be a multi-line block
|
||||
if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) {
|
||||
multiLine = true;
|
||||
}
|
||||
|
||||
const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine);
|
||||
if (!multiLine && singleLine) {
|
||||
setEmitFlags(block, EmitFlags.SingleLine);
|
||||
}
|
||||
|
||||
if (closeBraceLocation) {
|
||||
setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation);
|
||||
}
|
||||
|
||||
setOriginalNode(block, node.body);
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an ExpressionStatement that contains a destructuring assignment.
|
||||
*
|
||||
@ -2912,7 +2586,7 @@ namespace ts {
|
||||
const statements: Statement[] = [];
|
||||
startLexicalEnvironment();
|
||||
addRange(statements, prologue);
|
||||
addCaptureThisForNodeIfNeeded(statements, node);
|
||||
addCaptureThisForNodeIfNeeded(statements, node, enableSubstitutionsForCapturedThis);
|
||||
addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement));
|
||||
addRange(statements, endLexicalEnvironment());
|
||||
const clone = getMutableClone(node);
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
namespace ts {
|
||||
export function transformESNext(context: TransformationContext) {
|
||||
const {
|
||||
startLexicalEnvironment,
|
||||
endLexicalEnvironment,
|
||||
hoistVariableDeclaration,
|
||||
} = context;
|
||||
let currentSourceFile: SourceFile;
|
||||
@ -210,6 +208,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration {
|
||||
const hasRest = forEach(node.parameters, isObjectRestParameter);
|
||||
const body = hasRest ?
|
||||
transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block :
|
||||
visitEachChild(node.body, visitor, context);
|
||||
|
||||
return setOriginalNode(
|
||||
createFunctionDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
@ -219,13 +222,17 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.parameters, visitor, isParameter),
|
||||
/*type*/ undefined,
|
||||
transformFunctionBody(node) as Block,
|
||||
body,
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node);
|
||||
}
|
||||
|
||||
function visitArrowFunction(node: ArrowFunction) {
|
||||
const hasRest = forEach(node.parameters, isObjectRestParameter);
|
||||
const body = hasRest ?
|
||||
transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block :
|
||||
visitEachChild(node.body, visitor, context);
|
||||
const func = setOriginalNode(
|
||||
createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
@ -233,7 +240,7 @@ namespace ts {
|
||||
visitNodes(node.parameters, visitor, isParameter),
|
||||
/*type*/ undefined,
|
||||
node.equalsGreaterThanToken,
|
||||
transformFunctionBody(node),
|
||||
body,
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node
|
||||
@ -243,6 +250,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitFunctionExpression(node: FunctionExpression): Expression {
|
||||
const hasRest = forEach(node.parameters, isObjectRestParameter);
|
||||
const body = hasRest ?
|
||||
transformFunctionBody(node, visitor, currentSourceFile, context, noop, /*convertObjectRest*/ true) as Block :
|
||||
visitEachChild(node.body, visitor, context);
|
||||
return setOriginalNode(
|
||||
createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
@ -251,173 +262,11 @@ namespace ts {
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.parameters, visitor, isParameter),
|
||||
/*type*/ undefined,
|
||||
transformFunctionBody(node) as Block,
|
||||
body,
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the body of a function-like node.
|
||||
*
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
function transformFunctionBody(node: FunctionLikeDeclaration): Block | Expression {
|
||||
const hasRest = forEach(node.parameters, isObjectRestParameter);
|
||||
if (!hasRest) {
|
||||
return visitEachChild(node.body, visitor, context);
|
||||
}
|
||||
|
||||
let multiLine = false; // indicates whether the block *must* be emitted as multiple lines
|
||||
let singleLine = false; // indicates whether the block *may* be emitted as a single line
|
||||
let statementsLocation: TextRange;
|
||||
let closeBraceLocation: TextRange;
|
||||
|
||||
const statements: Statement[] = [];
|
||||
const body = node.body;
|
||||
let statementOffset: number;
|
||||
|
||||
startLexicalEnvironment();
|
||||
if (isBlock(body)) {
|
||||
// ensureUseStrict is false because no new prologue-directive should be added.
|
||||
// addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array
|
||||
statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor);
|
||||
}
|
||||
|
||||
addDefaultValueAssignmentsIfNeeded(statements, node);
|
||||
|
||||
// If we added any generated statements, this must be a multi-line block.
|
||||
if (!multiLine && statements.length > 0) {
|
||||
multiLine = true;
|
||||
}
|
||||
|
||||
if (isBlock(body)) {
|
||||
statementsLocation = body.statements;
|
||||
addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset));
|
||||
|
||||
// If the original body was a multi-line block, this must be a multi-line block.
|
||||
if (!multiLine && body.multiLine) {
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(node.kind === SyntaxKind.ArrowFunction);
|
||||
|
||||
// To align with the old emitter, we use a synthetic end position on the location
|
||||
// for the statement list we synthesize when we down-level an arrow function with
|
||||
// an expression function body. This prevents both comments and source maps from
|
||||
// being emitted for the end position only.
|
||||
statementsLocation = moveRangeEnd(body, -1);
|
||||
|
||||
const equalsGreaterThanToken = (<ArrowFunction>node).equalsGreaterThanToken;
|
||||
if (!nodeIsSynthesized(equalsGreaterThanToken) && !nodeIsSynthesized(body)) {
|
||||
if (rangeEndIsOnSameLineAsRangeStart(equalsGreaterThanToken, body, currentSourceFile)) {
|
||||
singleLine = true;
|
||||
}
|
||||
else {
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
const expression = visitNode(body, visitor, isExpression);
|
||||
const returnStatement = createReturn(expression, /*location*/ body);
|
||||
setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments);
|
||||
statements.push(returnStatement);
|
||||
|
||||
// To align with the source map emit for the old emitter, we set a custom
|
||||
// source map location for the close brace.
|
||||
closeBraceLocation = body;
|
||||
}
|
||||
|
||||
const lexicalEnvironment = endLexicalEnvironment();
|
||||
addRange(statements, lexicalEnvironment);
|
||||
|
||||
// If we added any final generated statements, this must be a multi-line block
|
||||
if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) {
|
||||
multiLine = true;
|
||||
}
|
||||
|
||||
const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine);
|
||||
if (!multiLine && singleLine) {
|
||||
setEmitFlags(block, EmitFlags.SingleLine);
|
||||
}
|
||||
|
||||
if (closeBraceLocation) {
|
||||
setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation);
|
||||
}
|
||||
|
||||
setOriginalNode(block, node.body);
|
||||
return block;
|
||||
}
|
||||
|
||||
function shouldAddDefaultValueAssignments(node: FunctionLikeDeclaration): boolean {
|
||||
return !!(node.transformFlags & TransformFlags.ContainsDefaultValueAssignments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node if it contains parameters with
|
||||
* binding patterns or initializers.
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param node A function-like node.
|
||||
*/
|
||||
function addDefaultValueAssignmentsIfNeeded(statements: Statement[], node: FunctionLikeDeclaration): void {
|
||||
if (!shouldAddDefaultValueAssignments(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const parameter of node.parameters) {
|
||||
// A rest parameter cannot have a binding pattern or an initializer,
|
||||
// so let's just ignore it.
|
||||
if (parameter.dotDotDotToken) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBindingPattern(parameter.name)) {
|
||||
addDefaultValueAssignmentForBindingPattern(statements, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds statements to the body of a function-like node for parameters with binding patterns
|
||||
*
|
||||
* @param statements The statements for the new function body.
|
||||
* @param parameter The parameter for the function.
|
||||
*/
|
||||
function addDefaultValueAssignmentForBindingPattern(statements: Statement[], parameter: ParameterDeclaration): void {
|
||||
const temp = getGeneratedNameForNode(parameter);
|
||||
|
||||
// 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 ((parameter.name as BindingPattern).elements.length > 0) {
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList(
|
||||
flattenParameterDestructuring(parameter, temp, visitor, /*transformRest*/ true)
|
||||
)
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (parameter.initializer) {
|
||||
statements.push(
|
||||
setEmitFlags(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
temp,
|
||||
visitNode(parameter.initializer, visitor, isExpression)
|
||||
)
|
||||
),
|
||||
EmitFlags.CustomPrologue
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user