Skip to content

Commit bd43538

Browse files
committed
feat: port class fields transformer
1 parent 6f385fd commit bd43538

File tree

13 files changed

+1794
-36
lines changed

13 files changed

+1794
-36
lines changed

internal/ast/utilities.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ func IsClassElement(node *Node) bool {
566566
return false
567567
}
568568

569-
func isMethodOrAccessor(node *Node) bool {
569+
func IsMethodOrAccessor(node *Node) bool {
570570
switch node.Kind {
571571
case KindMethodDeclaration, KindGetAccessor, KindSetAccessor:
572572
return true
@@ -575,7 +575,7 @@ func isMethodOrAccessor(node *Node) bool {
575575
}
576576

577577
func IsPrivateIdentifierClassElementDeclaration(node *Node) bool {
578-
return (IsPropertyDeclaration(node) || isMethodOrAccessor(node)) && IsPrivateIdentifier(node.Name())
578+
return (IsPropertyDeclaration(node) || IsMethodOrAccessor(node)) && IsPrivateIdentifier(node.Name())
579579
}
580580

581581
func IsObjectLiteralOrClassExpressionMethodOrAccessor(node *Node) bool {
@@ -1467,6 +1467,18 @@ func getAssignedName(node *Node) *Node {
14671467
return nil
14681468
}
14691469

1470+
func IsStaticPropertyDeclaration(member *ClassElement) bool {
1471+
return IsPropertyDeclaration(member) && HasStaticModifier(member)
1472+
}
1473+
1474+
func IsStaticPropertyDeclarationOrClassStaticBlockDeclaration(element *ClassElement) bool {
1475+
return IsStaticPropertyDeclaration(element) || IsClassStaticBlockDeclaration(element)
1476+
}
1477+
1478+
func GetStaticPropertiesAndClassStaticBlock(node *ClassLikeDeclaration) []*Node {
1479+
return core.Filter(node.Members(), IsStaticPropertyDeclarationOrClassStaticBlockDeclaration)
1480+
}
1481+
14701482
type JSDeclarationKind int
14711483

14721484
const (
@@ -3635,3 +3647,16 @@ func GetSemanticJsxChildren(children []*JsxChild) []*JsxChild {
36353647
}
36363648
})
36373649
}
3650+
3651+
func GetEffectiveBaseTypeNode(node *Node) *Node {
3652+
baseType := GetClassExtendsHeritageElement(node)
3653+
// !!! TODO: JSDoc support
3654+
// if (baseType && isInJSFile(node)) {
3655+
// // Prefer an @augments tag because it may have type parameters.
3656+
// const tag = getJSDocAugmentsTag(node);
3657+
// if (tag) {
3658+
// return tag.class;
3659+
// }
3660+
// }
3661+
return baseType
3662+
}

internal/core/stack.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,13 @@ func (s *Stack[T]) Peek() T {
3131
func (s *Stack[T]) Len() int {
3232
return len(s.data)
3333
}
34+
35+
func (s *Stack[T]) FindFromTop(predicate func(item T) bool) T {
36+
for i := len(s.data) - 1; i >= 0; i-- {
37+
if predicate(s.data[i]) {
38+
return s.data[i]
39+
}
40+
}
41+
var zero T
42+
return zero
43+
}

internal/printer/emitcontext.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,14 @@ func (c *EmitContext) SourceMapRange(node *ast.Node) core.TextRange {
575575
return node.Loc
576576
}
577577

578+
// Sets `EFNoComments` on a node and removes any leading and trailing synthetic comments.
579+
func (c *EmitContext) RemoveAllComments(node *ast.Node) {
580+
c.AddEmitFlags(node, EFNoComments)
581+
// !!! TODO: Also remove synthetic trailing/leading comments added by transforms
582+
// emitNode.leadingComments = undefined;
583+
// emitNode.trailingComments = undefined;
584+
}
585+
578586
// Sets the range to use for a node when emitting source maps.
579587
func (c *EmitContext) SetSourceMapRange(node *ast.Node, loc core.TextRange) {
580588
emitNode := c.emitNodes.Get(node)
@@ -616,6 +624,10 @@ func (c *EmitContext) SetTokenSourceMapRange(node *ast.Node, kind ast.Kind, loc
616624
emitNode.tokenSourceMapRanges[kind] = loc
617625
}
618626

627+
func (c *EmitContext) TextSource(node *ast.Node) *ast.Node {
628+
return c.textSource[node]
629+
}
630+
619631
func (c *EmitContext) AssignedName(node *ast.Node) *ast.Expression {
620632
return c.assignedName[node]
621633
}

internal/printer/factory.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,22 @@ func (f *NodeFactory) getName(node *ast.Declaration, emitFlags EmitFlags, opts A
442442
return f.NewGeneratedNameForNode(node)
443443
}
444444

445+
// Gets the internal name of a declaration. This is primarily used for declarations that can be referred to by name in
446+
// the body of an ES5 class function body. An internal name will *never* be prefixed with an module or namespace export
447+
// modifier like "exports." when emitted as an expression. An internal name will also *never* be renamed due to a
448+
// collision with a block scoped variable.
449+
func (f *NodeFactory) GetInternalName(node *ast.Declaration) *ast.IdentifierNode {
450+
return f.GetInternalNameEx(node, AssignedNameOptions{})
451+
}
452+
453+
// Gets the internal name of a declaration. This is primarily used for declarations that can be referred to by name in
454+
// the body of an ES5 class function body. An internal name will *never* be prefixed with an module or namespace export
455+
// modifier like "exports." when emitted as an expression. An internal name will also *never* be renamed due to a
456+
// collision with a block scoped variable.
457+
func (f *NodeFactory) GetInternalNameEx(node *ast.Declaration, opts AssignedNameOptions) *ast.IdentifierNode {
458+
return f.getName(node, EFLocalName|EFInternalName, opts)
459+
}
460+
445461
// Gets the local name of a declaration. This is primarily used for declarations that can be referred to by name in the
446462
// declaration's immediate scope (classes, enums, namespaces). A local name will *never* be prefixed with a module or
447463
// namespace export modifier like "exports." when emitted as an expression.
@@ -639,3 +655,30 @@ func (f *NodeFactory) NewRewriteRelativeImportExtensionsHelper(firstArgument *as
639655
ast.NodeFlagsNone,
640656
)
641657
}
658+
659+
// Allocate a new call expression to the `__classPrivateFieldGet` helper
660+
func (f *NodeFactory) NewClassPrivateFieldGetHelper(receiver *ast.Expression, state *ast.Identifier, kind PrivateIdentifierKind, farg *ast.Identifier) *ast.Expression {
661+
f.emitContext.RequestEmitHelper(classPrivateFieldGetHelper)
662+
var arguments []*ast.Expression
663+
if farg == nil {
664+
arguments = []*ast.Expression{
665+
receiver,
666+
state.AsNode(),
667+
f.NewStringLiteral(kind.String()),
668+
}
669+
} else {
670+
arguments = []*ast.Expression{
671+
receiver,
672+
state.AsNode(),
673+
f.NewStringLiteral(kind.String()),
674+
farg.AsNode(),
675+
}
676+
}
677+
return f.NewCallExpression(
678+
f.NewUnscopedHelperName("__classPrivateFieldGet"),
679+
nil, /*questionDotToken*/
680+
nil, /*typeArguments*/
681+
f.NewNodeList(arguments),
682+
ast.NodeFlagsNone,
683+
)
684+
}

internal/printer/helpers.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,14 @@ var rewriteRelativeImportExtensionsHelper = &EmitHelper{
233233
return path;
234234
};`,
235235
}
236+
237+
var classPrivateFieldGetHelper = &EmitHelper{
238+
Name: "typescript:classPrivateFieldGet",
239+
ImportName: "__classPrivateFieldGet",
240+
Scoped: false,
241+
Text: `var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
242+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
243+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
244+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
245+
};`,
246+
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package printer
2+
3+
import "github.com/microsoft/typescript-go/internal/ast"
4+
5+
type PrivateIdentifierKind int
6+
7+
const (
8+
PrivateIdentifierKindMethod PrivateIdentifierKind = iota
9+
PrivateIdentifierKindField
10+
PrivateIdentifierKindAccessor
11+
PrivateIdentifierKindUntransformed
12+
)
13+
14+
func (k PrivateIdentifierKind) String() string {
15+
switch k {
16+
case PrivateIdentifierKindMethod:
17+
return "m"
18+
case PrivateIdentifierKindField:
19+
return "f"
20+
case PrivateIdentifierKindAccessor:
21+
return "a"
22+
case PrivateIdentifierKindUntransformed:
23+
return "untransformed"
24+
default:
25+
panic("Unhandled PrivateIdentifierKind")
26+
}
27+
}
28+
29+
type PrivateIdentifierInfo interface {
30+
Kind() PrivateIdentifierKind
31+
IsValid() bool
32+
IsStatic() bool
33+
BrandCheckIdentifier() *ast.Identifier
34+
}
35+
36+
type PrivateIdentifierInfoBase struct {
37+
/**
38+
* brandCheckIdentifier can contain:
39+
* - For instance field: The WeakMap that will be the storage for the field.
40+
* - For instance methods or accessors: The WeakSet that will be used for brand checking.
41+
* - For static members: The constructor that will be used for brand checking.
42+
*/
43+
brandCheckIdentifier *ast.Identifier
44+
// Stores if the identifier is static or not
45+
isStatic bool
46+
// Stores if the identifier declaration is valid or not. Reserved names (e.g. #constructor)
47+
// or duplicate identifiers are considered invalid.
48+
isValid bool
49+
}
50+
51+
type PrivateIdentifierAccessorInfo struct {
52+
PrivateIdentifierInfoBase
53+
kind PrivateIdentifierKind
54+
// Identifier for a variable that will contain the private get accessor implementation, if any.
55+
GetterName *ast.Identifier
56+
// Identifier for a variable that will contain the private set accessor implementation, if any.
57+
SetterName *ast.Identifier
58+
}
59+
60+
func (p *PrivateIdentifierAccessorInfo) Kind() PrivateIdentifierKind {
61+
return p.kind
62+
}
63+
func (p *PrivateIdentifierAccessorInfo) IsValid() bool {
64+
return p.PrivateIdentifierInfoBase.isValid
65+
}
66+
func (p *PrivateIdentifierAccessorInfo) IsStatic() bool {
67+
return p.PrivateIdentifierInfoBase.isStatic
68+
}
69+
func (p *PrivateIdentifierAccessorInfo) BrandCheckIdentifier() *ast.Identifier {
70+
return p.PrivateIdentifierInfoBase.brandCheckIdentifier
71+
}
72+
73+
func NewPrivateIdentifierAccessorInfo(brandCheckIdentifier *ast.Identifier, getterName *ast.Identifier, setterName *ast.Identifier, isValid bool, isStatc bool) *PrivateIdentifierAccessorInfo {
74+
return &PrivateIdentifierAccessorInfo{
75+
kind: PrivateIdentifierKindAccessor,
76+
PrivateIdentifierInfoBase: PrivateIdentifierInfoBase{
77+
brandCheckIdentifier: brandCheckIdentifier,
78+
isStatic: isStatc,
79+
isValid: isValid,
80+
},
81+
GetterName: getterName,
82+
SetterName: setterName,
83+
}
84+
}
85+
86+
type PrivateIdentifierMethodInfo struct {
87+
PrivateIdentifierInfoBase
88+
kind PrivateIdentifierKind
89+
// Identifier for a variable that will contain the private method implementation.
90+
MethodName *ast.Identifier
91+
}
92+
93+
func (p *PrivateIdentifierMethodInfo) Kind() PrivateIdentifierKind {
94+
return p.kind
95+
}
96+
func (p *PrivateIdentifierMethodInfo) IsValid() bool {
97+
return p.PrivateIdentifierInfoBase.isValid
98+
}
99+
func (p *PrivateIdentifierMethodInfo) IsStatic() bool {
100+
return p.PrivateIdentifierInfoBase.isStatic
101+
}
102+
func (p *PrivateIdentifierMethodInfo) BrandCheckIdentifier() *ast.Identifier {
103+
return p.PrivateIdentifierInfoBase.brandCheckIdentifier
104+
}
105+
106+
func NewPrivateIdentifierMethodInfo(brandCheckIdentifier *ast.Identifier, methodName *ast.Identifier, isValid bool, isStatc bool) *PrivateIdentifierMethodInfo {
107+
return &PrivateIdentifierMethodInfo{
108+
kind: PrivateIdentifierKindMethod,
109+
PrivateIdentifierInfoBase: PrivateIdentifierInfoBase{
110+
brandCheckIdentifier: brandCheckIdentifier,
111+
isStatic: isStatc,
112+
isValid: isValid,
113+
},
114+
MethodName: methodName,
115+
}
116+
}
117+
118+
type PrivateIdentifierInstanceFieldInfo struct {
119+
PrivateIdentifierInfoBase
120+
kind PrivateIdentifierKind
121+
}
122+
123+
func NewPrivateIdentifierInstanceFieldInfo(brandCheckIdentifier *ast.Identifier, isValid bool) *PrivateIdentifierInstanceFieldInfo {
124+
return &PrivateIdentifierInstanceFieldInfo{
125+
kind: PrivateIdentifierKindField,
126+
PrivateIdentifierInfoBase: PrivateIdentifierInfoBase{
127+
brandCheckIdentifier: brandCheckIdentifier,
128+
isStatic: false,
129+
isValid: isValid,
130+
},
131+
}
132+
}
133+
134+
func (p *PrivateIdentifierInstanceFieldInfo) Kind() PrivateIdentifierKind {
135+
return p.kind
136+
}
137+
func (p *PrivateIdentifierInstanceFieldInfo) IsValid() bool {
138+
return p.PrivateIdentifierInfoBase.isValid
139+
}
140+
func (p *PrivateIdentifierInstanceFieldInfo) IsStatic() bool {
141+
return p.PrivateIdentifierInfoBase.isStatic
142+
}
143+
func (p *PrivateIdentifierInstanceFieldInfo) BrandCheckIdentifier() *ast.Identifier {
144+
return p.PrivateIdentifierInfoBase.brandCheckIdentifier
145+
}
146+
147+
type PrivateIdentifierStaticFieldInfo struct {
148+
PrivateIdentifierInfoBase
149+
kind PrivateIdentifierKind
150+
// Contains the variable that will serve as the storage for the field.
151+
VariableName *ast.Identifier
152+
}
153+
154+
func NewPrivateIdentifierStaticFieldInfo(brandCheckIdentifier *ast.Identifier, variableName *ast.Identifier, isValid bool) *PrivateIdentifierStaticFieldInfo {
155+
return &PrivateIdentifierStaticFieldInfo{
156+
kind: PrivateIdentifierKindField,
157+
PrivateIdentifierInfoBase: PrivateIdentifierInfoBase{
158+
brandCheckIdentifier: brandCheckIdentifier,
159+
isStatic: true,
160+
isValid: isValid,
161+
},
162+
VariableName: variableName,
163+
}
164+
}
165+
166+
func (p *PrivateIdentifierStaticFieldInfo) Kind() PrivateIdentifierKind {
167+
return p.kind
168+
}
169+
func (p *PrivateIdentifierStaticFieldInfo) IsValid() bool {
170+
return p.PrivateIdentifierInfoBase.isValid
171+
}
172+
func (p *PrivateIdentifierStaticFieldInfo) IsStatic() bool {
173+
return p.PrivateIdentifierInfoBase.isStatic
174+
}
175+
func (p *PrivateIdentifierStaticFieldInfo) BrandCheckIdentifier() *ast.Identifier {
176+
return p.PrivateIdentifierInfoBase.brandCheckIdentifier
177+
}
178+
179+
type PrivateIdentifierUntransformedInfo struct {
180+
PrivateIdentifierInfoBase
181+
kind PrivateIdentifierKind
182+
}
183+
184+
func NewPrivateIdentifierUntransformedInfo(brandCheckIdentifier *ast.Identifier, isValid bool, isStatic bool) *PrivateIdentifierUntransformedInfo {
185+
return &PrivateIdentifierUntransformedInfo{
186+
kind: PrivateIdentifierKindUntransformed,
187+
PrivateIdentifierInfoBase: PrivateIdentifierInfoBase{
188+
brandCheckIdentifier: brandCheckIdentifier,
189+
isStatic: isStatic,
190+
isValid: isValid,
191+
},
192+
}
193+
}
194+
195+
func (p *PrivateIdentifierUntransformedInfo) Kind() PrivateIdentifierKind {
196+
return p.kind
197+
}
198+
func (p *PrivateIdentifierUntransformedInfo) IsValid() bool {
199+
return p.PrivateIdentifierInfoBase.isValid
200+
}
201+
func (p *PrivateIdentifierUntransformedInfo) IsStatic() bool {
202+
return p.PrivateIdentifierInfoBase.isStatic
203+
}
204+
func (p *PrivateIdentifierUntransformedInfo) BrandCheckIdentifier() *ast.Identifier {
205+
return p.PrivateIdentifierInfoBase.brandCheckIdentifier
206+
}

0 commit comments

Comments
 (0)