vendor tsgo
This commit is contained in:
799
tools/tsgo/internal/transformers/estransforms/using.go
Normal file
799
tools/tsgo/internal/transformers/estransforms/using.go
Normal file
@@ -0,0 +1,799 @@
|
||||
package estransforms
|
||||
|
||||
import (
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/debug"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
"github.com/microsoft/typescript-go/internal/transformers"
|
||||
)
|
||||
|
||||
type usingDeclarationTransformer struct {
|
||||
transformers.Transformer
|
||||
|
||||
exportBindings map[string]*ast.ExportSpecifierNode
|
||||
exportBindingNames []string
|
||||
exportVars []*ast.VariableDeclarationNode
|
||||
defaultExportBinding *ast.IdentifierNode
|
||||
exportEqualsBinding *ast.IdentifierNode
|
||||
}
|
||||
|
||||
func newUsingDeclarationTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
|
||||
tx := &usingDeclarationTransformer{}
|
||||
return tx.NewTransformer(tx.visit, opts.Context)
|
||||
}
|
||||
|
||||
type usingKind uint
|
||||
|
||||
const (
|
||||
usingKindNone usingKind = iota
|
||||
usingKindSync
|
||||
usingKindAsync
|
||||
)
|
||||
|
||||
func (tx *usingDeclarationTransformer) visit(node *ast.Node) *ast.Node {
|
||||
if node.SubtreeFacts()&ast.SubtreeContainsUsing == 0 {
|
||||
return node
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
case ast.KindSourceFile:
|
||||
node = tx.visitSourceFile(node.AsSourceFile())
|
||||
case ast.KindBlock:
|
||||
node = tx.visitBlock(node.AsBlock())
|
||||
case ast.KindForStatement:
|
||||
node = tx.visitForStatement(node.AsForStatement())
|
||||
case ast.KindForOfStatement:
|
||||
node = tx.visitForOfStatement(node.AsForInOrOfStatement())
|
||||
default:
|
||||
node = tx.Visitor().VisitEachChild(node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
|
||||
if node.IsDeclarationFile {
|
||||
return node.AsNode()
|
||||
}
|
||||
|
||||
var visited *ast.SourceFileNode
|
||||
usingKind := getUsingKindOfStatements(node.Statements.Nodes)
|
||||
if usingKind != usingKindNone {
|
||||
// Imports and exports must stay at the top level. This means we must hoist all imports, exports, and
|
||||
// top-level function declarations and bindings out of the `try` statements we generate. For example:
|
||||
//
|
||||
// given:
|
||||
//
|
||||
// import { w } from "mod";
|
||||
// const x = expr1;
|
||||
// using y = expr2;
|
||||
// const z = expr3;
|
||||
// export function f() {
|
||||
// console.log(z);
|
||||
// }
|
||||
//
|
||||
// produces:
|
||||
//
|
||||
// import { x } from "mod"; // <-- preserved
|
||||
// const x = expr1; // <-- preserved
|
||||
// var y, z; // <-- hoisted
|
||||
// export function f() { // <-- hoisted
|
||||
// console.log(z);
|
||||
// }
|
||||
// const env_1 = { stack: [], error: void 0, hasError: false };
|
||||
// try {
|
||||
// y = __addDisposableResource(env_1, expr2, false);
|
||||
// z = expr3;
|
||||
// }
|
||||
// catch (e_1) {
|
||||
// env_1.error = e_1;
|
||||
// env_1.hasError = true;
|
||||
// }
|
||||
// finally {
|
||||
// __disposeResource(env_1);
|
||||
// }
|
||||
//
|
||||
// In this transformation, we hoist `y`, `z`, and `f` to a new outer statement list while moving all other
|
||||
// statements in the source file into the `try` block, which is the same approach we use for System module
|
||||
// emit. Unlike System module emit, we attempt to preserve all statements prior to the first top-level
|
||||
// `using` to isolate the complexity of the transformed output to only where it is necessary.
|
||||
tx.EmitContext().StartVariableEnvironment()
|
||||
|
||||
tx.exportBindings = make(map[string]*ast.ExportSpecifierNode)
|
||||
tx.exportVars = nil
|
||||
|
||||
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
|
||||
var topLevelStatements []*ast.Statement
|
||||
topLevelStatements = append(topLevelStatements, core.FirstResult(tx.Visitor().VisitSlice(prologue))...)
|
||||
|
||||
// Collect and transform any leading statements up to the first `using` or `await using`. This preserves
|
||||
// the original statement order much as is possible.
|
||||
|
||||
pos := 0
|
||||
for pos < len(rest) {
|
||||
statement := rest[pos]
|
||||
if getUsingKind(statement) != usingKindNone {
|
||||
if pos > 0 {
|
||||
topLevelStatements = append(topLevelStatements, core.FirstResult(tx.Visitor().VisitSlice(rest[:pos]))...)
|
||||
}
|
||||
break
|
||||
}
|
||||
pos++
|
||||
}
|
||||
|
||||
if pos >= len(rest) {
|
||||
panic("Should have encountered at least one 'using' statement.")
|
||||
}
|
||||
|
||||
// transform the rest of the body
|
||||
envBinding := tx.createEnvBinding()
|
||||
bodyStatements := tx.transformUsingDeclarations(rest[pos:], envBinding, &topLevelStatements)
|
||||
|
||||
// add `export {}` declarations for any hoisted bindings.
|
||||
if len(tx.exportBindings) > 0 {
|
||||
exportSpecifiers := make([]*ast.ExportSpecifierNode, 0, len(tx.exportBindingNames))
|
||||
for _, name := range tx.exportBindingNames {
|
||||
specifier := tx.exportBindings[name]
|
||||
debug.Assert(specifier != nil, "Missing export binding for hoisted export name")
|
||||
exportSpecifiers = append(exportSpecifiers, specifier)
|
||||
}
|
||||
topLevelStatements = append(
|
||||
topLevelStatements,
|
||||
tx.Factory().NewExportDeclaration(
|
||||
nil, /*modifiers*/
|
||||
false, /*isTypeOnly*/
|
||||
tx.Factory().NewNamedExports(
|
||||
tx.Factory().NewNodeList(
|
||||
exportSpecifiers,
|
||||
),
|
||||
),
|
||||
nil, /*moduleSpecifier*/
|
||||
nil, /*attributes*/
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
topLevelStatements = append(topLevelStatements, tx.EmitContext().EndVariableEnvironment()...)
|
||||
if len(tx.exportVars) > 0 {
|
||||
topLevelStatements = append(topLevelStatements, tx.Factory().NewVariableStatement(
|
||||
tx.Factory().NewModifierList([]*ast.Node{
|
||||
tx.Factory().NewModifier(ast.KindExportKeyword),
|
||||
}),
|
||||
tx.Factory().NewVariableDeclarationList(
|
||||
tx.Factory().NewNodeList(tx.exportVars),
|
||||
ast.NodeFlagsLet,
|
||||
),
|
||||
))
|
||||
}
|
||||
topLevelStatements = append(topLevelStatements, tx.createDownlevelUsingStatements(bodyStatements, envBinding, usingKind == usingKindAsync)...)
|
||||
|
||||
if tx.exportEqualsBinding != nil {
|
||||
topLevelStatements = append(topLevelStatements, tx.Factory().NewExportAssignment(
|
||||
nil, /*modifiers*/
|
||||
true, /*isExportEquals*/
|
||||
nil, /*typeNode*/
|
||||
tx.exportEqualsBinding,
|
||||
))
|
||||
}
|
||||
|
||||
visited = tx.Factory().UpdateSourceFile(node, tx.Factory().NewNodeList(topLevelStatements), node.EndOfFileToken)
|
||||
} else {
|
||||
visited = tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
|
||||
tx.exportVars = nil
|
||||
tx.exportBindings = nil
|
||||
tx.exportBindingNames = nil
|
||||
tx.defaultExportBinding = nil
|
||||
tx.exportEqualsBinding = nil
|
||||
return visited
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) visitBlock(node *ast.Block) *ast.Node {
|
||||
usingKind := getUsingKindOfStatements(node.Statements.Nodes)
|
||||
if usingKind != usingKindNone {
|
||||
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
|
||||
envBinding := tx.createEnvBinding()
|
||||
statements := make([]*ast.Statement, 0, len(prologue)+2)
|
||||
statements = append(statements, core.FirstResult(tx.Visitor().VisitSlice(prologue))...)
|
||||
statements = append(statements, tx.createDownlevelUsingStatements(
|
||||
tx.transformUsingDeclarations(rest, envBinding, nil /*topLevelStatements*/),
|
||||
envBinding,
|
||||
usingKind == usingKindAsync,
|
||||
)...)
|
||||
statementList := tx.Factory().NewNodeList(statements)
|
||||
statementList.Loc = node.Statements.Loc
|
||||
return tx.Factory().UpdateBlock(node, statementList, node.MultiLine)
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) visitForStatement(node *ast.ForStatement) *ast.Node {
|
||||
if node.Initializer != nil && isUsingVariableDeclarationList(node.Initializer) {
|
||||
// given:
|
||||
//
|
||||
// for (using x = expr; cond; incr) { ... }
|
||||
//
|
||||
// produces a shallow transformation to:
|
||||
//
|
||||
// {
|
||||
// using x = expr;
|
||||
// for (; cond; incr) { ... }
|
||||
// }
|
||||
//
|
||||
// before handing the shallow transformation back to the visitor for an in-depth transformation.
|
||||
return tx.Visitor().VisitNode(
|
||||
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
|
||||
tx.Factory().NewVariableStatement(nil /*modifiers*/, node.Initializer),
|
||||
tx.Factory().UpdateForStatement(
|
||||
node,
|
||||
nil, /*initializer*/
|
||||
node.Condition,
|
||||
node.Incrementor,
|
||||
node.Statement,
|
||||
),
|
||||
}), false /*multiLine*/),
|
||||
)
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) visitForOfStatement(node *ast.ForInOrOfStatement) *ast.Node {
|
||||
if isUsingVariableDeclarationList(node.Initializer) {
|
||||
// given:
|
||||
//
|
||||
// for (using x of y) { ... }
|
||||
//
|
||||
// produces a shallow transformation to:
|
||||
//
|
||||
// for (const x_1 of y) {
|
||||
// using x = x;
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// before handing the shallow transformation back to the visitor for an in-depth transformation.
|
||||
forInitializer := node.Initializer.AsVariableDeclarationList()
|
||||
forDecl := core.FirstOrNil(forInitializer.Declarations.Nodes)
|
||||
if forDecl == nil {
|
||||
forDecl = tx.Factory().NewVariableDeclaration(tx.Factory().NewTempVariable(), nil, nil, nil)
|
||||
}
|
||||
|
||||
isAwaitUsing := getUsingKindOfVariableDeclarationList(forInitializer) == usingKindAsync
|
||||
temp := tx.Factory().NewGeneratedNameForNode(forDecl.Name())
|
||||
usingVar := tx.Factory().UpdateVariableDeclaration(forDecl.AsVariableDeclaration(), forDecl.Name(), nil /*exclamationToken*/, nil /*type*/, temp)
|
||||
usingVarList := tx.Factory().NewVariableDeclarationList(
|
||||
tx.Factory().NewNodeList([]*ast.Node{usingVar}),
|
||||
core.IfElse(isAwaitUsing, ast.NodeFlagsAwaitUsing, ast.NodeFlagsUsing),
|
||||
)
|
||||
usingVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, usingVarList)
|
||||
var statement *ast.Statement
|
||||
if ast.IsBlock(node.Statement) {
|
||||
statements := make([]*ast.Statement, 0, len(node.Statement.Statements())+1)
|
||||
statements = append(statements, usingVarStatement)
|
||||
statements = append(statements, node.Statement.Statements()...)
|
||||
statement = tx.Factory().UpdateBlock(
|
||||
node.Statement.AsBlock(),
|
||||
tx.Factory().NewNodeList(statements),
|
||||
node.Statement.AsBlock().MultiLine,
|
||||
)
|
||||
} else {
|
||||
statement = tx.Factory().NewBlock(
|
||||
tx.Factory().NewNodeList([]*ast.Statement{
|
||||
usingVarStatement,
|
||||
node.Statement,
|
||||
}),
|
||||
true, /*multiLine*/
|
||||
)
|
||||
}
|
||||
return tx.Visitor().VisitNode(
|
||||
tx.Factory().UpdateForInOrOfStatement(
|
||||
node,
|
||||
node.AwaitModifier,
|
||||
tx.Factory().NewVariableDeclarationList(
|
||||
tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
|
||||
tx.Factory().NewVariableDeclaration(temp, nil /*exclamationToken*/, nil /*type*/, nil),
|
||||
}),
|
||||
ast.NodeFlagsConst,
|
||||
),
|
||||
node.Expression,
|
||||
statement,
|
||||
),
|
||||
)
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) transformUsingDeclarations(statementsIn []*ast.Statement, envBinding *ast.IdentifierNode, topLevelStatements *[]*ast.Statement) []*ast.Node {
|
||||
var statements []*ast.Statement
|
||||
|
||||
hoist := func(node *ast.Statement) *ast.Statement {
|
||||
if topLevelStatements == nil {
|
||||
return node
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
case ast.KindImportDeclaration,
|
||||
ast.KindImportEqualsDeclaration,
|
||||
ast.KindExportDeclaration,
|
||||
ast.KindFunctionDeclaration:
|
||||
tx.hoistImportOrExportOrHoistedDeclaration(node, topLevelStatements)
|
||||
return nil
|
||||
case ast.KindExportAssignment:
|
||||
return tx.hoistExportAssignment(node.AsExportAssignment())
|
||||
case ast.KindClassDeclaration:
|
||||
return tx.hoistClassDeclaration(node.AsClassDeclaration())
|
||||
case ast.KindVariableStatement:
|
||||
return tx.hoistVariableStatement(node.AsVariableStatement())
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
hoistOrAppendNode := func(node *ast.Node) {
|
||||
node = hoist(node)
|
||||
if node != nil {
|
||||
statements = append(statements, node)
|
||||
}
|
||||
}
|
||||
|
||||
for _, statement := range statementsIn {
|
||||
usingKind := getUsingKind(statement)
|
||||
if usingKind != usingKindNone {
|
||||
varStatement := statement.AsVariableStatement()
|
||||
declarationList := varStatement.DeclarationList
|
||||
var declarations []*ast.VariableDeclarationNode
|
||||
for _, declaration := range declarationList.AsVariableDeclarationList().Declarations.Nodes {
|
||||
if !ast.IsIdentifier(declaration.Name()) {
|
||||
// Since binding patterns are a grammar error, we reset `declarations` so we don't process this as a `using`.
|
||||
declarations = nil
|
||||
break
|
||||
}
|
||||
|
||||
// perform a shallow transform for any named evaluation
|
||||
if isNamedEvaluation(tx.EmitContext(), declaration) {
|
||||
declaration = transformNamedEvaluation(tx.EmitContext(), declaration, false /*ignoreEmptyStringLiteral*/, "" /*assignedName*/)
|
||||
}
|
||||
|
||||
initializer := tx.Visitor().VisitNode(declaration.Initializer())
|
||||
if initializer == nil {
|
||||
initializer = tx.Factory().NewVoidZeroExpression()
|
||||
}
|
||||
declarations = append(declarations, tx.Factory().UpdateVariableDeclaration(
|
||||
declaration.AsVariableDeclaration(),
|
||||
declaration.Name(),
|
||||
nil, /*exclamationToken*/
|
||||
nil, /*type*/
|
||||
tx.Factory().NewAddDisposableResourceHelper(
|
||||
envBinding,
|
||||
initializer,
|
||||
usingKind == usingKindAsync,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// Only replace the statement if it was valid.
|
||||
if len(declarations) > 0 {
|
||||
varList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList(declarations), ast.NodeFlagsConst)
|
||||
tx.EmitContext().SetOriginal(varList, declarationList)
|
||||
varList.Loc = declarationList.Loc
|
||||
hoistOrAppendNode(tx.Factory().UpdateVariableStatement(varStatement, nil /*modifiers*/, varList))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if result := tx.visit(statement); result != nil {
|
||||
if result.Kind == ast.KindSyntaxList {
|
||||
for _, node := range result.AsSyntaxList().Children {
|
||||
hoistOrAppendNode(node)
|
||||
}
|
||||
} else {
|
||||
hoistOrAppendNode(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return statements
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistImportOrExportOrHoistedDeclaration(node *ast.Statement, topLevelStatements *[]*ast.Statement) {
|
||||
// NOTE: `node` has already been visited
|
||||
*topLevelStatements = append(*topLevelStatements, node)
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistExportAssignment(node *ast.ExportAssignment) *ast.Statement {
|
||||
if node.IsExportEquals {
|
||||
return tx.hoistExportEquals(node)
|
||||
} else {
|
||||
return tx.hoistExportDefault(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistExportDefault(node *ast.ExportAssignment) *ast.Statement {
|
||||
// NOTE: `node` has already been visited
|
||||
if tx.defaultExportBinding != nil {
|
||||
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
|
||||
return node.AsNode()
|
||||
}
|
||||
|
||||
// given:
|
||||
//
|
||||
// export default expr;
|
||||
//
|
||||
// produces:
|
||||
//
|
||||
// // top level
|
||||
// var default_1;
|
||||
// export { default_1 as default };
|
||||
//
|
||||
// // body
|
||||
// default_1 = expr;
|
||||
|
||||
tx.defaultExportBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
|
||||
tx.hoistBindingIdentifier(tx.defaultExportBinding /*isExport*/, true, tx.Factory().NewIdentifier("default"), node.AsNode())
|
||||
|
||||
// give a class or function expression an assigned name, if needed.
|
||||
expression := node.Expression
|
||||
innerExpression := ast.SkipOuterExpressions(expression, ast.OEKAll)
|
||||
if isNamedEvaluation(tx.EmitContext(), innerExpression) {
|
||||
innerExpression = transformNamedEvaluation(tx.EmitContext(), innerExpression /*ignoreEmptyStringLiteral*/, false, "default")
|
||||
expression = tx.Factory().RestoreOuterExpressions(expression, innerExpression, ast.OEKAll)
|
||||
}
|
||||
|
||||
assignment := tx.Factory().NewAssignmentExpression(tx.defaultExportBinding, expression)
|
||||
return tx.Factory().NewExpressionStatement(assignment)
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistExportEquals(node *ast.ExportAssignment) *ast.Statement {
|
||||
// NOTE: `node` has already been visited
|
||||
if tx.exportEqualsBinding != nil {
|
||||
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
|
||||
return node.AsNode()
|
||||
}
|
||||
|
||||
// given:
|
||||
//
|
||||
// export = expr;
|
||||
//
|
||||
// produces:
|
||||
//
|
||||
// // top level
|
||||
// var default_1;
|
||||
//
|
||||
// try {
|
||||
// // body
|
||||
// default_1 = expr;
|
||||
// } ...
|
||||
//
|
||||
// // top level suffix
|
||||
// export = default_1;
|
||||
|
||||
tx.exportEqualsBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
|
||||
tx.EmitContext().AddVariableDeclaration(tx.exportEqualsBinding)
|
||||
|
||||
// give a class or function expression an assigned name, if needed.
|
||||
assignment := tx.Factory().NewAssignmentExpression(tx.exportEqualsBinding, node.Expression)
|
||||
return tx.Factory().NewExpressionStatement(assignment)
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistClassDeclaration(node *ast.ClassDeclaration) *ast.Statement {
|
||||
// NOTE: `node` has already been visited
|
||||
if node.Name() == nil && tx.defaultExportBinding != nil {
|
||||
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
|
||||
return node.AsNode()
|
||||
}
|
||||
|
||||
isExported := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport)
|
||||
isDefault := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsDefault)
|
||||
|
||||
// When hoisting a class declaration at the top level of a file containing a top-level `using` statement, we
|
||||
// must first convert it to a class expression so that we can hoist the binding outside of the `try`.
|
||||
expression := convertClassDeclarationToClassExpression(tx.EmitContext(), node)
|
||||
if node.Name() != nil {
|
||||
// given:
|
||||
//
|
||||
// using x = expr;
|
||||
// class C {}
|
||||
//
|
||||
// produces:
|
||||
//
|
||||
// var x, C;
|
||||
// const env_1 = { ... };
|
||||
// try {
|
||||
// x = __addDisposableResource(env_1, expr, false);
|
||||
// C = class {};
|
||||
// }
|
||||
// catch (e_1) {
|
||||
// env_1.error = e_1;
|
||||
// env_1.hasError = true;
|
||||
// }
|
||||
// finally {
|
||||
// __disposeResources(env_1);
|
||||
// }
|
||||
//
|
||||
// If the class is exported, we also produce an `export { C };`
|
||||
tx.hoistBindingIdentifier(tx.Factory().GetLocalName(node.AsNode()), isExported && !isDefault, nil /*exportAlias*/, node.AsNode())
|
||||
expression = tx.Factory().NewAssignmentExpression(tx.Factory().GetDeclarationName(node.AsNode()), expression)
|
||||
tx.EmitContext().SetOriginal(expression, node.AsNode())
|
||||
tx.EmitContext().SetSourceMapRange(expression, node.Loc)
|
||||
tx.EmitContext().SetCommentRange(expression, node.Loc)
|
||||
if isNamedEvaluation(tx.EmitContext(), expression) {
|
||||
expression = transformNamedEvaluation(tx.EmitContext(), expression, false /*ignoreEmptyStringLiteral*/, "" /*assignedName*/)
|
||||
}
|
||||
}
|
||||
|
||||
if isDefault && tx.defaultExportBinding == nil {
|
||||
// In the case of a default export, we create a temporary variable that we export as the default and then
|
||||
// assign to that variable.
|
||||
//
|
||||
// given:
|
||||
//
|
||||
// using x = expr;
|
||||
// export default class C {}
|
||||
//
|
||||
// produces:
|
||||
//
|
||||
// export { default_1 as default };
|
||||
// var x, C, default_1;
|
||||
// const env_1 = { ... };
|
||||
// try {
|
||||
// x = __addDisposableResource(env_1, expr, false);
|
||||
// default_1 = C = class {};
|
||||
// }
|
||||
// catch (e_1) {
|
||||
// env_1.error = e_1;
|
||||
// env_1.hasError = true;
|
||||
// }
|
||||
// finally {
|
||||
// __disposeResources(env_1);
|
||||
// }
|
||||
//
|
||||
// Though we will never reassign `default_1`, this most closely matches the specified runtime semantics.
|
||||
tx.defaultExportBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
|
||||
tx.hoistBindingIdentifier(tx.defaultExportBinding /*isExport*/, true, tx.Factory().NewIdentifier("default"), node.AsNode())
|
||||
expression = tx.Factory().NewAssignmentExpression(tx.defaultExportBinding, expression)
|
||||
tx.EmitContext().SetOriginal(expression, node.AsNode())
|
||||
if isNamedEvaluation(tx.EmitContext(), expression) {
|
||||
expression = transformNamedEvaluation(tx.EmitContext(), expression /*ignoreEmptyStringLiteral*/, false, "default")
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Factory().NewExpressionStatement(expression)
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistVariableStatement(node *ast.VariableStatement) *ast.Statement {
|
||||
// NOTE: `node` has already been visited
|
||||
var expressions []*ast.Expression
|
||||
isExported := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport)
|
||||
for _, variable := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
||||
tx.hoistBindingElement(variable, isExported, variable)
|
||||
if variable.Initializer() != nil {
|
||||
expressions = append(expressions, tx.hoistInitializedVariable(variable.AsVariableDeclaration()))
|
||||
}
|
||||
}
|
||||
if len(expressions) > 0 {
|
||||
statement := tx.Factory().NewExpressionStatement(tx.Factory().InlineExpressions(expressions))
|
||||
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
||||
tx.EmitContext().SetCommentRange(statement, node.Loc)
|
||||
tx.EmitContext().SetSourceMapRange(statement, node.Loc)
|
||||
return statement
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistInitializedVariable(node *ast.VariableDeclaration) *ast.Expression {
|
||||
// NOTE: `node` has already been visited
|
||||
if node.Initializer == nil {
|
||||
panic("Expected initializer")
|
||||
}
|
||||
var target *ast.Expression
|
||||
if ast.IsIdentifier(node.Name()) {
|
||||
target = node.Name().Clone(tx.Factory())
|
||||
tx.EmitContext().SetEmitFlags(target, tx.EmitContext().EmitFlags(target) & ^(printer.EFLocalName|printer.EFExportName))
|
||||
} else {
|
||||
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), node.Name().AsBindingPattern())
|
||||
}
|
||||
|
||||
assignment := tx.Factory().NewAssignmentExpression(target, node.Initializer)
|
||||
tx.EmitContext().SetOriginal(assignment, node.AsNode())
|
||||
tx.EmitContext().SetCommentRange(assignment, node.Loc)
|
||||
tx.EmitContext().SetSourceMapRange(assignment, node.Loc)
|
||||
return assignment
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistBindingElement(node *ast.Node /*VariableDeclaration|BindingElement*/, isExportedDeclaration bool, original *ast.Node) {
|
||||
// NOTE: `node` has already been visited
|
||||
if ast.IsBindingPattern(node.Name()) {
|
||||
for _, element := range node.Name().Elements() {
|
||||
if element.Name() != nil {
|
||||
tx.hoistBindingElement(element, isExportedDeclaration, original)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tx.hoistBindingIdentifier(node.Name(), isExportedDeclaration, nil /*exportAlias*/, original)
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) hoistBindingIdentifier(node *ast.IdentifierNode, isExport bool, exportAlias *ast.IdentifierNode, original *ast.Node) {
|
||||
// NOTE: `node` has already been visited
|
||||
name := node
|
||||
if !transformers.IsGeneratedIdentifier(tx.EmitContext(), node) {
|
||||
name = name.Clone(tx.Factory())
|
||||
}
|
||||
if isExport {
|
||||
if exportAlias == nil && !transformers.IsLocalName(tx.EmitContext(), name) {
|
||||
varDecl := tx.Factory().NewVariableDeclaration(name, nil /*exclamationToken*/, nil /*type*/, nil /*initializer*/)
|
||||
if original != nil {
|
||||
tx.EmitContext().SetOriginal(varDecl, original)
|
||||
}
|
||||
tx.exportVars = append(tx.exportVars, varDecl)
|
||||
return
|
||||
}
|
||||
|
||||
var localName *ast.ModuleExportName
|
||||
var exportName *ast.ModuleExportName
|
||||
if exportAlias != nil {
|
||||
localName = name
|
||||
exportName = exportAlias
|
||||
} else {
|
||||
exportName = name
|
||||
}
|
||||
specifier := tx.Factory().NewExportSpecifier( /*isTypeOnly*/ false, localName, exportName)
|
||||
if original != nil {
|
||||
tx.EmitContext().SetOriginal(specifier, original)
|
||||
}
|
||||
if tx.exportBindings == nil {
|
||||
tx.exportBindings = make(map[string]*ast.ExportSpecifierNode)
|
||||
}
|
||||
if _, ok := tx.exportBindings[name.Text()]; !ok {
|
||||
tx.exportBindingNames = append(tx.exportBindingNames, name.Text())
|
||||
}
|
||||
tx.exportBindings[name.Text()] = specifier
|
||||
}
|
||||
tx.EmitContext().AddVariableDeclaration(name)
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) createEnvBinding() *ast.IdentifierNode {
|
||||
return tx.Factory().NewUniqueName("env")
|
||||
}
|
||||
|
||||
func (tx *usingDeclarationTransformer) createDownlevelUsingStatements(bodyStatements []*ast.Node, envBinding *ast.IdentifierNode, async bool) []*ast.Statement {
|
||||
statements := make([]*ast.Statement, 0, 2)
|
||||
|
||||
// produces:
|
||||
//
|
||||
// const env_1 = { stack: [], error: void 0, hasError: false };
|
||||
//
|
||||
envObject := tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Expression{
|
||||
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("stack"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewArrayLiteralExpression(nil, false /*multiLine*/)),
|
||||
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("error"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewVoidZeroExpression()),
|
||||
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("hasError"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewFalseExpression()),
|
||||
}), false /*multiLine*/)
|
||||
envVar := tx.Factory().NewVariableDeclaration(envBinding, nil /*exclamationToken*/, nil /*typeNode*/, envObject)
|
||||
envVarList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{envVar}), ast.NodeFlagsConst)
|
||||
envVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, envVarList)
|
||||
statements = append(statements, envVarStatement)
|
||||
|
||||
// when `async` is `false`, produces:
|
||||
//
|
||||
// try {
|
||||
// <bodyStatements>
|
||||
// }
|
||||
// catch (e_1) {
|
||||
// env_1.error = e_1;
|
||||
// env_1.hasError = true;
|
||||
// }
|
||||
// finally {
|
||||
// __disposeResources(env_1);
|
||||
// }
|
||||
|
||||
// when `async` is `true`, produces:
|
||||
//
|
||||
// try {
|
||||
// <bodyStatements>
|
||||
// }
|
||||
// catch (e_1) {
|
||||
// env_1.error = e_1;
|
||||
// env_1.hasError = true;
|
||||
// }
|
||||
// finally {
|
||||
// const result_1 = __disposeResources(env_1);
|
||||
// if (result_1) {
|
||||
// await result_1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Unfortunately, it is necessary to use two properties to indicate an error because `throw undefined` is legal
|
||||
// JavaScript.
|
||||
tryBlock := tx.Factory().NewBlock(tx.Factory().NewNodeList(bodyStatements), true /*multiLine*/)
|
||||
bodyCatchBinding := tx.Factory().NewUniqueName("e")
|
||||
catchClause := tx.Factory().NewCatchClause(
|
||||
tx.Factory().NewVariableDeclaration(
|
||||
bodyCatchBinding,
|
||||
nil, /*exclamationToken*/
|
||||
nil, /*type*/
|
||||
nil, /*initializer*/
|
||||
),
|
||||
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
|
||||
tx.Factory().NewExpressionStatement(
|
||||
tx.Factory().NewAssignmentExpression(
|
||||
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("error"), ast.NodeFlagsNone),
|
||||
bodyCatchBinding,
|
||||
),
|
||||
),
|
||||
tx.Factory().NewExpressionStatement(
|
||||
tx.Factory().NewAssignmentExpression(
|
||||
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("hasError"), ast.NodeFlagsNone),
|
||||
tx.Factory().NewTrueExpression(),
|
||||
),
|
||||
),
|
||||
}), true /*multiLine*/),
|
||||
)
|
||||
|
||||
var finallyBlock *ast.BlockNode
|
||||
if async {
|
||||
result := tx.Factory().NewUniqueName("result")
|
||||
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
|
||||
tx.Factory().NewVariableStatement(
|
||||
nil, /*modifiers*/
|
||||
tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
|
||||
tx.Factory().NewVariableDeclaration(
|
||||
result,
|
||||
nil, /*exclamationToken*/
|
||||
nil, /*type*/
|
||||
tx.Factory().NewDisposeResourcesHelper(envBinding),
|
||||
),
|
||||
}), ast.NodeFlagsConst),
|
||||
),
|
||||
tx.Factory().NewIfStatement(result, tx.Factory().NewExpressionStatement(tx.Factory().NewAwaitExpression(result)), nil /*elseStatement*/),
|
||||
}), true /*multiLine*/)
|
||||
} else {
|
||||
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
|
||||
tx.Factory().NewExpressionStatement(
|
||||
tx.Factory().NewDisposeResourcesHelper(envBinding),
|
||||
),
|
||||
}), true /*multiLine*/)
|
||||
}
|
||||
|
||||
tryStatement := tx.Factory().NewTryStatement(tryBlock, catchClause, finallyBlock)
|
||||
statements = append(statements, tryStatement)
|
||||
return statements
|
||||
}
|
||||
|
||||
func isUsingVariableDeclarationList(node *ast.ForInitializer) bool {
|
||||
return ast.IsVariableDeclarationList(node) && getUsingKindOfVariableDeclarationList(node.AsVariableDeclarationList()) != usingKindNone
|
||||
}
|
||||
|
||||
func getUsingKindOfVariableDeclarationList(node *ast.VariableDeclarationList) usingKind {
|
||||
switch node.Flags & ast.NodeFlagsBlockScoped {
|
||||
case ast.NodeFlagsAwaitUsing:
|
||||
return usingKindAsync
|
||||
case ast.NodeFlagsUsing:
|
||||
return usingKindSync
|
||||
default:
|
||||
return usingKindNone
|
||||
}
|
||||
}
|
||||
|
||||
func getUsingKindOfVariableStatement(node *ast.VariableStatement) usingKind {
|
||||
return getUsingKindOfVariableDeclarationList(node.DeclarationList.AsVariableDeclarationList())
|
||||
}
|
||||
|
||||
func getUsingKind(statement *ast.Node) usingKind {
|
||||
if ast.IsVariableStatement(statement) {
|
||||
return getUsingKindOfVariableStatement(statement.AsVariableStatement())
|
||||
}
|
||||
return usingKindNone
|
||||
}
|
||||
|
||||
func getUsingKindOfStatements(statements []*ast.Node) usingKind {
|
||||
result := usingKindNone
|
||||
for _, statement := range statements {
|
||||
usingKind := getUsingKind(statement)
|
||||
if usingKind == usingKindAsync {
|
||||
return usingKindAsync
|
||||
}
|
||||
if usingKind > result {
|
||||
result = usingKind
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user