mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 01:49:41 -05:00
Fix and validate post-increment/decrement in module emit (#44968)
This commit is contained in:
@@ -2724,6 +2724,14 @@ namespace ts {
|
||||
node.operator = operator;
|
||||
node.operand = parenthesizerRules().parenthesizeOperandOfPrefixUnary(operand);
|
||||
node.transformFlags |= propagateChildFlags(node.operand);
|
||||
// Only set this flag for non-generated identifiers and non-"local" names. See the
|
||||
// comment in `visitPreOrPostfixUnaryExpression` in module.ts
|
||||
if ((operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken) &&
|
||||
isIdentifier(node.operand) &&
|
||||
!isGeneratedIdentifier(node.operand) &&
|
||||
!isLocalName(node.operand)) {
|
||||
node.transformFlags |= TransformFlags.ContainsUpdateExpressionForIdentifier;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -2739,7 +2747,14 @@ namespace ts {
|
||||
const node = createBaseExpression<PostfixUnaryExpression>(SyntaxKind.PostfixUnaryExpression);
|
||||
node.operator = operator;
|
||||
node.operand = parenthesizerRules().parenthesizeOperandOfPostfixUnary(operand);
|
||||
node.transformFlags = propagateChildFlags(node.operand);
|
||||
node.transformFlags |= propagateChildFlags(node.operand);
|
||||
// Only set this flag for non-generated identifiers and non-"local" names. See the
|
||||
// comment in `visitPreOrPostfixUnaryExpression` in module.ts
|
||||
if (isIdentifier(node.operand) &&
|
||||
!isGeneratedIdentifier(node.operand) &&
|
||||
!isLocalName(node.operand)) {
|
||||
node.transformFlags |= TransformFlags.ContainsUpdateExpressionForIdentifier;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.TaggedTemplateExpression); // Substitute calls to imported/exported symbols to avoid incorrect `this`.
|
||||
context.enableSubstitution(SyntaxKind.Identifier); // Substitutes expression identifiers with imported/exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.BinaryExpression); // Substitutes assignments to exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.PrefixUnaryExpression); // Substitutes updates to exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols.
|
||||
context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file.
|
||||
|
||||
@@ -47,7 +45,7 @@ namespace ts {
|
||||
|
||||
let currentSourceFile: SourceFile; // The current file.
|
||||
let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file.
|
||||
let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored.
|
||||
const noSubstitution: boolean[] = []; // Set of nodes for which substitution rules should be ignored.
|
||||
let needUMDDynamicImportHelper: boolean;
|
||||
|
||||
return chainBundle(context, transformSourceFile);
|
||||
@@ -96,7 +94,7 @@ namespace ts {
|
||||
|
||||
const statements: Statement[] = [];
|
||||
const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile));
|
||||
const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict && !isJsonSourceFile(node), sourceElementVisitor);
|
||||
const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict && !isJsonSourceFile(node), topLevelVisitor);
|
||||
|
||||
if (shouldEmitUnderscoreUnderscoreESModule()) {
|
||||
append(statements, createUnderscoreUnderscoreESModule());
|
||||
@@ -117,8 +115,8 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement));
|
||||
addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset));
|
||||
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, topLevelVisitor, isStatement));
|
||||
addRange(statements, visitNodes(node.statements, topLevelVisitor, isStatement, statementOffset));
|
||||
addExportEqualsIfNeeded(statements, /*emitAsReturn*/ false);
|
||||
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
|
||||
|
||||
@@ -430,7 +428,7 @@ namespace ts {
|
||||
startLexicalEnvironment();
|
||||
|
||||
const statements: Statement[] = [];
|
||||
const statementOffset = factory.copyPrologue(node.statements, statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor);
|
||||
const statementOffset = factory.copyPrologue(node.statements, statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, topLevelVisitor);
|
||||
|
||||
if (shouldEmitUnderscoreUnderscoreESModule()) {
|
||||
append(statements, createUnderscoreUnderscoreESModule());
|
||||
@@ -440,11 +438,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Visit each statement of the module body.
|
||||
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement));
|
||||
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, topLevelVisitor, isStatement));
|
||||
if (moduleKind === ModuleKind.AMD) {
|
||||
addRange(statements, mapDefined(currentModuleInfo.externalImports, getAMDImportExpressionForImport));
|
||||
}
|
||||
addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset));
|
||||
addRange(statements, visitNodes(node.statements, topLevelVisitor, isStatement, statementOffset));
|
||||
|
||||
// Append the 'export =' statement if provided.
|
||||
addExportEqualsIfNeeded(statements, /*emitAsReturn*/ true);
|
||||
@@ -471,7 +469,7 @@ namespace ts {
|
||||
*/
|
||||
function addExportEqualsIfNeeded(statements: Statement[], emitAsReturn: boolean) {
|
||||
if (currentModuleInfo.exportEquals) {
|
||||
const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, moduleExpressionElementVisitor);
|
||||
const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, visitor);
|
||||
if (expressionResult) {
|
||||
if (emitAsReturn) {
|
||||
const statement = factory.createReturnStatement(expressionResult);
|
||||
@@ -507,7 +505,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function sourceElementVisitor(node: Node): VisitResult<Node> {
|
||||
function topLevelVisitor(node: Node): VisitResult<Node> {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return visitImportDeclaration(node as ImportDeclaration);
|
||||
@@ -537,26 +535,50 @@ namespace ts {
|
||||
return visitEndOfDeclarationMarker(node as EndOfDeclarationMarker);
|
||||
|
||||
default:
|
||||
return visitEachChild(node, moduleExpressionElementVisitor, context);
|
||||
return visitor(node);
|
||||
}
|
||||
}
|
||||
|
||||
function moduleExpressionElementVisitor(node: Expression): VisitResult<Expression> {
|
||||
// This visitor does not need to descend into the tree if there is no dynamic import or destructuring assignment,
|
||||
function visitorWorker(node: Node, valueIsDiscarded: boolean): VisitResult<Node> {
|
||||
// This visitor does not need to descend into the tree if there is no dynamic import, destructuring assignment, or update expression
|
||||
// as export/import statements are only transformed at the top level of a file.
|
||||
if (!(node.transformFlags & TransformFlags.ContainsDynamicImport) && !(node.transformFlags & TransformFlags.ContainsDestructuringAssignment)) {
|
||||
if (!(node.transformFlags & (TransformFlags.ContainsDynamicImport | TransformFlags.ContainsDestructuringAssignment | TransformFlags.ContainsUpdateExpressionForIdentifier))) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (isImportCall(node)) {
|
||||
return visitImportCallExpression(node);
|
||||
}
|
||||
else if (isDestructuringAssignment(node)) {
|
||||
return visitDestructuringAssignment(node);
|
||||
}
|
||||
else {
|
||||
return visitEachChild(node, moduleExpressionElementVisitor, context);
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
return visitForStatement(node as ForStatement);
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return visitExpressionStatement(node as ExpressionStatement);
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return visitParenthesizedExpression(node as ParenthesizedExpression, valueIsDiscarded);
|
||||
case SyntaxKind.PartiallyEmittedExpression:
|
||||
return visitPartiallyEmittedExpression(node as PartiallyEmittedExpression, valueIsDiscarded);
|
||||
case SyntaxKind.CallExpression:
|
||||
if (isImportCall(node)) {
|
||||
return visitImportCallExpression(node);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
if (isDestructuringAssignment(node)) {
|
||||
return visitDestructuringAssignment(node, valueIsDiscarded);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return visitPreOrPostfixUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded);
|
||||
}
|
||||
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitor(node: Node): VisitResult<Node> {
|
||||
return visitorWorker(node, /*valueIsDiscarded*/ false);
|
||||
}
|
||||
|
||||
function discardedValueVisitor(node: Node): VisitResult<Node> {
|
||||
return visitorWorker(node, /*valueIsDiscarded*/ true);
|
||||
}
|
||||
|
||||
function destructuringNeedsFlattening(node: Expression): boolean {
|
||||
@@ -604,16 +626,91 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function visitDestructuringAssignment(node: DestructuringAssignment): Expression {
|
||||
function visitDestructuringAssignment(node: DestructuringAssignment, valueIsDiscarded: boolean): Expression {
|
||||
if (destructuringNeedsFlattening(node.left)) {
|
||||
return flattenDestructuringAssignment(node, moduleExpressionElementVisitor, context, FlattenLevel.All, /*needsValue*/ false, createAllExportExpressions);
|
||||
return flattenDestructuringAssignment(node, visitor, context, FlattenLevel.All, !valueIsDiscarded, createAllExportExpressions);
|
||||
}
|
||||
return visitEachChild(node, moduleExpressionElementVisitor, context);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitForStatement(node: ForStatement) {
|
||||
return factory.updateForStatement(
|
||||
node,
|
||||
visitNode(node.initializer, discardedValueVisitor, isForInitializer),
|
||||
visitNode(node.condition, visitor, isExpression),
|
||||
visitNode(node.incrementor, discardedValueVisitor, isExpression),
|
||||
visitIterationBody(node.statement, visitor, context)
|
||||
);
|
||||
}
|
||||
|
||||
function visitExpressionStatement(node: ExpressionStatement) {
|
||||
return factory.updateExpressionStatement(
|
||||
node,
|
||||
visitNode(node.expression, discardedValueVisitor, isExpression)
|
||||
);
|
||||
}
|
||||
|
||||
function visitParenthesizedExpression(node: ParenthesizedExpression, valueIsDiscarded: boolean) {
|
||||
return factory.updateParenthesizedExpression(node, visitNode(node.expression, valueIsDiscarded ? discardedValueVisitor : visitor, isExpression));
|
||||
}
|
||||
|
||||
function visitPartiallyEmittedExpression(node: PartiallyEmittedExpression, valueIsDiscarded: boolean) {
|
||||
return factory.updatePartiallyEmittedExpression(node, visitNode(node.expression, valueIsDiscarded ? discardedValueVisitor : visitor, isExpression));
|
||||
}
|
||||
|
||||
function visitPreOrPostfixUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded: boolean) {
|
||||
// When we see a prefix or postfix increment expression whose operand is an exported
|
||||
// symbol, we should ensure all exports of that symbol are updated with the correct
|
||||
// value.
|
||||
//
|
||||
// - We do not transform generated identifiers for any reason.
|
||||
// - We do not transform identifiers tagged with the LocalName flag.
|
||||
// - We do not transform identifiers that were originally the name of an enum or
|
||||
// namespace due to how they are transformed in TypeScript.
|
||||
// - We only transform identifiers that are exported at the top level.
|
||||
if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken)
|
||||
&& isIdentifier(node.operand)
|
||||
&& !isGeneratedIdentifier(node.operand)
|
||||
&& !isLocalName(node.operand)
|
||||
&& !isDeclarationNameOfEnumOrNamespace(node.operand)) {
|
||||
const exportedNames = getExports(node.operand);
|
||||
if (exportedNames) {
|
||||
let temp: Identifier | undefined;
|
||||
let expression: Expression = visitNode(node.operand, visitor, isExpression);
|
||||
if (isPrefixUnaryExpression(node)) {
|
||||
expression = factory.updatePrefixUnaryExpression(node, expression);
|
||||
}
|
||||
else {
|
||||
expression = factory.updatePostfixUnaryExpression(node, expression);
|
||||
if (!valueIsDiscarded) {
|
||||
temp = factory.createTempVariable(hoistVariableDeclaration);
|
||||
expression = factory.createAssignment(temp, expression);
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
expression = factory.createComma(expression, factory.cloneNode(node.operand));
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
|
||||
for (const exportName of exportedNames) {
|
||||
noSubstitution[getNodeId(expression)] = true;
|
||||
expression = createExportExpression(exportName, expression);
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
|
||||
if (temp) {
|
||||
noSubstitution[getNodeId(expression)] = true;
|
||||
expression = factory.createComma(expression, temp);
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitImportCallExpression(node: ImportCall): Expression {
|
||||
const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions);
|
||||
const firstArgument = visitNode(firstOrUndefined(node.arguments), moduleExpressionElementVisitor);
|
||||
const firstArgument = visitNode(firstOrUndefined(node.arguments), visitor);
|
||||
// Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output.
|
||||
const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument;
|
||||
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
|
||||
@@ -1110,10 +1207,10 @@ namespace ts {
|
||||
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
const id = getOriginalNodeId(node);
|
||||
deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true);
|
||||
deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true);
|
||||
}
|
||||
else {
|
||||
statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true);
|
||||
statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true);
|
||||
}
|
||||
|
||||
return singleOrMany(statements);
|
||||
@@ -1136,9 +1233,9 @@ namespace ts {
|
||||
node.asteriskToken,
|
||||
factory.getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.parameters, moduleExpressionElementVisitor),
|
||||
visitNodes(node.parameters, visitor),
|
||||
/*type*/ undefined,
|
||||
visitEachChild(node.body, moduleExpressionElementVisitor, context)
|
||||
visitEachChild(node.body, visitor, context)
|
||||
),
|
||||
/*location*/ node
|
||||
),
|
||||
@@ -1147,7 +1244,7 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
else {
|
||||
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
|
||||
statements = append(statements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
@@ -1178,8 +1275,8 @@ namespace ts {
|
||||
visitNodes(node.modifiers, modifierVisitor, isModifier),
|
||||
factory.getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.heritageClauses, moduleExpressionElementVisitor),
|
||||
visitNodes(node.members, moduleExpressionElementVisitor)
|
||||
visitNodes(node.heritageClauses, visitor),
|
||||
visitNodes(node.members, visitor)
|
||||
),
|
||||
node
|
||||
),
|
||||
@@ -1188,7 +1285,7 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
else {
|
||||
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
|
||||
statements = append(statements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
@@ -1242,7 +1339,7 @@ namespace ts {
|
||||
variable.name,
|
||||
variable.exclamationToken,
|
||||
variable.type,
|
||||
visitNode(variable.initializer, moduleExpressionElementVisitor)
|
||||
visitNode(variable.initializer, visitor)
|
||||
);
|
||||
|
||||
variables = append(variables, updatedVariable);
|
||||
@@ -1268,7 +1365,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
|
||||
statements = append(statements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
@@ -1307,7 +1404,7 @@ namespace ts {
|
||||
function transformInitializedVariable(node: InitializedVariableDeclaration): Expression {
|
||||
if (isBindingPattern(node.name)) {
|
||||
return flattenDestructuringAssignment(
|
||||
visitNode(node, moduleExpressionElementVisitor),
|
||||
visitNode(node, visitor),
|
||||
/*visitor*/ undefined,
|
||||
context,
|
||||
FlattenLevel.All,
|
||||
@@ -1324,7 +1421,7 @@ namespace ts {
|
||||
),
|
||||
/*location*/ node.name
|
||||
),
|
||||
node.initializer ? visitNode(node.initializer, moduleExpressionElementVisitor) : factory.createVoidZero()
|
||||
node.initializer ? visitNode(node.initializer, visitor) : factory.createVoidZero()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1674,13 +1771,11 @@ namespace ts {
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
currentSourceFile = node as SourceFile;
|
||||
currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)];
|
||||
noSubstitution = [];
|
||||
|
||||
previousOnEmitNode(hint, node, emitCallback);
|
||||
|
||||
currentSourceFile = undefined!;
|
||||
currentModuleInfo = undefined!;
|
||||
noSubstitution = undefined!;
|
||||
}
|
||||
else {
|
||||
previousOnEmitNode(hint, node, emitCallback);
|
||||
@@ -1749,9 +1844,6 @@ namespace ts {
|
||||
return substituteTaggedTemplateExpression(node as TaggedTemplateExpression);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return substituteBinaryExpression(node as BinaryExpression);
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
return substituteUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression);
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -1881,54 +1973,6 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitution for a UnaryExpression that may contain an imported or exported symbol.
|
||||
*
|
||||
* @param node The node to substitute.
|
||||
*/
|
||||
function substituteUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression): Expression {
|
||||
// When we see a prefix or postfix increment expression whose operand is an exported
|
||||
// symbol, we should ensure all exports of that symbol are updated with the correct
|
||||
// value.
|
||||
//
|
||||
// - We do not substitute generated identifiers for any reason.
|
||||
// - We do not substitute identifiers tagged with the LocalName flag.
|
||||
// - We do not substitute identifiers that were originally the name of an enum or
|
||||
// namespace due to how they are transformed in TypeScript.
|
||||
// - We only substitute identifiers that are exported at the top level.
|
||||
if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken)
|
||||
&& isIdentifier(node.operand)
|
||||
&& !isGeneratedIdentifier(node.operand)
|
||||
&& !isLocalName(node.operand)
|
||||
&& !isDeclarationNameOfEnumOrNamespace(node.operand)) {
|
||||
const exportedNames = getExports(node.operand);
|
||||
if (exportedNames) {
|
||||
let expression: Expression = node.kind === SyntaxKind.PostfixUnaryExpression
|
||||
? setTextRange(
|
||||
factory.createPrefixUnaryExpression(
|
||||
node.operator,
|
||||
node.operand
|
||||
),
|
||||
/*location*/ node)
|
||||
: node;
|
||||
for (const exportName of exportedNames) {
|
||||
// Mark the node to prevent triggering this rule again.
|
||||
noSubstitution[getNodeId(expression)] = true;
|
||||
expression = createExportExpression(exportName, expression);
|
||||
}
|
||||
if (node.kind === SyntaxKind.PostfixUnaryExpression) {
|
||||
noSubstitution[getNodeId(expression)] = true;
|
||||
expression = node.operator === SyntaxKind.PlusPlusToken
|
||||
? factory.createSubtract(expression, factory.createNumericLiteral(1))
|
||||
: factory.createAdd(expression, factory.createNumericLiteral(1));
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the additional exports of a name.
|
||||
*
|
||||
|
||||
@@ -23,8 +23,6 @@ namespace ts {
|
||||
context.enableSubstitution(SyntaxKind.Identifier); // Substitutes expression identifiers for imported symbols.
|
||||
context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes expression identifiers for imported symbols
|
||||
context.enableSubstitution(SyntaxKind.BinaryExpression); // Substitutes assignments to exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.PrefixUnaryExpression); // Substitutes updates to exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols.
|
||||
context.enableSubstitution(SyntaxKind.MetaProperty); // Substitutes 'import.meta'
|
||||
context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file.
|
||||
|
||||
@@ -225,7 +223,7 @@ namespace ts {
|
||||
|
||||
// Add any prologue directives.
|
||||
const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile));
|
||||
const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict, sourceElementVisitor);
|
||||
const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict, topLevelVisitor);
|
||||
|
||||
// var __moduleName = context_1 && context_1.id;
|
||||
statements.push(
|
||||
@@ -246,14 +244,14 @@ namespace ts {
|
||||
);
|
||||
|
||||
// Visit the synthetic external helpers import declaration if present
|
||||
visitNode(moduleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement);
|
||||
visitNode(moduleInfo.externalHelpersImportDeclaration, topLevelVisitor, isStatement);
|
||||
|
||||
// Visit the statements of the source file, emitting any transformations into
|
||||
// the `executeStatements` array. We do this *before* we fill the `setters` array
|
||||
// as we both emit transformations as well as aggregate some data used when creating
|
||||
// setters. This allows us to reduce the number of times we need to loop through the
|
||||
// statements of the source file.
|
||||
const executeStatements = visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset);
|
||||
const executeStatements = visitNodes(node.statements, topLevelVisitor, isStatement, statementOffset);
|
||||
|
||||
// Emit early exports for function declarations.
|
||||
addRange(statements, hoistedStatements);
|
||||
@@ -566,7 +564,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function sourceElementVisitor(node: Node): VisitResult<Node> {
|
||||
function topLevelVisitor(node: Node): VisitResult<Node> {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return visitImportDeclaration(node as ImportDeclaration);
|
||||
@@ -581,7 +579,7 @@ namespace ts {
|
||||
return visitExportAssignment(node as ExportAssignment);
|
||||
|
||||
default:
|
||||
return nestedElementVisitor(node);
|
||||
return topLevelNestedVisitor(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +645,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const expression = visitNode(node.expression, destructuringAndImportCallVisitor, isExpression);
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
const original = node.original;
|
||||
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
|
||||
// Defer exports until we encounter an EndOfDeclarationMarker node
|
||||
@@ -674,12 +672,12 @@ namespace ts {
|
||||
node.asteriskToken,
|
||||
factory.getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.parameters, destructuringAndImportCallVisitor, isParameterDeclaration),
|
||||
visitNodes(node.parameters, visitor, isParameterDeclaration),
|
||||
/*type*/ undefined,
|
||||
visitNode(node.body, destructuringAndImportCallVisitor, isBlock)));
|
||||
visitNode(node.body, visitor, isBlock)));
|
||||
}
|
||||
else {
|
||||
hoistedStatements = append(hoistedStatements, visitEachChild(node, destructuringAndImportCallVisitor, context));
|
||||
hoistedStatements = append(hoistedStatements, visitEachChild(node, visitor, context));
|
||||
}
|
||||
|
||||
if (hasAssociatedEndOfDeclarationMarker(node)) {
|
||||
@@ -714,12 +712,12 @@ namespace ts {
|
||||
name,
|
||||
setTextRange(
|
||||
factory.createClassExpression(
|
||||
visitNodes(node.decorators, destructuringAndImportCallVisitor, isDecorator),
|
||||
visitNodes(node.decorators, visitor, isDecorator),
|
||||
/*modifiers*/ undefined,
|
||||
node.name,
|
||||
/*typeParameters*/ undefined,
|
||||
visitNodes(node.heritageClauses, destructuringAndImportCallVisitor, isHeritageClause),
|
||||
visitNodes(node.members, destructuringAndImportCallVisitor, isClassElement)
|
||||
visitNodes(node.heritageClauses, visitor, isHeritageClause),
|
||||
visitNodes(node.members, visitor, isClassElement)
|
||||
),
|
||||
node
|
||||
)
|
||||
@@ -749,7 +747,7 @@ namespace ts {
|
||||
*/
|
||||
function visitVariableStatement(node: VariableStatement): VisitResult<Statement> {
|
||||
if (!shouldHoistVariableDeclarationList(node.declarationList)) {
|
||||
return visitNode(node, destructuringAndImportCallVisitor, isStatement);
|
||||
return visitNode(node, visitor, isStatement);
|
||||
}
|
||||
|
||||
let expressions: Expression[] | undefined;
|
||||
@@ -822,13 +820,13 @@ namespace ts {
|
||||
return isBindingPattern(node.name)
|
||||
? flattenDestructuringAssignment(
|
||||
node,
|
||||
destructuringAndImportCallVisitor,
|
||||
visitor,
|
||||
context,
|
||||
FlattenLevel.All,
|
||||
/*needsValue*/ false,
|
||||
createAssignment
|
||||
)
|
||||
: node.initializer ? createAssignment(node.name, visitNode(node.initializer, destructuringAndImportCallVisitor, isExpression)) : node.name;
|
||||
: node.initializer ? createAssignment(node.name, visitNode(node.initializer, visitor, isExpression)) : node.name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1153,7 +1151,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function nestedElementVisitor(node: Node): VisitResult<Node> {
|
||||
function topLevelNestedVisitor(node: Node): VisitResult<Node> {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableStatement:
|
||||
return visitVariableStatement(node as VariableStatement);
|
||||
@@ -1165,7 +1163,7 @@ namespace ts {
|
||||
return visitClassDeclaration(node as ClassDeclaration);
|
||||
|
||||
case SyntaxKind.ForStatement:
|
||||
return visitForStatement(node as ForStatement);
|
||||
return visitForStatement(node as ForStatement, /*isTopLevel*/ true);
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
return visitForInStatement(node as ForInStatement);
|
||||
@@ -1213,7 +1211,7 @@ namespace ts {
|
||||
return visitEndOfDeclarationMarker(node as EndOfDeclarationMarker);
|
||||
|
||||
default:
|
||||
return destructuringAndImportCallVisitor(node);
|
||||
return visitor(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1222,16 +1220,16 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitForStatement(node: ForStatement): VisitResult<Statement> {
|
||||
function visitForStatement(node: ForStatement, isTopLevel: boolean): VisitResult<Statement> {
|
||||
const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer;
|
||||
enclosingBlockScopedContainer = node;
|
||||
|
||||
node = factory.updateForStatement(
|
||||
node,
|
||||
node.initializer && visitForInitializer(node.initializer),
|
||||
visitNode(node.condition, destructuringAndImportCallVisitor, isExpression),
|
||||
visitNode(node.incrementor, destructuringAndImportCallVisitor, isExpression),
|
||||
visitIterationBody(node.statement, nestedElementVisitor, context)
|
||||
visitNode(node.initializer, isTopLevel ? visitForInitializer : discardedValueVisitor, isForInitializer),
|
||||
visitNode(node.condition, visitor, isExpression),
|
||||
visitNode(node.incrementor, discardedValueVisitor, isExpression),
|
||||
visitIterationBody(node.statement, isTopLevel ? topLevelNestedVisitor : visitor, context)
|
||||
);
|
||||
|
||||
enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer;
|
||||
@@ -1250,8 +1248,8 @@ namespace ts {
|
||||
node = factory.updateForInStatement(
|
||||
node,
|
||||
visitForInitializer(node.initializer),
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression),
|
||||
visitIterationBody(node.statement, nestedElementVisitor, context)
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context)
|
||||
);
|
||||
|
||||
enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer;
|
||||
@@ -1271,8 +1269,8 @@ namespace ts {
|
||||
node,
|
||||
node.awaitModifier,
|
||||
visitForInitializer(node.initializer),
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression),
|
||||
visitIterationBody(node.statement, nestedElementVisitor, context)
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context)
|
||||
);
|
||||
|
||||
enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer;
|
||||
@@ -1308,7 +1306,7 @@ namespace ts {
|
||||
return expressions ? factory.inlineExpressions(expressions) : factory.createOmittedExpression();
|
||||
}
|
||||
else {
|
||||
return visitEachChild(node, nestedElementVisitor, context);
|
||||
return visitNode(node, discardedValueVisitor, isExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1320,8 +1318,8 @@ namespace ts {
|
||||
function visitDoStatement(node: DoStatement): VisitResult<Statement> {
|
||||
return factory.updateDoStatement(
|
||||
node,
|
||||
visitIterationBody(node.statement, nestedElementVisitor, context),
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression)
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context),
|
||||
visitNode(node.expression, visitor, isExpression)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1333,8 +1331,8 @@ namespace ts {
|
||||
function visitWhileStatement(node: WhileStatement): VisitResult<Statement> {
|
||||
return factory.updateWhileStatement(
|
||||
node,
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression),
|
||||
visitIterationBody(node.statement, nestedElementVisitor, context)
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitIterationBody(node.statement, topLevelNestedVisitor, context)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1347,7 +1345,7 @@ namespace ts {
|
||||
return factory.updateLabeledStatement(
|
||||
node,
|
||||
node.label,
|
||||
visitNode(node.statement, nestedElementVisitor, isStatement, factory.liftToBlock)
|
||||
visitNode(node.statement, topLevelNestedVisitor, isStatement, factory.liftToBlock)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1359,8 +1357,8 @@ namespace ts {
|
||||
function visitWithStatement(node: WithStatement): VisitResult<Statement> {
|
||||
return factory.updateWithStatement(
|
||||
node,
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression),
|
||||
visitNode(node.statement, nestedElementVisitor, isStatement, factory.liftToBlock)
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitNode(node.statement, topLevelNestedVisitor, isStatement, factory.liftToBlock)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1372,8 +1370,8 @@ namespace ts {
|
||||
function visitSwitchStatement(node: SwitchStatement): VisitResult<Statement> {
|
||||
return factory.updateSwitchStatement(
|
||||
node,
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression),
|
||||
visitNode(node.caseBlock, nestedElementVisitor, isCaseBlock)
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitNode(node.caseBlock, topLevelNestedVisitor, isCaseBlock)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1388,7 +1386,7 @@ namespace ts {
|
||||
|
||||
node = factory.updateCaseBlock(
|
||||
node,
|
||||
visitNodes(node.clauses, nestedElementVisitor, isCaseOrDefaultClause)
|
||||
visitNodes(node.clauses, topLevelNestedVisitor, isCaseOrDefaultClause)
|
||||
);
|
||||
|
||||
enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer;
|
||||
@@ -1403,8 +1401,8 @@ namespace ts {
|
||||
function visitCaseClause(node: CaseClause): VisitResult<CaseOrDefaultClause> {
|
||||
return factory.updateCaseClause(
|
||||
node,
|
||||
visitNode(node.expression, destructuringAndImportCallVisitor, isExpression),
|
||||
visitNodes(node.statements, nestedElementVisitor, isStatement)
|
||||
visitNode(node.expression, visitor, isExpression),
|
||||
visitNodes(node.statements, topLevelNestedVisitor, isStatement)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1414,7 +1412,7 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitDefaultClause(node: DefaultClause): VisitResult<CaseOrDefaultClause> {
|
||||
return visitEachChild(node, nestedElementVisitor, context);
|
||||
return visitEachChild(node, topLevelNestedVisitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1423,7 +1421,7 @@ namespace ts {
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitTryStatement(node: TryStatement): VisitResult<Statement> {
|
||||
return visitEachChild(node, nestedElementVisitor, context);
|
||||
return visitEachChild(node, topLevelNestedVisitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1438,7 +1436,7 @@ namespace ts {
|
||||
node = factory.updateCatchClause(
|
||||
node,
|
||||
node.variableDeclaration,
|
||||
visitNode(node.block, nestedElementVisitor, isBlock)
|
||||
visitNode(node.block, topLevelNestedVisitor, isBlock)
|
||||
);
|
||||
|
||||
enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer;
|
||||
@@ -1454,7 +1452,7 @@ namespace ts {
|
||||
const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer;
|
||||
enclosingBlockScopedContainer = node;
|
||||
|
||||
node = visitEachChild(node, nestedElementVisitor, context);
|
||||
node = visitEachChild(node, topLevelNestedVisitor, context);
|
||||
|
||||
enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer;
|
||||
return node;
|
||||
@@ -1469,19 +1467,59 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function destructuringAndImportCallVisitor(node: Node): VisitResult<Node> {
|
||||
if (isDestructuringAssignment(node)) {
|
||||
return visitDestructuringAssignment(node);
|
||||
}
|
||||
else if (isImportCall(node)) {
|
||||
return visitImportCallExpression(node);
|
||||
}
|
||||
else if ((node.transformFlags & TransformFlags.ContainsDestructuringAssignment) || (node.transformFlags & TransformFlags.ContainsDynamicImport)) {
|
||||
return visitEachChild(node, destructuringAndImportCallVisitor, context);
|
||||
}
|
||||
else {
|
||||
function visitorWorker(node: Node, valueIsDiscarded: boolean): VisitResult<Node> {
|
||||
if (!(node.transformFlags & (TransformFlags.ContainsDestructuringAssignment | TransformFlags.ContainsDynamicImport | TransformFlags.ContainsUpdateExpressionForIdentifier))) {
|
||||
return node;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
return visitForStatement(node as ForStatement, /*isTopLevel*/ false);
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return visitExpressionStatement(node as ExpressionStatement);
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return visitParenthesizedExpression(node as ParenthesizedExpression, valueIsDiscarded);
|
||||
case SyntaxKind.PartiallyEmittedExpression:
|
||||
return visitPartiallyEmittedExpression(node as PartiallyEmittedExpression, valueIsDiscarded);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
if (isDestructuringAssignment(node)) {
|
||||
return visitDestructuringAssignment(node, valueIsDiscarded);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
if (isImportCall(node)) {
|
||||
return visitImportCallExpression(node);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return visitPrefixOrPostfixUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded);
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit nodes to flatten destructuring assignments to exported symbols.
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitor(node: Node): VisitResult<Node> {
|
||||
return visitorWorker(node, /*valueIsDiscarded*/ false);
|
||||
}
|
||||
|
||||
function discardedValueVisitor(node: Node): VisitResult<Node> {
|
||||
return visitorWorker(node, /*valueIsDiscarded*/ true);
|
||||
}
|
||||
|
||||
function visitExpressionStatement(node: ExpressionStatement) {
|
||||
return factory.updateExpressionStatement(node, visitNode(node.expression, discardedValueVisitor, isExpression));
|
||||
}
|
||||
|
||||
function visitParenthesizedExpression(node: ParenthesizedExpression, valueIsDiscarded: boolean) {
|
||||
return factory.updateParenthesizedExpression(node, visitNode(node.expression, valueIsDiscarded ? discardedValueVisitor : visitor, isExpression));
|
||||
}
|
||||
|
||||
function visitPartiallyEmittedExpression(node: PartiallyEmittedExpression, valueIsDiscarded: boolean) {
|
||||
return factory.updatePartiallyEmittedExpression(node, visitNode(node.expression, valueIsDiscarded ? discardedValueVisitor : visitor, isExpression));
|
||||
}
|
||||
|
||||
function visitImportCallExpression(node: ImportCall): Expression {
|
||||
@@ -1496,7 +1534,7 @@ namespace ts {
|
||||
// };
|
||||
// });
|
||||
const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions);
|
||||
const firstArgument = visitNode(firstOrUndefined(node.arguments), destructuringAndImportCallVisitor);
|
||||
const firstArgument = visitNode(firstOrUndefined(node.arguments), visitor);
|
||||
// Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output.
|
||||
const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument;
|
||||
return factory.createCallExpression(
|
||||
@@ -1514,18 +1552,18 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult<Expression> {
|
||||
function visitDestructuringAssignment(node: DestructuringAssignment, valueIsDiscarded: boolean): VisitResult<Expression> {
|
||||
if (hasExportedReferenceInDestructuringTarget(node.left)) {
|
||||
return flattenDestructuringAssignment(
|
||||
node,
|
||||
destructuringAndImportCallVisitor,
|
||||
visitor,
|
||||
context,
|
||||
FlattenLevel.All,
|
||||
/*needsValue*/ true
|
||||
!valueIsDiscarded
|
||||
);
|
||||
}
|
||||
|
||||
return visitEachChild(node, destructuringAndImportCallVisitor, context);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1561,6 +1599,54 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function visitPrefixOrPostfixUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded: boolean) {
|
||||
// When we see a prefix or postfix increment expression whose operand is an exported
|
||||
// symbol, we should ensure all exports of that symbol are updated with the correct
|
||||
// value.
|
||||
//
|
||||
// - We do not transform generated identifiers for any reason.
|
||||
// - We do not transform identifiers tagged with the LocalName flag.
|
||||
// - We do not transform identifiers that were originally the name of an enum or
|
||||
// namespace due to how they are transformed in TypeScript.
|
||||
// - We only transform identifiers that are exported at the top level.
|
||||
if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken)
|
||||
&& isIdentifier(node.operand)
|
||||
&& !isGeneratedIdentifier(node.operand)
|
||||
&& !isLocalName(node.operand)
|
||||
&& !isDeclarationNameOfEnumOrNamespace(node.operand)) {
|
||||
const exportedNames = getExports(node.operand);
|
||||
if (exportedNames) {
|
||||
let temp: Identifier | undefined;
|
||||
let expression: Expression = visitNode(node.operand, visitor, isExpression);
|
||||
if (isPrefixUnaryExpression(node)) {
|
||||
expression = factory.updatePrefixUnaryExpression(node, expression);
|
||||
}
|
||||
else {
|
||||
expression = factory.updatePostfixUnaryExpression(node, expression);
|
||||
if (!valueIsDiscarded) {
|
||||
temp = factory.createTempVariable(hoistVariableDeclaration);
|
||||
expression = factory.createAssignment(temp, expression);
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
expression = factory.createComma(expression, factory.cloneNode(node.operand));
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
|
||||
for (const exportName of exportedNames) {
|
||||
expression = createExportExpression(exportName, preventSubstitution(expression));
|
||||
}
|
||||
|
||||
if (temp) {
|
||||
expression = factory.createComma(expression, temp);
|
||||
setTextRange(expression, node);
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
//
|
||||
// Modifier Visitors
|
||||
//
|
||||
@@ -1705,9 +1791,6 @@ namespace ts {
|
||||
return substituteExpressionIdentifier(node as Identifier);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return substituteBinaryExpression(node as BinaryExpression);
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return substituteUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression);
|
||||
case SyntaxKind.MetaProperty:
|
||||
return substituteMetaProperty(node as MetaProperty);
|
||||
}
|
||||
@@ -1797,55 +1880,6 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitution for a UnaryExpression that may contain an imported or exported symbol.
|
||||
*
|
||||
* @param node The node to substitute.
|
||||
*/
|
||||
function substituteUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression): Expression {
|
||||
// When we see a prefix or postfix increment expression whose operand is an exported
|
||||
// symbol, we should ensure all exports of that symbol are updated with the correct
|
||||
// value.
|
||||
//
|
||||
// - We do not substitute generated identifiers for any reason.
|
||||
// - We do not substitute identifiers tagged with the LocalName flag.
|
||||
// - We do not substitute identifiers that were originally the name of an enum or
|
||||
// namespace due to how they are transformed in TypeScript.
|
||||
// - We only substitute identifiers that are exported at the top level.
|
||||
if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken)
|
||||
&& isIdentifier(node.operand)
|
||||
&& !isGeneratedIdentifier(node.operand)
|
||||
&& !isLocalName(node.operand)
|
||||
&& !isDeclarationNameOfEnumOrNamespace(node.operand)) {
|
||||
const exportedNames = getExports(node.operand);
|
||||
if (exportedNames) {
|
||||
let expression: Expression = node.kind === SyntaxKind.PostfixUnaryExpression
|
||||
? setTextRange(
|
||||
factory.createPrefixUnaryExpression(
|
||||
node.operator,
|
||||
node.operand
|
||||
),
|
||||
node
|
||||
)
|
||||
: node;
|
||||
|
||||
for (const exportName of exportedNames) {
|
||||
expression = createExportExpression(exportName, preventSubstitution(expression));
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.PostfixUnaryExpression) {
|
||||
expression = node.operator === SyntaxKind.PlusPlusToken
|
||||
? factory.createSubtract(preventSubstitution(expression), factory.createNumericLiteral(1))
|
||||
: factory.createAdd(preventSubstitution(expression), factory.createNumericLiteral(1));
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function substituteMetaProperty(node: MetaProperty) {
|
||||
if (isImportMeta(node)) {
|
||||
return factory.createPropertyAccessExpression(contextObject, factory.createIdentifier("meta"));
|
||||
|
||||
@@ -6640,6 +6640,7 @@ namespace ts {
|
||||
ContainsClassFields = 1 << 23,
|
||||
ContainsPossibleTopLevelAwait = 1 << 24,
|
||||
ContainsLexicalSuper = 1 << 25,
|
||||
ContainsUpdateExpressionForIdentifier = 1 << 26,
|
||||
// Please leave this as 1 << 29.
|
||||
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
|
||||
// It is a good reminder of how much room we have left
|
||||
|
||||
@@ -36,98 +36,424 @@ namespace evaluator {
|
||||
|
||||
const output = result.getOutput(source.main, "js")!;
|
||||
assert.isDefined(output);
|
||||
|
||||
globals = { Symbol: FakeSymbol, ...globals };
|
||||
return createLoader(fs, globals)(output.file);
|
||||
}
|
||||
|
||||
function createLoader(fs: vfs.FileSystem, globals: Record<string, any>) {
|
||||
interface Module {
|
||||
exports: any;
|
||||
}
|
||||
|
||||
const moduleCache = new ts.Map<string, Module>();
|
||||
return load;
|
||||
|
||||
function evaluate(text: string, file: string, module: Module) {
|
||||
const globalNames: string[] = [];
|
||||
const globalArgs: any[] = [];
|
||||
for (const name in globals) {
|
||||
if (ts.hasProperty(globals, name)) {
|
||||
globalNames.push(name);
|
||||
globalArgs.push(globals[name]);
|
||||
}
|
||||
}
|
||||
const base = vpath.dirname(file);
|
||||
const localRequire = (id: string) => requireModule(id, base);
|
||||
const evaluateText = `(function (module, exports, require, __dirname, __filename, ${globalNames.join(", ")}) { ${text} })`;
|
||||
// eslint-disable-next-line no-eval
|
||||
const evaluateThunk = (void 0, eval)(evaluateText) as (module: any, exports: any, require: (id: string) => any, dirname: string, filename: string, ...globalArgs: any[]) => void;
|
||||
evaluateThunk.call(globals, module, module.exports, localRequire, vpath.dirname(file), file, FakeSymbol, ...globalArgs);
|
||||
}
|
||||
|
||||
function loadModule(file: string): Module {
|
||||
if (!ts.isExternalModuleNameRelative(file)) throw new Error(`Module '${file}' could not be found.`);
|
||||
let module = moduleCache.get(file);
|
||||
if (module) return module;
|
||||
moduleCache.set(file, module = { exports: {} });
|
||||
try {
|
||||
const sourceText = fs.readFileSync(file, "utf8");
|
||||
evaluate(sourceText, file, module);
|
||||
return module;
|
||||
}
|
||||
catch (e) {
|
||||
moduleCache.delete(file);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function isFile(file: string) {
|
||||
return fs.existsSync(file) && fs.statSync(file).isFile();
|
||||
}
|
||||
|
||||
function loadAsFile(file: string): Module | undefined {
|
||||
if (isFile(file)) return loadModule(file);
|
||||
if (isFile(file + ".js")) return loadModule(file + ".js");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function loadIndex(dir: string): Module | undefined {
|
||||
const indexFile = vpath.resolve(dir, "index.js");
|
||||
if (isFile(indexFile)) return loadModule(indexFile);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function loadAsDirectory(dir: string): Module | undefined {
|
||||
const packageFile = vpath.resolve(dir, "package.json");
|
||||
if (isFile(packageFile)) {
|
||||
const text = fs.readFileSync(packageFile, "utf8");
|
||||
const json = JSON.parse(text);
|
||||
if (json.main) {
|
||||
const main = vpath.resolve(dir, json.main);
|
||||
const result = loadAsFile(main) || loadIndex(main);
|
||||
if (result === undefined) throw new Error("Module not found");
|
||||
}
|
||||
}
|
||||
return loadIndex(dir);
|
||||
}
|
||||
|
||||
function requireModule(id: string, base: string) {
|
||||
if (!ts.isExternalModuleNameRelative(id)) throw new Error(`Module '${id}' could not be found.`);
|
||||
const file = vpath.resolve(base, id);
|
||||
const module = loadAsFile(file) || loadAsDirectory(file);
|
||||
if (!module) throw new Error(`Module '${id}' could not be found.`);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
function load(file: string) {
|
||||
return requireModule(file, fs.cwd());
|
||||
}
|
||||
const loader = getLoader(compilerOptions, fs, globals);
|
||||
return loader.import(output.file);
|
||||
}
|
||||
|
||||
export function evaluateJavaScript(sourceText: string, globals?: Record<string, any>, sourceFile = sourceFileJs) {
|
||||
globals = { Symbol: FakeSymbol, ...globals };
|
||||
const fs = new vfs.FileSystem(/*ignoreCase*/ false, { files: { [sourceFile]: sourceText } });
|
||||
return createLoader(fs, globals)(sourceFile);
|
||||
return new CommonJsLoader(fs, globals).import(sourceFile);
|
||||
}
|
||||
|
||||
function getLoader(compilerOptions: ts.CompilerOptions, fs: vfs.FileSystem, globals: Record<string, any>): Loader<unknown> {
|
||||
const moduleKind = ts.getEmitModuleKind(compilerOptions);
|
||||
switch (moduleKind) {
|
||||
case ts.ModuleKind.UMD:
|
||||
case ts.ModuleKind.CommonJS:
|
||||
return new CommonJsLoader(fs, globals);
|
||||
case ts.ModuleKind.System:
|
||||
return new SystemLoader(fs, globals);
|
||||
case ts.ModuleKind.AMD:
|
||||
case ts.ModuleKind.None:
|
||||
default:
|
||||
throw new Error(`ModuleKind '${ts.ModuleKind[moduleKind]}' not supported by evaluator.`);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Loader<TModule> {
|
||||
protected readonly fs: vfs.FileSystem;
|
||||
protected readonly globals: Record<string, any>;
|
||||
|
||||
private moduleCache = new ts.Map<string, TModule>();
|
||||
|
||||
constructor(fs: vfs.FileSystem, globals: Record<string, any>) {
|
||||
this.fs = fs;
|
||||
this.globals = globals;
|
||||
}
|
||||
|
||||
protected isFile(file: string) {
|
||||
return this.fs.existsSync(file) && this.fs.statSync(file).isFile();
|
||||
}
|
||||
|
||||
protected abstract evaluate(text: string, file: string, module: TModule): void;
|
||||
protected abstract createModule(file: string): TModule;
|
||||
protected abstract getExports(module: TModule): any;
|
||||
|
||||
protected load(file: string): TModule {
|
||||
if (!ts.isExternalModuleNameRelative(file)) throw new Error(`Module '${file}' could not be found.`);
|
||||
let module = this.moduleCache.get(file);
|
||||
if (module) return module;
|
||||
this.moduleCache.set(file, module = this.createModule(file));
|
||||
try {
|
||||
const sourceText = this.fs.readFileSync(file, "utf8");
|
||||
this.evaluate(sourceText, file, module);
|
||||
return module;
|
||||
}
|
||||
catch (e) {
|
||||
this.moduleCache.delete(file);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected resolve(id: string, base: string) {
|
||||
return vpath.resolve(base, id);
|
||||
}
|
||||
|
||||
import(id: string, base = this.fs.cwd()) {
|
||||
if (!ts.isExternalModuleNameRelative(id)) throw new Error(`Module '${id}' could not be found.`);
|
||||
const file = this.resolve(id, base);
|
||||
const module = this.load(file);
|
||||
if (!module) throw new Error(`Module '${id}' could not be found.`);
|
||||
return this.getExports(module);
|
||||
}
|
||||
}
|
||||
|
||||
interface CommonJSModule {
|
||||
exports: any;
|
||||
}
|
||||
|
||||
class CommonJsLoader extends Loader<CommonJSModule> {
|
||||
private resolveAsFile(file: string) {
|
||||
if (this.isFile(file)) return file;
|
||||
if (this.isFile(file + ".js")) return file + ".js";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private resolveIndex(dir: string) {
|
||||
const indexFile = vpath.resolve(dir, "index.js");
|
||||
if (this.isFile(indexFile)) return indexFile;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private resolveAsDirectory(dir: string) {
|
||||
const packageFile = vpath.resolve(dir, "package.json");
|
||||
if (this.isFile(packageFile)) {
|
||||
const text = this.fs.readFileSync(packageFile, "utf8");
|
||||
const json = JSON.parse(text);
|
||||
if (json.main) {
|
||||
const main = vpath.resolve(dir, json.main);
|
||||
const result = this.resolveAsFile(main) || this.resolveIndex(main);
|
||||
if (result === undefined) throw new Error("Module not found");
|
||||
}
|
||||
}
|
||||
return this.resolveIndex(dir);
|
||||
}
|
||||
|
||||
protected resolve(id: string, base: string) {
|
||||
const file = vpath.resolve(base, id);
|
||||
const resolved = this.resolveAsFile(file) || this.resolveAsDirectory(file);
|
||||
if (!resolved) throw new Error(`Module '${id}' could not be found.`);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
protected createModule(): CommonJSModule {
|
||||
return { exports: {} };
|
||||
}
|
||||
|
||||
protected getExports(module: CommonJSModule) {
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
protected evaluate(text: string, file: string, module: CommonJSModule): void {
|
||||
const globalNames: string[] = [];
|
||||
const globalArgs: any[] = [];
|
||||
for (const name in this.globals) {
|
||||
if (ts.hasProperty(this.globals, name)) {
|
||||
globalNames.push(name);
|
||||
globalArgs.push(this.globals[name]);
|
||||
}
|
||||
}
|
||||
const base = vpath.dirname(file);
|
||||
const localRequire = (id: string) => this.import(id, base);
|
||||
const evaluateText = `(function (module, exports, require, __dirname, __filename, ${globalNames.join(", ")}) { ${text} })`;
|
||||
// eslint-disable-next-line no-eval
|
||||
const evaluateThunk = (void 0, eval)(evaluateText) as (module: any, exports: any, require: (id: string) => any, dirname: string, filename: string, ...globalArgs: any[]) => void;
|
||||
evaluateThunk.call(this.globals, module, module.exports, localRequire, vpath.dirname(file), file, ...globalArgs);
|
||||
}
|
||||
}
|
||||
|
||||
interface SystemModule {
|
||||
file: string;
|
||||
exports: any;
|
||||
hasExports: boolean;
|
||||
state: SystemModuleState;
|
||||
dependencies: SystemModule[];
|
||||
dependers: SystemModule[];
|
||||
setters: SystemModuleDependencySetter[];
|
||||
requestedDependencies?: string[];
|
||||
declaration?: SystemModuleDeclaration;
|
||||
hasError?: boolean;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
const enum SystemModuleState {
|
||||
// Instantiation phases:
|
||||
Uninstantiated,
|
||||
Instantiated,
|
||||
|
||||
// Linker phases:
|
||||
AddingDependencies,
|
||||
AllDependenciesAdded,
|
||||
AllDependenciesInstantiated,
|
||||
WiringSetters,
|
||||
Linked,
|
||||
|
||||
// Evaluation phases:
|
||||
Evaluating,
|
||||
Ready,
|
||||
}
|
||||
|
||||
interface SystemModuleExporter {
|
||||
<T>(name: string, value: T): T;
|
||||
<T extends object>(values: T): T;
|
||||
}
|
||||
|
||||
interface SystemModuleContext {
|
||||
import: (id: string) => Promise<any>;
|
||||
meta: any;
|
||||
}
|
||||
|
||||
type SystemModuleRegisterCallback = (exporter: SystemModuleExporter, context: SystemModuleContext) => SystemModuleDeclaration;
|
||||
type SystemModuleDependencySetter = (dependency: any) => void;
|
||||
|
||||
interface SystemModuleDeclaration {
|
||||
setters: SystemModuleDependencySetter[];
|
||||
execute: () => void;
|
||||
}
|
||||
|
||||
interface SystemGlobal {
|
||||
register(dependencies: string[], declare: SystemModuleRegisterCallback): void;
|
||||
}
|
||||
|
||||
class SystemLoader extends Loader<SystemModule> {
|
||||
protected createModule(file: string): SystemModule {
|
||||
return {
|
||||
file,
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
exports: Object.create(/*o*/ null),
|
||||
dependencies: [],
|
||||
dependers: [],
|
||||
setters: [],
|
||||
hasExports: false,
|
||||
state: SystemModuleState.Uninstantiated
|
||||
};
|
||||
}
|
||||
|
||||
protected getExports(module: SystemModule) {
|
||||
if (module.state < SystemModuleState.Ready) {
|
||||
this.resetDependers(module, []);
|
||||
this.evaluateModule(module, []);
|
||||
if (module.state < SystemModuleState.Ready) {
|
||||
const error = new Error("Module graph could not be loaded");
|
||||
this.handleError(module, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (module.hasError) {
|
||||
throw module.error;
|
||||
}
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
private handleError(module: SystemModule, error: any) {
|
||||
if (!module.hasError) {
|
||||
module.hasError = true;
|
||||
module.error = error;
|
||||
module.state = SystemModuleState.Ready;
|
||||
}
|
||||
}
|
||||
|
||||
protected evaluate(text: string, _file: string, module: SystemModule): void {
|
||||
const globalNames: string[] = [];
|
||||
const globalArgs: any[] = [];
|
||||
for (const name in this.globals) {
|
||||
if (ts.hasProperty(this.globals, name)) {
|
||||
globalNames.push(name);
|
||||
globalArgs.push(this.globals[name]);
|
||||
}
|
||||
}
|
||||
const localSystem: SystemGlobal = {
|
||||
register: (dependencies, declare) => this.instantiateModule(module, dependencies, declare)
|
||||
};
|
||||
const evaluateText = `(function (System, ${globalNames.join(", ")}) { ${text} })`;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
const evaluateThunk = (void 0, eval)(evaluateText) as (System: any, ...globalArgs: any[]) => void;
|
||||
evaluateThunk.call(this.globals, localSystem, ...globalArgs);
|
||||
}
|
||||
catch (e) {
|
||||
this.handleError(module, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private instantiateModule(module: SystemModule, dependencies: string[], registration?: SystemModuleRegisterCallback) {
|
||||
function exporter<T>(name: string, value: T): T;
|
||||
function exporter<T>(value: T): T;
|
||||
function exporter<T>(...args: [string, T] | [T]) {
|
||||
module.hasExports = true;
|
||||
const name = args.length === 1 ? undefined : args[0];
|
||||
const value = args.length === 1 ? args[0] : args[1];
|
||||
if (name !== undefined) {
|
||||
module.exports[name] = value;
|
||||
}
|
||||
else {
|
||||
for (const name in value) {
|
||||
module.exports[name] = value[name];
|
||||
}
|
||||
}
|
||||
for (const setter of module.setters) {
|
||||
setter(module.exports);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const context: SystemModuleContext = {
|
||||
import: (_id) => { throw new Error("Dynamic import not implemented."); },
|
||||
meta: {
|
||||
url: ts.isUrl(module.file) ? module.file : `file:///${ts.normalizeSlashes(module.file).replace(/^\//, "").split("/").map(encodeURIComponent).join("/")}`
|
||||
}
|
||||
};
|
||||
|
||||
module.requestedDependencies = dependencies;
|
||||
|
||||
try {
|
||||
module.declaration = registration?.(exporter, context);
|
||||
module.state = SystemModuleState.Instantiated;
|
||||
|
||||
for (const depender of module.dependers) {
|
||||
this.linkModule(depender);
|
||||
}
|
||||
|
||||
this.linkModule(module);
|
||||
}
|
||||
catch (e) {
|
||||
this.handleError(module, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private linkModule(module: SystemModule) {
|
||||
try {
|
||||
for (;;) {
|
||||
switch (module.state) {
|
||||
case SystemModuleState.Uninstantiated: {
|
||||
throw new Error("Module not yet instantiated");
|
||||
}
|
||||
case SystemModuleState.Instantiated: {
|
||||
// Module has been instantiated, start requesting dependencies.
|
||||
// Set state so that re-entry while adding dependencies does nothing.
|
||||
module.state = SystemModuleState.AddingDependencies;
|
||||
const base = vpath.dirname(module.file);
|
||||
const dependencies = module.requestedDependencies || [];
|
||||
|
||||
for (const dependencyId of dependencies) {
|
||||
const dependency = this.load(this.resolve(dependencyId, base));
|
||||
module.dependencies.push(dependency);
|
||||
dependency.dependers.push(module);
|
||||
}
|
||||
|
||||
// All dependencies have been added, switch state
|
||||
// to check whether all dependencies are instantiated
|
||||
module.state = SystemModuleState.AllDependenciesAdded;
|
||||
continue;
|
||||
}
|
||||
case SystemModuleState.AddingDependencies: {
|
||||
// in the middle of adding dependencies for this module, do nothing
|
||||
return;
|
||||
}
|
||||
case SystemModuleState.AllDependenciesAdded: {
|
||||
// all dependencies have been added, advance state if all dependencies are instantiated.
|
||||
for (const dependency of module.dependencies) {
|
||||
if (dependency.state === SystemModuleState.Uninstantiated) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate all dependencies are instantiated for this module.
|
||||
module.state = SystemModuleState.AllDependenciesInstantiated;
|
||||
|
||||
// trigger links for dependers of this module.
|
||||
for (const depender of module.dependers) {
|
||||
this.linkModule(depender);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case SystemModuleState.AllDependenciesInstantiated: {
|
||||
// all dependencies have been instantiated, start wiring setters
|
||||
module.state = SystemModuleState.WiringSetters;
|
||||
for (let i = 0; i < module.dependencies.length; i++) {
|
||||
const dependency = module.dependencies[i];
|
||||
const setter = module.declaration?.setters[i];
|
||||
if (setter) {
|
||||
dependency.setters.push(setter);
|
||||
if (dependency.hasExports || dependency.state === SystemModuleState.Ready) {
|
||||
// wire hoisted exports or ready dependencies.
|
||||
setter(dependency.exports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.state = SystemModuleState.Linked;
|
||||
|
||||
// ensure graph is fully linked
|
||||
for (const depender of module.dependers) {
|
||||
this.linkModule(depender);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case SystemModuleState.WiringSetters: // in the middle of wiring setters for this module, nothing to do
|
||||
case SystemModuleState.Linked: // module has already been linked, nothing to do
|
||||
case SystemModuleState.Evaluating: // module is currently evaluating, nothing to do
|
||||
case SystemModuleState.Ready: // module is done evaluating, nothing to do
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.handleError(module, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private resetDependers(module: SystemModule, stack: SystemModule[]) {
|
||||
if (stack.lastIndexOf(module) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack.push(module);
|
||||
module.dependers.length = 0;
|
||||
for (const dependency of module.dependencies) {
|
||||
this.resetDependers(dependency, stack);
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
private evaluateModule(module: SystemModule, stack: SystemModule[]) {
|
||||
if (module.state < SystemModuleState.Linked) throw new Error("Invalid state for evaluation.");
|
||||
if (module.state !== SystemModuleState.Linked) return;
|
||||
|
||||
if (stack.lastIndexOf(module) !== -1) {
|
||||
// we are already evaluating this module
|
||||
return;
|
||||
}
|
||||
|
||||
stack.push(module);
|
||||
module.state = SystemModuleState.Evaluating;
|
||||
try {
|
||||
for (const dependency of module.dependencies) {
|
||||
this.evaluateModule(dependency, stack);
|
||||
}
|
||||
module.declaration?.execute?.();
|
||||
module.state = SystemModuleState.Ready;
|
||||
}
|
||||
catch (e) {
|
||||
this.handleError(module, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"unittests/evaluation/optionalCall.ts",
|
||||
"unittests/evaluation/objectRest.ts",
|
||||
"unittests/evaluation/superInStaticInitializer.ts",
|
||||
"unittests/evaluation/updateExpressionInModule.ts",
|
||||
"unittests/services/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/services/colorization.ts",
|
||||
"unittests/services/convertToAsyncFunction.ts",
|
||||
|
||||
125
src/testRunner/unittests/evaluation/updateExpressionInModule.ts
Normal file
125
src/testRunner/unittests/evaluation/updateExpressionInModule.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
describe("unittests:: evaluation:: updateExpressionInModule", () => {
|
||||
// only run BigInt tests if BigInt is supported in the host environment
|
||||
const itIfBigInt = typeof BigInt === "function" ? it : it.skip;
|
||||
|
||||
it("pre-increment in commonjs using Number", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = 1;
|
||||
export { a };
|
||||
export const b = ++a;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.CommonJS });
|
||||
assert.equal(result.a, 2);
|
||||
assert.equal(result.b, 2);
|
||||
});
|
||||
it("pre-increment in System using Number", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = 1;
|
||||
export { a };
|
||||
export const b = ++a;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.System });
|
||||
assert.equal(result.a, 2);
|
||||
assert.equal(result.b, 2);
|
||||
});
|
||||
itIfBigInt("pre-increment in commonjs using BigInt", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = BigInt(1);
|
||||
export { a };
|
||||
export const b = ++a;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.CommonJS }, { BigInt });
|
||||
assert.equal(result.a, BigInt(2));
|
||||
assert.equal(result.b, BigInt(2));
|
||||
});
|
||||
itIfBigInt("pre-increment in System using BigInt", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = BigInt(1);
|
||||
export { a };
|
||||
export const b = ++a;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.System }, { BigInt });
|
||||
assert.equal(result.a, BigInt(2));
|
||||
assert.equal(result.b, BigInt(2));
|
||||
});
|
||||
it("post-increment in commonjs using Number", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = 1;
|
||||
export { a };
|
||||
export const b = a++;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.CommonJS });
|
||||
assert.equal(result.a, 2);
|
||||
assert.equal(result.b, 1);
|
||||
});
|
||||
it("post-increment in System using Number", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = 1;
|
||||
export { a };
|
||||
export const b = a++;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.System });
|
||||
assert.equal(result.a, 2);
|
||||
assert.equal(result.b, 1);
|
||||
});
|
||||
itIfBigInt("post-increment in commonjs using BigInt", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = BigInt(1);
|
||||
export { a };
|
||||
export const b = a++;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.CommonJS }, { BigInt });
|
||||
assert.equal(result.a, BigInt(2));
|
||||
assert.equal(result.b, BigInt(1));
|
||||
});
|
||||
itIfBigInt("post-increment in System using BigInt", () => {
|
||||
const result = evaluator.evaluateTypeScript({
|
||||
files: {
|
||||
"/.src/main.ts": `
|
||||
let a = BigInt(1);
|
||||
export { a };
|
||||
export const b = a++;
|
||||
`
|
||||
},
|
||||
rootFiles: ["/.src/main.ts"],
|
||||
main: "/.src/main.ts"
|
||||
}, { module: ts.ModuleKind.System }, { BigInt });
|
||||
assert.equal(result.a, BigInt(2));
|
||||
assert.equal(result.b, BigInt(1));
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ var buzz = 10;
|
||||
buzz += 3;
|
||||
|
||||
var bizz = 8;
|
||||
bizz++; // compiles to exports.bizz = bizz += 1
|
||||
bizz++; // compiles to exports.bizz = (bizz++, bizz)
|
||||
bizz--; // similarly
|
||||
++bizz; // compiles to exports.bizz = ++bizz
|
||||
|
||||
@@ -32,6 +32,6 @@ exports.buzz = buzz;
|
||||
exports.buzz = buzz += 3;
|
||||
var bizz = 8;
|
||||
exports.bizz = bizz;
|
||||
(exports.bizz = ++bizz) - 1; // compiles to exports.bizz = bizz += 1
|
||||
(exports.bizz = --bizz) + 1; // similarly
|
||||
exports.bizz = (bizz++, bizz); // compiles to exports.bizz = (bizz++, bizz)
|
||||
exports.bizz = (bizz--, bizz); // similarly
|
||||
exports.bizz = ++bizz; // compiles to exports.bizz = ++bizz
|
||||
|
||||
@@ -20,7 +20,7 @@ buzz += 3;
|
||||
var bizz = 8;
|
||||
>bizz : Symbol(bizz, Decl(server.ts, 9, 3))
|
||||
|
||||
bizz++; // compiles to exports.bizz = bizz += 1
|
||||
bizz++; // compiles to exports.bizz = (bizz++, bizz)
|
||||
>bizz : Symbol(bizz, Decl(server.ts, 9, 3))
|
||||
|
||||
bizz--; // similarly
|
||||
|
||||
@@ -30,7 +30,7 @@ var bizz = 8;
|
||||
>bizz : number
|
||||
>8 : 8
|
||||
|
||||
bizz++; // compiles to exports.bizz = bizz += 1
|
||||
bizz++; // compiles to exports.bizz = (bizz++, bizz)
|
||||
>bizz++ : number
|
||||
>bizz : number
|
||||
|
||||
|
||||
@@ -18,21 +18,22 @@ export { x };
|
||||
|
||||
//// [moduleExportsUnaryExpression.js]
|
||||
"use strict";
|
||||
var _a, _b, _c, _d;
|
||||
exports.__esModule = true;
|
||||
exports.x = exports.foo = void 0;
|
||||
var x = 1;
|
||||
exports.x = x;
|
||||
function foo(y) {
|
||||
if (y <= (exports.x = ++x) - 1)
|
||||
return y <= (exports.x = ++x) - 1;
|
||||
if (y <= (exports.x = --x) + 1)
|
||||
return y <= (exports.x = --x) + 1;
|
||||
if (y <= (exports.x = (_a = x++, x), _a))
|
||||
return y <= (exports.x = (_b = x++, x), _b);
|
||||
if (y <= (exports.x = (_c = x--, x), _c))
|
||||
return y <= (exports.x = (_d = x--, x), _d);
|
||||
if (y <= (exports.x = ++x))
|
||||
return y <= (exports.x = ++x);
|
||||
if (y <= (exports.x = --x))
|
||||
return y <= (exports.x = --x);
|
||||
(exports.x = ++x) - 1;
|
||||
(exports.x = --x) + 1;
|
||||
exports.x = (x++, x);
|
||||
exports.x = (x--, x);
|
||||
exports.x = ++x;
|
||||
exports.x = --x;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ System.register([], function (exports_1, context_1) {
|
||||
setters: [],
|
||||
execute: function () {
|
||||
exports_1("x", x = 1);
|
||||
exports_1("x", ++x) - 1;
|
||||
exports_1("x", --x) + 1;
|
||||
exports_1("x", (x++, x));
|
||||
exports_1("x", (x--, x));
|
||||
exports_1("x", ++x);
|
||||
exports_1("x", --x);
|
||||
exports_1("x", x += 1);
|
||||
@@ -55,8 +55,8 @@ System.register([], function (exports_1, context_1) {
|
||||
x - 1;
|
||||
x & 1;
|
||||
x | 1;
|
||||
for (exports_1("x", x = 5);; exports_1("x", ++x) - 1) { }
|
||||
for (exports_1("x", x = 8);; exports_1("x", --x) + 1) { }
|
||||
for (exports_1("x", x = 5);; exports_1("x", (x++, x))) { }
|
||||
for (exports_1("x", x = 8);; exports_1("x", (x--, x))) { }
|
||||
for (exports_1("x", x = 15);; exports_1("x", ++x)) { }
|
||||
for (exports_1("x", x = 18);; exports_1("x", --x)) { }
|
||||
for (var x_1 = 50;;) { }
|
||||
|
||||
@@ -12,7 +12,7 @@ var buzz = 10;
|
||||
buzz += 3;
|
||||
|
||||
var bizz = 8;
|
||||
bizz++; // compiles to exports.bizz = bizz += 1
|
||||
bizz++; // compiles to exports.bizz = (bizz++, bizz)
|
||||
bizz--; // similarly
|
||||
++bizz; // compiles to exports.bizz = ++bizz
|
||||
|
||||
|
||||
Reference in New Issue
Block a user