vendor tsgo
This commit is contained in:
381
tools/tsgo/internal/api/encoder/decoder.go
Normal file
381
tools/tsgo/internal/api/encoder/decoder.go
Normal file
@@ -0,0 +1,381 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
// astDecoder reconstructs real *ast.Node objects from binary-encoded data.
|
||||
type astDecoder struct {
|
||||
raw []byte
|
||||
strTable uint32
|
||||
strData uint32
|
||||
extData uint32
|
||||
nodeOff uint32
|
||||
nodeCount int
|
||||
factory *ast.NodeFactory
|
||||
childBuf []int
|
||||
// Single Go string covering all string data; substrings are zero-alloc slices.
|
||||
allStringData string
|
||||
// Arena for batch-allocating []*ast.Node slices used by NodeLists.
|
||||
nodeArena []*ast.Node
|
||||
// Results
|
||||
nodes []*ast.Node
|
||||
nodeLists []*ast.NodeList
|
||||
}
|
||||
|
||||
// DecodeSourceFile decodes binary-encoded data into an *ast.SourceFile.
|
||||
func DecodeSourceFile(data []byte) (*ast.SourceFile, error) {
|
||||
node, err := DecodeNodes(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node.Kind != ast.KindSourceFile {
|
||||
return nil, fmt.Errorf("expected SourceFile root, got %v", node.Kind)
|
||||
}
|
||||
return node.AsSourceFile(), nil
|
||||
}
|
||||
|
||||
// DecodeNodes decodes binary-encoded AST data into a tree of *ast.Node objects.
|
||||
func DecodeNodes(data []byte) (*ast.Node, error) {
|
||||
d, err := newASTDecoder(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.decode()
|
||||
}
|
||||
|
||||
func newASTDecoder(data []byte) (*astDecoder, error) {
|
||||
if len(data) < HeaderSize {
|
||||
return nil, fmt.Errorf("data too short for header: %d bytes", len(data))
|
||||
}
|
||||
version := data[HeaderOffsetMetadata+3]
|
||||
if version != ProtocolVersion {
|
||||
return nil, fmt.Errorf("unsupported protocol version %d (expected %d)", version, ProtocolVersion)
|
||||
}
|
||||
|
||||
strTable := readLE32(data, HeaderOffsetStringOffsets)
|
||||
strData := readLE32(data, HeaderOffsetStringData)
|
||||
extData := readLE32(data, HeaderOffsetExtendedData)
|
||||
nodeOff := readLE32(data, HeaderOffsetNodes)
|
||||
|
||||
dataLen := uint32(len(data))
|
||||
|
||||
// Validate that all offsets are within the buffer.
|
||||
if strTable > dataLen || strData > dataLen || extData > dataLen || nodeOff > dataLen {
|
||||
return nil, fmt.Errorf("invalid AST header offsets: offsets exceed data length (%d)", dataLen)
|
||||
}
|
||||
|
||||
// Validate monotonic non-decreasing order of regions.
|
||||
if !(strTable <= strData && strData <= extData && extData <= nodeOff) {
|
||||
return nil, fmt.Errorf("invalid AST header offsets: expected strTable <= strData <= extData <= nodeOff (got %d, %d, %d, %d)", strTable, strData, extData, nodeOff)
|
||||
}
|
||||
|
||||
d := &astDecoder{
|
||||
raw: data,
|
||||
strTable: strTable,
|
||||
strData: strData,
|
||||
extData: extData,
|
||||
nodeOff: nodeOff,
|
||||
factory: ast.NewNodeFactory(ast.NodeFactoryHooks{}),
|
||||
}
|
||||
|
||||
d.nodeCount = (len(data) - int(d.nodeOff)) / NodeSize
|
||||
|
||||
// Convert entire string data region to a single Go string upfront.
|
||||
// Substringing a Go string shares the backing array, so subsequent
|
||||
// getString calls produce substrings with zero allocations.
|
||||
d.allStringData = string(data[d.strData:])
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// allocNodeSlice returns a zero-length slice with the given capacity, backed by
|
||||
// the pre-allocated nodeArena. This avoids a heap allocation per NodeList.
|
||||
func (d *astDecoder) allocNodeSlice(capacity int) []*ast.Node {
|
||||
start := len(d.nodeArena)
|
||||
d.nodeArena = d.nodeArena[:start+capacity]
|
||||
return d.nodeArena[start : start : start+capacity]
|
||||
}
|
||||
|
||||
// nodeField reads a uint32 field from node i at the given field offset.
|
||||
func (d *astDecoder) nodeField(i int, field int) uint32 {
|
||||
return readLE32(d.raw, int(d.nodeOff)+i*NodeSize+field)
|
||||
}
|
||||
|
||||
func (d *astDecoder) getString(idx uint32) string {
|
||||
offBase := int(d.strTable) + int(idx)*4
|
||||
start := readLE32(d.raw, offBase)
|
||||
end := readLE32(d.raw, offBase+4)
|
||||
return d.allStringData[start:end]
|
||||
}
|
||||
|
||||
// collectChildren returns indices of direct children of node i.
|
||||
// The returned slice is reused across calls; callers must not retain it.
|
||||
func (d *astDecoder) collectChildren(i int) []int {
|
||||
d.childBuf = d.childBuf[:0]
|
||||
if i+1 >= d.nodeCount {
|
||||
return d.childBuf
|
||||
}
|
||||
firstChild := i + 1
|
||||
if d.nodeField(firstChild, NodeOffsetParent) != uint32(i) {
|
||||
return d.childBuf
|
||||
}
|
||||
d.childBuf = append(d.childBuf, firstChild)
|
||||
next := int(d.nodeField(firstChild, NodeOffsetNext))
|
||||
for next != 0 {
|
||||
d.childBuf = append(d.childBuf, next)
|
||||
next = int(d.nodeField(next, NodeOffsetNext))
|
||||
}
|
||||
return d.childBuf
|
||||
}
|
||||
|
||||
func (d *astDecoder) decode() (*ast.Node, error) {
|
||||
if d.nodeCount < 2 {
|
||||
return nil, errors.New("no nodes to decode")
|
||||
}
|
||||
|
||||
d.nodes = make([]*ast.Node, d.nodeCount)
|
||||
d.nodeLists = make([]*ast.NodeList, d.nodeCount)
|
||||
// Pre-allocate arena for NodeList child slices. Each node can appear as a
|
||||
// child at most once, so nodeCount is an upper bound on total child pointers.
|
||||
d.nodeArena = make([]*ast.Node, 0, d.nodeCount)
|
||||
|
||||
// Process bottom-up so children exist before parents.
|
||||
for i := d.nodeCount - 1; i >= 1; i-- {
|
||||
kind := d.nodeField(i, NodeOffsetKind)
|
||||
pos := d.nodeField(i, NodeOffsetPos)
|
||||
end := d.nodeField(i, NodeOffsetEnd)
|
||||
data := d.nodeField(i, NodeOffsetData)
|
||||
childIndices := d.collectChildren(i)
|
||||
|
||||
if kind == SyntaxKindNodeList {
|
||||
childNodes := d.allocNodeSlice(len(childIndices))
|
||||
for _, ci := range childIndices {
|
||||
if d.nodes[ci] != nil {
|
||||
childNodes = append(childNodes, d.nodes[ci])
|
||||
}
|
||||
}
|
||||
nl := d.factory.NewNodeList(childNodes)
|
||||
nl.Loc = core.NewTextRange(int(pos), int(end))
|
||||
d.nodeLists[i] = nl
|
||||
continue
|
||||
}
|
||||
|
||||
node, err := d.createNode(ast.Kind(kind), data, childIndices)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("at node %d (kind %v): %w", i, ast.Kind(kind), err)
|
||||
}
|
||||
node.Loc = core.NewTextRange(int(pos), int(end))
|
||||
node.Flags = ast.NodeFlags(d.nodeField(i, NodeOffsetFlags))
|
||||
d.nodes[i] = node
|
||||
}
|
||||
|
||||
return d.nodes[1], nil
|
||||
}
|
||||
|
||||
// getModifierList creates a *ast.ModifierList from a child index that is a NodeList.
|
||||
func (d *astDecoder) getModifierList(ci int) *ast.ModifierList {
|
||||
nl := d.nodeLists[ci]
|
||||
if nl == nil {
|
||||
return nil
|
||||
}
|
||||
ml := d.factory.NewModifierList(nl.Nodes)
|
||||
ml.Loc = nl.Loc
|
||||
return ml
|
||||
}
|
||||
|
||||
// childIterator helps walk through children based on a bitmask.
|
||||
type childIterator struct {
|
||||
indices []int
|
||||
pos int
|
||||
}
|
||||
|
||||
func newChildIter(indices []int) childIterator {
|
||||
return childIterator{indices: indices}
|
||||
}
|
||||
|
||||
// next returns the index of the next child, advancing the position.
|
||||
func (it *childIterator) next() int {
|
||||
if it.pos >= len(it.indices) {
|
||||
return 0
|
||||
}
|
||||
ci := it.indices[it.pos]
|
||||
it.pos++
|
||||
return ci
|
||||
}
|
||||
|
||||
// nextIf returns the index of the next child if the corresponding mask bit is set.
|
||||
func (it *childIterator) nextIf(mask uint8, bit uint8) int {
|
||||
if mask&(1<<bit) == 0 {
|
||||
return 0
|
||||
}
|
||||
return it.next()
|
||||
}
|
||||
|
||||
func (d *astDecoder) nodeAt(ci int) *ast.Node {
|
||||
if ci == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.nodes[ci]
|
||||
}
|
||||
|
||||
func (d *astDecoder) nodeListAt(ci int) *ast.NodeList {
|
||||
if ci == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.nodeLists[ci]
|
||||
}
|
||||
|
||||
func (d *astDecoder) modifierListAt(ci int) *ast.ModifierList {
|
||||
if ci == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.getModifierList(ci)
|
||||
}
|
||||
|
||||
func (d *astDecoder) createNode(kind ast.Kind, data uint32, childIndices []int) (*ast.Node, error) {
|
||||
dataType := data & NodeDataTypeMask
|
||||
commonData := uint8((data >> 24) & 0x3f)
|
||||
|
||||
switch dataType {
|
||||
case NodeDataTypeString:
|
||||
return d.createStringNode(kind, data, commonData)
|
||||
case NodeDataTypeExtendedData:
|
||||
return d.createExtendedNode(kind, data, childIndices, commonData)
|
||||
default:
|
||||
return d.createChildrenNode(kind, data, childIndices, commonData)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_SourceFile(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
fileNameIdx := readLE32(d.raw, extOff+4)
|
||||
pathIdx := readLE32(d.raw, extOff+8)
|
||||
text := d.getString(textIdx)
|
||||
fileName := d.getString(fileNameIdx)
|
||||
path := d.getString(pathIdx)
|
||||
|
||||
// Recover parse options from header.
|
||||
parseOpts := readLE32(d.raw, HeaderOffsetParseOptions)
|
||||
opts := ast.SourceFileParseOptions{
|
||||
FileName: fileName,
|
||||
Path: tspath.Path(path),
|
||||
ExternalModuleIndicatorOptions: ast.ExternalModuleIndicatorOptions{
|
||||
JSX: parseOpts&1 != 0,
|
||||
Force: parseOpts&2 != 0,
|
||||
},
|
||||
}
|
||||
|
||||
// Collect children: first is statements NodeList, second is EndOfFile.
|
||||
var stmts *ast.NodeList
|
||||
var endOfFile *ast.Node
|
||||
for _, ci := range childIndices {
|
||||
if d.nodeField(ci, NodeOffsetKind) == SyntaxKindNodeList {
|
||||
stmts = d.nodeListAt(ci)
|
||||
} else if d.nodes[ci] != nil && d.nodes[ci].Kind == ast.KindEndOfFile {
|
||||
endOfFile = d.nodes[ci]
|
||||
}
|
||||
}
|
||||
if endOfFile == nil {
|
||||
endOfFile = d.factory.NewToken(ast.KindEndOfFile)
|
||||
}
|
||||
return d.factory.NewSourceFile(opts, text, stmts, endOfFile), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_TemplateHead(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
rawTextIdx := readLE32(d.raw, extOff+4)
|
||||
flags := readLE32(d.raw, extOff+8)
|
||||
return d.factory.NewTemplateHead(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_TemplateMiddle(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
rawTextIdx := readLE32(d.raw, extOff+4)
|
||||
flags := readLE32(d.raw, extOff+8)
|
||||
return d.factory.NewTemplateMiddle(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_TemplateTail(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
rawTextIdx := readLE32(d.raw, extOff+4)
|
||||
flags := readLE32(d.raw, extOff+8)
|
||||
return d.factory.NewTemplateTail(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) singleChild(childIndices []int) *ast.Node {
|
||||
if len(childIndices) == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.nodes[childIndices[0]]
|
||||
}
|
||||
|
||||
func (d *astDecoder) singleNodeListChild(childIndices []int) *ast.NodeList {
|
||||
if len(childIndices) == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.nodeLists[childIndices[0]]
|
||||
}
|
||||
|
||||
func readLE32(data []byte, offset int) uint32 {
|
||||
if offset < 0 || offset+4 > len(data) {
|
||||
return 0
|
||||
}
|
||||
return binary.LittleEndian.Uint32(data[offset : offset+4])
|
||||
}
|
||||
|
||||
// Hand-written commonData decoding functions. Each extracts the original values
|
||||
// from the 6-bit commonData that were packed by the corresponding
|
||||
// getNodeCommonData_* function.
|
||||
|
||||
func decodeNodeCommonData_SyntheticExpression(_ uint8) (any, bool) {
|
||||
panic("SyntheticExpression should never be decoded")
|
||||
}
|
||||
|
||||
// Hand-written extended data decoding functions for literal nodes.
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_StringLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
flags := readLE32(d.raw, extOff+4)
|
||||
return d.factory.NewStringLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_NumericLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
flags := readLE32(d.raw, extOff+4)
|
||||
return d.factory.NewNumericLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_BigIntLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
flags := readLE32(d.raw, extOff+4)
|
||||
return d.factory.NewBigIntLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_RegularExpressionLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
flags := readLE32(d.raw, extOff+4)
|
||||
return d.factory.NewRegularExpressionLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
|
||||
func (d *astDecoder) decodeExtendedData_NoSubstitutionTemplateLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
|
||||
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
|
||||
textIdx := readLE32(d.raw, extOff)
|
||||
flags := readLE32(d.raw, extOff+4)
|
||||
return d.factory.NewNoSubstitutionTemplateLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
|
||||
}
|
||||
1140
tools/tsgo/internal/api/encoder/decoder_generated.go
Normal file
1140
tools/tsgo/internal/api/encoder/decoder_generated.go
Normal file
File diff suppressed because it is too large
Load Diff
450
tools/tsgo/internal/api/encoder/decoder_test.go
Normal file
450
tools/tsgo/internal/api/encoder/decoder_test.go
Normal file
@@ -0,0 +1,450 @@
|
||||
package encoder_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/api/encoder"
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/parser"
|
||||
"github.com/microsoft/typescript-go/internal/repo"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func parseSourceFile(code string) *ast.SourceFile {
|
||||
return parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/test.ts",
|
||||
Path: "/test.ts",
|
||||
}, code, core.ScriptKindTS)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_Basic(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let x = 1;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, decoded.AsNode().Kind, ast.KindSourceFile)
|
||||
assert.Equal(t, decoded.FileName(), "/test.ts")
|
||||
assert.Equal(t, decoded.Text(), "let x = 1;")
|
||||
assert.Assert(t, decoded.Statements != nil)
|
||||
assert.Assert(t, decoded.EndOfFileToken != nil)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_Statements(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let a = 1;\nlet b = 2;\nlet c = 3;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(decoded.Statements.Nodes), 3)
|
||||
for i, stmt := range decoded.Statements.Nodes {
|
||||
assert.Equal(t, stmt.Kind, ast.KindVariableStatement, "statement %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_VariableDeclaration(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let x = 1;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
varStmt := decoded.Statements.Nodes[0].AsVariableStatement()
|
||||
assert.Assert(t, varStmt.DeclarationList != nil)
|
||||
declList := varStmt.DeclarationList.AsVariableDeclarationList()
|
||||
assert.Assert(t, declList.Declarations != nil)
|
||||
assert.Equal(t, len(declList.Declarations.Nodes), 1)
|
||||
|
||||
decl := declList.Declarations.Nodes[0].AsVariableDeclaration()
|
||||
assert.Equal(t, decl.Name().Kind, ast.KindIdentifier)
|
||||
assert.Equal(t, decl.Name().AsIdentifier().Text, "x")
|
||||
assert.Assert(t, decl.Initializer != nil)
|
||||
assert.Equal(t, decl.Initializer.Kind, ast.KindNumericLiteral)
|
||||
assert.Equal(t, decl.Initializer.AsNumericLiteral().Text, "1")
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_VariableDeclarationListFlags(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
code string
|
||||
expected ast.NodeFlags
|
||||
}{
|
||||
{"const", "const x = 1;", ast.NodeFlagsConst},
|
||||
{"let", "let x = 1;", ast.NodeFlagsLet},
|
||||
{"var", "var x = 1;", ast.NodeFlagsNone},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile(tt.code)
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
declList := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList()
|
||||
got := declList.Flags & (ast.NodeFlagsLet | ast.NodeFlagsConst)
|
||||
assert.Equal(t, got, tt.expected, "flags for %q: got %d, want %d", tt.code, got, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_FunctionDeclaration(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("function add(a: number, b: number): number { return a + b; }")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration()
|
||||
assert.Assert(t, funcDecl.Name() != nil)
|
||||
assert.Equal(t, funcDecl.Name().AsIdentifier().Text, "add")
|
||||
assert.Assert(t, funcDecl.Parameters != nil)
|
||||
assert.Equal(t, len(funcDecl.Parameters.Nodes), 2)
|
||||
assert.Assert(t, funcDecl.Type != nil)
|
||||
assert.Assert(t, funcDecl.Body != nil)
|
||||
|
||||
param0 := funcDecl.Parameters.Nodes[0].AsParameterDeclaration()
|
||||
assert.Equal(t, param0.Name().AsIdentifier().Text, "a")
|
||||
assert.Assert(t, param0.Type != nil)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_ImportDeclaration(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile(`import { bar } from "bar";`)
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
imp := decoded.Statements.Nodes[0].AsImportDeclaration()
|
||||
assert.Assert(t, imp.ImportClause != nil)
|
||||
assert.Assert(t, imp.ModuleSpecifier != nil)
|
||||
assert.Equal(t, imp.ModuleSpecifier.AsStringLiteral().Text, "bar")
|
||||
|
||||
clause := imp.ImportClause.AsImportClause()
|
||||
assert.Assert(t, clause.NamedBindings != nil)
|
||||
namedImports := clause.NamedBindings.AsNamedImports()
|
||||
assert.Assert(t, namedImports.Elements != nil)
|
||||
assert.Equal(t, len(namedImports.Elements.Nodes), 1)
|
||||
spec := namedImports.Elements.Nodes[0].AsImportSpecifier()
|
||||
assert.Equal(t, spec.Name().AsIdentifier().Text, "bar")
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_IfStatement(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("if (true) { } else { }")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
ifStmt := decoded.Statements.Nodes[0].AsIfStatement()
|
||||
assert.Assert(t, ifStmt.Expression != nil)
|
||||
assert.Assert(t, ifStmt.ThenStatement != nil)
|
||||
assert.Assert(t, ifStmt.ElseStatement != nil)
|
||||
assert.Equal(t, ifStmt.ThenStatement.Kind, ast.KindBlock)
|
||||
assert.Equal(t, ifStmt.ElseStatement.Kind, ast.KindBlock)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_TemplateExpression(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let x = `hello ${name} world`;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
varDecl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
|
||||
tmplExpr := varDecl.Initializer.AsTemplateExpression()
|
||||
assert.Assert(t, tmplExpr.Head != nil)
|
||||
assert.Equal(t, tmplExpr.Head.AsTemplateHead().Text, "hello ")
|
||||
assert.Assert(t, tmplExpr.TemplateSpans != nil)
|
||||
assert.Equal(t, len(tmplExpr.TemplateSpans.Nodes), 1)
|
||||
|
||||
span := tmplExpr.TemplateSpans.Nodes[0].AsTemplateSpan()
|
||||
assert.Assert(t, span.Expression != nil)
|
||||
assert.Equal(t, span.Expression.Kind, ast.KindIdentifier)
|
||||
assert.Assert(t, span.Literal != nil)
|
||||
assert.Equal(t, span.Literal.AsTemplateTail().Text, " world")
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_ExportModifier(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("export function foo() {}")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration()
|
||||
assert.Assert(t, funcDecl.Modifiers() != nil)
|
||||
assert.Equal(t, len(funcDecl.Modifiers().Nodes), 1)
|
||||
assert.Equal(t, funcDecl.Modifiers().Nodes[0].Kind, ast.KindExportKeyword)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_Positions(t *testing.T) {
|
||||
t.Parallel()
|
||||
code := "let x = 1;"
|
||||
sf := parseSourceFile(code)
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, decoded.AsNode().Pos(), 0)
|
||||
assert.Equal(t, decoded.AsNode().End(), len(code))
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_ClassDeclaration(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("class Foo { bar(): void {} }")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
classDecl := decoded.Statements.Nodes[0].AsClassDeclaration()
|
||||
assert.Assert(t, classDecl.Name() != nil)
|
||||
assert.Equal(t, classDecl.Name().AsIdentifier().Text, "Foo")
|
||||
assert.Assert(t, classDecl.Members != nil)
|
||||
assert.Equal(t, len(classDecl.Members.Nodes), 1)
|
||||
assert.Equal(t, classDecl.Members.Nodes[0].Kind, ast.KindMethodDeclaration)
|
||||
}
|
||||
|
||||
func TestDecodeNodes_SubtreeRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("function greet(name: string) { return `Hello, ${name}!`; }")
|
||||
|
||||
var funcNode *ast.Node
|
||||
visitor := &ast.NodeVisitor{}
|
||||
visitor.Visit = func(node *ast.Node) *ast.Node {
|
||||
if node.Kind == ast.KindFunctionDeclaration && funcNode == nil {
|
||||
funcNode = node
|
||||
}
|
||||
return node
|
||||
}
|
||||
visitor.VisitEachChild(sf.AsNode())
|
||||
assert.Assert(t, funcNode != nil)
|
||||
|
||||
buf, _, err := encoder.EncodeNode(funcNode, sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeNodes(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, decoded.Kind, ast.KindFunctionDeclaration)
|
||||
funcDecl := decoded.AsFunctionDeclaration()
|
||||
assert.Assert(t, funcDecl.Name() != nil)
|
||||
assert.Equal(t, funcDecl.Name().AsIdentifier().Text, "greet")
|
||||
assert.Assert(t, funcDecl.Parameters != nil)
|
||||
assert.Equal(t, len(funcDecl.Parameters.Nodes), 1)
|
||||
assert.Assert(t, funcDecl.Body != nil)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_BinaryExpression(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let x = 1 + 2;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
|
||||
binExpr := decl.Initializer.AsBinaryExpression()
|
||||
assert.Assert(t, binExpr.Left != nil)
|
||||
assert.Assert(t, binExpr.Right != nil)
|
||||
assert.Assert(t, binExpr.OperatorToken != nil)
|
||||
assert.Equal(t, binExpr.Left.Kind, ast.KindNumericLiteral)
|
||||
assert.Equal(t, binExpr.Right.Kind, ast.KindNumericLiteral)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_KeywordExpressions(t *testing.T) {
|
||||
t.Parallel()
|
||||
// "this" must decode as KeywordExpression, not Token, or the printer panics
|
||||
sf := parseSourceFile("const x = this;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Navigate: const x = this -> VariableStatement -> declaration -> initializer
|
||||
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
|
||||
thisExpr := decl.Initializer
|
||||
assert.Equal(t, thisExpr.Kind, ast.KindThisKeyword)
|
||||
// This would panic if decoded as Token instead of KeywordExpression
|
||||
assert.Assert(t, thisExpr.AsKeywordExpression() != nil)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_EmptyModuleBlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("namespace N { }")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Navigate: namespace N { } -> ModuleDeclaration -> ModuleBlock
|
||||
mod := decoded.Statements.Nodes[0].AsModuleDeclaration()
|
||||
assert.Assert(t, mod.Body != nil)
|
||||
block := mod.Body.AsModuleBlock()
|
||||
// Statements must be non-nil even when empty, otherwise the printer panics
|
||||
assert.Assert(t, block.Statements != nil)
|
||||
assert.Equal(t, len(block.Statements.Nodes), 0)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_EmptyBlockAndParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Empty blocks and parameter lists must decode with non-nil NodeLists (not nil),
|
||||
// matching parser behavior. Previously the decoder left them nil, crashing the printer.
|
||||
sf := parseSourceFile("function foo() {}")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration()
|
||||
assert.Assert(t, funcDecl.Parameters != nil, "FunctionDeclaration.Parameters must be non-nil for foo()")
|
||||
assert.Equal(t, len(funcDecl.Parameters.Nodes), 0)
|
||||
assert.Assert(t, funcDecl.Body != nil)
|
||||
block := funcDecl.Body.AsBlock()
|
||||
assert.Assert(t, block.Statements != nil, "Block.Statements must be non-nil for empty blocks")
|
||||
assert.Equal(t, len(block.Statements.Nodes), 0)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_ArrowFunctionEmptyParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
// `() => {}` must decode with non-nil Parameters (empty NodeList),
|
||||
// matching parser behavior. Previously the decoder left it nil, crashing the printer.
|
||||
sf := parseSourceFile("const f = () => {};")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
|
||||
arrow := decl.Initializer.AsArrowFunction()
|
||||
assert.Assert(t, arrow.Parameters != nil, "ArrowFunction.Parameters must be non-nil for () => {}")
|
||||
assert.Equal(t, len(arrow.Parameters.Nodes), 0)
|
||||
assert.Assert(t, arrow.Body != nil)
|
||||
block := arrow.Body.AsBlock()
|
||||
assert.Assert(t, block.Statements != nil, "Block.Statements must be non-nil for empty body")
|
||||
assert.Equal(t, len(block.Statements.Nodes), 0)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_FunctionExpressionEmptyParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
// `function() {}` must decode with non-nil Parameters (empty NodeList).
|
||||
sf := parseSourceFile("const f = function() {};")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
|
||||
funcExpr := decl.Initializer.AsFunctionExpression()
|
||||
assert.Assert(t, funcExpr.Parameters != nil, "FunctionExpression.Parameters must be non-nil for function() {}")
|
||||
assert.Equal(t, len(funcExpr.Parameters.Nodes), 0)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_PostfixUnaryOperator(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let i = 0; i++;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement()
|
||||
postfix := exprStmt.Expression.AsPostfixUnaryExpression()
|
||||
assert.Equal(t, postfix.Operator, ast.KindPlusPlusToken)
|
||||
assert.Equal(t, postfix.Operand.Kind, ast.KindIdentifier)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_PrefixUnaryOperator(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let x = true; !x;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement()
|
||||
prefix := exprStmt.Expression.AsPrefixUnaryExpression()
|
||||
assert.Equal(t, prefix.Operator, ast.KindExclamationToken)
|
||||
assert.Equal(t, prefix.Operand.Kind, ast.KindIdentifier)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_PostfixDecrement(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("let n = 5; n--;")
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement()
|
||||
postfix := exprStmt.Expression.AsPostfixUnaryExpression()
|
||||
assert.Equal(t, postfix.Operator, ast.KindMinusMinusToken)
|
||||
}
|
||||
|
||||
func BenchmarkDecodeSourceFile(b *testing.B) {
|
||||
repo.SkipIfNoTypeScriptSubmodule(b)
|
||||
filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts")
|
||||
fileContent, err := os.ReadFile(filePath)
|
||||
assert.NilError(b, err)
|
||||
code := string(fileContent)
|
||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/checker.ts",
|
||||
Path: "/checker.ts",
|
||||
}, code, core.ScriptKindTS)
|
||||
|
||||
buf, _, err := encoder.EncodeSourceFile(sourceFile)
|
||||
assert.NilError(b, err)
|
||||
|
||||
b.Run("parse", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/checker.ts",
|
||||
Path: "/checker.ts",
|
||||
}, code, core.ScriptKindTS)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("decode", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_, decodeErr := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(b, decodeErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
844
tools/tsgo/internal/api/encoder/encoder.go
Normal file
844
tools/tsgo/internal/api/encoder/encoder.go
Normal file
@@ -0,0 +1,844 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/zeebo/xxh3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if ast.KindLastUnaryOperator > 0x3f {
|
||||
panic(fmt.Sprintf("KindLastUnaryOperator (%d) exceeds the 6-bit commonData capacity (max 63)", ast.KindLastUnaryOperator))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
NodeOffsetKind = iota * 4
|
||||
NodeOffsetPos
|
||||
NodeOffsetEnd
|
||||
NodeOffsetNext
|
||||
NodeOffsetParent
|
||||
NodeOffsetData
|
||||
NodeOffsetFlags
|
||||
// NodeSize is the number of bytes that represents a single node in the encoded format.
|
||||
NodeSize
|
||||
)
|
||||
|
||||
const (
|
||||
NodeDataTypeChildren uint32 = iota << 30
|
||||
NodeDataTypeString
|
||||
NodeDataTypeExtendedData
|
||||
)
|
||||
|
||||
const (
|
||||
NodeDataTypeMask uint32 = 0xc0_00_00_00
|
||||
NodeDataChildMask uint32 = 0x00_00_00_ff
|
||||
NodeDataStringIndexMask uint32 = 0x00_ff_ff_ff
|
||||
)
|
||||
|
||||
const (
|
||||
SyntaxKindNodeList uint32 = 1<<32 - 1
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderOffsetMetadata = iota * 4
|
||||
HeaderOffsetHashLo0
|
||||
HeaderOffsetHashLo1
|
||||
HeaderOffsetHashHi0
|
||||
HeaderOffsetHashHi1
|
||||
HeaderOffsetParseOptions
|
||||
HeaderOffsetStringOffsets
|
||||
HeaderOffsetStringData
|
||||
HeaderOffsetExtendedData
|
||||
HeaderOffsetStructuredData
|
||||
HeaderOffsetNodes
|
||||
HeaderSize
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolVersion uint8 = 5
|
||||
)
|
||||
|
||||
// Source File Binary Format
|
||||
// =========================
|
||||
//
|
||||
// The following defines a protocol for serializing TypeScript SourceFile objects to a compact binary format. All integer
|
||||
// values are little-endian.
|
||||
//
|
||||
// Overview
|
||||
// --------
|
||||
//
|
||||
// The format comprises seven sections:
|
||||
//
|
||||
// | Section | Length | Description |
|
||||
// | ------------------ | ------------------ | ----------------------------------------------------------------------------------------------- |
|
||||
// | Header | 44 bytes | Contains the content hash, parse options, flags, and byte offsets to the start of each section. |
|
||||
// | String offsets | 8 bytes per string | Pairs of starting byte offsets and ending byte offsets into the **string data** section. |
|
||||
// | String data | variable | UTF-8 encoded string data. |
|
||||
// | Extended node data | variable | Extra data for some kinds of nodes. |
|
||||
// | Structured data | variable | Msgpack-encoded metadata blobs (e.g. file references). |
|
||||
// | Nodes | 28 bytes per node | Defines the AST structure of the file, with references to strings and extended data. |
|
||||
//
|
||||
// Header (44 bytes)
|
||||
// -----------------
|
||||
//
|
||||
// The header contains the following fields:
|
||||
//
|
||||
// | Byte offset | Type | Field |
|
||||
// | ----------- | --------- | ------------------------------------------------- |
|
||||
// | 0 | uint8 | Protocol version |
|
||||
// | 1-3 | | Reserved |
|
||||
// | 4-19 | uint128 | Source file content hash (xxh3, LE) |
|
||||
// | 20-23 | uint32 | Parse options (bitmask; bit 0: JSX, bit 1: Force) |
|
||||
// | 24-27 | uint32 | Byte offset to string offsets section |
|
||||
// | 28-31 | uint32 | Byte offset to string data section |
|
||||
// | 32-35 | uint32 | Byte offset to extended node data section |
|
||||
// | 36-39 | uint32 | Byte offset to structured data section |
|
||||
// | 40-43 | uint32 | Byte offset to nodes section |
|
||||
//
|
||||
// String offsets (8 bytes per string)
|
||||
// -----------------------------------
|
||||
//
|
||||
// Each string offset entry consists of two 4-byte unsigned integers, representing the start and end byte offsets into the
|
||||
// **string data** section.
|
||||
//
|
||||
// String data (variable)
|
||||
// ----------------------
|
||||
//
|
||||
// The string data section contains UTF-8 encoded string data, with WTF-8 used for JS strings containing lone UTF-16
|
||||
// surrogates. In typical cases, the entirety of the string data is the source file text, and individual nodes with
|
||||
// string properties reference their positional slice of the file text. In cases where a node's string property is not
|
||||
// equal to the slice of file text at its position, the unique string is appended to the string data section after the
|
||||
// file text.
|
||||
//
|
||||
// Extended node data (variable)
|
||||
// -----------------------------
|
||||
//
|
||||
// The extended node data section contains additional data for specific node types. The length and meaning of each entry
|
||||
// is defined by the node type.
|
||||
//
|
||||
// Currently, the only node types that use this section are `TemplateHead`, `TemplateMiddle`, `TemplateTail`, and
|
||||
// `SourceFile`. The extended data format for the first three is:
|
||||
//
|
||||
// | Byte offset | Type | Field |
|
||||
// | ----------- | ------ | ------------------------------------------------ |
|
||||
// | 0-4 | uint32 | Index of `text` in the string offsets section |
|
||||
// | 4-8 | uint32 | Index of `rawText` in the string offsets section |
|
||||
// | 8-12 | uint32 | Value of `templateFlags` |
|
||||
//
|
||||
// and for `SourceFile` is:
|
||||
//
|
||||
// | Byte offset | Type | Field |
|
||||
// | ----------- | ------ | -------------------------------------------------------------- |
|
||||
// | 0-4 | uint32 | Index of `text` in the string offsets section |
|
||||
// | 4-8 | uint32 | Index of `fileName` in the string offsets section |
|
||||
// | 8-12 | uint32 | Index of `path` in the string offsets section |
|
||||
// | 12-16 | uint32 | Value of `languageVariant` |
|
||||
// | 16-20 | uint32 | Value of `scriptKind` |
|
||||
// | 20-24 | uint32 | Byte offset of `referencedFiles` in structured data section |
|
||||
// | 24-28 | uint32 | Byte offset of `typeReferenceDirectives` in structured data |
|
||||
// | 28-32 | uint32 | Byte offset of `libReferenceDirectives` in structured data |
|
||||
// | 32-36 | uint32 | Byte offset of `imports` node index array in structured data |
|
||||
// | 36-40 | uint32 | Byte offset of `moduleAugmentations` node index array |
|
||||
// | 40-44 | uint32 | Byte offset of `ambientModuleNames` string array |
|
||||
// | 44-48 | uint32 | Node index of `externalModuleIndicator` (0 = nil) |
|
||||
//
|
||||
// Structured data (variable)
|
||||
// --------------------------
|
||||
//
|
||||
// The structured data section contains msgpack-encoded metadata blobs. Each blob is a self-contained
|
||||
// msgpack value. File reference arrays use the following tuple format:
|
||||
//
|
||||
// [pos: uint, end: uint, fileName: string, resolutionMode: uint, preserve: bool]
|
||||
//
|
||||
// Node index arrays (imports, moduleAugmentations) are msgpack arrays of uint values, where each
|
||||
// value is a node index into the nodes section. String arrays (ambientModuleNames) are msgpack
|
||||
// arrays of string values.
|
||||
//
|
||||
// An offset of 0xFFFFFFFF indicates no data (empty array).
|
||||
//
|
||||
// Nodes (28 bytes per node)
|
||||
// -------------------------
|
||||
//
|
||||
// The nodes section contains the AST structure of the file. Nodes are represented in a flat array in source order,
|
||||
// heavily inspired by https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/. Each node has the following
|
||||
// structure:
|
||||
//
|
||||
// | Byte offset | Type | Field |
|
||||
// | ----------- | ------ | -------------------------- |
|
||||
// | 0-4 | uint32 | Kind |
|
||||
// | 4-8 | uint32 | Pos |
|
||||
// | 8-12 | uint32 | End |
|
||||
// | 12-16 | uint32 | Node index of next sibling |
|
||||
// | 16-20 | uint32 | Node index of parent |
|
||||
// | 20-24 | | Node data |
|
||||
// | 24-28 | uint32 | Node flags |
|
||||
//
|
||||
// The first 28 bytes of the nodes section are zeros representing a nil node, such that nodes without a parent or next
|
||||
// sibling can unambiuously use `0` for those indices.
|
||||
//
|
||||
// NodeLists are represented as normal nodes with the special `kind` value `0xff_ff_ff_ff`. They are considered the parent
|
||||
// of their contents in the encoded format. A client reconstructing an AST similar to TypeScript's internal representation
|
||||
// should instead set the `parent` pointers of a NodeList's children to the NodeList's parent. A NodeList's `data` field
|
||||
// is the uint32 length of the list, and does not use one of the data types described below.
|
||||
//
|
||||
// For node types other than NodeList, the node data field encodes one of the following, determined by the first 2 bits of
|
||||
// the field:
|
||||
//
|
||||
// | Value | Data type | Description |
|
||||
// | ----- | --------- | ------------------------------------------------------------------------------------ |
|
||||
// | 0b00 | Children | Disambiguates which named properties of the node its children should be assigned to. |
|
||||
// | 0b01 | String | The index of the node's string property in the **string offsets** section. |
|
||||
// | 0b10 | Extended | The byte offset of the node's extended data into the **extended node data** section. |
|
||||
// | 0b11 | Reserved | Reserved for future use. |
|
||||
//
|
||||
// In all node data types, the remaining 6 bits of the first byte are used to encode small values specific to the node
|
||||
// type. For most node types, these are individual boolean flags. For unary expressions, all 6 bits encode the operator's
|
||||
// SyntaxKind value (e.g., PlusPlusToken=45, TildeToken=54), which fits because KindLastUnaryOperator (54) <= 0x3f (63).
|
||||
//
|
||||
// | Node type | Bits 0-5 | Notes |
|
||||
// | ---------------------------- | ------------------------------------- | ------------------------------ |
|
||||
// | `ImportSpecifier` | Bit 0: `isTypeOnly` | |
|
||||
// | `ImportClause` | Bit 0: `isTypeOnly`, Bit 1: `isDefer` | |
|
||||
// | `ExportSpecifier` | Bit 0: `isTypeOnly` | |
|
||||
// | `ImportEqualsDeclaration` | Bit 0: `isTypeOnly` | |
|
||||
// | `ExportDeclaration` | Bit 0: `isTypeOnly` | |
|
||||
// | `ImportTypeNode` | Bit 0: `isTypeOf` | |
|
||||
// | `ExportAssignment` | Bit 0: `isExportEquals` | |
|
||||
// | `Block` | Bit 0: `multiline` | |
|
||||
// | `ArrayLiteralExpression` | Bit 0: `multiline` | |
|
||||
// | `ObjectLiteralExpression` | Bit 0: `multiline` | |
|
||||
// | `JsxText` | Bit 0: `containsOnlyTriviaWhiteSpaces`| |
|
||||
// | `JSDocTypeLiteral` | Bit 0: `isArrayType` | |
|
||||
// | `JsDocPropertyTag` | Bit 0: `isBracketed`, Bit 1: `isNameFirst` | |
|
||||
// | `JsDocParameterTag` | Bit 0: `isBracketed`, Bit 1: `isNameFirst` | |
|
||||
// | `VariableDeclarationList` | Bit 0: is `let`, Bit 1: is `const` | |
|
||||
// | `ImportAttributes` | Bit 0: `multiline`, Bit 1: is `assert`| |
|
||||
// | `PrefixUnaryExpression` | Bits 0-5: operator SyntaxKind | e.g., `!`, `~`, `++`, `--` |
|
||||
// | `PostfixUnaryExpression` | Bits 0-5: operator SyntaxKind | e.g., `++`, `--` |
|
||||
//
|
||||
// The remaining 3 bytes of the node data field vary by data type:
|
||||
//
|
||||
// ### Children (0b00)
|
||||
//
|
||||
// If a node has fewer children than its type allows, additional data is needed to determine which properties the children
|
||||
// correspond to. The last byte of the 4-byte data field is a bitmask representing the child properties of the node type,
|
||||
// in visitor order, where `1` indicates that the child at that property is present and `0` indicates that the property is
|
||||
// nil. For example, a `MethodDeclaration` has the following child properties:
|
||||
//
|
||||
// | Property name | Bit position |
|
||||
// | -------------- | ------------ |
|
||||
// | modifiers | 0 |
|
||||
// | asteriskToken | 1 |
|
||||
// | name | 2 |
|
||||
// | postfixToken | 3 |
|
||||
// | typeParameters | 4 |
|
||||
// | parameters | 5 |
|
||||
// | returnType | 6 |
|
||||
// | body | 7 |
|
||||
//
|
||||
// A bitmask with value `0b01100101` would indicate that the next four direct descendants (i.e., node records that have a
|
||||
// `parent` set to the node index of the `MethodDeclaration`) of the node are its `modifiers`, `name`, `parameters`, and
|
||||
// `body` properties, in that order. The remaining properties are nil. (To reconstruct the node with named properties, the
|
||||
// client must consult a static table of each node type's child property names.)
|
||||
//
|
||||
// The bitmask may be zero for node types that can only have a single child, since no disambiguation is needed.
|
||||
// Additionally, the children data type may be used for nodes that can never have children, but do not require other
|
||||
// data types.
|
||||
//
|
||||
// ### String (0b01)
|
||||
//
|
||||
// The string data type is used for nodes with a single string property. (Currently, the name of that property is always
|
||||
// `text`.) The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e.,
|
||||
// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is an index into the **string offsets** section. The *N*th 32-bit
|
||||
// unsigned integer in the **string offsets** section is the byte offset of the start of the string in the **string data**
|
||||
// section, and the *N+1*th 32-bit unsigned integer is the byte offset of the end of the string in the
|
||||
// **string data** section.
|
||||
//
|
||||
// ### Extended (0b10)
|
||||
//
|
||||
// The extended data type is used for nodes with properties that don't fit into either the children or string data types.
|
||||
// The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e.,
|
||||
// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is a byte offset into the **extended node data** section. The length and
|
||||
// meaning of the data at that offset is defined by the node type. See the **Extended node data** section for details on
|
||||
// the format of the extended data for specific node types.
|
||||
//
|
||||
// Encoding Arbitrary Nodes
|
||||
// ------------------------
|
||||
//
|
||||
// The same binary format can be used to encode an arbitrary subtree of a SourceFile, not just a whole SourceFile. When
|
||||
// encoding a non-SourceFile node, the format is identical with the following differences:
|
||||
//
|
||||
// - The content hash fields in the header (bytes 4-19) are zero.
|
||||
// - The parse options field in the header (bytes 20-23) is zero.
|
||||
// - The root node in the nodes section uses its actual node kind and data encoding (via getNodeData) rather than the
|
||||
// SourceFile-specific extended data format.
|
||||
//
|
||||
// The string data section contains only the strings referenced by nodes in the subtree, rather than the full source
|
||||
// file text. The EncodeNode function provides this entrypoint.
|
||||
|
||||
// SourceFileHash returns the 128-bit content hash for a source file as a hex string.
|
||||
func SourceFileHash(sourceFile *ast.SourceFile) string {
|
||||
h := sourceFile.Hash
|
||||
return fmt.Sprintf("%016x%016x", h.Hi, h.Lo)
|
||||
}
|
||||
|
||||
// encodeParseOptions encodes the per-file ExternalModuleIndicatorOptions as a uint32 bitmask.
|
||||
func encodeParseOptions(opts ast.ExternalModuleIndicatorOptions) uint32 {
|
||||
var bits uint32
|
||||
if opts.JSX {
|
||||
bits |= 1
|
||||
}
|
||||
if opts.Force {
|
||||
bits |= 2
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
||||
// NodeIndexTable maps between AST nodes and their encoder indices for O(1) node handle resolution.
|
||||
type NodeIndexTable struct {
|
||||
Nodes []*ast.Node // index → node (for resolution)
|
||||
sortedOnce sync.Once
|
||||
sortedIdx []uint32 // indices into Nodes, sorted by node ID; built lazily
|
||||
}
|
||||
|
||||
var nodeIndexTableKey = ast.NewSourceFileDataKey[*NodeIndexTable]()
|
||||
|
||||
// GetIndex returns the encoder index for the given node.
|
||||
// On the first call the sortedIdx array is built (O(n log n) sort on a flat []uint32),
|
||||
// then subsequent calls use binary search (O(log n)). This turns out to be much faster than
|
||||
// building a map[*ast.Node]uint32 and not significantly slower for lookups.
|
||||
func (t *NodeIndexTable) GetIndex(node *ast.Node) uint32 {
|
||||
t.sortedOnce.Do(func() {
|
||||
idx := make([]uint32, 0, len(t.Nodes))
|
||||
for i, n := range t.Nodes {
|
||||
if n != nil {
|
||||
idx = append(idx, uint32(i))
|
||||
}
|
||||
}
|
||||
nodes := t.Nodes
|
||||
slices.SortFunc(idx, func(a, b uint32) int {
|
||||
return cmp.Compare(ast.GetNodeId(nodes[a]), ast.GetNodeId(nodes[b]))
|
||||
})
|
||||
t.sortedIdx = idx
|
||||
})
|
||||
target := ast.GetNodeId(node)
|
||||
i, found := core.BinarySearchUniqueFunc(t.sortedIdx, func(_ int, el uint32) int {
|
||||
return cmp.Compare(ast.GetNodeId(t.Nodes[el]), target)
|
||||
})
|
||||
if found {
|
||||
return t.sortedIdx[i]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// BuildNodeIndexTable walks the AST in the same order as encodeTree and builds
|
||||
// a NodeIndexTable without performing the full binary encoding. This is used to
|
||||
// eagerly create index tables for files that need node handles before getSourceFile
|
||||
// is called. The indices produced are guaranteed to match those from EncodeSourceFile.
|
||||
func BuildNodeIndexTable(sourceFile *ast.SourceFile) *NodeIndexTable {
|
||||
var nodeCount uint32
|
||||
nodeTable := make([]*ast.Node, 1, sourceFile.NodeCount+1) // index 0 = nil sentinel
|
||||
|
||||
visitor := &ast.NodeVisitor{
|
||||
Hooks: ast.NodeVisitorHooks{
|
||||
VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
|
||||
if nodeList == nil {
|
||||
return nodeList
|
||||
}
|
||||
nodeCount++
|
||||
nodeTable = append(nodeTable, nil) // NodeLists are not *ast.Node
|
||||
visitor.VisitSlice(nodeList.Nodes)
|
||||
return nodeList
|
||||
},
|
||||
VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList {
|
||||
if modifiers != nil && len(modifiers.Nodes) > 0 {
|
||||
visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor)
|
||||
}
|
||||
return modifiers
|
||||
},
|
||||
},
|
||||
}
|
||||
visitor.Visit = func(node *ast.Node) *ast.Node {
|
||||
nodeCount++
|
||||
nodeTable = append(nodeTable, node)
|
||||
visitor.VisitEachChild(node)
|
||||
for _, jsdoc := range node.JSDoc(sourceFile) {
|
||||
visitor.Visit(jsdoc)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
rootNode := sourceFile.AsNode()
|
||||
// Index 1 = root node (matches encodeTree)
|
||||
nodeCount++
|
||||
nodeTable = append(nodeTable, rootNode)
|
||||
|
||||
visitor.VisitEachChild(rootNode)
|
||||
for _, jsdoc := range rootNode.JSDoc(sourceFile) {
|
||||
visitor.Visit(jsdoc)
|
||||
}
|
||||
|
||||
return &NodeIndexTable{Nodes: nodeTable}
|
||||
}
|
||||
|
||||
func GetNodeIndexTable(sourceFile *ast.SourceFile) *NodeIndexTable {
|
||||
return ast.GetOrComputeSourceFileData(sourceFile, nodeIndexTableKey, BuildNodeIndexTable)
|
||||
}
|
||||
|
||||
// EncodeSourceFile encodes an entire source file AST into the binary format.
|
||||
// Returns the encoded bytes and a NodeIndexTable mapping encoder indices to AST nodes.
|
||||
func EncodeSourceFile(sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) {
|
||||
data, nodeTable, err := encodeTree(sourceFile.AsNode(), sourceFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
nodeTable = ast.GetOrComputeSourceFileData(sourceFile, nodeIndexTableKey, func(*ast.SourceFile) *NodeIndexTable {
|
||||
return nodeTable
|
||||
})
|
||||
return data, nodeTable, nil
|
||||
}
|
||||
|
||||
// EncodeNode encodes an arbitrary AST node and its descendants into the binary format.
|
||||
// The sourceFile is needed to provide the source text for efficient string encoding.
|
||||
// When encoding a non-SourceFile node, the header hash and parse options fields will be zero.
|
||||
// Returns the encoded bytes and a NodeIndexTable mapping encoder indices to AST nodes.
|
||||
func EncodeNode(node *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) {
|
||||
return encodeTree(node, sourceFile)
|
||||
}
|
||||
|
||||
func encodeTree(rootNode *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) {
|
||||
var parentIndex, nodeCount, prevIndex uint32
|
||||
var extendedData []byte
|
||||
var structuredData []byte
|
||||
var strs *stringTable
|
||||
var positionMap *ast.PositionMap
|
||||
if rootNode.Kind == ast.KindSourceFile {
|
||||
strs = newStringTable(sourceFile.Text(), sourceFile.TextCount)
|
||||
positionMap = sourceFile.GetPositionMap()
|
||||
} else {
|
||||
strs = newStringTable("", 0)
|
||||
if sourceFile != nil {
|
||||
positionMap = sourceFile.GetPositionMap()
|
||||
}
|
||||
}
|
||||
if positionMap == nil {
|
||||
positionMap = ast.ComputePositionMap("")
|
||||
}
|
||||
utf16 := func(pos int) uint32 {
|
||||
return uint32(positionMap.UTF8ToUTF16(pos))
|
||||
}
|
||||
var initialNodeCount int
|
||||
if sourceFile != nil {
|
||||
initialNodeCount = sourceFile.NodeCount
|
||||
}
|
||||
nodes := make([]byte, 0, (initialNodeCount+1)*NodeSize)
|
||||
|
||||
// Build node index table for O(1) handle resolution.
|
||||
// Index 0 is a nil sentinel; real nodes start at index 1.
|
||||
nodeTable := make([]*ast.Node, 1, initialNodeCount+1) // index 0 = nil sentinel
|
||||
|
||||
// Build a small map of nodes we need to track indices for (imports + moduleAugmentations).
|
||||
// Values start at 0 and are filled in during the walk.
|
||||
var nodeIndexMap map[*ast.Node]uint32
|
||||
var sfExtendedDataOffset int // byte offset in extendedData where SourceFile fields start
|
||||
if rootNode.Kind == ast.KindSourceFile {
|
||||
sf := rootNode.AsSourceFile()
|
||||
total := len(sf.Imports()) + len(sf.ModuleAugmentations)
|
||||
if sf.ExternalModuleIndicator != nil && sf.ExternalModuleIndicator != rootNode {
|
||||
total++
|
||||
}
|
||||
if total > 0 {
|
||||
nodeIndexMap = make(map[*ast.Node]uint32, total)
|
||||
for _, imp := range sf.Imports() {
|
||||
nodeIndexMap[imp.AsNode()] = 0
|
||||
}
|
||||
for _, aug := range sf.ModuleAugmentations {
|
||||
nodeIndexMap[aug.AsNode()] = 0
|
||||
}
|
||||
if sf.ExternalModuleIndicator != nil && sf.ExternalModuleIndicator != rootNode {
|
||||
nodeIndexMap[sf.ExternalModuleIndicator] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitor := &ast.NodeVisitor{
|
||||
Hooks: ast.NodeVisitorHooks{
|
||||
VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
|
||||
if nodeList == nil {
|
||||
return nodeList
|
||||
}
|
||||
|
||||
nodeCount++
|
||||
nodeTable = append(nodeTable, nil) // NodeLists are not *ast.Node
|
||||
if prevIndex != 0 {
|
||||
// this is the next sibling of `prevNode`
|
||||
b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24)
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
|
||||
}
|
||||
|
||||
nodes = appendUint32s(nodes, SyntaxKindNodeList, utf16(nodeList.Pos()), utf16(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)), 0)
|
||||
|
||||
saveParentIndex := parentIndex
|
||||
|
||||
currentIndex := nodeCount
|
||||
prevIndex = 0
|
||||
parentIndex = currentIndex
|
||||
visitor.VisitSlice(nodeList.Nodes)
|
||||
prevIndex = currentIndex
|
||||
parentIndex = saveParentIndex
|
||||
|
||||
return nodeList
|
||||
},
|
||||
VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList {
|
||||
if modifiers != nil && len(modifiers.Nodes) > 0 {
|
||||
visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor)
|
||||
}
|
||||
return modifiers
|
||||
},
|
||||
},
|
||||
}
|
||||
visitor.Visit = func(node *ast.Node) *ast.Node {
|
||||
nodeCount++
|
||||
nodeTable = append(nodeTable, node)
|
||||
if prevIndex != 0 {
|
||||
// this is the next sibling of `prevNode`
|
||||
b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24)
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2
|
||||
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
|
||||
}
|
||||
|
||||
nodes = appendUint32s(nodes, uint32(node.Kind), utf16(node.Pos()), utf16(node.End()), 0, parentIndex, getNodeData(node, strs, positionMap, &extendedData, &structuredData), uint32(node.Flags))
|
||||
|
||||
if nodeIndexMap != nil {
|
||||
if _, ok := nodeIndexMap[node]; ok {
|
||||
nodeIndexMap[node] = nodeCount
|
||||
}
|
||||
}
|
||||
|
||||
saveParentIndex := parentIndex
|
||||
|
||||
currentIndex := nodeCount
|
||||
prevIndex = 0
|
||||
parentIndex = currentIndex
|
||||
visitor.VisitEachChild(node)
|
||||
if sourceFile != nil {
|
||||
for _, jsdoc := range node.JSDoc(sourceFile) {
|
||||
visitor.Visit(jsdoc)
|
||||
}
|
||||
}
|
||||
prevIndex = currentIndex
|
||||
parentIndex = saveParentIndex
|
||||
return node
|
||||
}
|
||||
|
||||
nodes = appendUint32s(nodes, 0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
nodeCount++
|
||||
parentIndex++
|
||||
nodeTable = append(nodeTable, rootNode) // index 1 = root node
|
||||
|
||||
sfExtendedDataOffset = len(extendedData)
|
||||
nodes = appendUint32s(nodes, uint32(rootNode.Kind), utf16(rootNode.Pos()), utf16(rootNode.End()), 0, 0, getNodeData(rootNode, strs, positionMap, &extendedData, &structuredData), uint32(rootNode.Flags))
|
||||
|
||||
visitor.VisitEachChild(rootNode)
|
||||
if sourceFile != nil {
|
||||
for _, jsdoc := range rootNode.JSDoc(sourceFile) {
|
||||
visitor.Visit(jsdoc)
|
||||
}
|
||||
}
|
||||
|
||||
var hash xxh3.Uint128
|
||||
var parseOpts uint32
|
||||
if rootNode.Kind == ast.KindSourceFile {
|
||||
hash = sourceFile.Hash
|
||||
parseOpts = encodeParseOptions(sourceFile.ParseOptions().ExternalModuleIndicatorOptions)
|
||||
|
||||
// Encode imports, moduleAugmentations, and ambientModuleNames into structured data,
|
||||
// and patch the placeholder offsets in the SourceFile extended data.
|
||||
sf := rootNode.AsSourceFile()
|
||||
importsOffset := encodeNodeIndexArray(sf.Imports(), nodeIndexMap, &structuredData)
|
||||
moduleAugmentationsOffset := encodeModuleAugmentations(sf.ModuleAugmentations, nodeIndexMap, &structuredData)
|
||||
ambientModuleNamesOffset := encodeStringArray(sf.AmbientModuleNames, &structuredData)
|
||||
// Patch the 3 placeholder uint32s at sfExtendedDataOffset + 32, 36, 40
|
||||
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+32:], importsOffset)
|
||||
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+36:], moduleAugmentationsOffset)
|
||||
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+40:], ambientModuleNamesOffset)
|
||||
// Patch externalModuleIndicator node index at offset 44
|
||||
var externalModuleIndicatorIndex uint32
|
||||
if sf.ExternalModuleIndicator != nil {
|
||||
if sf.ExternalModuleIndicator == rootNode {
|
||||
externalModuleIndicatorIndex = 1 // root node index
|
||||
} else {
|
||||
externalModuleIndicatorIndex = nodeIndexMap[sf.ExternalModuleIndicator]
|
||||
}
|
||||
}
|
||||
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+44:], externalModuleIndicatorIndex)
|
||||
}
|
||||
|
||||
metadata := uint32(ProtocolVersion) << 24
|
||||
offsetStringTableOffsets := HeaderSize
|
||||
offsetStringTableData := HeaderSize + len(strs.offsets)*4
|
||||
offsetExtendedData := offsetStringTableData + strs.stringLength()
|
||||
offsetStructuredData := offsetExtendedData + len(extendedData)
|
||||
offsetNodes := offsetStructuredData + len(structuredData)
|
||||
|
||||
header := []uint32{
|
||||
metadata,
|
||||
uint32(hash.Lo), uint32(hash.Lo >> 32),
|
||||
uint32(hash.Hi), uint32(hash.Hi >> 32),
|
||||
parseOpts,
|
||||
uint32(offsetStringTableOffsets),
|
||||
uint32(offsetStringTableData),
|
||||
uint32(offsetExtendedData),
|
||||
uint32(offsetStructuredData),
|
||||
uint32(offsetNodes),
|
||||
}
|
||||
|
||||
var headerBytes, strsBytes []byte
|
||||
headerBytes = appendUint32s(nil, header...)
|
||||
strsBytes = strs.encode()
|
||||
|
||||
return slices.Concat(
|
||||
headerBytes,
|
||||
strsBytes,
|
||||
extendedData,
|
||||
structuredData,
|
||||
nodes,
|
||||
), &NodeIndexTable{Nodes: nodeTable}, nil
|
||||
}
|
||||
|
||||
func appendUint32s(buf []byte, values ...uint32) []byte {
|
||||
for _, value := range values {
|
||||
buf = binary.LittleEndian.AppendUint32(buf, value)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func getNodeData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {
|
||||
t := getNodeDataType(node)
|
||||
switch t {
|
||||
case NodeDataTypeChildren:
|
||||
return t | getNodeCommonData(node) | uint32(getChildrenPropertyMask(node))
|
||||
case NodeDataTypeString:
|
||||
return t | getNodeCommonData(node) | recordNodeStrings(node, strs)
|
||||
case NodeDataTypeExtendedData:
|
||||
return t | getNodeCommonData(node) | recordExtendedData(node, strs, positionMap, extendedData, structuredData)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
const noStructuredData = 0xFFFFFFFF
|
||||
|
||||
func recordExtendedData_SourceFile(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
|
||||
sf := node.AsSourceFile()
|
||||
textIndex := strs.add(sf.Text(), sf.Kind, sf.Pos(), sf.End())
|
||||
fileNameIndex := strs.add(sf.FileName(), 0, 0, 0)
|
||||
pathIndex := strs.add(string(sf.Path()), 0, 0, 0)
|
||||
referencedFilesOffset := encodeFileReferences(sf.ReferencedFiles, positionMap, structuredData)
|
||||
typeRefDirectivesOffset := encodeFileReferences(sf.TypeReferenceDirectives, positionMap, structuredData)
|
||||
libRefDirectivesOffset := encodeFileReferences(sf.LibReferenceDirectives, positionMap, structuredData)
|
||||
// imports, moduleAugmentations, ambientModuleNames offsets are placeholders;
|
||||
// they will be patched after the tree walk when node indices are known.
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, fileNameIndex, pathIndex, uint32(sf.LanguageVariant), uint32(sf.ScriptKind), referencedFilesOffset, typeRefDirectivesOffset, libRefDirectivesOffset, noStructuredData, noStructuredData, noStructuredData, 0)
|
||||
}
|
||||
|
||||
func recordExtendedData_TemplateHead(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
|
||||
n := node.AsTemplateHead()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags))
|
||||
}
|
||||
|
||||
func recordExtendedData_TemplateMiddle(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
|
||||
n := node.AsTemplateMiddle()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags))
|
||||
}
|
||||
|
||||
func recordExtendedData_TemplateTail(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
|
||||
n := node.AsTemplateTail()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags))
|
||||
}
|
||||
|
||||
func boolToByte(b bool) byte {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// hasModifiers returns true if the modifier list is non-nil and has at least one modifier.
|
||||
func hasModifiers(modifiers *ast.ModifierList) bool {
|
||||
return modifiers != nil && len(modifiers.Nodes) > 0
|
||||
}
|
||||
|
||||
// encodeFileReferences encodes a slice of FileReferences as a msgpack array of tuples
|
||||
// into the structured data buffer. Returns the byte offset into the buffer, or
|
||||
// noStructuredData (0xFFFFFFFF) if the slice is empty.
|
||||
func encodeFileReferences(refs []*ast.FileReference, positionMap *ast.PositionMap, buf *[]byte) uint32 {
|
||||
if len(refs) == 0 {
|
||||
return noStructuredData
|
||||
}
|
||||
offset := uint32(len(*buf))
|
||||
*buf = msgpackWriteArrayHeader(*buf, len(refs))
|
||||
for _, ref := range refs {
|
||||
// Each entry is a 5-element tuple: [pos, end, fileName, resolutionMode, preserve]
|
||||
*buf = msgpackWriteArrayHeader(*buf, 5)
|
||||
*buf = msgpackWriteUint(*buf, uint32(positionMap.UTF8ToUTF16(ref.Pos())))
|
||||
*buf = msgpackWriteUint(*buf, uint32(positionMap.UTF8ToUTF16(ref.End())))
|
||||
*buf = msgpackWriteString(*buf, ref.FileName)
|
||||
*buf = msgpackWriteUint(*buf, uint32(ref.ResolutionMode))
|
||||
*buf = msgpackWriteBool(*buf, ref.Preserve)
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
// encodeNodeIndexArray encodes a slice of LiteralLikeNodes as a msgpack array of
|
||||
// uint node indices. Returns the byte offset into the buffer, or noStructuredData
|
||||
// if the slice is empty.
|
||||
func encodeNodeIndexArray(nodes []*ast.LiteralLikeNode, indexMap map[*ast.Node]uint32, buf *[]byte) uint32 {
|
||||
if len(nodes) == 0 {
|
||||
return noStructuredData
|
||||
}
|
||||
offset := uint32(len(*buf))
|
||||
*buf = msgpackWriteArrayHeader(*buf, len(nodes))
|
||||
for _, node := range nodes {
|
||||
*buf = msgpackWriteUint(*buf, indexMap[node.AsNode()])
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
// encodeModuleAugmentations encodes a slice of ModuleName nodes as a msgpack array
|
||||
// of uint node indices. Returns the byte offset into the buffer, or noStructuredData
|
||||
// if the slice is empty.
|
||||
func encodeModuleAugmentations(nodes []*ast.ModuleName, indexMap map[*ast.Node]uint32, buf *[]byte) uint32 {
|
||||
if len(nodes) == 0 {
|
||||
return noStructuredData
|
||||
}
|
||||
offset := uint32(len(*buf))
|
||||
*buf = msgpackWriteArrayHeader(*buf, len(nodes))
|
||||
for _, node := range nodes {
|
||||
*buf = msgpackWriteUint(*buf, indexMap[node.AsNode()])
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
// encodeStringArray encodes a slice of strings as a msgpack array of strings.
|
||||
// Returns the byte offset into the buffer, or noStructuredData if the slice is empty.
|
||||
func encodeStringArray(strs []string, buf *[]byte) uint32 {
|
||||
if len(strs) == 0 {
|
||||
return noStructuredData
|
||||
}
|
||||
offset := uint32(len(*buf))
|
||||
*buf = msgpackWriteArrayHeader(*buf, len(strs))
|
||||
for _, s := range strs {
|
||||
*buf = msgpackWriteString(*buf, s)
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
// Minimal msgpack writers for the structured data section.
|
||||
|
||||
func msgpackWriteArrayHeader(buf []byte, length int) []byte {
|
||||
if length <= 0x0f {
|
||||
return append(buf, byte(0x90|length))
|
||||
}
|
||||
if length <= 0xffff {
|
||||
return append(buf, 0xdc, byte(length>>8), byte(length))
|
||||
}
|
||||
return append(buf, 0xdd, byte(length>>24), byte(length>>16), byte(length>>8), byte(length))
|
||||
}
|
||||
|
||||
func msgpackWriteUint(buf []byte, value uint32) []byte {
|
||||
if value <= 0x7f {
|
||||
return append(buf, byte(value))
|
||||
}
|
||||
if value <= 0xff {
|
||||
return append(buf, 0xcc, byte(value))
|
||||
}
|
||||
if value <= 0xffff {
|
||||
return append(buf, 0xcd, byte(value>>8), byte(value))
|
||||
}
|
||||
return append(buf, 0xce, byte(value>>24), byte(value>>16), byte(value>>8), byte(value))
|
||||
}
|
||||
|
||||
func msgpackWriteString(buf []byte, s string) []byte {
|
||||
n := len(s)
|
||||
if n <= 0x1f {
|
||||
buf = append(buf, byte(0xa0|n))
|
||||
} else if n <= 0xff {
|
||||
buf = append(buf, 0xd9, byte(n))
|
||||
} else if n <= 0xffff {
|
||||
buf = append(buf, 0xda, byte(n>>8), byte(n))
|
||||
} else {
|
||||
buf = append(buf, 0xdb, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
|
||||
}
|
||||
return append(buf, s...)
|
||||
}
|
||||
|
||||
func msgpackWriteBool(buf []byte, value bool) []byte {
|
||||
if value {
|
||||
return append(buf, 0xc3)
|
||||
}
|
||||
return append(buf, 0xc2)
|
||||
}
|
||||
|
||||
// Hand-written commonData encoding functions for nodes whose non-bool data
|
||||
// members cannot be automatically encoded by the generator. Each function
|
||||
// packs relevant fields into the 6-bit commonData area (bits 24-29) of the
|
||||
// 32-bit node data word.
|
||||
|
||||
func getNodeCommonData_SyntheticExpression(_ *ast.Node) uint32 {
|
||||
// SyntheticExpression is an internal compiler node that is never part of a parsed AST.
|
||||
// It should never be encoded.
|
||||
panic("SyntheticExpression should never be encoded")
|
||||
}
|
||||
|
||||
// Hand-written extended data encoding functions for literal nodes that were
|
||||
// previously string-type but whose TokenFlags/TemplateFlags cannot fit in 6 bits.
|
||||
|
||||
func recordExtendedData_StringLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
|
||||
n := node.AsStringLiteral()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
|
||||
}
|
||||
|
||||
func recordExtendedData_NumericLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
|
||||
n := node.AsNumericLiteral()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
|
||||
}
|
||||
|
||||
func recordExtendedData_BigIntLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
|
||||
n := node.AsBigIntLiteral()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
|
||||
}
|
||||
|
||||
func recordExtendedData_RegularExpressionLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
|
||||
n := node.AsRegularExpressionLiteral()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
|
||||
}
|
||||
|
||||
func recordExtendedData_NoSubstitutionTemplateLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
|
||||
n := node.AsNoSubstitutionTemplateLiteral()
|
||||
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
|
||||
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TemplateFlags))
|
||||
}
|
||||
707
tools/tsgo/internal/api/encoder/encoder_generated.go
Normal file
707
tools/tsgo/internal/api/encoder/encoder_generated.go
Normal file
@@ -0,0 +1,707 @@
|
||||
// Code generated by _scripts/generate-encoder.ts. DO NOT EDIT.
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
)
|
||||
|
||||
func getNodeDataType(node *ast.Node) uint32 {
|
||||
switch node.Kind {
|
||||
case ast.KindIdentifier,
|
||||
ast.KindPrivateIdentifier,
|
||||
ast.KindJsxText,
|
||||
ast.KindJSDocText,
|
||||
ast.KindJSDocLink,
|
||||
ast.KindJSDocLinkPlain,
|
||||
ast.KindJSDocLinkCode:
|
||||
return NodeDataTypeString
|
||||
case ast.KindStringLiteral,
|
||||
ast.KindNumericLiteral,
|
||||
ast.KindBigIntLiteral,
|
||||
ast.KindRegularExpressionLiteral,
|
||||
ast.KindNoSubstitutionTemplateLiteral,
|
||||
ast.KindTemplateHead,
|
||||
ast.KindTemplateMiddle,
|
||||
ast.KindTemplateTail,
|
||||
ast.KindSourceFile:
|
||||
return NodeDataTypeExtendedData
|
||||
default:
|
||||
return NodeDataTypeChildren
|
||||
}
|
||||
}
|
||||
|
||||
func getChildrenPropertyMask(node *ast.Node) uint8 {
|
||||
switch node.Kind {
|
||||
case ast.KindQualifiedName:
|
||||
n := node.AsQualifiedName()
|
||||
return (boolToByte(n.Left != nil) << 0) | (boolToByte(n.Right != nil) << 1)
|
||||
case ast.KindComputedPropertyName:
|
||||
n := node.AsComputedPropertyName()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindDecorator:
|
||||
n := node.AsDecorator()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindIfStatement:
|
||||
n := node.AsIfStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThenStatement != nil) << 1) | (boolToByte(n.ElseStatement != nil) << 2)
|
||||
case ast.KindDoStatement:
|
||||
n := node.AsDoStatement()
|
||||
return (boolToByte(n.Statement != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
||||
case ast.KindWhileStatement:
|
||||
n := node.AsWhileStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
|
||||
case ast.KindForStatement:
|
||||
n := node.AsForStatement()
|
||||
return (boolToByte(n.Initializer != nil) << 0) | (boolToByte(n.Condition != nil) << 1) | (boolToByte(n.Incrementor != nil) << 2) | (boolToByte(n.Statement != nil) << 3)
|
||||
case ast.KindForInStatement, ast.KindForOfStatement:
|
||||
n := node.AsForInOrOfStatement()
|
||||
return (boolToByte(n.AwaitModifier != nil) << 0) | (boolToByte(n.Initializer != nil) << 1) | (boolToByte(n.Expression != nil) << 2) | (boolToByte(n.Statement != nil) << 3)
|
||||
case ast.KindBreakStatement:
|
||||
n := node.AsBreakStatement()
|
||||
return (boolToByte(n.Label != nil) << 0)
|
||||
case ast.KindContinueStatement:
|
||||
n := node.AsContinueStatement()
|
||||
return (boolToByte(n.Label != nil) << 0)
|
||||
case ast.KindReturnStatement:
|
||||
n := node.AsReturnStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindWithStatement:
|
||||
n := node.AsWithStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
|
||||
case ast.KindSwitchStatement:
|
||||
n := node.AsSwitchStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.CaseBlock != nil) << 1)
|
||||
case ast.KindCaseBlock:
|
||||
n := node.AsCaseBlock()
|
||||
return (boolToByte(n.Clauses != nil) << 0)
|
||||
case ast.KindCaseClause, ast.KindDefaultClause:
|
||||
n := node.AsCaseOrDefaultClause()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statements != nil) << 1)
|
||||
case ast.KindThrowStatement:
|
||||
n := node.AsThrowStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindTryStatement:
|
||||
n := node.AsTryStatement()
|
||||
return (boolToByte(n.TryBlock != nil) << 0) | (boolToByte(n.CatchClause != nil) << 1) | (boolToByte(n.FinallyBlock != nil) << 2)
|
||||
case ast.KindCatchClause:
|
||||
n := node.AsCatchClause()
|
||||
return (boolToByte(n.VariableDeclaration != nil) << 0) | (boolToByte(n.Block != nil) << 1)
|
||||
case ast.KindLabeledStatement:
|
||||
n := node.AsLabeledStatement()
|
||||
return (boolToByte(n.Label != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
|
||||
case ast.KindExpressionStatement:
|
||||
n := node.AsExpressionStatement()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindBlock:
|
||||
n := node.AsBlock()
|
||||
return (boolToByte(n.Statements != nil) << 0)
|
||||
case ast.KindVariableStatement:
|
||||
n := node.AsVariableStatement()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.DeclarationList != nil) << 1)
|
||||
case ast.KindVariableDeclaration:
|
||||
n := node.AsVariableDeclaration()
|
||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.ExclamationToken != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
|
||||
case ast.KindVariableDeclarationList:
|
||||
n := node.AsVariableDeclarationList()
|
||||
return (boolToByte(n.Declarations != nil) << 0)
|
||||
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
|
||||
n := node.AsBindingPattern()
|
||||
return (boolToByte(n.Elements != nil) << 0)
|
||||
case ast.KindParameter:
|
||||
n := node.AsParameterDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.DotDotDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Initializer != nil) << 5)
|
||||
case ast.KindBindingElement:
|
||||
n := node.AsBindingElement()
|
||||
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.PropertyName != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
|
||||
case ast.KindMissingDeclaration:
|
||||
n := node.AsMissingDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0)
|
||||
case ast.KindFunctionDeclaration:
|
||||
n := node.AsFunctionDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6)
|
||||
case ast.KindClassDeclaration:
|
||||
n := node.AsClassDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
|
||||
case ast.KindClassExpression:
|
||||
n := node.AsClassExpression()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
|
||||
case ast.KindHeritageClause:
|
||||
n := node.AsHeritageClause()
|
||||
return (boolToByte(n.Types != nil) << 0)
|
||||
case ast.KindInterfaceDeclaration:
|
||||
n := node.AsInterfaceDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
|
||||
case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
|
||||
n := node.AsTypeAliasDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Type != nil) << 3)
|
||||
case ast.KindEnumMember:
|
||||
n := node.AsEnumMember()
|
||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1)
|
||||
case ast.KindEnumDeclaration:
|
||||
n := node.AsEnumDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Members != nil) << 2)
|
||||
case ast.KindModuleBlock:
|
||||
n := node.AsModuleBlock()
|
||||
return (boolToByte(n.Statements != nil) << 0)
|
||||
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
|
||||
n := node.AsImportDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
|
||||
case ast.KindExternalModuleReference:
|
||||
n := node.AsExternalModuleReference()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindNamespaceImport:
|
||||
n := node.AsNamespaceImport()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindNamedImports:
|
||||
n := node.AsNamedImports()
|
||||
return (boolToByte(n.Elements != nil) << 0)
|
||||
case ast.KindExportAssignment:
|
||||
n := node.AsExportAssignment()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Type != nil) << 1) | (boolToByte(n.Expression != nil) << 2)
|
||||
case ast.KindNamespaceExportDeclaration:
|
||||
n := node.AsNamespaceExportDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1)
|
||||
case ast.KindNamespaceExport:
|
||||
n := node.AsNamespaceExport()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindNamedExports:
|
||||
n := node.AsNamedExports()
|
||||
return (boolToByte(n.Elements != nil) << 0)
|
||||
case ast.KindExportSpecifier:
|
||||
n := node.AsExportSpecifier()
|
||||
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
|
||||
case ast.KindCallSignature:
|
||||
n := node.AsCallSignatureDeclaration()
|
||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
||||
case ast.KindConstructSignature:
|
||||
n := node.AsConstructSignatureDeclaration()
|
||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
||||
case ast.KindConstructor:
|
||||
n := node.AsConstructorDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Body != nil) << 4)
|
||||
case ast.KindGetAccessor:
|
||||
n := node.AsGetAccessorDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5)
|
||||
case ast.KindSetAccessor:
|
||||
n := node.AsSetAccessorDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5)
|
||||
case ast.KindIndexSignature:
|
||||
n := node.AsIndexSignatureDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
||||
case ast.KindMethodSignature:
|
||||
n := node.AsMethodSignatureDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5)
|
||||
case ast.KindMethodDeclaration:
|
||||
n := node.AsMethodDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.PostfixToken != nil) << 3) | (boolToByte(n.TypeParameters != nil) << 4) | (boolToByte(n.Parameters != nil) << 5) | (boolToByte(n.Type != nil) << 6) | (boolToByte(n.Body != nil) << 7)
|
||||
case ast.KindPropertySignature:
|
||||
n := node.AsPropertySignatureDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
|
||||
case ast.KindPropertyDeclaration:
|
||||
n := node.AsPropertyDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
|
||||
case ast.KindClassStaticBlockDeclaration:
|
||||
n := node.AsClassStaticBlockDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Body != nil) << 1)
|
||||
case ast.KindBinaryExpression:
|
||||
n := node.AsBinaryExpression()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Left != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.OperatorToken != nil) << 3) | (boolToByte(n.Right != nil) << 4)
|
||||
case ast.KindPrefixUnaryExpression:
|
||||
n := node.AsPrefixUnaryExpression()
|
||||
return (boolToByte(n.Operand != nil) << 0)
|
||||
case ast.KindPostfixUnaryExpression:
|
||||
n := node.AsPostfixUnaryExpression()
|
||||
return (boolToByte(n.Operand != nil) << 0)
|
||||
case ast.KindYieldExpression:
|
||||
n := node.AsYieldExpression()
|
||||
return (boolToByte(n.AsteriskToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
||||
case ast.KindArrowFunction:
|
||||
n := node.AsArrowFunction()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsGreaterThanToken != nil) << 4) | (boolToByte(n.Body != nil) << 5)
|
||||
case ast.KindFunctionExpression:
|
||||
n := node.AsFunctionExpression()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6)
|
||||
case ast.KindAsExpression:
|
||||
n := node.AsAsExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1)
|
||||
case ast.KindSatisfiesExpression:
|
||||
n := node.AsSatisfiesExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1)
|
||||
case ast.KindConditionalExpression:
|
||||
n := node.AsConditionalExpression()
|
||||
return (boolToByte(n.Condition != nil) << 0) | (boolToByte(n.QuestionToken != nil) << 1) | (boolToByte(n.WhenTrue != nil) << 2) | (boolToByte(n.ColonToken != nil) << 3) | (boolToByte(n.WhenFalse != nil) << 4)
|
||||
case ast.KindPropertyAccessExpression:
|
||||
n := node.AsPropertyAccessExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2)
|
||||
case ast.KindElementAccessExpression:
|
||||
n := node.AsElementAccessExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.ArgumentExpression != nil) << 2)
|
||||
case ast.KindCallExpression:
|
||||
n := node.AsCallExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Arguments != nil) << 3)
|
||||
case ast.KindNewExpression:
|
||||
n := node.AsNewExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Arguments != nil) << 2)
|
||||
case ast.KindMetaProperty:
|
||||
n := node.AsMetaProperty()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindNonNullExpression:
|
||||
n := node.AsNonNullExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindSpreadElement:
|
||||
n := node.AsSpreadElement()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindTemplateExpression:
|
||||
n := node.AsTemplateExpression()
|
||||
return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1)
|
||||
case ast.KindTemplateSpan:
|
||||
n := node.AsTemplateSpan()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Literal != nil) << 1)
|
||||
case ast.KindTaggedTemplateExpression:
|
||||
n := node.AsTaggedTemplateExpression()
|
||||
return (boolToByte(n.Tag != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Template != nil) << 3)
|
||||
case ast.KindParenthesizedExpression:
|
||||
n := node.AsParenthesizedExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindArrayLiteralExpression:
|
||||
n := node.AsArrayLiteralExpression()
|
||||
return (boolToByte(n.Elements != nil) << 0)
|
||||
case ast.KindObjectLiteralExpression:
|
||||
n := node.AsObjectLiteralExpression()
|
||||
return (boolToByte(n.Properties != nil) << 0)
|
||||
case ast.KindSpreadAssignment:
|
||||
n := node.AsSpreadAssignment()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindPropertyAssignment:
|
||||
n := node.AsPropertyAssignment()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
|
||||
case ast.KindShorthandPropertyAssignment:
|
||||
n := node.AsShorthandPropertyAssignment()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsToken != nil) << 4) | (boolToByte(n.ObjectAssignmentInitializer != nil) << 5)
|
||||
case ast.KindDeleteExpression:
|
||||
n := node.AsDeleteExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindTypeOfExpression:
|
||||
n := node.AsTypeOfExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindVoidExpression:
|
||||
n := node.AsVoidExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindAwaitExpression:
|
||||
n := node.AsAwaitExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindTypeAssertionExpression:
|
||||
n := node.AsTypeAssertion()
|
||||
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
||||
case ast.KindUnionType:
|
||||
n := node.AsUnionTypeNode()
|
||||
return (boolToByte(n.Types != nil) << 0)
|
||||
case ast.KindIntersectionType:
|
||||
n := node.AsIntersectionTypeNode()
|
||||
return (boolToByte(n.Types != nil) << 0)
|
||||
case ast.KindConditionalType:
|
||||
n := node.AsConditionalTypeNode()
|
||||
return (boolToByte(n.CheckType != nil) << 0) | (boolToByte(n.ExtendsType != nil) << 1) | (boolToByte(n.TrueType != nil) << 2) | (boolToByte(n.FalseType != nil) << 3)
|
||||
case ast.KindTypeOperator:
|
||||
n := node.AsTypeOperatorNode()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindInferType:
|
||||
n := node.AsInferTypeNode()
|
||||
return (boolToByte(n.TypeParameter != nil) << 0)
|
||||
case ast.KindArrayType:
|
||||
n := node.AsArrayTypeNode()
|
||||
return (boolToByte(n.ElementType != nil) << 0)
|
||||
case ast.KindIndexedAccessType:
|
||||
n := node.AsIndexedAccessTypeNode()
|
||||
return (boolToByte(n.ObjectType != nil) << 0) | (boolToByte(n.IndexType != nil) << 1)
|
||||
case ast.KindTypeReference:
|
||||
n := node.AsTypeReferenceNode()
|
||||
return (boolToByte(n.TypeName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
|
||||
case ast.KindExpressionWithTypeArguments:
|
||||
n := node.AsExpressionWithTypeArguments()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
|
||||
case ast.KindLiteralType:
|
||||
n := node.AsLiteralTypeNode()
|
||||
return (boolToByte(n.Literal != nil) << 0)
|
||||
case ast.KindTypePredicate:
|
||||
n := node.AsTypePredicateNode()
|
||||
return (boolToByte(n.AssertsModifier != nil) << 0) | (boolToByte(n.ParameterName != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
||||
case ast.KindImportAttribute:
|
||||
n := node.AsImportAttribute()
|
||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Value != nil) << 1)
|
||||
case ast.KindImportAttributes:
|
||||
n := node.AsImportAttributes()
|
||||
return (boolToByte(n.Attributes != nil) << 0)
|
||||
case ast.KindTypeQuery:
|
||||
n := node.AsTypeQueryNode()
|
||||
return (boolToByte(n.ExprName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
|
||||
case ast.KindMappedType:
|
||||
n := node.AsMappedTypeNode()
|
||||
return (boolToByte(n.ReadonlyToken != nil) << 0) | (boolToByte(n.TypeParameter != nil) << 1) | (boolToByte(n.NameType != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Members != nil) << 5)
|
||||
case ast.KindTypeLiteral:
|
||||
n := node.AsTypeLiteralNode()
|
||||
return (boolToByte(n.Members != nil) << 0)
|
||||
case ast.KindTupleType:
|
||||
n := node.AsTupleTypeNode()
|
||||
return (boolToByte(n.Elements != nil) << 0)
|
||||
case ast.KindNamedTupleMember:
|
||||
n := node.AsNamedTupleMember()
|
||||
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.QuestionToken != nil) << 2) | (boolToByte(n.Type != nil) << 3)
|
||||
case ast.KindOptionalType:
|
||||
n := node.AsOptionalTypeNode()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindRestType:
|
||||
n := node.AsRestTypeNode()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindParenthesizedType:
|
||||
n := node.AsParenthesizedTypeNode()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindFunctionType:
|
||||
n := node.AsFunctionTypeNode()
|
||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
||||
case ast.KindConstructorType:
|
||||
n := node.AsConstructorTypeNode()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3)
|
||||
case ast.KindTemplateLiteralType:
|
||||
n := node.AsTemplateLiteralTypeNode()
|
||||
return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1)
|
||||
case ast.KindTemplateLiteralTypeSpan:
|
||||
n := node.AsTemplateLiteralTypeSpan()
|
||||
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Literal != nil) << 1)
|
||||
case ast.KindSyntheticExpression:
|
||||
n := node.AsSyntheticExpression()
|
||||
return (boolToByte(n.TupleNameSource != nil) << 0)
|
||||
case ast.KindPartiallyEmittedExpression:
|
||||
n := node.AsPartiallyEmittedExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindJsxElement:
|
||||
n := node.AsJsxElement()
|
||||
return (boolToByte(n.OpeningElement != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingElement != nil) << 2)
|
||||
case ast.KindJsxAttributes:
|
||||
n := node.AsJsxAttributes()
|
||||
return (boolToByte(n.Properties != nil) << 0)
|
||||
case ast.KindJsxNamespacedName:
|
||||
n := node.AsJsxNamespacedName()
|
||||
return (boolToByte(n.Namespace != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
|
||||
case ast.KindJsxOpeningElement:
|
||||
n := node.AsJsxOpeningElement()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2)
|
||||
case ast.KindJsxSelfClosingElement:
|
||||
n := node.AsJsxSelfClosingElement()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2)
|
||||
case ast.KindJsxFragment:
|
||||
n := node.AsJsxFragment()
|
||||
return (boolToByte(n.OpeningFragment != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingFragment != nil) << 2)
|
||||
case ast.KindJsxAttribute:
|
||||
n := node.AsJsxAttribute()
|
||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1)
|
||||
case ast.KindJsxSpreadAttribute:
|
||||
n := node.AsJsxSpreadAttribute()
|
||||
return (boolToByte(n.Expression != nil) << 0)
|
||||
case ast.KindJsxClosingElement:
|
||||
n := node.AsJsxClosingElement()
|
||||
return (boolToByte(n.TagName != nil) << 0)
|
||||
case ast.KindJsxExpression:
|
||||
n := node.AsJsxExpression()
|
||||
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
||||
case ast.KindSyntaxList:
|
||||
n := node.AsSyntaxList()
|
||||
return (boolToByte(len(n.Children) > 0) << 0)
|
||||
case ast.KindJSDoc:
|
||||
n := node.AsJSDoc()
|
||||
return (boolToByte(n.Comment != nil) << 0) | (boolToByte(n.Tags != nil) << 1)
|
||||
case ast.KindJSDocTypeExpression:
|
||||
n := node.AsJSDocTypeExpression()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindJSDocNonNullableType:
|
||||
n := node.AsJSDocNonNullableType()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindJSDocNullableType:
|
||||
n := node.AsJSDocNullableType()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindJSDocVariadicType:
|
||||
n := node.AsJSDocVariadicType()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindJSDocOptionalType:
|
||||
n := node.AsJSDocOptionalType()
|
||||
return (boolToByte(n.Type != nil) << 0)
|
||||
case ast.KindJSDocTypeTag:
|
||||
n := node.AsJSDocTypeTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocUnknownTag:
|
||||
n := node.AsJSDocUnknownTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocTemplateTag:
|
||||
n := node.AsJSDocTemplateTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Constraint != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
||||
case ast.KindJSDocReturnTag:
|
||||
n := node.AsJSDocReturnTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocPublicTag:
|
||||
n := node.AsJSDocPublicTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocPrivateTag:
|
||||
n := node.AsJSDocPrivateTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocProtectedTag:
|
||||
n := node.AsJSDocProtectedTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocReadonlyTag:
|
||||
n := node.AsJSDocReadonlyTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocOverrideTag:
|
||||
n := node.AsJSDocOverrideTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocDeprecatedTag:
|
||||
n := node.AsJSDocDeprecatedTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
||||
case ast.KindJSDocSeeTag:
|
||||
n := node.AsJSDocSeeTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.NameExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocImplementsTag:
|
||||
n := node.AsJSDocImplementsTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocAugmentsTag:
|
||||
n := node.AsJSDocAugmentsTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocSatisfiesTag:
|
||||
n := node.AsJSDocSatisfiesTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocThrowsTag:
|
||||
n := node.AsJSDocThrowsTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocThisTag:
|
||||
n := node.AsJSDocThisTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocImportTag:
|
||||
n := node.AsJSDocImportTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3) | (boolToByte(n.Comment != nil) << 4)
|
||||
case ast.KindJSDocCallbackTag:
|
||||
n := node.AsJSDocCallbackTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
||||
case ast.KindJSDocOverloadTag:
|
||||
n := node.AsJSDocOverloadTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
||||
case ast.KindJSDocTypedefTag:
|
||||
n := node.AsJSDocTypedefTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
||||
case ast.KindJSDocSignature:
|
||||
n := node.AsJSDocSignature()
|
||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
||||
case ast.KindJSDocNameReference:
|
||||
n := node.AsJSDocNameReference()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindModuleDeclaration:
|
||||
n := node.AsModuleDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Body != nil) << 2)
|
||||
case ast.KindImportEqualsDeclaration:
|
||||
n := node.AsImportEqualsDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.ModuleReference != nil) << 2)
|
||||
case ast.KindExportDeclaration:
|
||||
n := node.AsExportDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.ExportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
|
||||
case ast.KindImportType:
|
||||
n := node.AsImportTypeNode()
|
||||
return (boolToByte(n.Argument != nil) << 0) | (boolToByte(n.Attributes != nil) << 1) | (boolToByte(n.Qualifier != nil) << 2) | (boolToByte(n.TypeArguments != nil) << 3)
|
||||
case ast.KindImportClause:
|
||||
n := node.AsImportClause()
|
||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.NamedBindings != nil) << 1)
|
||||
case ast.KindImportSpecifier:
|
||||
n := node.AsImportSpecifier()
|
||||
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
|
||||
case ast.KindJSDocLink:
|
||||
n := node.AsJSDocLink()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindJSDocLinkPlain:
|
||||
n := node.AsJSDocLinkPlain()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindJSDocLinkCode:
|
||||
n := node.AsJSDocLinkCode()
|
||||
return (boolToByte(n.Name() != nil) << 0)
|
||||
case ast.KindTypeParameter:
|
||||
n := node.AsTypeParameterDeclaration()
|
||||
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Constraint != nil) << 2) | (boolToByte(n.Expression != nil) << 3) | (boolToByte(n.DefaultType != nil) << 4)
|
||||
case ast.KindSyntheticReferenceExpression:
|
||||
n := node.AsSyntheticReferenceExpression()
|
||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThisArg != nil) << 1)
|
||||
case ast.KindJSDocTypeLiteral:
|
||||
n := node.AsJSDocTypeLiteral()
|
||||
return (boolToByte(len(n.JSDocPropertyTags) > 0) << 0)
|
||||
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
|
||||
n := node.AsJSDocParameterOrPropertyTag()
|
||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeExpression != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getNodeCommonData(node *ast.Node) uint32 {
|
||||
switch node.Kind {
|
||||
case ast.KindBlock:
|
||||
n := node.AsBlock()
|
||||
return uint32(boolToByte(n.MultiLine)) << 24
|
||||
case ast.KindHeritageClause:
|
||||
n := node.AsHeritageClause()
|
||||
var tokenIdx uint32
|
||||
switch n.Token {
|
||||
case ast.KindImplementsKeyword:
|
||||
tokenIdx = 1
|
||||
}
|
||||
return tokenIdx << 24
|
||||
case ast.KindExportAssignment:
|
||||
n := node.AsExportAssignment()
|
||||
return uint32(boolToByte(n.IsExportEquals)) << 24
|
||||
case ast.KindExportSpecifier:
|
||||
n := node.AsExportSpecifier()
|
||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
||||
case ast.KindPrefixUnaryExpression:
|
||||
n := node.AsPrefixUnaryExpression()
|
||||
var operatorIdx uint32
|
||||
switch n.Operator {
|
||||
case ast.KindMinusToken:
|
||||
operatorIdx = 1
|
||||
case ast.KindTildeToken:
|
||||
operatorIdx = 2
|
||||
case ast.KindExclamationToken:
|
||||
operatorIdx = 3
|
||||
case ast.KindPlusPlusToken:
|
||||
operatorIdx = 4
|
||||
case ast.KindMinusMinusToken:
|
||||
operatorIdx = 5
|
||||
}
|
||||
return operatorIdx << 24
|
||||
case ast.KindPostfixUnaryExpression:
|
||||
n := node.AsPostfixUnaryExpression()
|
||||
var operatorIdx uint32
|
||||
switch n.Operator {
|
||||
case ast.KindMinusMinusToken:
|
||||
operatorIdx = 1
|
||||
}
|
||||
return operatorIdx << 24
|
||||
case ast.KindMetaProperty:
|
||||
n := node.AsMetaProperty()
|
||||
var keywordTokenIdx uint32
|
||||
switch n.KeywordToken {
|
||||
case ast.KindNewKeyword:
|
||||
keywordTokenIdx = 1
|
||||
}
|
||||
return keywordTokenIdx << 24
|
||||
case ast.KindArrayLiteralExpression:
|
||||
n := node.AsArrayLiteralExpression()
|
||||
return uint32(boolToByte(n.MultiLine)) << 24
|
||||
case ast.KindObjectLiteralExpression:
|
||||
n := node.AsObjectLiteralExpression()
|
||||
return uint32(boolToByte(n.MultiLine)) << 24
|
||||
case ast.KindTypeOperator:
|
||||
n := node.AsTypeOperatorNode()
|
||||
var operatorIdx uint32
|
||||
switch n.Operator {
|
||||
case ast.KindReadonlyKeyword:
|
||||
operatorIdx = 1
|
||||
case ast.KindUniqueKeyword:
|
||||
operatorIdx = 2
|
||||
}
|
||||
return operatorIdx << 24
|
||||
case ast.KindImportAttributes:
|
||||
n := node.AsImportAttributes()
|
||||
var tokenIdx uint32
|
||||
switch n.Token {
|
||||
case ast.KindAssertKeyword:
|
||||
tokenIdx = 1
|
||||
}
|
||||
return uint32(boolToByte(n.MultiLine))<<24 | tokenIdx<<25
|
||||
case ast.KindSyntheticExpression:
|
||||
return getNodeCommonData_SyntheticExpression(node)
|
||||
case ast.KindJsxText:
|
||||
n := node.AsJsxText()
|
||||
return uint32(boolToByte(n.ContainsOnlyTriviaWhiteSpaces)) << 24
|
||||
case ast.KindModuleDeclaration:
|
||||
n := node.AsModuleDeclaration()
|
||||
var keywordIdx uint32
|
||||
switch n.Keyword {
|
||||
case ast.KindNamespaceKeyword:
|
||||
keywordIdx = 1
|
||||
}
|
||||
return keywordIdx << 24
|
||||
case ast.KindImportEqualsDeclaration:
|
||||
n := node.AsImportEqualsDeclaration()
|
||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
||||
case ast.KindExportDeclaration:
|
||||
n := node.AsExportDeclaration()
|
||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
||||
case ast.KindImportType:
|
||||
n := node.AsImportTypeNode()
|
||||
return uint32(boolToByte(n.IsTypeOf)) << 24
|
||||
case ast.KindImportClause:
|
||||
n := node.AsImportClause()
|
||||
var phaseModifierIdx uint32
|
||||
switch n.PhaseModifier {
|
||||
case ast.KindTypeKeyword:
|
||||
phaseModifierIdx = 1
|
||||
case ast.KindDeferKeyword:
|
||||
phaseModifierIdx = 2
|
||||
}
|
||||
return phaseModifierIdx << 24
|
||||
case ast.KindImportSpecifier:
|
||||
n := node.AsImportSpecifier()
|
||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
||||
case ast.KindJSDocTypeLiteral:
|
||||
n := node.AsJSDocTypeLiteral()
|
||||
return uint32(boolToByte(n.IsArrayType)) << 24
|
||||
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
|
||||
n := node.AsJSDocParameterOrPropertyTag()
|
||||
return uint32(boolToByte(n.IsBracketed))<<24 | uint32(boolToByte(n.IsNameFirst))<<25
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {
|
||||
switch node.Kind {
|
||||
case ast.KindIdentifier:
|
||||
return strs.add(node.AsIdentifier().Text, node.Kind, node.Pos(), node.End())
|
||||
case ast.KindPrivateIdentifier:
|
||||
return strs.add(node.AsPrivateIdentifier().Text, node.Kind, node.Pos(), node.End())
|
||||
case ast.KindJsxText:
|
||||
return strs.add(node.AsJsxText().Text, node.Kind, node.Pos(), node.End())
|
||||
case ast.KindJSDocText:
|
||||
return strs.add(node.AsJSDocText().Text(), node.Kind, node.Pos(), node.End())
|
||||
case ast.KindJSDocLink:
|
||||
return strs.add(node.AsJSDocLink().Text(), node.Kind, node.Pos(), node.End())
|
||||
case ast.KindJSDocLinkPlain:
|
||||
return strs.add(node.AsJSDocLinkPlain().Text(), node.Kind, node.Pos(), node.End())
|
||||
case ast.KindJSDocLinkCode:
|
||||
return strs.add(node.AsJSDocLinkCode().Text(), node.Kind, node.Pos(), node.End())
|
||||
default:
|
||||
panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))
|
||||
}
|
||||
}
|
||||
|
||||
func recordExtendedData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {
|
||||
offset := uint32(len(*extendedData))
|
||||
switch node.Kind {
|
||||
case ast.KindStringLiteral:
|
||||
recordExtendedData_StringLiteral(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindNumericLiteral:
|
||||
recordExtendedData_NumericLiteral(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindBigIntLiteral:
|
||||
recordExtendedData_BigIntLiteral(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindRegularExpressionLiteral:
|
||||
recordExtendedData_RegularExpressionLiteral(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindNoSubstitutionTemplateLiteral:
|
||||
recordExtendedData_NoSubstitutionTemplateLiteral(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindTemplateHead:
|
||||
recordExtendedData_TemplateHead(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindTemplateMiddle:
|
||||
recordExtendedData_TemplateMiddle(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindTemplateTail:
|
||||
recordExtendedData_TemplateTail(node, strs, positionMap, extendedData, structuredData)
|
||||
case ast.KindSourceFile:
|
||||
recordExtendedData_SourceFile(node, strs, positionMap, extendedData, structuredData)
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind))
|
||||
}
|
||||
return offset
|
||||
}
|
||||
161
tools/tsgo/internal/api/encoder/encoder_test.go
Normal file
161
tools/tsgo/internal/api/encoder/encoder_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package encoder_test
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/api/encoder"
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/parser"
|
||||
"github.com/microsoft/typescript-go/internal/repo"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/baseline"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestEncodeSourceFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/test.ts",
|
||||
Path: "/test.ts",
|
||||
}, "import { bar } from \"bar\";\nexport function foo<T, U>(a: string, b: string): any {}\nfoo();", core.ScriptKindTS)
|
||||
t.Run("baseline", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf, _, err := encoder.EncodeSourceFile(sourceFile)
|
||||
assert.NilError(t, err)
|
||||
|
||||
str := formatEncodedSourceFile(buf)
|
||||
baseline.Run(t, "encodeSourceFile.txt", str, baseline.Options{
|
||||
Subfolder: "api",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestEncodeSourceFileWithUnicodeEscapes(t *testing.T) {
|
||||
t.Parallel()
|
||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/test.ts",
|
||||
Path: "/test.ts",
|
||||
}, `let a = "😃"; let b = "\ud83d\ude03"; let c = "\udc00\ud83d\ude03"; let d = "\ud83d\ud83d\ude03"`, core.ScriptKindTS)
|
||||
t.Run("baseline", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
buf, _, err := encoder.EncodeSourceFile(sourceFile)
|
||||
assert.NilError(t, err)
|
||||
|
||||
str := formatEncodedSourceFile(buf)
|
||||
baseline.Run(t, "encodeSourceFileWithUnicodeEscapes.txt", str, baseline.Options{
|
||||
Subfolder: "api",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildNodeIndexTableMatchesEncode(t *testing.T) {
|
||||
t.Parallel()
|
||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/test.ts",
|
||||
Path: "/test.ts",
|
||||
}, "import { bar } from \"bar\";\nexport function foo<T, U>(a: string, b: string): any {}\nfoo();", core.ScriptKindTS)
|
||||
|
||||
_, encodeTable, err := encoder.EncodeSourceFile(sourceFile)
|
||||
assert.NilError(t, err)
|
||||
|
||||
buildTable := encoder.BuildNodeIndexTable(sourceFile)
|
||||
|
||||
// Both tables should produce identical Nodes slices
|
||||
assert.Equal(t, len(buildTable.Nodes), len(encodeTable.Nodes), "Nodes slice length mismatch")
|
||||
|
||||
// Every index should map to the same node
|
||||
for i := range encodeTable.Nodes {
|
||||
assert.Equal(t, buildTable.Nodes[i], encodeTable.Nodes[i], "node mismatch at index %d", i)
|
||||
}
|
||||
|
||||
// GetIndex on both tables should agree for every non-nil node
|
||||
for i, node := range encodeTable.Nodes {
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
encIdx := encodeTable.GetIndex(node)
|
||||
buildIdx := buildTable.GetIndex(node)
|
||||
assert.Equal(t, encIdx, uint32(i), "encodeTable.GetIndex mismatch at index %d, node kind=%s", i, node.Kind.String())
|
||||
assert.Equal(t, buildIdx, encIdx, "buildTable.GetIndex mismatch for node kind=%s", node.Kind.String())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeSourceFile(b *testing.B) {
|
||||
repo.SkipIfNoTypeScriptSubmodule(b)
|
||||
filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts")
|
||||
fileContent, err := os.ReadFile(filePath)
|
||||
assert.NilError(b, err)
|
||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/checker.ts",
|
||||
Path: "/checker.ts",
|
||||
}, string(fileContent), core.ScriptKindTS)
|
||||
|
||||
for b.Loop() {
|
||||
_, _, err := encoder.EncodeSourceFile(sourceFile)
|
||||
assert.NilError(b, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBuildNodeIndexTable(b *testing.B) {
|
||||
repo.SkipIfNoTypeScriptSubmodule(b)
|
||||
filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts")
|
||||
fileContent, err := os.ReadFile(filePath)
|
||||
assert.NilError(b, err)
|
||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
||||
FileName: "/checker.ts",
|
||||
Path: "/checker.ts",
|
||||
}, string(fileContent), core.ScriptKindTS)
|
||||
|
||||
for b.Loop() {
|
||||
encoder.BuildNodeIndexTable(sourceFile)
|
||||
}
|
||||
}
|
||||
|
||||
func readUint32(buf []byte, offset int) uint32 {
|
||||
return binary.LittleEndian.Uint32(buf[offset : offset+4])
|
||||
}
|
||||
|
||||
func formatEncodedSourceFile(encoded []byte) string {
|
||||
var result strings.Builder
|
||||
var getIndent func(parentIndex uint32) string
|
||||
offsetNodes := readUint32(encoded, encoder.HeaderOffsetNodes)
|
||||
offsetStringOffsets := readUint32(encoded, encoder.HeaderOffsetStringOffsets)
|
||||
offsetStrings := readUint32(encoded, encoder.HeaderOffsetStringData)
|
||||
getIndent = func(parentIndex uint32) string {
|
||||
if parentIndex == 0 {
|
||||
return ""
|
||||
}
|
||||
return " " + getIndent(readUint32(encoded, int(offsetNodes)+int(parentIndex)*encoder.NodeSize+encoder.NodeOffsetParent))
|
||||
}
|
||||
j := 1
|
||||
for i := int(offsetNodes) + encoder.NodeSize; i < len(encoded); i += encoder.NodeSize {
|
||||
kind := readUint32(encoded, i+encoder.NodeOffsetKind)
|
||||
pos := readUint32(encoded, i+encoder.NodeOffsetPos)
|
||||
end := readUint32(encoded, i+encoder.NodeOffsetEnd)
|
||||
parentIndex := readUint32(encoded, i+encoder.NodeOffsetParent)
|
||||
result.WriteString(getIndent(parentIndex))
|
||||
if kind == encoder.SyntaxKindNodeList {
|
||||
result.WriteString("NodeList")
|
||||
} else {
|
||||
result.WriteString(ast.Kind(kind).String())
|
||||
}
|
||||
data := readUint32(encoded, i+encoder.NodeOffsetData)
|
||||
dataType := data & encoder.NodeDataTypeMask
|
||||
if ast.Kind(kind) == ast.KindIdentifier || (dataType == encoder.NodeDataTypeString) {
|
||||
stringIndex := data & encoder.NodeDataStringIndexMask
|
||||
strStart := readUint32(encoded, int(offsetStringOffsets+stringIndex*4))
|
||||
strEnd := readUint32(encoded, int(offsetStringOffsets+stringIndex*4)+4)
|
||||
str := string(encoded[offsetStrings+strStart : offsetStrings+strEnd])
|
||||
result.WriteString(fmt.Sprintf(" \"%s\"", str))
|
||||
}
|
||||
fmt.Fprintf(&result, " [%d, %d), i=%d, next=%d", pos, end, j, encoded[i+encoder.NodeOffsetNext])
|
||||
result.WriteString("\n")
|
||||
j++
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
68
tools/tsgo/internal/api/encoder/stringtable.go
Normal file
68
tools/tsgo/internal/api/encoder/stringtable.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
)
|
||||
|
||||
type stringTable struct {
|
||||
fileText string
|
||||
otherStrings *strings.Builder
|
||||
// offsets are pos/end pairs
|
||||
offsets []uint32
|
||||
}
|
||||
|
||||
func newStringTable(fileText string, stringCount int) *stringTable {
|
||||
builder := &strings.Builder{}
|
||||
return &stringTable{
|
||||
fileText: fileText,
|
||||
otherStrings: builder,
|
||||
offsets: make([]uint32, 0, stringCount*2),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *stringTable) add(text string, kind ast.Kind, pos int, end int) uint32 {
|
||||
index := uint32(len(t.offsets))
|
||||
if kind == ast.KindSourceFile {
|
||||
t.offsets = append(t.offsets, uint32(pos), uint32(end))
|
||||
return index
|
||||
}
|
||||
length := len(text)
|
||||
if end-pos > 0 && end <= len(t.fileText) {
|
||||
// pos includes leading trivia, but we can usually infer the actual start of the
|
||||
// string from the kind and end
|
||||
endOffset := 0
|
||||
if kind == ast.KindStringLiteral || kind == ast.KindTemplateTail || kind == ast.KindNoSubstitutionTemplateLiteral {
|
||||
endOffset = 1
|
||||
}
|
||||
end = end - endOffset
|
||||
start := end - length
|
||||
fileSlice := t.fileText[start:end]
|
||||
if fileSlice == text {
|
||||
t.offsets = append(t.offsets, uint32(start), uint32(end))
|
||||
return index
|
||||
}
|
||||
}
|
||||
// no exact match, so we need to add it to the string table
|
||||
offset := len(t.fileText) + t.otherStrings.Len()
|
||||
t.otherStrings.WriteString(text)
|
||||
t.offsets = append(t.offsets, uint32(offset), uint32(offset+length))
|
||||
return index
|
||||
}
|
||||
|
||||
func (t *stringTable) encode() []byte {
|
||||
result := make([]byte, 0, t.encodedLength())
|
||||
result = appendUint32s(result, t.offsets...)
|
||||
result = append(result, t.fileText...)
|
||||
result = append(result, t.otherStrings.String()...)
|
||||
return result
|
||||
}
|
||||
|
||||
func (t *stringTable) stringLength() int {
|
||||
return len(t.fileText) + t.otherStrings.Len()
|
||||
}
|
||||
|
||||
func (t *stringTable) encodedLength() int {
|
||||
return len(t.offsets)*4 + len(t.fileText) + t.otherStrings.Len()
|
||||
}
|
||||
14
tools/tsgo/internal/api/encoder/testmain_test.go
Normal file
14
tools/tsgo/internal/api/encoder/testmain_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package encoder_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/baseline"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
core.ApplyDebugStackLimit()
|
||||
defer baseline.Track()()
|
||||
m.Run()
|
||||
}
|
||||
Reference in New Issue
Block a user