2025-10-15 10:12:44 +03:00

816 lines
42 KiB
Go

package encoder
import (
"encoding/binary"
"fmt"
"slices"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
)
const (
NodeOffsetKind = iota * 4
NodeOffsetPos
NodeOffsetEnd
NodeOffsetNext
NodeOffsetParent
NodeOffsetData
// 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
HeaderOffsetStringOffsets
HeaderOffsetStringData
HeaderOffsetExtendedData
HeaderOffsetNodes
HeaderSize
)
const (
ProtocolVersion uint8 = 1
)
// 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 six sections:
//
// | Section | Length | Description |
// | ------------------ | ------------------ | ---------------------------------------------------------------------------------------- |
// | Header | 20 bytes | Contains 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. |
// | Nodes | 24 bytes per node | Defines the AST structure of the file, with references to strings and extended data. |
//
// Header (20 bytes)
// -----------------
//
// The header contains the following fields:
//
// | Byte offset | Type | Field |
// | ----------- | ------ | ----------------------------------------- |
// | 0 | uint8 | Protocol version |
// | 1-4 | | Reserved |
// | 4-8 | uint32 | Byte offset to string offsets section |
// | 8-12 | uint32 | Byte offset to string data section |
// | 12-16 | uint32 | Byte offset to extended node data section |
// | 16-20 | 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. 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 `id` in the string offsets section |
//
// Nodes (24 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 |
//
// The first 24 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 booleans specific to the node type:
//
// | Node type | Bits 2-5 | Bit 1 | Bit 0 |
// | ------------------------- | -------- | ------------- | ------------------------------- |
// | `ImportSpecifier` | | | `isTypeOnly` |
// | `ImportClause` | | | `isTypeOnly` |
// | `ExportSpecifier` | | | `isTypeOnly` |
// | `ImportEqualsDeclaration` | | | `isTypeOnly` |
// | `ExportDeclaration` | | | `isTypeOnly` |
// | `ImportTypeNode` | | | `isTypeOf` |
// | `ExportAssignment` | | | `isExportEquals` |
// | `Block` | | | `multiline` |
// | `ArrayLiteralExpression` | | | `multiline` |
// | `ObjectLiteralExpression` | | | `multiline` |
// | `JsxText` | | | `containsOnlyTriviaWhiteSpaces` |
// | `JSDocTypeLiteral` | | | `isArrayType` |
// | `JsDocPropertyTag` | | `isNameFirst` | `isBracketed` |
// | `JsDocParameterTag` | | `isNameFirst` | `isBracketed` |
// | `VariableDeclarationList` | | is `const` | is `let` |
// | `ImportAttributes` | | is `assert` | `multiline` |
//
// 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.
func EncodeSourceFile(sourceFile *ast.SourceFile, id string) ([]byte, error) {
var parentIndex, nodeCount, prevIndex uint32
var extendedData []byte
strs := newStringTable(sourceFile.Text(), sourceFile.TextCount)
nodes := make([]byte, 0, (sourceFile.NodeCount+1)*NodeSize)
visitor := &ast.NodeVisitor{
Hooks: ast.NodeVisitorHooks{
VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
if nodeList == nil || len(nodeList.Nodes) == 0 {
return nodeList
}
nodeCount++
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, uint32(nodeList.Pos()), uint32(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)))
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++
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), uint32(node.Pos()), uint32(node.End()), 0, parentIndex, getNodeData(node, strs, &extendedData))
saveParentIndex := parentIndex
currentIndex := nodeCount
prevIndex = 0
parentIndex = currentIndex
visitor.VisitEachChild(node)
prevIndex = currentIndex
parentIndex = saveParentIndex
return node
}
nodes = appendUint32s(nodes, 0, 0, 0, 0, 0, 0)
nodeCount++
parentIndex++
nodes = appendUint32s(nodes, uint32(sourceFile.Kind), uint32(sourceFile.Pos()), uint32(sourceFile.End()), 0, 0, getSourceFileData(sourceFile, id, strs, &extendedData))
visitor.VisitEachChild(sourceFile.AsNode())
metadata := uint32(ProtocolVersion) << 24
offsetStringTableOffsets := HeaderSize
offsetStringTableData := HeaderSize + len(strs.offsets)*4
offsetExtendedData := offsetStringTableData + strs.stringLength()
offsetNodes := offsetExtendedData + len(extendedData)
header := []uint32{
metadata,
uint32(offsetStringTableOffsets),
uint32(offsetStringTableData),
uint32(offsetExtendedData),
uint32(offsetNodes),
}
var headerBytes, strsBytes []byte
headerBytes = appendUint32s(nil, header...)
strsBytes = strs.encode()
return slices.Concat(
headerBytes,
strsBytes,
extendedData,
nodes,
), nil
}
func appendUint32s(buf []byte, values ...uint32) []byte {
for _, value := range values {
var err error
if buf, err = binary.Append(buf, binary.LittleEndian, value); err != nil {
// The only error binary.Append can return is for values that are not fixed-size.
// This can never happen here, since we are always appending uint32.
panic(fmt.Sprintf("failed to append uint32: %v", err))
}
}
return buf
}
func getSourceFileData(sourceFile *ast.SourceFile, id string, strs *stringTable, extendedData *[]byte) uint32 {
t := NodeDataTypeExtendedData
extendedDataOffset := len(*extendedData)
textIndex := strs.add(sourceFile.Text(), sourceFile.Kind, sourceFile.Pos(), sourceFile.End())
fileNameIndex := strs.add(sourceFile.FileName(), 0, 0, 0)
idIndex := strs.add(id, 0, 0, 0)
*extendedData = appendUint32s(*extendedData, textIndex, fileNameIndex, idIndex)
return t | uint32(extendedDataOffset)
}
func getNodeData(node *ast.Node, strs *stringTable, extendedData *[]byte) uint32 {
t := getNodeDataType(node)
switch t {
case NodeDataTypeChildren:
return t | getNodeDefinedData(node) | uint32(getChildrenPropertyMask(node))
case NodeDataTypeString:
return t | getNodeDefinedData(node) | recordNodeStrings(node, strs)
case NodeDataTypeExtendedData:
return t | getNodeDefinedData(node) | recordExtendedData(node, strs, extendedData)
default:
panic("unreachable")
}
}
func getNodeDataType(node *ast.Node) uint32 {
switch node.Kind {
case ast.KindJsxText,
ast.KindIdentifier,
ast.KindPrivateIdentifier,
ast.KindStringLiteral,
ast.KindNumericLiteral,
ast.KindBigIntLiteral,
ast.KindRegularExpressionLiteral,
ast.KindNoSubstitutionTemplateLiteral,
ast.KindJSDocText:
return NodeDataTypeString
case ast.KindTemplateHead,
ast.KindTemplateMiddle,
ast.KindTemplateTail,
ast.KindSourceFile:
return NodeDataTypeExtendedData
default:
return NodeDataTypeChildren
}
}
// getChildrenPropertyMask returns a mask of which children properties are present in the node.
// It is defined for node kinds that have more than one property that is a pointer to a child node.
// Example: QualifiedName has two children properties: Left and Right, which are visited in that order.
// result&1 is non-zero if Left is present, and result&2 is non-zero if Right is present. If the client
// knows that QualifiedName has properties ["Left", "Right"] and sees an encoded node with only one
// child, it can use the mask to determine which property is present.
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.KindTypeParameter:
n := node.AsTypeParameter()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Constraint != nil) << 2) | (boolToByte(n.DefaultType != nil) << 3)
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.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.KindCaseClause, ast.KindDefaultClause:
n := node.AsCaseOrDefaultClause()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statements != nil) << 1)
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.KindVariableStatement:
n := node.AsVariableStatement()
return (boolToByte(n.Modifiers() != nil) << 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.KindParameter:
n := node.AsParameterDeclaration()
return (boolToByte(n.Modifiers() != nil) << 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.KindFunctionDeclaration:
n := node.AsFunctionDeclaration()
return (boolToByte(n.Modifiers() != nil) << 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.KindInterfaceDeclaration:
n := node.AsInterfaceDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
case ast.KindTypeAliasDeclaration:
n := node.AsTypeAliasDeclaration()
return (boolToByte(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Members != nil) << 2)
case ast.KindModuleDeclaration:
n := node.AsModuleDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Body != nil) << 2)
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.ModuleReference != nil) << 2)
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
n := node.AsImportDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
case ast.KindImportSpecifier:
n := node.AsImportSpecifier()
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
case ast.KindImportClause:
n := node.AsImportClause()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.NamedBindings != nil) << 1)
case ast.KindExportAssignment, ast.KindJSExportAssignment:
n := node.AsExportAssignment()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
case ast.KindNamespaceExportDeclaration:
n := node.AsNamespaceExportDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.ExportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
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(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindMethodSignature:
n := node.AsMethodSignatureDeclaration()
return (boolToByte(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
case ast.KindBinaryExpression:
n := node.AsBinaryExpression()
return (boolToByte(n.Left != nil) << 0) | (boolToByte(n.OperatorToken != nil) << 1) | (boolToByte(n.Right != nil) << 2)
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(n.Modifiers() != nil) << 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(n.Modifiers() != nil) << 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.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.KindPropertyAssignment:
n := node.AsPropertyAssignment()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
case ast.KindShorthandPropertyAssignment:
n := node.AsShorthandPropertyAssignment()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.EqualsToken != nil) << 3) | (boolToByte(n.ObjectAssignmentInitializer != nil) << 4)
case ast.KindTypeAssertionExpression:
n := node.AsTypeAssertion()
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
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.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.KindTypePredicate:
n := node.AsTypePredicateNode()
return (boolToByte(n.AssertsModifier != nil) << 0) | (boolToByte(n.ParameterName != nil) << 1) | (boolToByte(n.Type != nil) << 2)
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.KindImportAttribute:
n := node.AsImportAttribute()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Value != nil) << 1)
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.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.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(n.Modifiers() != nil) << 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.KindJsxElement:
n := node.AsJsxElement()
return (boolToByte(n.OpeningElement != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingElement != nil) << 2)
case ast.KindJsxNamespacedName:
n := node.AsJsxNamespacedName()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Namespace != 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.KindJsxExpression:
n := node.AsJsxExpression()
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
case ast.KindJSDoc:
n := node.AsJSDoc()
return (boolToByte(n.Comment != nil) << 0) | (boolToByte(n.Tags != nil) << 1)
case ast.KindJSDocTypeTag:
n := node.AsJSDocTypeTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocTag:
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.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.FullName != 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.KindClassStaticBlockDeclaration:
n := node.AsClassStaticBlockDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Body != nil) << 1)
case ast.KindClassDeclaration:
n := node.AsClassDeclaration()
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
n := node.AsJSDocParameterOrPropertyTag()
if n.IsNameFirst {
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeExpression != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
}
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
default:
return 0
}
}
func getNodeDefinedData(node *ast.Node) uint32 {
switch node.Kind {
case ast.KindJSDocTypeLiteral:
n := node.AsJSDocTypeLiteral()
return uint32(boolToByte(n.IsArrayType)) << 24
case ast.KindImportSpecifier:
n := node.AsImportSpecifier()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindImportClause:
n := node.AsImportClause()
return uint32(boolToByte(n.PhaseModifier == ast.KindTypeKeyword))<<24 | uint32(boolToByte(n.PhaseModifier == ast.KindDeferKeyword))<<25
case ast.KindExportSpecifier:
n := node.AsExportSpecifier()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindImportType:
n := node.AsImportTypeNode()
return uint32(boolToByte(n.IsTypeOf)) << 24
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindExportAssignment:
n := node.AsExportAssignment()
return uint32(boolToByte(n.IsExportEquals)) << 24
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindBlock:
n := node.AsBlock()
return uint32(boolToByte(n.Multiline)) << 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.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
n := node.AsJSDocParameterOrPropertyTag()
return uint32(boolToByte(n.IsBracketed))<<24 | uint32(boolToByte(n.IsNameFirst))<<25
case ast.KindJsxText:
n := node.AsJsxText()
return uint32(boolToByte(n.ContainsOnlyTriviaWhiteSpaces)) << 24
case ast.KindVariableDeclarationList:
n := node.AsVariableDeclarationList()
return uint32(n.Flags & (ast.NodeFlagsLet | ast.NodeFlagsConst) << 24)
case ast.KindImportAttributes:
n := node.AsImportAttributes()
return uint32(boolToByte(n.MultiLine))<<24 | uint32(boolToByte(n.Token == ast.KindAssertKeyword))<<25
}
return 0
}
func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {
switch node.Kind {
case ast.KindJsxText:
return strs.add(node.AsJsxText().Text, node.Kind, node.Pos(), node.End())
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.KindStringLiteral:
return strs.add(node.AsStringLiteral().Text, node.Kind, node.Pos(), node.End())
case ast.KindNumericLiteral:
return strs.add(node.AsNumericLiteral().Text, node.Kind, node.Pos(), node.End())
case ast.KindBigIntLiteral:
return strs.add(node.AsBigIntLiteral().Text, node.Kind, node.Pos(), node.End())
case ast.KindRegularExpressionLiteral:
return strs.add(node.AsRegularExpressionLiteral().Text, node.Kind, node.Pos(), node.End())
case ast.KindNoSubstitutionTemplateLiteral:
return strs.add(node.AsNoSubstitutionTemplateLiteral().Text, node.Kind, node.Pos(), node.End())
case ast.KindJSDocText:
return strs.add(node.AsJSDocText().Text(), node.Kind, node.Pos(), node.End())
default:
panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))
}
}
func recordExtendedData(node *ast.Node, strs *stringTable, extendedData *[]byte) uint32 {
offset := uint32(len(*extendedData))
var text, rawText string
var templateFlags uint32
switch node.Kind {
case ast.KindTemplateTail:
n := node.AsTemplateTail()
text = n.Text
rawText = n.RawText
templateFlags = uint32(n.TemplateFlags)
case ast.KindTemplateMiddle:
n := node.AsTemplateMiddle()
text = n.Text
rawText = n.RawText
templateFlags = uint32(n.TemplateFlags)
case ast.KindTemplateHead:
n := node.AsTemplateHead()
text = n.Text
rawText = n.RawText
templateFlags = uint32(n.TemplateFlags)
}
textIndex := strs.add(text, node.Kind, node.Pos(), node.End())
rawTextIndex := strs.add(rawText, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, templateFlags)
return offset
}
func boolToByte(b bool) byte {
if b {
return 1
}
return 0
}