Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -15087,7 +15087,7 @@ func (c *Checker) resolveEntityName(name *ast.Node, meaning ast.SymbolFlags, ign
name.Parent != nil && name.Parent.Kind == ast.KindJSExportAssignment) {
c.markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, nil /*finalTarget*/, true /*overwriteEmpty*/, nil, "")
}
if symbol.Flags&meaning == 0 && !dontResolveAlias {
if symbol.Flags&meaning == 0 && !dontResolveAlias && symbol.Flags&ast.SymbolFlagsAlias != 0 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In strada, checkExpressionCached on a.b.c.d caches resolved symbols on a.b.c and a.b and a. getConstantValue uses these cached symbols to shortcut its' logic. In corsa, it does not, and, in so doing, reveals that here we try to call resolveAlias on non-alias (module) symbols when we consider if we want to inline module.exports. This fixes that.

return c.resolveAlias(symbol)
}
}
Expand Down
7 changes: 7 additions & 0 deletions internal/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1025,3 +1025,10 @@ func (r *emitResolver) GetResolutionModeOverride(node *ast.Node) core.Resolution
defer r.checkerMu.Unlock()
return r.checker.GetResolutionModeOverride(node.AsImportAttributes(), false)
}

func (r *emitResolver) GetConstantValue(node *ast.Node) any {
// node = emitContext.ParseNode(node)
r.checkerMu.Lock()
defer r.checkerMu.Unlock()
return r.checker.GetConstantValue(node)
}
8 changes: 7 additions & 1 deletion internal/compiler/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/microsoft/typescript-go/internal/transformers"
"github.com/microsoft/typescript-go/internal/transformers/declarations"
"github.com/microsoft/typescript-go/internal/transformers/estransforms"
"github.com/microsoft/typescript-go/internal/transformers/inliners"
"github.com/microsoft/typescript-go/internal/transformers/jsxtransforms"
"github.com/microsoft/typescript-go/internal/transformers/moduletransforms"
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
Expand Down Expand Up @@ -86,7 +87,7 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo

var emitResolver printer.EmitResolver
var referenceResolver binder.ReferenceResolver
if importElisionEnabled || options.GetJSXTransformEnabled() {
if importElisionEnabled || options.GetJSXTransformEnabled() || !options.GetIsolatedModules() { // full emit resolver is needed for import ellision and const enum inlining
emitResolver = host.GetEmitResolver()
emitResolver.MarkLinkedReferencesRecursively(sourceFile)
referenceResolver = emitResolver
Expand Down Expand Up @@ -128,6 +129,11 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo

// transform module syntax
tx = append(tx, getModuleTransformer(&opts))

// inlining (formerly done via substitutions)
if !options.GetIsolatedModules() {
tx = append(tx, inliners.NewConstEnumInliningTransformer(&opts))
}
return tx
}

Expand Down
44 changes: 44 additions & 0 deletions internal/printer/emitcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,14 @@ const (
hasSourceMapRange
)

type SynthesizedComment struct {
Kind ast.Kind
Loc core.TextRange
HasLeadingNewLine bool
HasTrailingNewLine bool
Text string
}

type emitNode struct {
flags emitNodeFlags
emitFlags EmitFlags
Expand All @@ -519,6 +527,8 @@ type emitNode struct {
tokenSourceMapRanges map[ast.Kind]core.TextRange
helpers []*EmitHelper
externalHelpersModuleName *ast.IdentifierNode
leadingComments []SynthesizedComment
trailingComments []SynthesizedComment
}

// NOTE: This method is not guaranteed to be thread-safe
Expand Down Expand Up @@ -917,3 +927,37 @@ func (c *EmitContext) VisitIterationBody(body *ast.Statement, visitor *ast.NodeV

return updated
}

func (c *EmitContext) SetSyntheticLeadingComments(node *ast.Node, comments []SynthesizedComment) *ast.Node {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not this PR, but there are some callers of this I left commented out in the node builder (probably more than that though, just opining)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sent #1607 before this task slipped out of my brain.

c.emitNodes.Get(node).leadingComments = comments
return node
}

func (c *EmitContext) AddSyntheticLeadingComment(node *ast.Node, kind ast.Kind, text string, hasTrailingNewLine bool) *ast.Node {
c.emitNodes.Get(node).leadingComments = append(c.emitNodes.Get(node).leadingComments, SynthesizedComment{Kind: kind, Loc: core.NewTextRange(-1, -1), HasTrailingNewLine: hasTrailingNewLine, Text: text})
return node
}

func (c *EmitContext) GetSyntheticLeadingComments(node *ast.Node) []SynthesizedComment {
if c.emitNodes.Has(node) {
return c.emitNodes.Get(node).leadingComments
}
return nil
}

func (c *EmitContext) SetSyntheticTrailingComments(node *ast.Node, comments []SynthesizedComment) *ast.Node {
c.emitNodes.Get(node).trailingComments = comments
return node
}

func (c *EmitContext) AddSyntheticTrailingComment(node *ast.Node, kind ast.Kind, text string, hasTrailingNewLine bool) *ast.Node {
c.emitNodes.Get(node).trailingComments = append(c.emitNodes.Get(node).trailingComments, SynthesizedComment{Kind: kind, Loc: core.NewTextRange(-1, -1), HasTrailingNewLine: hasTrailingNewLine, Text: text})
return node
}

func (c *EmitContext) GetSyntheticTrailingComments(node *ast.Node) []SynthesizedComment {
if c.emitNodes.Has(node) {
return c.emitNodes.Get(node).trailingComments
}
return nil
}
3 changes: 3 additions & 0 deletions internal/printer/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type EmitResolver interface {
GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags
GetResolutionModeOverride(node *ast.Node) core.ResolutionMode

// const enum inlining
GetConstantValue(node *ast.Node) any

// JSX Emit
GetJsxFactoryEntity(location *ast.Node) *ast.Node
GetJsxFragmentFactoryEntity(location *ast.Node) *ast.Node
Expand Down
98 changes: 70 additions & 28 deletions internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (

"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/jsnum"
"github.com/microsoft/typescript-go/internal/scanner"
"github.com/microsoft/typescript-go/internal/sourcemap"
"github.com/microsoft/typescript-go/internal/stringutil"
Expand Down Expand Up @@ -630,26 +629,30 @@ func (p *Printer) writeCommentRange(comment ast.CommentRange) {
}

text := p.currentSourceFile.Text()
if comment.Kind == ast.KindMultiLineCommentTrivia {
lineMap := p.currentSourceFile.LineMap()
lineMap := p.currentSourceFile.LineMap()
p.writeCommentRangeWorker(text, lineMap, comment.Kind, comment.TextRange)
}

func (p *Printer) writeCommentRangeWorker(text string, lineMap []core.TextPos, kind ast.Kind, loc core.TextRange) {
if kind == ast.KindMultiLineCommentTrivia {
indentSize := len(getIndentString(1))
firstLine := scanner.ComputeLineOfPosition(lineMap, comment.Pos())
firstLine := scanner.ComputeLineOfPosition(lineMap, loc.Pos())
lineCount := len(lineMap)
firstCommentLineIndent := -1
pos := comment.Pos()
pos := loc.Pos()
currentLine := firstLine
for ; pos < comment.End(); currentLine++ {
for ; pos < loc.End(); currentLine++ {
var nextLineStart int
if currentLine+1 == lineCount {
nextLineStart = len(text) + 1
} else {
nextLineStart = int(lineMap[currentLine+1])
}

if pos != comment.Pos() {
if pos != loc.Pos() {
// If we are not emitting first line, we need to write the spaces to adjust the alignment
if firstCommentLineIndent == -1 {
firstCommentLineIndent = calculateIndent(text, int(lineMap[firstLine]), comment.Pos())
firstCommentLineIndent = calculateIndent(text, int(lineMap[firstLine]), loc.Pos())
}

// These are number of spaces writer is going to write at current indent
Expand Down Expand Up @@ -689,11 +692,11 @@ func (p *Printer) writeCommentRange(comment ast.CommentRange) {
}

// Write the comment line text
end := min(comment.End(), nextLineStart-1)
end := min(loc.End(), nextLineStart-1)
currentLineText := strings.TrimSpace(text[pos:end])
if len(currentLineText) > 0 {
p.writeComment(currentLineText)
if end != comment.End() {
if end != loc.End() {
p.writeLine()
}
} else {
Expand All @@ -705,19 +708,14 @@ func (p *Printer) writeCommentRange(comment ast.CommentRange) {
}
} else {
// Single line comment of style //....
p.writeComment(text[comment.Pos():comment.End()])
p.writeComment(text[loc.Pos():loc.End()])
}
}

//
// Custom emit behavior stubs (i.e., from `EmitNode`, `EmitFlags`, etc.)
//

func (p *Printer) getConstantValue(node *ast.Node) any {
// !!! Const-enum inlining (low priority)
return nil
}

func (p *Printer) shouldEmitComments(node *ast.Node) bool {
return !p.commentsDisabled &&
p.currentSourceFile != nil &&
Expand Down Expand Up @@ -2428,12 +2426,6 @@ func (p *Printer) mayNeedDotDotForPropertyAccess(expression *ast.Expression) boo
!strings.Contains(text, scanner.TokenToString(ast.KindDotToken)) &&
!strings.Contains(text, "E") &&
!strings.Contains(text, "e")
} else if ast.IsAccessExpression(expression) {
// check if constant enum value is a non-negative integer
if constantValue, ok := p.getConstantValue(expression).(jsnum.Number); ok {
return !constantValue.IsInf() && constantValue >= 0 && constantValue.Floor() == constantValue
}
return false
}
return false
}
Expand Down Expand Up @@ -5017,7 +5009,7 @@ func (p *Printer) emitCommentsBeforeNode(node *ast.Node) *commentState {

// Emit leading comments
p.emitLeadingCommentsOfNode(node, emitFlags, commentRange)
p.emitLeadingSyntheticCommentsOfNode(node)
p.emitLeadingSyntheticCommentsOfNode(node, emitFlags)
if emitFlags&EFNoNestedComments != 0 {
p.commentsDisabled = true
}
Expand All @@ -5043,7 +5035,7 @@ func (p *Printer) emitCommentsAfterNode(node *ast.Node, state *commentState) {
p.commentsDisabled = false
}

p.emitTrailingSyntheticCommentsOfNode(node)
p.emitTrailingSyntheticCommentsOfNode(node, emitFlags)
p.emitTrailingCommentsOfNode(node, emitFlags, commentRange, containerPos, containerEnd, declarationListContainerEnd)

// !!! Preserve comments from type annotation:
Expand Down Expand Up @@ -5182,12 +5174,62 @@ func (p *Printer) emitTrailingCommentsOfNode(node *ast.Node, emitFlags EmitFlags
}
}

func (p *Printer) emitLeadingSyntheticCommentsOfNode(node *ast.Node) {
// !!!
func (p *Printer) emitLeadingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
if emitFlags&EFNoLeadingComments != 0 {
return
}
synth := p.emitContext.GetSyntheticLeadingComments(node)
for _, c := range synth {
p.emitLeadingSynthesizedComment(c)
}
}

func (p *Printer) emitTrailingSyntheticCommentsOfNode(node *ast.Node) {
// !!!
func (p *Printer) emitLeadingSynthesizedComment(comment SynthesizedComment) {
if comment.HasLeadingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
p.writer.WriteLine()
}
p.writeSynthesizedComment(comment)
if comment.HasTrailingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
p.writer.WriteLine()
} else {
p.writer.WriteSpace(" ")
}
}

func (p *Printer) emitTrailingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
if emitFlags&EFNoTrailingComments != 0 {
return
}
synth := p.emitContext.GetSyntheticTrailingComments(node)
for _, c := range synth {
p.emitTrailingSynthesizedComment(c)
}
}

func (p *Printer) emitTrailingSynthesizedComment(comment SynthesizedComment) {
if !p.writer.IsAtStartOfLine() {
p.writer.WriteSpace(" ")
}
p.writeSynthesizedComment(comment)
if comment.HasTrailingNewLine {
p.writer.WriteLine()
}
}

func formatSynthesizedComment(comment SynthesizedComment) string {
if comment.Kind == ast.KindMultiLineCommentTrivia {
return "/*" + comment.Text + "*/"
}
return "//" + comment.Text
}

func (p *Printer) writeSynthesizedComment(comment SynthesizedComment) {
text := formatSynthesizedComment(comment)
var lineMap []core.TextPos
if comment.Kind == ast.KindMultiLineCommentTrivia {
lineMap = core.ComputeLineStarts(text)
}
p.writeCommentRangeWorker(text, lineMap, comment.Kind, core.NewTextRange(0, len(text)))
}

func (p *Printer) emitLeadingComments(pos int, elided bool) bool {
Expand Down
88 changes: 88 additions & 0 deletions internal/transformers/inliners/constenum.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there more inliners than just constenum?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of right now, no, but we've toyed with adding minifer-like emit passes in the past, so it's a logical place to group them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's going to happen, but 😄

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package inliners

import (
"strings"

"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/jsnum"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
"github.com/microsoft/typescript-go/internal/transformers"
)

type ConstEnumInliningTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
currentSourceFile *ast.SourceFile
emitResolver printer.EmitResolver
}

func NewConstEnumInliningTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opt.CompilerOptions
emitContext := opt.Context
if compilerOptions.GetIsolatedModules() {
debug.Fail("const enums are not inlined under isolated modules")
}
tx := &ConstEnumInliningTransformer{compilerOptions: compilerOptions, emitResolver: opt.EmitResolver}
return tx.NewTransformer(tx.visit, emitContext)
}

func (tx *ConstEnumInliningTransformer) visit(node *ast.Node) *ast.Node {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, I love how short this is

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I inlined some indirections from strada.

switch node.Kind {
case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
{
parse := tx.EmitContext().ParseNode(node)
if parse == nil {
return tx.Visitor().VisitEachChild(node)
}
value := tx.emitResolver.GetConstantValue(parse)
if value != nil {
var replacement *ast.Node
switch v := value.(type) {
case jsnum.Number:
if v.IsInf() {
if v.Abs() == v {
replacement = tx.Factory().NewIdentifier("Infinity")
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewIdentifier("Infinity"))
}
} else if v.IsNaN() {
replacement = tx.Factory().NewIdentifier("NaN")
} else if v.Abs() == v {
replacement = tx.Factory().NewNumericLiteral(v.String())
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewNumericLiteral(v.Abs().String()))
}
case string:
replacement = tx.Factory().NewStringLiteral(v)
case jsnum.PseudoBigInt: // technically not supported by strada, and issues a checker error, handled here for completeness
if v == (jsnum.PseudoBigInt{}) {
replacement = tx.Factory().NewBigIntLiteral("0")
} else if !v.Negative {
replacement = tx.Factory().NewBigIntLiteral(v.Base10Value)
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewBigIntLiteral(v.Base10Value))
}
}

if tx.compilerOptions.RemoveComments.IsFalseOrUnknown() {
original := tx.EmitContext().MostOriginal(node)
if original != nil && !ast.NodeIsSynthesized(original) {
originalText := scanner.GetTextOfNode(original)
escapedText := " " + safeMultiLineComment(originalText) + " "
tx.EmitContext().AddSyntheticTrailingComment(replacement, ast.KindMultiLineCommentTrivia, escapedText, false)
}
}
return replacement
}
return tx.Visitor().VisitEachChild(node)
}
}
return tx.Visitor().VisitEachChild(node)
}

func safeMultiLineComment(text string) string {
return strings.ReplaceAll(text, "*/", "*_/")
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function foo(x) {
var y = x; // Ok
}
foo(5);
foo(E.A);
foo(0 /* E.A */);
class A {
a;
}
Expand Down
Loading
Loading