Remove EmitHelperState, general helper cleanup.

This commit is contained in:
Ron Buckton
2016-11-08 16:32:41 -08:00
parent 5f5b117406
commit 12cd0bfb69
13 changed files with 725 additions and 620 deletions
+32 -25
View File
@@ -1695,23 +1695,8 @@ namespace ts {
// Helpers
export interface EmitHelperState {
currentSourceFile: SourceFile;
compilerOptions: CompilerOptions;
requestedHelpers?: EmitHelper[];
}
export function getHelperName(helperState: EmitHelperState, name: string) {
const externalHelpersModuleName = getOrCreateExternalHelpersModuleName(helperState.currentSourceFile, helperState.compilerOptions);
return externalHelpersModuleName
? createPropertyAccess(externalHelpersModuleName, name)
: createIdentifier(name);
}
export function requestEmitHelper(helperState: EmitHelperState, helper: EmitHelper) {
if (!contains(helperState.requestedHelpers, helper)) {
helperState.requestedHelpers = append(helperState.requestedHelpers, helper);
}
export function getHelperName(name: string) {
return setEmitFlags(createIdentifier(name), EmitFlags.HelperName | EmitFlags.AdviseOnEmitNode);
}
export interface CallBinding {
@@ -2061,6 +2046,10 @@ namespace ts {
// Utilities
export function convertToFunctionBody(node: ConciseBody) {
return isBlock(node) ? node : createBlock([createReturn(node, /*location*/ node)], /*location*/ node);
}
function isUseStrictPrologue(node: ExpressionStatement): boolean {
return (node.expression as StringLiteral).text === "use strict";
}
@@ -2110,6 +2099,13 @@ namespace ts {
return statementOffset;
}
export function startsWithUseStrict(statements: Statement[]) {
const firstStatement = firstOrUndefined(statements);
return firstStatement !== undefined
&& isPrologueDirective(firstStatement)
&& isUseStrictPrologue(firstStatement);
}
/**
* Ensures "use strict" directive is added
*
@@ -2606,7 +2602,7 @@ namespace ts {
*
* @param node The node.
*/
function getOrCreateEmitNode(node: Node) {
export function getOrCreateEmitNode(node: Node) {
if (!node.emitNode) {
if (isParseTreeNode(node)) {
// To avoid holding onto transformation artifacts, we keep track of any
@@ -2735,14 +2731,25 @@ namespace ts {
return emitNode && emitNode.externalHelpersModuleName;
}
export function getOrCreateExternalHelpersModuleName(node: SourceFile, compilerOptions: CompilerOptions) {
export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions) {
if (compilerOptions.importHelpers && (isExternalModule(node) || compilerOptions.isolatedModules)) {
const parseNode = getOriginalNode(node, isSourceFile);
const emitNode = getOrCreateEmitNode(parseNode);
return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText));
const externalHelpersModuleName = getExternalHelpersModuleName(node);
if (externalHelpersModuleName) {
return externalHelpersModuleName;
}
const helpers = getEmitHelpers(node);
if (helpers) {
for (const helper of helpers) {
if (!helper.scoped) {
const parseNode = getOriginalNode(node, isSourceFile);
const emitNode = getOrCreateEmitNode(parseNode);
return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText));
}
}
}
}
}
/**
* Adds an EmitHelper to a node.
*/
@@ -2930,7 +2937,7 @@ namespace ts {
hasExportStarsToExportValues: boolean; // whether this module contains export*
}
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo {
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo {
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
const exportSpecifiers = createMap<ExportSpecifier[]>();
const exportedBindings = createMap<Identifier[]>();
@@ -2940,7 +2947,7 @@ namespace ts {
let exportEquals: ExportAssignment = undefined;
let hasExportStarsToExportValues = false;
const externalHelpersModuleName = getExternalHelpersModuleName(sourceFile);
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions);
const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
+71 -107
View File
@@ -26,84 +26,6 @@ namespace ts {
EmitNotifications = 1 << 1,
}
export interface TransformationResult {
/**
* Gets the transformed source files.
*/
transformed: SourceFile[];
/**
* Emits the substitute for a node, if one is available; otherwise, emits the node.
*
* @param emitContext The current emit context.
* @param node The node to substitute.
* @param emitCallback A callback used to emit the node or its substitute.
*/
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Emits a node with possible notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
}
export interface TransformationContext extends LexicalEnvironment {
getCompilerOptions(): CompilerOptions;
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/**
* Hoists a function declaration to the containing scope.
*/
hoistFunctionDeclaration(node: FunctionDeclaration): void;
/**
* Hoists a variable declaration to the containing scope.
*/
hoistVariableDeclaration(node: Identifier): void;
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
enableSubstitution(kind: SyntaxKind): void;
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
/**
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
* SyntaxKind.
*/
enableEmitNotification(kind: SyntaxKind): void;
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;
/**
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
}
/* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
export function getTransformers(compilerOptions: CompilerOptions) {
const jsx = compilerOptions.jsx;
const languageVersion = getEmitScriptTarget(compilerOptions);
@@ -149,14 +71,18 @@ namespace ts {
* @param transforms An array of Transformers.
*/
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult {
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
let scopeModificationDisabled = false;
let lexicalEnvironmentVariableDeclarations: VariableDeclaration[];
let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[];
let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
let lexicalEnvironmentStackOffset = 0;
let hoistedVariableDeclarations: VariableDeclaration[];
let hoistedFunctionDeclarations: FunctionDeclaration[];
let lexicalEnvironmentDisabled: boolean;
let lexicalEnvironmentSuspended = false;
let emitHelpers: EmitHelper[];
// The transformation context is provided to each transformer as part of transformer
// initialization.
@@ -164,10 +90,14 @@ namespace ts {
getCompilerOptions: () => host.getCompilerOptions(),
getEmitResolver: () => resolver,
getEmitHost: () => host,
startLexicalEnvironment,
suspendLexicalEnvironment,
resumeLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
hoistFunctionDeclaration,
startLexicalEnvironment,
endLexicalEnvironment,
requestEmitHelper,
readEmitHelpers,
onSubstituteNode: (_emitContext, node) => node,
enableSubstitution,
isSubstitutionEnabled,
@@ -183,7 +113,7 @@ namespace ts {
const transformed = map(sourceFiles, transformSourceFile);
// Disable modification of the lexical environment.
lexicalEnvironmentDisabled = true;
scopeModificationDisabled = true;
return {
transformed,
@@ -278,13 +208,13 @@ namespace ts {
* Records a hoisted variable declaration for the provided name within a lexical environment.
*/
function hoistVariableDeclaration(name: Identifier): void {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase.");
Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase.");
const decl = createVariableDeclaration(name);
if (!hoistedVariableDeclarations) {
hoistedVariableDeclarations = [decl];
if (!lexicalEnvironmentVariableDeclarations) {
lexicalEnvironmentVariableDeclarations = [decl];
}
else {
hoistedVariableDeclarations.push(decl);
lexicalEnvironmentVariableDeclarations.push(decl);
}
}
@@ -292,31 +222,46 @@ namespace ts {
* Records a hoisted function declaration within a lexical environment.
*/
function hoistFunctionDeclaration(func: FunctionDeclaration): void {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot modify the lexical environment during the print phase.");
if (!hoistedFunctionDeclarations) {
hoistedFunctionDeclarations = [func];
Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase.");
if (!lexicalEnvironmentFunctionDeclarations) {
lexicalEnvironmentFunctionDeclarations = [func];
}
else {
hoistedFunctionDeclarations.push(func);
lexicalEnvironmentFunctionDeclarations.push(func);
}
}
/** Suspends the current lexical environment, usually after visiting a parameter list. */
function suspendLexicalEnvironment(): void {
Debug.assert(!scopeModificationDisabled, "Cannot suspend a lexical environment during the print phase.");
Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended.");
lexicalEnvironmentSuspended = true;
}
/** Resumes a suspended lexical environment, usually before visiting a function body. */
function resumeLexicalEnvironment(): void {
Debug.assert(!scopeModificationDisabled, "Cannot resume a lexical environment during the print phase.");
Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended suspended.");
lexicalEnvironmentSuspended = false;
}
/**
* Starts a new lexical environment. Any existing hoisted variable or function declarations
* are pushed onto a stack, and the related storage variables are reset.
*/
function startLexicalEnvironment(): void {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot start a lexical environment during the print phase.");
Debug.assert(!scopeModificationDisabled, "Cannot start a lexical environment during the print phase.");
Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
// Save the current lexical environment. Rather than resizing the array we adjust the
// stack size variable. This allows us to reuse existing array slots we've
// already allocated between transformations to avoid allocation and GC overhead during
// transformation.
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations;
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations;
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations;
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations;
lexicalEnvironmentStackOffset++;
hoistedVariableDeclarations = undefined;
hoistedFunctionDeclarations = undefined;
lexicalEnvironmentVariableDeclarations = undefined;
lexicalEnvironmentFunctionDeclarations = undefined;
}
/**
@@ -324,18 +269,19 @@ namespace ts {
* any hoisted declarations added in this environment are returned.
*/
function endLexicalEnvironment(): Statement[] {
Debug.assert(!lexicalEnvironmentDisabled, "Cannot end a lexical environment during the print phase.");
Debug.assert(!scopeModificationDisabled, "Cannot end a lexical environment during the print phase.");
Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
let statements: Statement[];
if (hoistedVariableDeclarations || hoistedFunctionDeclarations) {
if (hoistedFunctionDeclarations) {
statements = [...hoistedFunctionDeclarations];
if (lexicalEnvironmentVariableDeclarations || lexicalEnvironmentFunctionDeclarations) {
if (lexicalEnvironmentFunctionDeclarations) {
statements = [...lexicalEnvironmentFunctionDeclarations];
}
if (hoistedVariableDeclarations) {
if (lexicalEnvironmentVariableDeclarations) {
const statement = createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(hoistedVariableDeclarations)
createVariableDeclarationList(lexicalEnvironmentVariableDeclarations)
);
if (!statements) {
@@ -349,9 +295,27 @@ namespace ts {
// Restore the previous lexical environment.
lexicalEnvironmentStackOffset--;
hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
if (lexicalEnvironmentStackOffset === 0) {
lexicalEnvironmentVariableDeclarationsStack = [];
lexicalEnvironmentFunctionDeclarationsStack = [];
}
return statements;
}
function requestEmitHelper(helper: EmitHelper): void {
Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase.");
Debug.assert(!helper.scoped, "Cannot request a scoped emit helper.");
emitHelpers = append(emitHelpers, helper);
}
function readEmitHelpers(): EmitHelper[] | undefined {
Debug.assert(!scopeModificationDisabled, "Cannot modify the lexical environment during the print phase.");
const helpers = emitHelpers;
emitHelpers = undefined;
return helpers;
}
}
}
+131 -115
View File
@@ -165,11 +165,11 @@ namespace ts {
export function transformES2015(context: TransformationContext) {
const {
startLexicalEnvironment,
resumeLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
} = context;
const compilerOptions = context.getCompilerOptions();
const resolver = context.getEmitResolver();
const previousOnSubstituteNode = context.onSubstituteNode;
const previousOnEmitNode = context.onEmitNode;
@@ -187,7 +187,6 @@ namespace ts {
let enclosingNonArrowFunction: FunctionLikeDeclaration;
let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement;
let isInConstructorWithCapturedSuper: boolean;
let helperState: EmitHelperState;
/**
* Used to track if we are emitting body of the converted loop
@@ -210,14 +209,12 @@ namespace ts {
currentSourceFile = node;
currentText = node.text;
helperState = { currentSourceFile, compilerOptions };
const visited = visitNode(node, visitor, isSourceFile);
addEmitHelpers(visited, helperState.requestedHelpers);
const visited = saveStateAndInvoke(node, visitSourceFile);
addEmitHelpers(visited, context.readEmitHelpers());
currentSourceFile = undefined;
currentText = undefined;
helperState = undefined;
return visited;
}
@@ -264,6 +261,47 @@ namespace ts {
return visited;
}
function onBeforeVisitNode(node: Node) {
if (currentNode) {
if (isBlockScope(currentNode, currentParent)) {
enclosingBlockScopeContainer = currentNode;
enclosingBlockScopeContainerParent = currentParent;
}
if (isFunctionLike(currentNode)) {
enclosingFunction = currentNode;
if (currentNode.kind !== SyntaxKind.ArrowFunction) {
enclosingNonArrowFunction = currentNode;
if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) {
enclosingNonAsyncFunctionBody = currentNode;
}
}
}
// keep track of the enclosing variable statement when in the context of
// variable statements, variable declarations, binding elements, and binding
// patterns.
switch (currentNode.kind) {
case SyntaxKind.VariableStatement:
enclosingVariableStatement = <VariableStatement>currentNode;
break;
case SyntaxKind.VariableDeclarationList:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
break;
default:
enclosingVariableStatement = undefined;
}
}
currentParent = currentNode;
currentNode = node;
}
function returnCapturedThis(node: Node): Node {
return setOriginalNode(createReturn(createIdentifier("_this")), node);
}
@@ -288,7 +326,7 @@ namespace ts {
else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) {
// we want to dive in this branch either if node has children with ES2015 specific syntax
// or we are inside constructor that captures result of the super call so all returns without expression should be
// rewritten. Note: we skip expressions since returns should never appear there
// rewritten. Note: we skip expressions since returns should never appear there
return visitEachChild(node, visitor, context);
}
else {
@@ -426,6 +464,9 @@ namespace ts {
case SyntaxKind.YieldExpression:
return visitYieldExpression(<YieldExpression>node);
case SyntaxKind.SpreadElementExpression:
return visitSpreadElementExpression(<SpreadElementExpression>node);
case SyntaxKind.SuperKeyword:
return visitSuperKeyword();
@@ -436,9 +477,6 @@ namespace ts {
case SyntaxKind.MethodDeclaration:
return visitMethodDeclaration(<MethodDeclaration>node);
case SyntaxKind.SourceFile:
return visitSourceFileNode(<SourceFile>node);
case SyntaxKind.VariableStatement:
return visitVariableStatement(<VariableStatement>node);
@@ -446,48 +484,19 @@ namespace ts {
Debug.failBadSyntaxKind(node);
return visitEachChild(node, visitor, context);
}
}
function onBeforeVisitNode(node: Node) {
if (currentNode) {
if (isBlockScope(currentNode, currentParent)) {
enclosingBlockScopeContainer = currentNode;
enclosingBlockScopeContainerParent = currentParent;
}
if (isFunctionLike(currentNode)) {
enclosingFunction = currentNode;
if (currentNode.kind !== SyntaxKind.ArrowFunction) {
enclosingNonArrowFunction = currentNode;
if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) {
enclosingNonAsyncFunctionBody = currentNode;
}
}
}
// keep track of the enclosing variable statement when in the context of
// variable statements, variable declarations, binding elements, and binding
// patterns.
switch (currentNode.kind) {
case SyntaxKind.VariableStatement:
enclosingVariableStatement = <VariableStatement>currentNode;
break;
case SyntaxKind.VariableDeclarationList:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
break;
default:
enclosingVariableStatement = undefined;
}
}
currentParent = currentNode;
currentNode = node;
function visitSourceFile(node: SourceFile): SourceFile {
const statements: Statement[] = [];
startLexicalEnvironment();
const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor);
addCaptureThisForNodeIfNeeded(statements, node);
addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset));
addRange(statements, endLexicalEnvironment());
return updateSourceFileNode(
node,
createNodeArray(statements, node.statements)
);
}
function visitSwitchStatement(node: SwitchStatement): SwitchStatement {
@@ -787,7 +796,7 @@ namespace ts {
if (extendsClauseElement) {
statements.push(
createStatement(
createExtendsHelper(helperState, getLocalName(node)),
createExtendsHelper(context, getLocalName(node)),
/*location*/ extendsClauseElement
)
);
@@ -837,11 +846,8 @@ namespace ts {
// `super` call.
// If this is the case, we do not include the synthetic `...args` parameter and
// will instead use the `arguments` object in ES5/3.
if (constructor && !hasSynthesizedSuper) {
return visitNodes(constructor.parameters, visitor, isParameter);
}
return [];
return visitParameterList(constructor && !hasSynthesizedSuper && constructor.parameters, visitor, context)
|| [];
}
/**
@@ -855,14 +861,14 @@ namespace ts {
*/
function transformConstructorBody(constructor: ConstructorDeclaration | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments, hasSynthesizedSuper: boolean) {
const statements: Statement[] = [];
startLexicalEnvironment();
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 = 1;
statementOffset = 0;
}
else if (constructor) {
// Otherwise, try to emit all potential prologue directives first.
@@ -1386,20 +1392,13 @@ namespace ts {
function transformClassMethodDeclarationToStatement(receiver: LeftHandSideExpression, member: MethodDeclaration) {
const commentRange = getCommentRange(member);
const sourceMapRange = getSourceMapRange(member);
const func = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined);
setEmitFlags(func, EmitFlags.NoComments);
setSourceMapRange(func, sourceMapRange);
const memberName = createMemberAccessForPropertyName(receiver, visitNode(member.name, visitor, isPropertyName), /*location*/ member.name);
const memberFunction = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined);
setEmitFlags(memberFunction, EmitFlags.NoComments);
setSourceMapRange(memberFunction, sourceMapRange);
const statement = createStatement(
createAssignment(
createMemberAccessForPropertyName(
receiver,
visitNode(member.name, visitor, isPropertyName),
/*location*/ member.name
),
func
),
createAssignment(memberName, memberFunction),
/*location*/ member
);
@@ -1497,8 +1496,17 @@ namespace ts {
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
enableSubstitutionsForCapturedThis();
}
const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined);
const func = createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
node
);
setOriginalNode(func, node);
setEmitFlags(func, EmitFlags.CapturesThis);
return func;
}
@@ -1509,7 +1517,17 @@ namespace ts {
* @param node a FunctionExpression node.
*/
function visitFunctionExpression(node: FunctionExpression): Expression {
return transformFunctionLikeToExpression(node, /*location*/ node, node.name);
return updateFunctionExpression(
node,
/*modifiers*/ undefined,
node.name,
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
node.transformFlags & TransformFlags.ES2015
? transformFunctionBody(node)
: visitFunctionBody(node.body, visitor, context)
);
}
/**
@@ -1518,19 +1536,18 @@ namespace ts {
* @param node a FunctionDeclaration node.
*/
function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration {
return setOriginalNode(
createFunctionDeclaration(
/*decorators*/ undefined,
node.modifiers,
node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
),
/*original*/ node);
return updateFunctionDeclaration(
node,
/*decorators*/ undefined,
node.modifiers,
node.name,
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
node.transformFlags & TransformFlags.ES2015
? transformFunctionBody(node)
: visitFunctionBody(node.body, visitor, context)
);
}
/**
@@ -1552,7 +1569,7 @@ namespace ts {
node.asteriskToken,
name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
saveStateAndInvoke(node, transformFunctionBody),
location
@@ -1579,7 +1596,8 @@ namespace ts {
const body = node.body;
let statementOffset: number;
startLexicalEnvironment();
resumeLexicalEnvironment();
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
@@ -1633,15 +1651,21 @@ namespace ts {
closeBraceLocation = body;
}
const lexicalEnvironment = endLexicalEnvironment();
addRange(statements, lexicalEnvironment);
const declarations = endLexicalEnvironment();
addRange(statements, declarations);
// If we added any final generated statements, this must be a multi-line block
if (!multiLine && lexicalEnvironment && lexicalEnvironment.length) {
if (!multiLine && declarations && declarations.length) {
multiLine = true;
}
const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine);
const block = createBlock(
createNodeArray(statements, statementsLocation),
node.body,
multiLine);
setOriginalNode(block, node.body);
if (!multiLine && singleLine) {
setEmitFlags(block, EmitFlags.SingleLine);
}
@@ -1650,7 +1674,6 @@ namespace ts {
setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation);
}
setOriginalNode(block, node.body);
return block;
}
@@ -1659,7 +1682,7 @@ namespace ts {
*
* @param node An ExpressionStatement node.
*/
function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement {
function visitExpressionStatement(node: ExpressionStatement): Statement {
// If we are here it is most likely because our expression is a destructuring assignment.
switch (node.expression.kind) {
case SyntaxKind.ParenthesizedExpression:
@@ -1713,8 +1736,11 @@ namespace ts {
*/
function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression {
// If we are here it is because this is a destructuring assignment.
Debug.assert(isDestructuringAssignment(node));
return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor);
if (isDestructuringAssignment(node)) {
return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor);
}
return visitEachChild(node, visitor, context);
}
function visitVariableStatement(node: VariableStatement): Statement {
@@ -2894,6 +2920,10 @@ namespace ts {
);
}
function visitSpreadElementExpression(node: SpreadElementExpression) {
return visitNode(node.expression, visitor, isExpression);
}
/**
* Transforms the expression of a SpreadElementExpression node.
*
@@ -3080,19 +3110,6 @@ namespace ts {
: createIdentifier("_super");
}
function visitSourceFileNode(node: SourceFile): SourceFile {
const [prologue, remaining] = span(node.statements, isPrologueDirective);
const statements: Statement[] = [];
startLexicalEnvironment();
addRange(statements, prologue);
addCaptureThisForNodeIfNeeded(statements, node);
addRange(statements, visitNodes(createNodeArray(remaining), visitor, isStatement));
addRange(statements, endLexicalEnvironment());
const clone = getMutableClone(node);
clone.statements = createNodeArray(statements, /*location*/ node.statements);
return clone;
}
/**
* Called by the printer just before a node is printed.
*
@@ -3254,8 +3271,7 @@ namespace ts {
return false;
}
const parameter = singleOrUndefined(constructor.parameters);
if (!parameter || !nodeIsSynthesized(parameter) || !parameter.dotDotDotToken) {
if (some(constructor.parameters)) {
return false;
}
@@ -3280,14 +3296,14 @@ namespace ts {
}
const expression = (<SpreadElementExpression>callArgument).expression;
return isIdentifier(expression) && expression === parameter.name;
return isIdentifier(expression) && expression.text === "arguments";
}
}
function createExtendsHelper(helperState: EmitHelperState, name: Identifier) {
requestEmitHelper(helperState, extendsHelper);
function createExtendsHelper(context: TransformationContext, name: Identifier) {
context.requestEmitHelper(extendsHelper);
return createCall(
getHelperName(helperState, "__extends"),
getHelperName("__extends"),
/*typeArguments*/ undefined,
[
name,
+57 -84
View File
@@ -13,6 +13,7 @@ namespace ts {
export function transformES2017(context: TransformationContext) {
const {
startLexicalEnvironment,
resumeLexicalEnvironment,
endLexicalEnvironment,
} = context;
@@ -22,7 +23,6 @@ namespace ts {
// These variables contain state that changes as we descend into the tree.
let currentSourceFile: SourceFile;
let helperState: EmitHelperState;
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
@@ -50,8 +50,6 @@ namespace ts {
context.onEmitNode = onEmitNode;
context.onSubstituteNode = onSubstituteNode;
let currentScope: SourceFile | Block | ModuleBlock | CaseBlock;
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
@@ -60,14 +58,11 @@ namespace ts {
}
currentSourceFile = node;
helperState = { currentSourceFile, compilerOptions };
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, helperState.requestedHelpers);
addEmitHelpers(visited, context.readEmitHelpers());
currentSourceFile = undefined;
helperState = undefined;
return visited;
}
@@ -142,22 +137,17 @@ namespace ts {
*/
function visitMethodDeclaration(node: MethodDeclaration) {
Debug.assert(hasModifier(node, ModifierFlags.Async));
const method = createMethod(
const updated = updateMethod(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, visitor, isModifier),
node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
transformFunctionBody(node)
);
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setOriginalNode(method, node);
return method;
return updated;
}
/**
@@ -170,20 +160,17 @@ namespace ts {
*/
function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> {
Debug.assert(hasModifier(node, ModifierFlags.Async));
const func = createFunctionDeclaration(
const updated = updateFunctionDeclaration(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, visitor, isModifier),
node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
transformFunctionBody(node)
);
setOriginalNode(func, node);
return func;
return updated;
}
/**
@@ -199,20 +186,18 @@ namespace ts {
if (nodeIsMissing(node.body)) {
return createOmittedExpression();
}
const func = createFunctionExpression(
/*modifiers*/ undefined,
node.asteriskToken,
const updated = updateFunctionExpression(
node,
visitNodes(node.modifiers, visitor, isModifier),
node.name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
transformFunctionBody(node)
);
setOriginalNode(func, node);
return func;
setOriginalNode(updated, node);
return updated;
}
/**
@@ -225,42 +210,24 @@ namespace ts {
*/
function visitArrowFunction(node: ArrowFunction) {
Debug.assert(hasModifier(node, ModifierFlags.Async));
const func = createArrowFunction(
const updated = updateArrowFunction(
node,
visitNodes(node.modifiers, visitor, isModifier),
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
node.equalsGreaterThanToken,
transformConciseBody(node),
/*location*/ node
transformFunctionBody(node)
);
setOriginalNode(func, node);
return func;
setOriginalNode(updated, node);
return updated;
}
function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody {
return <FunctionBody>transformAsyncFunctionBody(node);
}
function transformConciseBody(node: ArrowFunction): ConciseBody {
return transformAsyncFunctionBody(node);
}
function transformFunctionBodyWorker(body: Block, start = 0) {
const savedCurrentScope = currentScope;
currentScope = body;
startLexicalEnvironment();
const statements = visitNodes(body.statements, visitor, isStatement, start);
const visited = updateBlock(body, statements);
const declarations = endLexicalEnvironment();
currentScope = savedCurrentScope;
return mergeFunctionBodyLexicalEnvironment(visited, declarations);
}
function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody | FunctionBody {
const nodeType = node.original ? (<FunctionLikeDeclaration>node.original).type : node.type;
function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody;
function transformFunctionBody(node: ArrowFunction): ConciseBody;
function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody {
const original = getOriginalNode(node, isFunctionLike);
const nodeType = original.type;
const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined;
const isArrowFunction = node.kind === SyntaxKind.ArrowFunction;
const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0;
@@ -271,6 +238,7 @@ namespace ts {
// passed to `__awaiter` is executed inside of the callback to the
// promise constructor.
resumeLexicalEnvironment();
if (!isArrowFunction) {
const statements: Statement[] = [];
@@ -278,7 +246,7 @@ namespace ts {
statements.push(
createReturn(
createAwaiterHelper(
helperState,
context,
hasLexicalArguments,
promiseConstructor,
transformFunctionBodyWorker(<Block>node.body, statementOffset)
@@ -286,6 +254,8 @@ namespace ts {
)
);
addRange(statements, endLexicalEnvironment());
const block = createBlock(statements, /*location*/ node.body, /*multiLine*/ true);
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
@@ -304,37 +274,40 @@ namespace ts {
return block;
}
else {
return createAwaiterHelper(
helperState,
const expression = createAwaiterHelper(
context,
hasLexicalArguments,
promiseConstructor,
<Block>transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true)
transformFunctionBodyWorker(node.body)
);
const declarations = endLexicalEnvironment();
if (some(declarations)) {
const block = convertToFunctionBody(expression);
const statements = mergeLexicalEnvironment(block.statements, declarations);
return updateBlock(block, statements);
}
return expression;
}
}
function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) {
function transformFunctionBodyWorker(body: ConciseBody, start?: number) {
if (isBlock(body)) {
return transformFunctionBodyWorker(body);
return updateBlock(
body,
visitLexicalEnvironment(body.statements, visitor, context, start));
}
else {
startLexicalEnvironment();
const visited: Expression | Block = visitNode(body, visitor, isConciseBody);
const declarations = endLexicalEnvironment();
const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations);
if (forceBlockFunctionBody && !isBlock(merged)) {
return createBlock([
createReturn(<Expression>merged)
]);
}
else {
return merged;
}
const visited = convertToFunctionBody(visitNode(body, visitor, isConciseBody));
const statements = mergeLexicalEnvironment(visited.statements, endLexicalEnvironment());
return updateBlock(visited, statements);
}
}
function getPromiseConstructor(type: TypeNode) {
const typeName = getEntityNameFromTypeNode(type);
const typeName = type && getEntityNameFromTypeNode(type);
if (typeName && isEntityName(typeName)) {
const serializationKind = resolver.getTypeReferenceSerializationKind(typeName);
if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue
@@ -506,7 +479,8 @@ namespace ts {
}
}
function createAwaiterHelper(helperState: EmitHelperState, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) {
function createAwaiterHelper(context: TransformationContext, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) {
context.requestEmitHelper(awaiterHelper);
const generatorFunc = createFunctionExpression(
/*modifiers*/ undefined,
createToken(SyntaxKind.AsteriskToken),
@@ -520,9 +494,8 @@ namespace ts {
// Mark this node as originally an async function
(generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody;
requestEmitHelper(helperState, awaiterHelper);
return createCall(
getHelperName(helperState, "__awaiter"),
getHelperName("__awaiter"),
/*typeArguments*/ undefined,
[
createThis(),
+5 -8
View File
@@ -242,7 +242,6 @@ namespace ts {
let currentSourceFile: SourceFile;
let renamedCatchVariables: Map<boolean>;
let renamedCatchVariableDeclarations: Map<Identifier>;
let helperState: EmitHelperState;
let inGeneratorFunctionBody: boolean;
let inStatementContainingYield: boolean;
@@ -298,13 +297,11 @@ namespace ts {
}
currentSourceFile = node;
helperState = { currentSourceFile, compilerOptions };
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, helperState.requestedHelpers);
addEmitHelpers(visited, context.readEmitHelpers());
currentSourceFile = undefined;
helperState = undefined;
return visited;
}
@@ -2590,7 +2587,7 @@ namespace ts {
const buildResult = buildStatements();
return createGeneratorHelper(
helperState,
context,
setEmitFlags(
createFunctionExpression(
/*modifiers*/ undefined,
@@ -3083,10 +3080,10 @@ namespace ts {
}
}
function createGeneratorHelper(helperState: EmitHelperState, body: FunctionExpression) {
requestEmitHelper(helperState, generatorHelper);
function createGeneratorHelper(context: TransformationContext, body: FunctionExpression) {
context.requestEmitHelper(generatorHelper);
return createCall(
getHelperName(helperState, "__generator"),
getHelperName("__generator"),
/*typeArguments*/ undefined,
[createThis(), body]);
}
+5 -8
View File
@@ -6,7 +6,6 @@ namespace ts {
export function transformJsx(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
let currentSourceFile: SourceFile;
let helperState: EmitHelperState;
return transformSourceFile;
@@ -21,13 +20,11 @@ namespace ts {
}
currentSourceFile = node;
helperState = { currentSourceFile, compilerOptions };
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, helperState.requestedHelpers);
addEmitHelpers(visited, context.readEmitHelpers());
currentSourceFile = undefined;
helperState = undefined;
return visited;
}
@@ -116,7 +113,7 @@ namespace ts {
// a call to the __assign helper.
objectProperties = singleOrUndefined(segments);
if (!objectProperties) {
objectProperties = createAssignHelper(helperState, segments);
objectProperties = createAssignHelper(context, segments);
}
}
@@ -538,10 +535,10 @@ namespace ts {
"diams": 0x2666
});
function createAssignHelper(helperState: EmitHelperState, attributesSegments: Expression[]) {
requestEmitHelper(helperState, assignHelper);
function createAssignHelper(context: TransformationContext, attributesSegments: Expression[]) {
context.requestEmitHelper(assignHelper);
return createCall(
getHelperName(helperState, "__assign"),
getHelperName("__assign"),
/*typeArguments*/ undefined,
attributesSegments
);
+59 -1
View File
@@ -5,6 +5,14 @@
namespace ts {
export function transformES2015Module(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
const previousOnEmitNode = context.onEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
context.onEmitNode = onEmitNode;
context.onSubstituteNode = onSubstituteNode;
context.enableEmitNotification(SyntaxKind.SourceFile);
context.enableSubstitution(SyntaxKind.Identifier);
let currentSourceFile: SourceFile;
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
@@ -13,7 +21,7 @@ namespace ts {
}
if (isExternalModule(node) || compilerOptions.isolatedModules) {
const externalHelpersModuleName = getExternalHelpersModuleName(node);
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(node, compilerOptions);
if (externalHelpersModuleName) {
const statements: Statement[] = [];
const statementOffset = addPrologueDirectives(statements, node.statements);
@@ -55,5 +63,55 @@ namespace ts {
// Elide `export=` as it is not legal with --module ES6
return node.isExportEquals ? undefined : node;
}
//
// Emit Notification
//
/**
* Hook for node emit.
*
* @param emitContext A context hint for the emitter.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void {
if (isSourceFile(node)) {
currentSourceFile = node;
previousOnEmitNode(emitContext, node, emitCallback);
currentSourceFile = undefined;
}
else {
previousOnEmitNode(emitContext, node, emitCallback);
}
}
//
// Substitutions
//
/**
* Hooks node substitutions.
*
* @param emitContext A context hint for the emitter.
* @param node The node to substitute.
*/
function onSubstituteNode(emitContext: EmitContext, node: Node) {
node = previousOnSubstituteNode(emitContext, node);
if (isIdentifier(node) && emitContext === EmitContext.Expression) {
return substituteExpressionIdentifier(node);
}
return node;
}
function substituteExpressionIdentifier(node: Identifier): Expression {
if (getEmitFlags(node) & EmitFlags.HelperName) {
const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile);
if (externalHelpersModuleName) {
return createPropertyAccess(externalHelpersModuleName, node);
}
}
return node;
}
}
}
+9 -1
View File
@@ -61,7 +61,7 @@ namespace ts {
}
currentSourceFile = node;
currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver);
currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver, compilerOptions);
// Perform the transformation.
const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None];
@@ -1187,6 +1187,14 @@ namespace ts {
* @param node The node to substitute.
*/
function substituteExpressionIdentifier(node: Identifier): Expression {
if (getEmitFlags(node) & EmitFlags.HelperName) {
const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile);
if (externalHelpersModuleName) {
return createPropertyAccess(externalHelpersModuleName, node);
}
return node;
}
if (!isGeneratedIdentifier(node) && !isLocalName(node)) {
const exportContainer = resolver.getReferencedExportContainer(node, isExportName(node));
if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) {
+9 -1
View File
@@ -73,7 +73,7 @@ namespace ts {
// see comment to 'substitutePostfixUnaryExpression' for more details
// Collect information about the external module and dependency groups.
moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver);
moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver, compilerOptions);
// Make sure that the name of the 'exports' function does not conflict with
// existing identifiers.
@@ -1609,6 +1609,14 @@ namespace ts {
* @param node The node to substitute.
*/
function substituteExpressionIdentifier(node: Identifier): Expression {
if (getEmitFlags(node) & EmitFlags.HelperName) {
const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile);
if (externalHelpersModuleName) {
return createPropertyAccess(externalHelpersModuleName, node);
}
return node;
}
// When we see an identifier in an expression position that
// points to an imported symbol, we should substitute a qualified
// reference to the imported symbol if one is needed.
+138 -207
View File
@@ -21,6 +21,7 @@ namespace ts {
export function transformTypeScript(context: TransformationContext) {
const {
startLexicalEnvironment,
resumeLexicalEnvironment,
endLexicalEnvironment,
hoistVariableDeclaration,
} = context;
@@ -48,7 +49,6 @@ namespace ts {
let currentNamespaceContainerName: Identifier;
let currentScope: SourceFile | Block | ModuleBlock | CaseBlock;
let currentScopeFirstDeclarationsOfName: Map<Node>;
let helperState: EmitHelperState;
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
@@ -81,21 +81,11 @@ namespace ts {
}
currentSourceFile = node;
currentScope = node;
currentScopeFirstDeclarationsOfName = createMap<Node>();
helperState = { currentSourceFile, compilerOptions };
let visited = visitEachChild(node, sourceElementVisitor, context);
if (compilerOptions.alwaysStrict) {
visited = updateSourceFileNode(visited, ensureUseStrict(visited.statements));
}
addEmitHelpers(visited, helperState.requestedHelpers);
const visited = saveStateAndInvoke(node, visitSourceFile);
addEmitHelpers(visited, context.readEmitHelpers());
currentSourceFile = undefined;
currentScope = undefined;
currentScopeFirstDeclarationsOfName = undefined;
helperState = undefined;
return visited;
}
@@ -123,6 +113,32 @@ namespace ts {
return visited;
}
/**
* Performs actions that should always occur immediately before visiting a node.
*
* @param node The node to visit.
*/
function onBeforeVisitNode(node: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.CaseBlock:
case SyntaxKind.ModuleBlock:
case SyntaxKind.Block:
currentScope = <SourceFile | CaseBlock | ModuleBlock | Block>node;
currentScopeFirstDeclarationsOfName = undefined;
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.FunctionDeclaration:
if (hasModifier(node, ModifierFlags.Ambient)) {
break;
}
recordEmittedDeclarationInScope(node);
break;
}
}
/**
* General-purpose node visitor.
*
@@ -451,29 +467,10 @@ namespace ts {
}
}
/**
* Performs actions that should always occur immediately before visiting a node.
*
* @param node The node to visit.
*/
function onBeforeVisitNode(node: Node) {
switch (node.kind) {
case SyntaxKind.CaseBlock:
case SyntaxKind.ModuleBlock:
case SyntaxKind.Block:
currentScope = <SourceFile | CaseBlock | ModuleBlock | Block>node;
currentScopeFirstDeclarationsOfName = undefined;
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.FunctionDeclaration:
if (hasModifier(node, ModifierFlags.Ambient)) {
break;
}
recordEmittedDeclarationInScope(node);
break;
}
function visitSourceFile(node: SourceFile) {
return updateSourceFileNode(
node,
visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, compilerOptions.alwaysStrict));
}
/**
@@ -845,9 +842,8 @@ namespace ts {
// downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array.
// Instead, we'll avoid using a rest parameter and spread into the super call as
// 'super(...arguments)' instead of 'super(...args)', as you can see in "transformConstructorBody".
return constructor
? visitNodes(constructor.parameters, visitor, isParameter)
: <ParameterDeclaration[]>[];
return visitParameterList(constructor && constructor.parameters, visitor, context)
|| <ParameterDeclaration[]>[];
}
/**
@@ -863,7 +859,7 @@ namespace ts {
let indexOfFirstStatement = 0;
// The body of a constructor is a new lexical environment
startLexicalEnvironment();
resumeLexicalEnvironment();
if (constructor) {
indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements);
@@ -919,15 +915,13 @@ namespace ts {
// End the lexical environment.
addRange(statements, endLexicalEnvironment());
return setMultiLine(
createBlock(
createNodeArray(
statements,
/*location*/ constructor ? constructor.body.statements : node.members
),
/*location*/ constructor ? constructor.body : /*location*/ undefined
return createBlock(
createNodeArray(
statements,
/*location*/ constructor ? constructor.body.statements : node.members
),
true
/*location*/ constructor ? constructor.body : /*location*/ undefined,
/*multiLine*/ true
);
}
@@ -1385,7 +1379,7 @@ namespace ts {
: undefined;
const helper = createDecorateHelper(
helperState,
context,
decoratorExpressions,
prefix,
memberName,
@@ -1423,7 +1417,7 @@ namespace ts {
const classAlias = classAliases && classAliases[getOriginalNodeId(node)];
const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
const decorate = createDecorateHelper(helperState, decoratorExpressions, localName);
const decorate = createDecorateHelper(context, decoratorExpressions, localName);
const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate);
setEmitFlags(expression, EmitFlags.NoComments);
setSourceMapRange(expression, moveRangePastDecorators(node));
@@ -1451,7 +1445,7 @@ namespace ts {
expressions = [];
for (const decorator of decorators) {
const helper = createParamHelper(
helperState,
context,
transformDecorator(decorator),
parameterOffset,
/*location*/ decorator.expression);
@@ -1481,13 +1475,13 @@ namespace ts {
function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (compilerOptions.emitDecoratorMetadata) {
if (shouldAddTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper(helperState, "design:type", serializeTypeOfNode(node)));
decoratorExpressions.push(createMetadataHelper(context, "design:type", serializeTypeOfNode(node)));
}
if (shouldAddParamTypesMetadata(node)) {
decoratorExpressions.push(createMetadataHelper(helperState, "design:paramtypes", serializeParameterTypesOfNode(node)));
decoratorExpressions.push(createMetadataHelper(context, "design:paramtypes", serializeParameterTypesOfNode(node)));
}
if (shouldAddReturnTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper(helperState, "design:returntype", serializeReturnTypeOfNode(node)));
decoratorExpressions.push(createMetadataHelper(context, "design:returntype", serializeReturnTypeOfNode(node)));
}
}
}
@@ -1505,7 +1499,7 @@ namespace ts {
(properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node))));
}
if (properties) {
decoratorExpressions.push(createMetadataHelper(helperState, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true)));
decoratorExpressions.push(createMetadataHelper(context, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true)));
}
}
}
@@ -2020,26 +2014,23 @@ namespace ts {
if (!shouldEmitFunctionLikeDeclaration(node)) {
return undefined;
}
const method = createMethod(
const updated = updateMethod(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, modifierVisitor, isModifier),
node.asteriskToken,
visitPropertyNameOfClassElement(node),
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
visitFunctionBody(node.body, visitor, context)
);
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(method, node);
setSourceMapRange(method, moveRangePastDecorators(node));
setOriginalNode(method, node);
return method;
if (updated !== node) {
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(updated, node);
setSourceMapRange(updated, moveRangePastDecorators(node));
}
return updated;
}
/**
@@ -2065,24 +2056,22 @@ namespace ts {
if (!shouldEmitAccessorDeclaration(node)) {
return undefined;
}
const accessor = createGetAccessor(
const updated = updateGetAccessor(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, modifierVisitor, isModifier),
visitPropertyNameOfClassElement(node),
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
node.body ? visitEachChild(node.body, visitor, context) : createBlock([]),
/*location*/ node
visitFunctionBody(node.body, visitor, context) || createBlock([])
);
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setOriginalNode(accessor, node);
setCommentRange(accessor, node);
setSourceMapRange(accessor, moveRangePastDecorators(node));
return accessor;
if (updated !== node) {
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(updated, node);
setSourceMapRange(updated, moveRangePastDecorators(node));
}
return updated;
}
/**
@@ -2098,23 +2087,21 @@ namespace ts {
if (!shouldEmitAccessorDeclaration(node)) {
return undefined;
}
const accessor = createSetAccessor(
const updated = updateSetAccessor(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, modifierVisitor, isModifier),
visitPropertyNameOfClassElement(node),
visitNodes(node.parameters, visitor, isParameter),
node.body ? visitEachChild(node.body, visitor, context) : createBlock([]),
/*location*/ node
visitParameterList(node.parameters, visitor, context),
visitFunctionBody(node.body, visitor, context) || createBlock([])
);
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setOriginalNode(accessor, node);
setCommentRange(accessor, node);
setSourceMapRange(accessor, moveRangePastDecorators(node));
return accessor;
if (updated !== node) {
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(updated, node);
setSourceMapRange(updated, moveRangePastDecorators(node));
}
return updated;
}
/**
@@ -2131,27 +2118,22 @@ namespace ts {
if (!shouldEmitFunctionLikeDeclaration(node)) {
return createNotEmittedStatement(node);
}
const func = createFunctionDeclaration(
const updated = updateFunctionDeclaration(
node,
/*decorators*/ undefined,
visitNodes(node.modifiers, modifierVisitor, isModifier),
node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
visitFunctionBody(node.body, visitor, context) || createBlock([])
);
setOriginalNode(func, node);
if (isNamespaceExport(node)) {
const statements: Statement[] = [func];
const statements: Statement[] = [updated];
addExportMemberAssignment(statements, node);
return statements;
}
return func;
return updated;
}
/**
@@ -2166,21 +2148,16 @@ namespace ts {
if (nodeIsMissing(node.body)) {
return createOmittedExpression();
}
const func = createFunctionExpression(
const updated = updateFunctionExpression(
node,
visitNodes(node.modifiers, modifierVisitor, isModifier),
node.asteriskToken,
node.name,
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node),
/*location*/ node
visitFunctionBody(node.body, visitor, context)
);
setOriginalNode(func, node);
return func;
return updated;
}
/**
@@ -2189,62 +2166,15 @@ namespace ts {
* - The node has type annotations
*/
function visitArrowFunction(node: ArrowFunction) {
const func = createArrowFunction(
const updated = updateArrowFunction(
node,
visitNodes(node.modifiers, modifierVisitor, isModifier),
/*typeParameters*/ undefined,
visitNodes(node.parameters, visitor, isParameter),
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
node.equalsGreaterThanToken,
transformConciseBody(node),
/*location*/ node
visitFunctionBody(node.body, visitor, context)
);
setOriginalNode(func, node);
return func;
}
function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody {
return transformFunctionBodyWorker(node.body);
}
function transformFunctionBodyWorker(body: Block, start = 0) {
const savedCurrentScope = currentScope;
const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName;
currentScope = body;
currentScopeFirstDeclarationsOfName = createMap<Node>();
startLexicalEnvironment();
const statements = visitNodes(body.statements, visitor, isStatement, start);
const visited = updateBlock(body, statements);
const declarations = endLexicalEnvironment();
currentScope = savedCurrentScope;
currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName;
return mergeFunctionBodyLexicalEnvironment(visited, declarations);
}
function transformConciseBody(node: ArrowFunction): ConciseBody {
return transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ false);
}
function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) {
if (isBlock(body)) {
return transformFunctionBodyWorker(body);
}
else {
startLexicalEnvironment();
const visited: Expression | Block = visitNode(body, visitor, isConciseBody);
const declarations = endLexicalEnvironment();
const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations);
if (forceBlockFunctionBody && !isBlock(merged)) {
return createBlock([
createReturn(<Expression>merged)
]);
}
else {
return merged;
}
}
return updated;
}
/**
@@ -3316,7 +3246,7 @@ namespace ts {
function trySubstituteNamespaceExportedName(node: Identifier): Expression {
// If this is explicitly a local name, do not substitute.
if (enabledSubstitutions & applicableSubstitutions && !isLocalName(node)) {
if (enabledSubstitutions & applicableSubstitutions && !isGeneratedIdentifier(node) && !isLocalName(node)) {
// If we are nested within a namespace declaration, we may need to qualifiy
// an identifier that is exported from a merged namespace.
const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false);
@@ -3372,10 +3302,20 @@ namespace ts {
}
}
function createParamHelper(helperState: EmitHelperState, expression: Expression, parameterOffset: number, location?: TextRange) {
requestEmitHelper(helperState, paramHelper);
const paramHelper: EmitHelper = {
name: "typescript:param",
scoped: false,
priority: 4,
text: `
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};`
};
function createParamHelper(context: TransformationContext, expression: Expression, parameterOffset: number, location?: TextRange) {
context.requestEmitHelper(paramHelper);
return createCall(
getHelperName(helperState, "__param"),
getHelperName("__param"),
/*typeArguments*/ undefined,
[
createLiteral(parameterOffset),
@@ -3385,10 +3325,20 @@ namespace ts {
);
}
function createMetadataHelper(helperState: EmitHelperState, metadataKey: string, metadataValue: Expression) {
requestEmitHelper(helperState, metadataHelper);
const metadataHelper: EmitHelper = {
name: "typescript:metadata",
scoped: false,
priority: 3,
text: `
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};`
};
function createMetadataHelper(context: TransformationContext, metadataKey: string, metadataValue: Expression) {
context.requestEmitHelper(metadataHelper);
return createCall(
getHelperName(helperState, "__metadata"),
getHelperName("__metadata"),
/*typeArguments*/ undefined,
[
createLiteral(metadataKey),
@@ -3397,20 +3347,6 @@ namespace ts {
);
}
function createDecorateHelper(helperState: EmitHelperState, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) {
const argumentsArray: Expression[] = [];
argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true));
argumentsArray.push(target);
if (memberName) {
argumentsArray.push(memberName);
if (descriptor) {
argumentsArray.push(descriptor);
}
}
requestEmitHelper(helperState, decorateHelper);
return createCall(getHelperName(helperState, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location);
}
const decorateHelper: EmitHelper = {
name: "typescript:decorate",
@@ -3425,23 +3361,18 @@ namespace ts {
};`
};
const metadataHelper: EmitHelper = {
name: "typescript:metadata",
scoped: false,
priority: 3,
text: `
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};`
};
function createDecorateHelper(context: TransformationContext, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) {
context.requestEmitHelper(decorateHelper);
const argumentsArray: Expression[] = [];
argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true));
argumentsArray.push(target);
if (memberName) {
argumentsArray.push(memberName);
if (descriptor) {
argumentsArray.push(descriptor);
}
}
const paramHelper: EmitHelper = {
name: "typescript:param",
scoped: false,
priority: 4,
text: `
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};`
};
return createCall(getHelperName("__decorate"), /*typeArguments*/ undefined, argumentsArray, location);
}
}
+124 -11
View File
@@ -1503,6 +1503,11 @@ namespace ts {
expression: Expression;
}
/* @internal */
export interface PrologueDirective extends ExpressionStatement {
expression: StringLiteral;
}
export interface IfStatement extends Statement {
kind: SyntaxKind.IfStatement;
expression: Expression;
@@ -3541,15 +3546,16 @@ namespace ts {
NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node.
NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node.
NoNestedComments = 1 << 11,
ExportName = 1 << 12, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
LocalName = 1 << 13, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
Indented = 1 << 14, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
NoIndentation = 1 << 15, // Do not indent the node.
AsyncFunctionBody = 1 << 16,
ReuseTempVariableScope = 1 << 17, // Reuse the existing temp variable scope during emit.
CustomPrologue = 1 << 18, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
NoHoisting = 1 << 19, // Do not hoist this declaration in --module system
HasEndOfDeclarationMarker = 1 << 20, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
HelperName = 1 << 12,
ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
NoIndentation = 1 << 16, // Do not indent the node.
AsyncFunctionBody = 1 << 17,
ReuseTempVariableScope = 1 << 18, // Reuse the existing temp variable scope during emit.
CustomPrologue = 1 << 19, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
NoHoisting = 1 << 20, // Do not hoist this declaration in --module system
HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
}
/* @internal */
@@ -3568,16 +3574,123 @@ namespace ts {
Unspecified, // Emitting an otherwise unspecified node
}
/** Additional context provided to `visitEachChild` */
/* @internal */
export interface LexicalEnvironment {
export interface EmitHost extends ScriptReferenceHost {
getSourceFiles(): SourceFile[];
/* @internal */
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
getCommonSourceDirectory(): string;
getCanonicalFileName(fileName: string): string;
getNewLine(): string;
isEmitBlocked(emitFileName: string): boolean;
writeFile: WriteFileCallback;
}
/* @internal */
export interface TransformationContext {
getCompilerOptions(): CompilerOptions;
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/** Starts a new lexical environment. */
startLexicalEnvironment(): void;
/** Suspends the current lexical environment, usually after visiting a parameter list. */
suspendLexicalEnvironment(): void;
/** Resumes a suspended lexical environment, usually before visiting a function body. */
resumeLexicalEnvironment(): void;
/** Ends a lexical environment, returning any declarations. */
endLexicalEnvironment(): Statement[];
/**
* Hoists a function declaration to the containing scope.
*/
hoistFunctionDeclaration(node: FunctionDeclaration): void;
/**
* Hoists a variable declaration to the containing scope.
*/
hoistVariableDeclaration(node: Identifier): void;
/**
* Records a request for a non-scoped emit helper in the current context.
*/
requestEmitHelper(helper: EmitHelper): void;
/**
* Gets and resets the requested non-scoped emit helpers.
*/
readEmitHelpers(): EmitHelper[] | undefined;
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
enableSubstitution(kind: SyntaxKind): void;
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
/**
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
* SyntaxKind.
*/
enableEmitNotification(kind: SyntaxKind): void;
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;
/**
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void;
}
/* @internal */
export interface TransformationResult {
/**
* Gets the transformed source files.
*/
transformed: SourceFile[];
/**
* Emits the substitute for a node, if one is available; otherwise, emits the node.
*
* @param emitContext The current emit context.
* @param node The node to substitute.
* @param emitCallback A callback used to emit the node or its substitute.
*/
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Emits a node with possible notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
}
/* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
export interface TextSpan {
start: number;
+3 -17
View File
@@ -28,21 +28,6 @@ namespace ts {
string(): string;
}
export interface EmitHost extends ScriptReferenceHost {
getSourceFiles(): SourceFile[];
/* @internal */
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
getCommonSourceDirectory(): string;
getCanonicalFileName(fileName: string): string;
getNewLine(): string;
isEmitBlocked(emitFileName: string): boolean;
writeFile: WriteFileCallback;
}
// Pool writers to avoid needing to allocate them for every symbol we write.
const stringWriters: StringSymbolWriter[] = [];
export function getSingleLineStringWriter(): StringSymbolWriter {
@@ -595,8 +580,9 @@ namespace ts {
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).expression.kind === SyntaxKind.SuperKeyword;
}
export function isPrologueDirective(node: Node): boolean {
return node.kind === SyntaxKind.ExpressionStatement && (<ExpressionStatement>node).expression.kind === SyntaxKind.StringLiteral;
export function isPrologueDirective(node: Node): node is PrologueDirective {
return node.kind === SyntaxKind.ExpressionStatement
&& (<ExpressionStatement>node).expression.kind === SyntaxKind.StringLiteral;
}
export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile) {
+82 -35
View File
@@ -653,6 +653,53 @@ namespace ts {
return updated || nodes;
}
/**
* Starts a new lexical environment and visits a statement list, ending the lexical environment
* and merging hoisted declarations upon completion.
*/
export function visitLexicalEnvironment(statements: NodeArray<Statement>, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext, start?: number, ensureUseStrict?: boolean) {
context.startLexicalEnvironment();
statements = visitNodes(statements, visitor, isStatement, start);
if (ensureUseStrict && !startsWithUseStrict(statements)) {
statements = createNodeArray([createStatement(createLiteral("use strict")), ...statements], statements);
}
statements = mergeLexicalEnvironment(statements, context.endLexicalEnvironment());
return statements;
}
/**
* Starts a new lexical environment and visits a parameter list, suspending the lexical
* environment upon completion.
*/
export function visitParameterList(nodes: NodeArray<ParameterDeclaration>, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext) {
context.startLexicalEnvironment();
const updated = visitNodes(nodes, visitor, isParameterDeclaration);
context.suspendLexicalEnvironment();
return updated;
}
/**
* Resumes a suspended lexical environment and visits a function body, ending the lexical
* environment and merging hoisted declarations upon completion.
*/
export function visitFunctionBody(node: FunctionBody, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext, optional?: boolean): FunctionBody;
/**
* Resumes a suspended lexical environment and visits a concise body, ending the lexical
* environment and merging hoisted declarations upon completion.
*/
export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext): ConciseBody;
export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext, optional?: boolean): ConciseBody {
context.resumeLexicalEnvironment();
const updated = visitNode(node, visitor, isConciseBody, optional);
const declarations = context.endLexicalEnvironment();
if (some(declarations)) {
const block = convertToFunctionBody(updated);
const statements = mergeLexicalEnvironment(block.statements, declarations);
return updateBlock(block, statements);
}
return updated;
}
/**
* Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place.
*
@@ -660,8 +707,8 @@ namespace ts {
* @param visitor The callback used to visit each child.
* @param context A lexical environment context for the visitor.
*/
export function visitEachChild<T extends Node>(node: T, visitor: (node: Node) => VisitResult<Node>, context: LexicalEnvironment): T;
export function visitEachChild(node: Node, visitor: (node: Node) => VisitResult<Node>, context: LexicalEnvironment): Node {
export function visitEachChild<T extends Node>(node: T, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext): T;
export function visitEachChild(node: Node, visitor: (node: Node) => VisitResult<Node>, context: TransformationContext): Node {
if (node === undefined) {
return undefined;
}
@@ -714,41 +761,33 @@ namespace ts {
visitNodes((<MethodDeclaration>node).modifiers, visitor, isModifier),
visitNode((<MethodDeclaration>node).name, visitor, isPropertyName),
visitNodes((<MethodDeclaration>node).typeParameters, visitor, isTypeParameter),
(context.startLexicalEnvironment(), visitNodes((<MethodDeclaration>node).parameters, visitor, isParameter)),
visitParameterList((<MethodDeclaration>node).parameters, visitor, context),
visitNode((<MethodDeclaration>node).type, visitor, isTypeNode, /*optional*/ true),
mergeFunctionBodyLexicalEnvironment(
visitNode((<MethodDeclaration>node).body, visitor, isFunctionBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitFunctionBody((<MethodDeclaration>node).body, visitor, context, /*optional*/ true));
case SyntaxKind.Constructor:
return updateConstructor(<ConstructorDeclaration>node,
visitNodes((<ConstructorDeclaration>node).decorators, visitor, isDecorator),
visitNodes((<ConstructorDeclaration>node).modifiers, visitor, isModifier),
(context.startLexicalEnvironment(), visitNodes((<ConstructorDeclaration>node).parameters, visitor, isParameter)),
mergeFunctionBodyLexicalEnvironment(
visitNode((<ConstructorDeclaration>node).body, visitor, isFunctionBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitParameterList((<ConstructorDeclaration>node).parameters, visitor, context),
visitFunctionBody((<ConstructorDeclaration>node).body, visitor, context, /*optional*/ true));
case SyntaxKind.GetAccessor:
return updateGetAccessor(<GetAccessorDeclaration>node,
visitNodes((<GetAccessorDeclaration>node).decorators, visitor, isDecorator),
visitNodes((<GetAccessorDeclaration>node).modifiers, visitor, isModifier),
visitNode((<GetAccessorDeclaration>node).name, visitor, isPropertyName),
(context.startLexicalEnvironment(), visitNodes((<GetAccessorDeclaration>node).parameters, visitor, isParameter)),
visitParameterList((<GetAccessorDeclaration>node).parameters, visitor, context),
visitNode((<GetAccessorDeclaration>node).type, visitor, isTypeNode, /*optional*/ true),
mergeFunctionBodyLexicalEnvironment(
visitNode((<GetAccessorDeclaration>node).body, visitor, isFunctionBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitFunctionBody((<GetAccessorDeclaration>node).body, visitor, context, /*optional*/ true));
case SyntaxKind.SetAccessor:
return updateSetAccessor(<SetAccessorDeclaration>node,
visitNodes((<SetAccessorDeclaration>node).decorators, visitor, isDecorator),
visitNodes((<SetAccessorDeclaration>node).modifiers, visitor, isModifier),
visitNode((<SetAccessorDeclaration>node).name, visitor, isPropertyName),
(context.startLexicalEnvironment(), visitNodes((<SetAccessorDeclaration>node).parameters, visitor, isParameter)),
mergeFunctionBodyLexicalEnvironment(
visitNode((<SetAccessorDeclaration>node).body, visitor, isFunctionBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitParameterList((<SetAccessorDeclaration>node).parameters, visitor, context),
visitFunctionBody((<SetAccessorDeclaration>node).body, visitor, context, /*optional*/ true));
// Binding patterns
case SyntaxKind.ObjectBindingPattern:
@@ -810,21 +849,17 @@ namespace ts {
visitNodes((<FunctionExpression>node).modifiers, visitor, isModifier),
visitNode((<FunctionExpression>node).name, visitor, isPropertyName),
visitNodes((<FunctionExpression>node).typeParameters, visitor, isTypeParameter),
(context.startLexicalEnvironment(), visitNodes((<FunctionExpression>node).parameters, visitor, isParameter)),
visitParameterList((<FunctionExpression>node).parameters, visitor, context),
visitNode((<FunctionExpression>node).type, visitor, isTypeNode, /*optional*/ true),
mergeFunctionBodyLexicalEnvironment(
visitNode((<FunctionExpression>node).body, visitor, isFunctionBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitFunctionBody((<FunctionExpression>node).body, visitor, context, /*optional*/ true));
case SyntaxKind.ArrowFunction:
return updateArrowFunction(<ArrowFunction>node,
visitNodes((<ArrowFunction>node).modifiers, visitor, isModifier),
visitNodes((<ArrowFunction>node).typeParameters, visitor, isTypeParameter),
(context.startLexicalEnvironment(), visitNodes((<ArrowFunction>node).parameters, visitor, isParameter)),
visitParameterList((<ArrowFunction>node).parameters, visitor, context),
visitNode((<ArrowFunction>node).type, visitor, isTypeNode, /*optional*/ true),
mergeFunctionBodyLexicalEnvironment(
visitNode((<ArrowFunction>node).body, visitor, isConciseBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitFunctionBody((<ArrowFunction>node).body, visitor, context));
case SyntaxKind.DeleteExpression:
return updateDelete(<DeleteExpression>node,
@@ -995,11 +1030,9 @@ namespace ts {
visitNodes((<FunctionDeclaration>node).modifiers, visitor, isModifier),
visitNode((<FunctionDeclaration>node).name, visitor, isPropertyName),
visitNodes((<FunctionDeclaration>node).typeParameters, visitor, isTypeParameter),
(context.startLexicalEnvironment(), visitNodes((<FunctionDeclaration>node).parameters, visitor, isParameter)),
visitParameterList((<FunctionDeclaration>node).parameters, visitor, context),
visitNode((<FunctionDeclaration>node).type, visitor, isTypeNode, /*optional*/ true),
mergeFunctionBodyLexicalEnvironment(
visitNode((<FunctionDeclaration>node).body, visitor, isFunctionBody, /*optional*/ true),
context.endLexicalEnvironment()));
visitFunctionBody((<FunctionExpression>node).body, visitor, context, /*optional*/ true));
case SyntaxKind.ClassDeclaration:
return updateClassDeclaration(<ClassDeclaration>node,
@@ -1129,11 +1162,7 @@ namespace ts {
case SyntaxKind.SourceFile:
context.startLexicalEnvironment();
return updateSourceFileNode(<SourceFile>node,
createNodeArray(
concatenate(
visitNodes((<SourceFile>node).statements, visitor, isStatement),
context.endLexicalEnvironment()),
(<SourceFile>node).statements));
visitLexicalEnvironment((<SourceFile>node).statements, visitor, context));
// Transformation nodes
case SyntaxKind.PartiallyEmittedExpression:
@@ -1167,6 +1196,24 @@ namespace ts {
// return node;
}
/**
* Merges generated lexical declarations into a new statement list.
*/
export function mergeLexicalEnvironment(statements: NodeArray<Statement>, declarations: Statement[]): NodeArray<Statement>;
/**
* Appends generated lexical declarations to an array of statements.
*/
export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]): Statement[];
export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]) {
if (!some(declarations)) {
return statements;
}
return isNodeArray(statements)
? createNodeArray(concatenate(statements, declarations), statements)
: addRange(statements, declarations);
}
/**
* Merges generated lexical declarations into the FunctionBody of a non-arrow function-like declaration.
*