goscript 0.0.56 → 0.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/analysis.go +89 -4
- package/compiler/assignment.go +64 -2
- package/compiler/compiler.go +3 -0
- package/compiler/composite-lit.go +21 -4
- package/compiler/decl.go +360 -7
- package/compiler/expr-selector.go +4 -4
- package/compiler/expr.go +64 -5
- package/compiler/spec-struct.go +7 -3
- package/compiler/spec-value.go +41 -10
- package/compiler/spec.go +19 -4
- package/compiler/stmt.go +92 -11
- package/dist/gs/builtin/type.d.ts +1 -0
- package/dist/gs/builtin/type.js +54 -7
- package/dist/gs/builtin/type.js.map +1 -1
- package/gs/builtin/type.ts +58 -8
- package/package.json +1 -1
package/compiler/analysis.go
CHANGED
|
@@ -589,6 +589,12 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
589
589
|
|
|
590
590
|
case *ast.TypeAssertExpr:
|
|
591
591
|
return v.visitTypeAssertExpr(n)
|
|
592
|
+
|
|
593
|
+
case *ast.CompositeLit:
|
|
594
|
+
// Traverse into composite literal elements to detect &variable expressions
|
|
595
|
+
// This is important for cases like: arr := []interface{}{value1, &value2}
|
|
596
|
+
// where value2 needs to be marked as NeedsVarRef due to the &value2 usage
|
|
597
|
+
return v.visitCompositeLit(n)
|
|
592
598
|
}
|
|
593
599
|
|
|
594
600
|
// For all other nodes, continue traversal
|
|
@@ -869,10 +875,89 @@ func (v *analysisVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
|
|
|
869
875
|
return v
|
|
870
876
|
}
|
|
871
877
|
|
|
872
|
-
// visitTypeAssertExpr handles type assertion
|
|
873
|
-
func (v *analysisVisitor) visitTypeAssertExpr(
|
|
874
|
-
//
|
|
875
|
-
v.
|
|
878
|
+
// visitTypeAssertExpr handles type assertion analysis for interface method implementations
|
|
879
|
+
func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) ast.Visitor {
|
|
880
|
+
// Get the type being asserted to
|
|
881
|
+
assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
|
|
882
|
+
if assertedType == nil {
|
|
883
|
+
return v
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Check if the asserted type is an interface
|
|
887
|
+
interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
|
|
888
|
+
if !isInterface {
|
|
889
|
+
return v
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// Get the type of the expression being asserted
|
|
893
|
+
exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
|
|
894
|
+
if exprType == nil {
|
|
895
|
+
return v
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Handle pointer types by getting the element type
|
|
899
|
+
if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
|
|
900
|
+
exprType = ptrType.Elem()
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Check if the expression type is a named struct type
|
|
904
|
+
namedType, isNamed := exprType.(*types.Named)
|
|
905
|
+
if !isNamed {
|
|
906
|
+
return v
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// For each method in the interface, check if the struct implements it
|
|
910
|
+
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
911
|
+
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
912
|
+
|
|
913
|
+
// Find the corresponding method in the struct type
|
|
914
|
+
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
915
|
+
if structMethod != nil {
|
|
916
|
+
// Determine if this struct method is async using unified system
|
|
917
|
+
isAsync := false
|
|
918
|
+
if obj := structMethod; obj != nil {
|
|
919
|
+
isAsync = v.analysis.IsAsyncFunc(obj)
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Track this interface implementation
|
|
923
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return v
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// visitCompositeLit analyzes composite literals for address-of expressions
|
|
930
|
+
// This is important for detecting cases like: arr := []interface{}{value1, &value2}
|
|
931
|
+
// where value2 needs to be marked as NeedsVarRef due to the &value2 usage
|
|
932
|
+
func (v *analysisVisitor) visitCompositeLit(compLit *ast.CompositeLit) ast.Visitor {
|
|
933
|
+
// Analyze each element of the composite literal
|
|
934
|
+
for _, elt := range compLit.Elts {
|
|
935
|
+
// Handle both direct elements and key-value pairs
|
|
936
|
+
var expr ast.Expr
|
|
937
|
+
if kv, ok := elt.(*ast.KeyValueExpr); ok {
|
|
938
|
+
// For key-value pairs, analyze the value expression
|
|
939
|
+
expr = kv.Value
|
|
940
|
+
} else {
|
|
941
|
+
// For direct elements, analyze the element expression
|
|
942
|
+
expr = elt
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Check if this element is an address-of expression
|
|
946
|
+
if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
|
|
947
|
+
// Found &something in the composite literal
|
|
948
|
+
if ident, ok := unaryExpr.X.(*ast.Ident); ok {
|
|
949
|
+
// Found &variable - mark the variable as needing VarRef
|
|
950
|
+
if obj := v.pkg.TypesInfo.ObjectOf(ident); obj != nil {
|
|
951
|
+
// Record that this variable has its address taken
|
|
952
|
+
usageInfo := v.getOrCreateUsageInfo(obj)
|
|
953
|
+
usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
|
|
954
|
+
Object: nil, // No specific destination object for composite literals
|
|
955
|
+
Type: AddressOfAssignment,
|
|
956
|
+
})
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
876
961
|
return v
|
|
877
962
|
}
|
|
878
963
|
|
package/compiler/assignment.go
CHANGED
|
@@ -56,10 +56,12 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
56
56
|
|
|
57
57
|
// Handle the RHS expression (potentially adding .clone() for structs)
|
|
58
58
|
if shouldApplyClone(c.pkg, rhs[0]) {
|
|
59
|
+
// When cloning for value assignment, mark the result as struct value
|
|
60
|
+
c.tsw.WriteLiterally("$.markAsStructValue(")
|
|
59
61
|
if err := c.WriteValueExpr(rhs[0]); err != nil {
|
|
60
62
|
return err
|
|
61
63
|
}
|
|
62
|
-
c.tsw.WriteLiterally(".clone()")
|
|
64
|
+
c.tsw.WriteLiterally(".clone())")
|
|
63
65
|
} else {
|
|
64
66
|
if err := c.WriteValueExpr(rhs[0]); err != nil {
|
|
65
67
|
return err
|
|
@@ -338,8 +340,37 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
338
340
|
}
|
|
339
341
|
}
|
|
340
342
|
|
|
343
|
+
// Check for pointer-to-pointer assignment
|
|
344
|
+
if rhsIsIdent && rhsObj != nil && len(lhs) == 1 {
|
|
345
|
+
lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
|
|
346
|
+
rhsType := rhsObj.Type()
|
|
347
|
+
|
|
348
|
+
if lhsType != nil && rhsType != nil {
|
|
349
|
+
// Check if both LHS and RHS are pointer types
|
|
350
|
+
if _, lhsIsPtr := lhsType.(*types.Pointer); lhsIsPtr {
|
|
351
|
+
if _, rhsIsPtr := rhsType.(*types.Pointer); rhsIsPtr {
|
|
352
|
+
// This is pointer-to-pointer assignment
|
|
353
|
+
// The key question: is the RHS variable itself varref'd?
|
|
354
|
+
// - If RHS is varref'd (like pp1), use .value to get the actual pointer
|
|
355
|
+
// - If RHS is not varref'd (like p1), use the variable directly
|
|
356
|
+
|
|
357
|
+
if c.analysis.NeedsVarRef(rhsObj) {
|
|
358
|
+
// RHS variable is varref'd, so we need its .value to get the actual pointer
|
|
359
|
+
c.WriteIdent(rhsIdent, true) // Add .value access
|
|
360
|
+
} else {
|
|
361
|
+
// RHS variable is not varref'd, so it directly holds the pointer
|
|
362
|
+
c.WriteIdent(rhsIdent, false) // No .value access
|
|
363
|
+
}
|
|
364
|
+
continue
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
341
370
|
// Handle different cases for struct cloning
|
|
342
371
|
if shouldApplyClone(c.pkg, r) {
|
|
372
|
+
// When cloning for value assignment, mark the result as struct value
|
|
373
|
+
c.tsw.WriteLiterally("$.markAsStructValue(")
|
|
343
374
|
// For other expressions, we need to handle variable referenced access differently
|
|
344
375
|
if _, isIdent := r.(*ast.Ident); isIdent {
|
|
345
376
|
// For identifiers, WriteValueExpr already adds .value if needed
|
|
@@ -357,8 +388,39 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
357
388
|
}
|
|
358
389
|
}
|
|
359
390
|
|
|
360
|
-
c.tsw.WriteLiterally(".clone()") // Always add clone for struct values
|
|
391
|
+
c.tsw.WriteLiterally(".clone())") // Always add clone for struct values
|
|
361
392
|
} else {
|
|
393
|
+
// Check if this is a pointer variable assignment to an interface type
|
|
394
|
+
if rhsIsIdent && rhsObj != nil {
|
|
395
|
+
// Check if LHS is interface type and RHS is a pointer variable
|
|
396
|
+
if len(lhs) == 1 {
|
|
397
|
+
lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
|
|
398
|
+
rhsType := rhsObj.Type()
|
|
399
|
+
|
|
400
|
+
if lhsType != nil && rhsType != nil {
|
|
401
|
+
// Check if LHS is interface and RHS is pointer
|
|
402
|
+
if _, isInterface := lhsType.Underlying().(*types.Interface); isInterface {
|
|
403
|
+
if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
|
|
404
|
+
// This is pointer-to-interface assignment
|
|
405
|
+
// For pointer variables that point to varrefed values, write without .value
|
|
406
|
+
// We want to pass the VarRef object itself to the interface, not its .value
|
|
407
|
+
if c.analysis.NeedsVarRefAccess(rhsObj) {
|
|
408
|
+
// Write the pointer variable without .value access
|
|
409
|
+
c.WriteIdent(rhsIdent, false)
|
|
410
|
+
continue
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Check if this is a struct pointer for the element type
|
|
414
|
+
if _, isStruct := ptrType.Elem().Underlying().(*types.Struct); isStruct {
|
|
415
|
+
// Struct pointer to interface - might need special handling
|
|
416
|
+
// Continue to normal WriteValueExpr handling
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
362
424
|
// Non-struct case: write RHS normally
|
|
363
425
|
if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
|
|
364
426
|
return err
|
package/compiler/compiler.go
CHANGED
|
@@ -704,6 +704,9 @@ type GoToTSCompiler struct {
|
|
|
704
704
|
pkg *packages.Package
|
|
705
705
|
|
|
706
706
|
analysis *Analysis
|
|
707
|
+
|
|
708
|
+
// Context flags
|
|
709
|
+
insideAddressOf bool // true when processing operand of & operator
|
|
707
710
|
}
|
|
708
711
|
|
|
709
712
|
// It initializes the compiler with a `TSCodeWriter` for output,
|
|
@@ -212,6 +212,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
212
212
|
var structType *types.Struct
|
|
213
213
|
isStructLiteral := false
|
|
214
214
|
isAnonymousStruct := false
|
|
215
|
+
needsValueMarkerClose := false // Track if we need to close $.markAsStructValue()
|
|
215
216
|
|
|
216
217
|
if namedType, ok := litType.(*types.Named); ok {
|
|
217
218
|
if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
|
|
@@ -224,8 +225,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
224
225
|
return err
|
|
225
226
|
}
|
|
226
227
|
} else {
|
|
227
|
-
// Named struct, use constructor
|
|
228
|
-
c.
|
|
228
|
+
// Named struct value, use constructor
|
|
229
|
+
if !c.insideAddressOf {
|
|
230
|
+
// Only mark as struct value if not inside address-of operator
|
|
231
|
+
c.tsw.WriteLiterally("$.markAsStructValue(new ")
|
|
232
|
+
needsValueMarkerClose = true
|
|
233
|
+
} else {
|
|
234
|
+
c.tsw.WriteLiterally("new ")
|
|
235
|
+
}
|
|
229
236
|
c.WriteTypeExpr(exp.Type)
|
|
230
237
|
}
|
|
231
238
|
}
|
|
@@ -241,8 +248,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
241
248
|
return err
|
|
242
249
|
}
|
|
243
250
|
} else {
|
|
244
|
-
// Type alias for struct, use constructor
|
|
245
|
-
c.
|
|
251
|
+
// Type alias for struct value, use constructor
|
|
252
|
+
if !c.insideAddressOf {
|
|
253
|
+
// Only mark as struct value if not inside address-of operator
|
|
254
|
+
c.tsw.WriteLiterally("$.markAsStructValue(new ")
|
|
255
|
+
needsValueMarkerClose = true
|
|
256
|
+
} else {
|
|
257
|
+
c.tsw.WriteLiterally("new ")
|
|
258
|
+
}
|
|
246
259
|
c.WriteTypeExpr(exp.Type)
|
|
247
260
|
}
|
|
248
261
|
}
|
|
@@ -483,6 +496,10 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
483
496
|
c.tsw.WriteLiterally("}")
|
|
484
497
|
} else {
|
|
485
498
|
c.tsw.WriteLiterally("})")
|
|
499
|
+
// Close markAsStructValue wrapper if we opened one
|
|
500
|
+
if needsValueMarkerClose {
|
|
501
|
+
c.tsw.WriteLiterally(")")
|
|
502
|
+
}
|
|
486
503
|
}
|
|
487
504
|
|
|
488
505
|
} else {
|
package/compiler/decl.go
CHANGED
|
@@ -3,7 +3,9 @@ package compiler
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
|
+
"go/token"
|
|
6
7
|
"go/types"
|
|
8
|
+
"sort"
|
|
7
9
|
)
|
|
8
10
|
|
|
9
11
|
// WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
|
|
@@ -17,33 +19,384 @@ import (
|
|
|
17
19
|
// variables, or type definitions: It iterates through `d.Specs` and calls
|
|
18
20
|
// `WriteSpec` for each specification.
|
|
19
21
|
//
|
|
22
|
+
// Type declarations are sorted by dependencies to ensure referenced types are
|
|
23
|
+
// defined before types that reference them, avoiding initialization order issues.
|
|
20
24
|
// A newline is added after each processed declaration or spec group for readability.
|
|
21
25
|
// Unknown declaration types result in a printed diagnostic message.
|
|
22
26
|
func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
|
|
27
|
+
// Separate type declarations from other declarations for dependency sorting
|
|
28
|
+
var typeSpecs []*ast.TypeSpec
|
|
29
|
+
var varSpecs []*ast.ValueSpec
|
|
30
|
+
var otherDecls []ast.Decl
|
|
31
|
+
var otherSpecs []ast.Spec
|
|
32
|
+
|
|
23
33
|
for _, decl := range decls {
|
|
24
34
|
switch d := decl.(type) {
|
|
25
35
|
case *ast.FuncDecl:
|
|
26
36
|
// Only handle top-level functions here. Methods are handled within WriteTypeSpec.
|
|
27
37
|
if d.Recv == nil {
|
|
28
|
-
|
|
29
|
-
return err
|
|
30
|
-
}
|
|
31
|
-
c.tsw.WriteLine("") // Add space after function
|
|
38
|
+
otherDecls = append(otherDecls, d)
|
|
32
39
|
}
|
|
33
40
|
case *ast.GenDecl:
|
|
34
41
|
for _, spec := range d.Specs {
|
|
35
|
-
if
|
|
36
|
-
|
|
42
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
43
|
+
typeSpecs = append(typeSpecs, typeSpec)
|
|
44
|
+
} else if varSpec, ok := spec.(*ast.ValueSpec); ok && d.Tok == token.VAR {
|
|
45
|
+
varSpecs = append(varSpecs, varSpec)
|
|
46
|
+
} else {
|
|
47
|
+
otherSpecs = append(otherSpecs, spec)
|
|
37
48
|
}
|
|
38
|
-
c.tsw.WriteLine("") // Add space after spec
|
|
39
49
|
}
|
|
50
|
+
default:
|
|
51
|
+
otherDecls = append(otherDecls, d)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sort type declarations by dependencies
|
|
56
|
+
sortedTypeSpecs, err := c.sortTypeSpecsByDependencies(typeSpecs)
|
|
57
|
+
if err != nil {
|
|
58
|
+
return fmt.Errorf("failed to sort type declarations: %w", err)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Sort variable declarations by type dependencies
|
|
62
|
+
sortedVarSpecs, err := c.sortVarSpecsByTypeDependencies(varSpecs, typeSpecs)
|
|
63
|
+
if err != nil {
|
|
64
|
+
return fmt.Errorf("failed to sort variable declarations: %w", err)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Write non-type, non-var declarations first (imports, constants)
|
|
68
|
+
for _, spec := range otherSpecs {
|
|
69
|
+
if err := c.WriteSpec(spec); err != nil {
|
|
70
|
+
return err
|
|
71
|
+
}
|
|
72
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Write sorted type declarations
|
|
76
|
+
for _, typeSpec := range sortedTypeSpecs {
|
|
77
|
+
if err := c.WriteSpec(typeSpec); err != nil {
|
|
78
|
+
return err
|
|
79
|
+
}
|
|
80
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Write sorted variable declarations
|
|
84
|
+
for _, varSpec := range sortedVarSpecs {
|
|
85
|
+
if err := c.WriteSpec(varSpec); err != nil {
|
|
86
|
+
return err
|
|
87
|
+
}
|
|
88
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Write function declarations last
|
|
92
|
+
for _, decl := range otherDecls {
|
|
93
|
+
switch d := decl.(type) {
|
|
94
|
+
case *ast.FuncDecl:
|
|
95
|
+
if err := c.WriteFuncDeclAsFunction(d); err != nil {
|
|
96
|
+
return err
|
|
97
|
+
}
|
|
98
|
+
c.tsw.WriteLine("") // Add space after function
|
|
40
99
|
default:
|
|
41
100
|
return fmt.Errorf("unknown decl: %#v", decl)
|
|
42
101
|
}
|
|
43
102
|
}
|
|
103
|
+
|
|
44
104
|
return nil
|
|
45
105
|
}
|
|
46
106
|
|
|
107
|
+
// sortTypeSpecsByDependencies performs a topological sort of type specifications
|
|
108
|
+
// based on their dependencies to ensure referenced types are defined before
|
|
109
|
+
// types that reference them.
|
|
110
|
+
func (c *GoToTSCompiler) sortTypeSpecsByDependencies(typeSpecs []*ast.TypeSpec) ([]*ast.TypeSpec, error) {
|
|
111
|
+
if len(typeSpecs) <= 1 {
|
|
112
|
+
return typeSpecs, nil
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Build dependency graph
|
|
116
|
+
dependencies := make(map[string][]string) // typeName -> list of types it depends on
|
|
117
|
+
typeSpecMap := make(map[string]*ast.TypeSpec)
|
|
118
|
+
|
|
119
|
+
// First pass: collect all type names
|
|
120
|
+
for _, typeSpec := range typeSpecs {
|
|
121
|
+
typeName := typeSpec.Name.Name
|
|
122
|
+
typeSpecMap[typeName] = typeSpec
|
|
123
|
+
dependencies[typeName] = []string{}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Second pass: analyze dependencies
|
|
127
|
+
for _, typeSpec := range typeSpecs {
|
|
128
|
+
typeName := typeSpec.Name.Name
|
|
129
|
+
deps := c.extractTypeDependencies(typeSpec.Type, typeSpecMap)
|
|
130
|
+
dependencies[typeName] = deps
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Perform topological sort
|
|
134
|
+
sorted, err := c.topologicalSort(dependencies)
|
|
135
|
+
if err != nil {
|
|
136
|
+
return nil, err
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Build result in sorted order
|
|
140
|
+
var result []*ast.TypeSpec
|
|
141
|
+
for _, typeName := range sorted {
|
|
142
|
+
if typeSpec, exists := typeSpecMap[typeName]; exists {
|
|
143
|
+
result = append(result, typeSpec)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return result, nil
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// extractTypeDependencies extracts structural type dependencies from a type expression.
|
|
151
|
+
// Only dependencies that affect class initialization order are considered:
|
|
152
|
+
// - Struct field types (cause initialization order issues)
|
|
153
|
+
// - Embedded types (directly affect struct layout)
|
|
154
|
+
// - Type aliases (direct type references)
|
|
155
|
+
// Method parameters and return types are ignored as they're just annotations.
|
|
156
|
+
func (c *GoToTSCompiler) extractTypeDependencies(typeExpr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) []string {
|
|
157
|
+
var deps []string
|
|
158
|
+
|
|
159
|
+
switch t := typeExpr.(type) {
|
|
160
|
+
case *ast.Ident:
|
|
161
|
+
// Direct type reference (e.g., type MyType OtherType)
|
|
162
|
+
if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
|
|
163
|
+
deps = append(deps, t.Name)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case *ast.StructType:
|
|
167
|
+
// Struct type - check field types only
|
|
168
|
+
if t.Fields != nil {
|
|
169
|
+
for _, field := range t.Fields.List {
|
|
170
|
+
fieldDeps := c.extractTypeDependencies(field.Type, typeSpecMap)
|
|
171
|
+
deps = append(deps, fieldDeps...)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case *ast.ArrayType:
|
|
176
|
+
// Array type - check element type
|
|
177
|
+
elemDeps := c.extractTypeDependencies(t.Elt, typeSpecMap)
|
|
178
|
+
deps = append(deps, elemDeps...)
|
|
179
|
+
|
|
180
|
+
case *ast.StarExpr:
|
|
181
|
+
// Pointer type - check pointed-to type
|
|
182
|
+
ptrDeps := c.extractTypeDependencies(t.X, typeSpecMap)
|
|
183
|
+
deps = append(deps, ptrDeps...)
|
|
184
|
+
|
|
185
|
+
case *ast.MapType:
|
|
186
|
+
// Map type - check key and value types
|
|
187
|
+
keyDeps := c.extractTypeDependencies(t.Key, typeSpecMap)
|
|
188
|
+
valueDeps := c.extractTypeDependencies(t.Value, typeSpecMap)
|
|
189
|
+
deps = append(deps, keyDeps...)
|
|
190
|
+
deps = append(deps, valueDeps...)
|
|
191
|
+
|
|
192
|
+
case *ast.InterfaceType:
|
|
193
|
+
// Interface type - methods don't create initialization dependencies
|
|
194
|
+
// Only embedded interfaces matter, but those are rare and complex to handle
|
|
195
|
+
// For now, interfaces are considered to have no dependencies
|
|
196
|
+
|
|
197
|
+
case *ast.FuncType:
|
|
198
|
+
// Function types don't create initialization dependencies
|
|
199
|
+
|
|
200
|
+
case *ast.SelectorExpr:
|
|
201
|
+
// External package types don't create local dependencies
|
|
202
|
+
|
|
203
|
+
// Add other type expressions as needed
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Sort dependencies for deterministic output
|
|
207
|
+
sort.Strings(deps)
|
|
208
|
+
return deps
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// sortVarSpecsByTypeDependencies sorts variable declarations based on their type dependencies
|
|
212
|
+
func (c *GoToTSCompiler) sortVarSpecsByTypeDependencies(varSpecs []*ast.ValueSpec, typeSpecs []*ast.TypeSpec) ([]*ast.ValueSpec, error) {
|
|
213
|
+
if len(varSpecs) <= 1 {
|
|
214
|
+
return varSpecs, nil
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Build type name map
|
|
218
|
+
typeSpecMap := make(map[string]*ast.TypeSpec)
|
|
219
|
+
for _, typeSpec := range typeSpecs {
|
|
220
|
+
typeSpecMap[typeSpec.Name.Name] = typeSpec
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Group variables by dependency status with names for sorting
|
|
224
|
+
type namedVarSpec struct {
|
|
225
|
+
spec *ast.ValueSpec
|
|
226
|
+
name string
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
var independentVars []namedVarSpec
|
|
230
|
+
var dependentVars []namedVarSpec
|
|
231
|
+
|
|
232
|
+
for _, varSpec := range varSpecs {
|
|
233
|
+
// Get variable name for sorting
|
|
234
|
+
varName := ""
|
|
235
|
+
if len(varSpec.Names) > 0 {
|
|
236
|
+
varName = varSpec.Names[0].Name
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
hasDependency := false
|
|
240
|
+
|
|
241
|
+
// Check type annotation
|
|
242
|
+
if varSpec.Type != nil {
|
|
243
|
+
deps := c.extractTypeDependencies(varSpec.Type, typeSpecMap)
|
|
244
|
+
if len(deps) > 0 {
|
|
245
|
+
hasDependency = true
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Check initializer expressions for type usage
|
|
250
|
+
if !hasDependency {
|
|
251
|
+
for _, value := range varSpec.Values {
|
|
252
|
+
if c.hasTypeReferences(value, typeSpecMap) {
|
|
253
|
+
hasDependency = true
|
|
254
|
+
break
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
namedVar := namedVarSpec{spec: varSpec, name: varName}
|
|
260
|
+
if hasDependency {
|
|
261
|
+
dependentVars = append(dependentVars, namedVar)
|
|
262
|
+
} else {
|
|
263
|
+
independentVars = append(independentVars, namedVar)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Sort both groups by name for deterministic output
|
|
268
|
+
sort.Slice(independentVars, func(i, j int) bool {
|
|
269
|
+
return independentVars[i].name < independentVars[j].name
|
|
270
|
+
})
|
|
271
|
+
sort.Slice(dependentVars, func(i, j int) bool {
|
|
272
|
+
return dependentVars[i].name < dependentVars[j].name
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
// Return independent variables first, then dependent ones
|
|
276
|
+
result := make([]*ast.ValueSpec, 0, len(varSpecs))
|
|
277
|
+
for _, namedVar := range independentVars {
|
|
278
|
+
result = append(result, namedVar.spec)
|
|
279
|
+
}
|
|
280
|
+
for _, namedVar := range dependentVars {
|
|
281
|
+
result = append(result, namedVar.spec)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return result, nil
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// hasTypeReferences checks if an expression contains references to local types
|
|
288
|
+
func (c *GoToTSCompiler) hasTypeReferences(expr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) bool {
|
|
289
|
+
hasRef := false
|
|
290
|
+
|
|
291
|
+
ast.Inspect(expr, func(n ast.Node) bool {
|
|
292
|
+
switch t := n.(type) {
|
|
293
|
+
case *ast.CallExpr:
|
|
294
|
+
// Check function calls like new Message(), makeChannel<Message>()
|
|
295
|
+
if ident, ok := t.Fun.(*ast.Ident); ok {
|
|
296
|
+
if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
|
|
297
|
+
hasRef = true
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Check type arguments in generic calls
|
|
302
|
+
if funcType, ok := t.Fun.(*ast.IndexExpr); ok {
|
|
303
|
+
if c.hasTypeReferences(funcType.Index, typeSpecMap) {
|
|
304
|
+
hasRef = true
|
|
305
|
+
return false
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
case *ast.CompositeLit:
|
|
309
|
+
// Check composite literals like Message{...}
|
|
310
|
+
if ident, ok := t.Type.(*ast.Ident); ok {
|
|
311
|
+
if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
|
|
312
|
+
hasRef = true
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
case *ast.Ident:
|
|
317
|
+
// Check direct type references
|
|
318
|
+
if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
|
|
319
|
+
hasRef = true
|
|
320
|
+
return false
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return !hasRef // Stop walking if we found a reference
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
return hasRef
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// topologicalSort performs a topological sort of the dependency graph
|
|
330
|
+
func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]string, error) {
|
|
331
|
+
// Kahn's algorithm for topological sorting with deterministic ordering
|
|
332
|
+
inDegree := make(map[string]int)
|
|
333
|
+
graph := make(map[string][]string)
|
|
334
|
+
|
|
335
|
+
// Initialize in-degree counts and reverse graph
|
|
336
|
+
for node := range dependencies {
|
|
337
|
+
inDegree[node] = 0
|
|
338
|
+
graph[node] = []string{}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Build reverse graph and count in-degrees
|
|
342
|
+
for node, deps := range dependencies {
|
|
343
|
+
// Sort dependencies for consistent output
|
|
344
|
+
sortedDeps := make([]string, len(deps))
|
|
345
|
+
copy(sortedDeps, deps)
|
|
346
|
+
sort.Strings(sortedDeps)
|
|
347
|
+
|
|
348
|
+
for _, dep := range sortedDeps {
|
|
349
|
+
if _, exists := inDegree[dep]; exists {
|
|
350
|
+
graph[dep] = append(graph[dep], node)
|
|
351
|
+
inDegree[node]++
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Sort neighbors in graph for consistency
|
|
357
|
+
for node := range graph {
|
|
358
|
+
sort.Strings(graph[node])
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Find nodes with no incoming edges and sort them
|
|
362
|
+
var queue []string
|
|
363
|
+
for node, degree := range inDegree {
|
|
364
|
+
if degree == 0 {
|
|
365
|
+
queue = append(queue, node)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
sort.Strings(queue) // Sort initial queue for deterministic output
|
|
369
|
+
|
|
370
|
+
var result []string
|
|
371
|
+
|
|
372
|
+
for len(queue) > 0 {
|
|
373
|
+
// Remove node from queue (already sorted)
|
|
374
|
+
current := queue[0]
|
|
375
|
+
queue = queue[1:]
|
|
376
|
+
result = append(result, current)
|
|
377
|
+
|
|
378
|
+
// Collect new zero-degree nodes
|
|
379
|
+
var newZeroNodes []string
|
|
380
|
+
for _, neighbor := range graph[current] {
|
|
381
|
+
inDegree[neighbor]--
|
|
382
|
+
if inDegree[neighbor] == 0 {
|
|
383
|
+
newZeroNodes = append(newZeroNodes, neighbor)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Sort new zero-degree nodes and add to queue
|
|
388
|
+
sort.Strings(newZeroNodes)
|
|
389
|
+
queue = append(queue, newZeroNodes...)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Check for cycles
|
|
393
|
+
if len(result) != len(dependencies) {
|
|
394
|
+
return nil, fmt.Errorf("circular dependency detected in type declarations")
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return result, nil
|
|
398
|
+
}
|
|
399
|
+
|
|
47
400
|
// WriteFuncDeclAsFunction translates a Go function declaration (`ast.FuncDecl`)
|
|
48
401
|
// that does not have a receiver (i.e., it's a regular function, not a method)
|
|
49
402
|
// into a TypeScript function.
|
|
@@ -239,24 +239,24 @@ func (c *GoToTSCompiler) writeMethodValue(exp *ast.SelectorExpr, selection *type
|
|
|
239
239
|
// The receiver should be a copy of the dereferenced value
|
|
240
240
|
c.tsw.WriteLiterally(".value.")
|
|
241
241
|
c.WriteIdent(exp.Sel, false)
|
|
242
|
-
c.tsw.WriteLiterally(".bind(")
|
|
242
|
+
c.tsw.WriteLiterally(".bind($.markAsStructValue(")
|
|
243
243
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
244
244
|
return fmt.Errorf("failed to write method value receiver for binding: %w", err)
|
|
245
245
|
}
|
|
246
|
-
c.tsw.WriteLiterally("!.value.clone())")
|
|
246
|
+
c.tsw.WriteLiterally("!.value.clone()))")
|
|
247
247
|
} else if !isPointerReceiver && !baseIsPointer {
|
|
248
248
|
// Value receiver method on value type: t.Mv
|
|
249
249
|
// The receiver should be a copy of the value
|
|
250
250
|
c.tsw.WriteLiterally(".")
|
|
251
251
|
c.WriteIdent(exp.Sel, false)
|
|
252
|
-
c.tsw.WriteLiterally(".bind(")
|
|
252
|
+
c.tsw.WriteLiterally(".bind($.markAsStructValue(")
|
|
253
253
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
254
254
|
return fmt.Errorf("failed to write method value receiver for binding: %w", err)
|
|
255
255
|
}
|
|
256
256
|
if baseIsPointer {
|
|
257
257
|
c.tsw.WriteLiterally("!")
|
|
258
258
|
}
|
|
259
|
-
c.tsw.WriteLiterally(".clone())")
|
|
259
|
+
c.tsw.WriteLiterally(".clone()))")
|
|
260
260
|
} else {
|
|
261
261
|
// Pointer receiver method on pointer type: pt.Mp
|
|
262
262
|
// The receiver should be the pointer itself
|