goscript 0.0.26 → 0.0.29

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.
Files changed (228) hide show
  1. package/README.md +4 -4
  2. package/cmd/goscript/cmd_compile.go +0 -3
  3. package/cmd/goscript/deps.go +11 -0
  4. package/compiler/analysis.go +298 -55
  5. package/compiler/assignment.go +2 -2
  6. package/compiler/builtin_test.go +1 -1
  7. package/compiler/compiler.go +200 -68
  8. package/compiler/compiler_test.go +17 -24
  9. package/compiler/composite-lit.go +32 -8
  10. package/compiler/decl.go +6 -6
  11. package/compiler/expr-call.go +170 -15
  12. package/compiler/expr-selector.go +100 -0
  13. package/compiler/expr.go +1 -1
  14. package/compiler/protobuf.go +557 -0
  15. package/compiler/spec-struct.go +4 -0
  16. package/compiler/spec-value.go +89 -10
  17. package/compiler/spec.go +254 -1
  18. package/compiler/stmt-assign.go +35 -0
  19. package/compiler/type-assert.go +87 -0
  20. package/compiler/type.go +4 -1
  21. package/dist/gs/builtin/builtin.d.ts +20 -1
  22. package/dist/gs/builtin/builtin.js +95 -4
  23. package/dist/gs/builtin/builtin.js.map +1 -1
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +21 -2
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/errors/errors.d.ts +5 -6
  28. package/dist/gs/errors/errors.js.map +1 -1
  29. package/dist/gs/internal/oserror/errors.d.ts +6 -0
  30. package/dist/gs/internal/oserror/errors.js +7 -0
  31. package/dist/gs/internal/oserror/errors.js.map +1 -0
  32. package/dist/gs/internal/oserror/index.d.ts +1 -0
  33. package/dist/gs/internal/oserror/index.js +2 -0
  34. package/dist/gs/internal/oserror/index.js.map +1 -0
  35. package/dist/gs/io/fs/format.d.ts +3 -0
  36. package/dist/gs/io/fs/format.js +56 -0
  37. package/dist/gs/io/fs/format.js.map +1 -0
  38. package/dist/gs/io/fs/fs.d.ts +79 -0
  39. package/dist/gs/io/fs/fs.js +200 -0
  40. package/dist/gs/io/fs/fs.js.map +1 -0
  41. package/dist/gs/io/fs/glob.d.ts +10 -0
  42. package/dist/gs/io/fs/glob.js +141 -0
  43. package/dist/gs/io/fs/glob.js.map +1 -0
  44. package/dist/gs/io/fs/index.d.ts +8 -0
  45. package/dist/gs/io/fs/index.js +9 -0
  46. package/dist/gs/io/fs/index.js.map +1 -0
  47. package/dist/gs/io/fs/readdir.d.ts +7 -0
  48. package/dist/gs/io/fs/readdir.js +152 -0
  49. package/dist/gs/io/fs/readdir.js.map +1 -0
  50. package/dist/gs/io/fs/readfile.d.ts +6 -0
  51. package/dist/gs/io/fs/readfile.js +118 -0
  52. package/dist/gs/io/fs/readfile.js.map +1 -0
  53. package/dist/gs/io/fs/stat.d.ts +6 -0
  54. package/dist/gs/io/fs/stat.js +87 -0
  55. package/dist/gs/io/fs/stat.js.map +1 -0
  56. package/dist/gs/io/fs/sub.d.ts +6 -0
  57. package/dist/gs/io/fs/sub.js +172 -0
  58. package/dist/gs/io/fs/sub.js.map +1 -0
  59. package/dist/gs/io/fs/walk.d.ts +7 -0
  60. package/dist/gs/io/fs/walk.js +76 -0
  61. package/dist/gs/io/fs/walk.js.map +1 -0
  62. package/dist/gs/io/index.d.ts +1 -0
  63. package/dist/gs/io/index.js +2 -0
  64. package/dist/gs/io/index.js.map +1 -0
  65. package/dist/gs/io/io.d.ts +107 -0
  66. package/dist/gs/io/io.js +385 -0
  67. package/dist/gs/io/io.js.map +1 -0
  68. package/dist/gs/path/index.d.ts +2 -0
  69. package/dist/gs/path/index.js +3 -0
  70. package/dist/gs/path/index.js.map +1 -0
  71. package/dist/gs/path/match.d.ts +6 -0
  72. package/dist/gs/path/match.js +281 -0
  73. package/dist/gs/path/match.js.map +1 -0
  74. package/dist/gs/path/path.d.ts +7 -0
  75. package/dist/gs/path/path.js +256 -0
  76. package/dist/gs/path/path.js.map +1 -0
  77. package/dist/gs/strings/builder.d.ts +18 -0
  78. package/dist/gs/strings/builder.js +205 -0
  79. package/dist/gs/strings/builder.js.map +1 -0
  80. package/dist/gs/strings/clone.d.ts +1 -0
  81. package/dist/gs/strings/clone.js +16 -0
  82. package/dist/gs/strings/clone.js.map +1 -0
  83. package/dist/gs/strings/compare.d.ts +1 -0
  84. package/dist/gs/strings/compare.js +14 -0
  85. package/dist/gs/strings/compare.js.map +1 -0
  86. package/dist/gs/strings/index.d.ts +2 -0
  87. package/dist/gs/strings/index.js +3 -0
  88. package/dist/gs/strings/index.js.map +1 -0
  89. package/dist/gs/strings/iter.d.ts +8 -0
  90. package/dist/gs/strings/iter.js +160 -0
  91. package/dist/gs/strings/iter.js.map +1 -0
  92. package/dist/gs/strings/reader.d.ts +34 -0
  93. package/dist/gs/strings/reader.js +418 -0
  94. package/dist/gs/strings/reader.js.map +1 -0
  95. package/dist/gs/strings/replace.d.ts +106 -0
  96. package/dist/gs/strings/replace.js +1136 -0
  97. package/dist/gs/strings/replace.js.map +1 -0
  98. package/dist/gs/strings/search.d.ts +24 -0
  99. package/dist/gs/strings/search.js +169 -0
  100. package/dist/gs/strings/search.js.map +1 -0
  101. package/dist/gs/strings/strings.d.ts +47 -0
  102. package/dist/gs/strings/strings.js +418 -0
  103. package/dist/gs/strings/strings.js.map +1 -0
  104. package/dist/gs/stringslite/index.d.ts +1 -0
  105. package/dist/gs/stringslite/index.js +2 -0
  106. package/dist/gs/stringslite/index.js.map +1 -0
  107. package/dist/gs/stringslite/strings.d.ts +11 -0
  108. package/dist/gs/stringslite/strings.js +67 -0
  109. package/dist/gs/stringslite/strings.js.map +1 -0
  110. package/dist/gs/sync/index.d.ts +1 -0
  111. package/dist/gs/sync/index.js +2 -0
  112. package/dist/gs/sync/index.js.map +1 -0
  113. package/dist/gs/sync/sync.d.ts +79 -0
  114. package/dist/gs/sync/sync.js +392 -0
  115. package/dist/gs/sync/sync.js.map +1 -0
  116. package/dist/gs/time/time.d.ts +11 -2
  117. package/dist/gs/time/time.js +337 -12
  118. package/dist/gs/time/time.js.map +1 -1
  119. package/dist/gs/unicode/index.d.ts +1 -0
  120. package/dist/gs/unicode/index.js +2 -0
  121. package/dist/gs/unicode/index.js.map +1 -0
  122. package/dist/gs/unicode/unicode.d.ts +105 -0
  123. package/dist/gs/unicode/unicode.js +332 -0
  124. package/dist/gs/unicode/unicode.js.map +1 -0
  125. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  126. package/dist/gs/unicode/utf8/index.js +3 -0
  127. package/dist/gs/unicode/utf8/index.js.map +1 -0
  128. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  129. package/dist/gs/unicode/utf8/utf8.js +196 -0
  130. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  131. package/dist/gs/unsafe/index.d.ts +1 -0
  132. package/dist/gs/unsafe/index.js +2 -0
  133. package/dist/gs/unsafe/index.js.map +1 -0
  134. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  135. package/dist/gs/unsafe/unsafe.js +44 -0
  136. package/dist/gs/unsafe/unsafe.js.map +1 -0
  137. package/go.mod +2 -1
  138. package/go.sum +6 -2
  139. package/gs/README.md +6 -0
  140. package/gs/builtin/builtin.ts +171 -0
  141. package/gs/builtin/channel.ts +683 -0
  142. package/gs/builtin/defer.ts +58 -0
  143. package/gs/builtin/index.ts +1 -0
  144. package/gs/builtin/io.ts +22 -0
  145. package/gs/builtin/map.ts +50 -0
  146. package/gs/builtin/slice.ts +1030 -0
  147. package/gs/builtin/type.ts +1106 -0
  148. package/gs/builtin/varRef.ts +25 -0
  149. package/gs/cmp/godoc.txt +8 -0
  150. package/gs/cmp/index.ts +29 -0
  151. package/gs/context/context.ts +401 -0
  152. package/gs/context/godoc.txt +69 -0
  153. package/gs/context/index.ts +1 -0
  154. package/gs/errors/errors.ts +223 -0
  155. package/gs/errors/godoc.txt +63 -0
  156. package/gs/errors/index.ts +1 -0
  157. package/gs/internal/goarch/godoc.txt +39 -0
  158. package/gs/internal/goarch/index.ts +18 -0
  159. package/gs/internal/oserror/errors.ts +14 -0
  160. package/gs/internal/oserror/index.ts +1 -0
  161. package/gs/io/fs/format.ts +65 -0
  162. package/gs/io/fs/fs.ts +359 -0
  163. package/gs/io/fs/glob.ts +167 -0
  164. package/gs/io/fs/godoc.txt +35 -0
  165. package/gs/io/fs/index.ts +8 -0
  166. package/gs/io/fs/readdir.ts +126 -0
  167. package/gs/io/fs/readfile.ts +77 -0
  168. package/gs/io/fs/stat.ts +38 -0
  169. package/gs/io/fs/sub.ts +208 -0
  170. package/gs/io/fs/walk.ts +89 -0
  171. package/gs/io/godoc.txt +61 -0
  172. package/gs/io/index.ts +1 -0
  173. package/gs/io/io.go +75 -0
  174. package/gs/io/io.ts +546 -0
  175. package/gs/iter/godoc.txt +203 -0
  176. package/gs/iter/index.ts +1 -0
  177. package/gs/iter/iter.ts +117 -0
  178. package/gs/math/bits/index.ts +356 -0
  179. package/gs/math/godoc.txt +76 -0
  180. package/gs/path/index.ts +2 -0
  181. package/gs/path/match.ts +307 -0
  182. package/gs/path/path.ts +301 -0
  183. package/gs/runtime/godoc.txt +331 -0
  184. package/gs/runtime/index.ts +1 -0
  185. package/gs/runtime/runtime.ts +178 -0
  186. package/gs/slices/godoc.txt +44 -0
  187. package/gs/slices/index.ts +1 -0
  188. package/gs/slices/slices.ts +22 -0
  189. package/gs/strings/builder.test.ts +121 -0
  190. package/gs/strings/builder.ts +223 -0
  191. package/gs/strings/clone.test.ts +43 -0
  192. package/gs/strings/clone.ts +17 -0
  193. package/gs/strings/compare.test.ts +84 -0
  194. package/gs/strings/compare.ts +13 -0
  195. package/gs/strings/godoc.txt +66 -0
  196. package/gs/strings/index.ts +2 -0
  197. package/gs/strings/iter.test.ts +343 -0
  198. package/gs/strings/iter.ts +171 -0
  199. package/gs/strings/reader.test.ts +242 -0
  200. package/gs/strings/reader.ts +451 -0
  201. package/gs/strings/replace.test.ts +181 -0
  202. package/gs/strings/replace.ts +1310 -0
  203. package/gs/strings/search.test.ts +214 -0
  204. package/gs/strings/search.ts +213 -0
  205. package/gs/strings/strings.test.ts +477 -0
  206. package/gs/strings/strings.ts +510 -0
  207. package/gs/stringslite/godoc.txt +17 -0
  208. package/gs/stringslite/index.ts +1 -0
  209. package/gs/stringslite/strings.ts +82 -0
  210. package/gs/sync/godoc.txt +21 -0
  211. package/gs/sync/index.ts +1 -0
  212. package/gs/sync/sync.go +64 -0
  213. package/gs/sync/sync.ts +449 -0
  214. package/gs/time/godoc.txt +116 -0
  215. package/gs/time/index.ts +1 -0
  216. package/gs/time/time.ts +585 -0
  217. package/gs/unicode/godoc.txt +52 -0
  218. package/gs/unicode/index.ts +1 -0
  219. package/gs/unicode/unicode.go +38 -0
  220. package/gs/unicode/unicode.ts +418 -0
  221. package/gs/unicode/utf8/godoc.txt +22 -0
  222. package/gs/unicode/utf8/index.ts +2 -0
  223. package/gs/unicode/utf8/utf8.ts +227 -0
  224. package/gs/unsafe/godoc.txt +19 -0
  225. package/gs/unsafe/index.ts +1 -0
  226. package/gs/unsafe/unsafe.test.ts +68 -0
  227. package/gs/unsafe/unsafe.ts +77 -0
  228. package/package.json +4 -3
package/README.md CHANGED
@@ -152,9 +152,10 @@ This example imports the `compile` function, configures it to compile a Go packa
152
152
  Check [the compliance tests](./compliance/COMPLIANCE.md) for implementation progress.
153
153
 
154
154
  - [X] Simple programs compile & run
155
- - [ ] Compliance for basic language features complete
156
- - [ ] Function coloring and async logic
157
- - [ ] Work our way up to more complex programs
155
+ - [X] Compliance for basic language features complete
156
+ - [X] Function coloring and async logic
157
+ - [X] Work our way up to more complex programs
158
+ - [ ] Implement most of the Go standard library
158
159
  - [ ] Generate init() function to recursively initialize packages
159
160
  - [ ] Tooling to integrate with typescript compiler
160
161
  - [ ] "go test" implementation with Go -> Ts transformation
@@ -162,7 +163,6 @@ Check [the compliance tests](./compliance/COMPLIANCE.md) for implementation prog
162
163
  - [ ] performance testing
163
164
  - [ ] examples of calling Go code from TypeScript
164
165
 
165
-
166
166
  ## Generated Code
167
167
 
168
168
  Below is a simple example of how code is generated:
@@ -8,9 +8,6 @@ import (
8
8
  "github.com/aperturerobotics/goscript/compiler"
9
9
  "github.com/pkg/errors"
10
10
  "github.com/sirupsen/logrus"
11
-
12
- // _ ensure we include the gs package
13
- _ "github.com/aperturerobotics/goscript"
14
11
  )
15
12
 
16
13
  var (
@@ -0,0 +1,11 @@
1
+ package main
2
+
3
+ // This file has _ imports to ensure we include these in the go module.
4
+
5
+ import (
6
+ // _ ensure we include the gs package
7
+ _ "github.com/aperturerobotics/goscript"
8
+ // _ ensure we include the gs metadata packages
9
+ _ "github.com/aperturerobotics/goscript/gs/sync"
10
+ _ "github.com/aperturerobotics/goscript/gs/unicode"
11
+ )
@@ -4,6 +4,8 @@ import (
4
4
  "go/ast"
5
5
  "go/token"
6
6
  "go/types"
7
+ "path/filepath"
8
+ "strings"
7
9
 
8
10
  "golang.org/x/tools/go/packages"
9
11
  )
@@ -54,6 +56,8 @@ type NodeInfo struct {
54
56
  IsBareReturn bool
55
57
  EnclosingFuncDecl *ast.FuncDecl
56
58
  EnclosingFuncLit *ast.FuncLit
59
+ IsInsideFunction bool // true if this declaration is inside a function body
60
+ IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
57
61
  }
58
62
 
59
63
  // Analysis holds information gathered during the analysis phase of the Go code compilation.
@@ -79,16 +83,39 @@ type Analysis struct {
79
83
  // Keep specialized maps that serve different purposes
80
84
  // FuncLitData tracks function literal specific data since they don't have types.Object
81
85
  FuncLitData map[*ast.FuncLit]*FunctionInfo
86
+
87
+ // PackageMetadata holds package-level metadata
88
+ PackageMetadata map[string]interface{}
89
+ }
90
+
91
+ // PackageAnalysis holds cross-file analysis data for a package
92
+ type PackageAnalysis struct {
93
+ // FunctionDefs maps file names to the functions defined in that file
94
+ // Key: filename (without .go extension), Value: list of function names
95
+ FunctionDefs map[string][]string
96
+
97
+ // FunctionCalls maps file names to the functions they call from other files
98
+ // Key: filename (without .go extension), Value: map[sourceFile][]functionNames
99
+ FunctionCalls map[string]map[string][]string
82
100
  }
83
101
 
84
102
  // NewAnalysis creates a new Analysis instance.
85
103
  func NewAnalysis() *Analysis {
86
104
  return &Analysis{
87
- VariableUsage: make(map[types.Object]*VariableUsageInfo),
88
- Imports: make(map[string]*fileImport),
89
- FunctionData: make(map[types.Object]*FunctionInfo),
90
- NodeData: make(map[ast.Node]*NodeInfo),
91
- FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
105
+ VariableUsage: make(map[types.Object]*VariableUsageInfo),
106
+ Imports: make(map[string]*fileImport),
107
+ FunctionData: make(map[types.Object]*FunctionInfo),
108
+ NodeData: make(map[ast.Node]*NodeInfo),
109
+ FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
110
+ PackageMetadata: make(map[string]interface{}),
111
+ }
112
+ }
113
+
114
+ // NewPackageAnalysis creates a new PackageAnalysis instance
115
+ func NewPackageAnalysis() *PackageAnalysis {
116
+ return &PackageAnalysis{
117
+ FunctionDefs: make(map[string][]string),
118
+ FunctionCalls: make(map[string]map[string][]string),
92
119
  }
93
120
  }
94
121
 
@@ -210,54 +237,16 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
210
237
  return false
211
238
  }
212
239
 
213
- // NeedsVarRefDeref determines whether a pointer dereference operation (*ptr) needs
214
- // additional .value access beyond the standard !.value pattern.
215
- //
216
- // Standard pattern: ptr!.value (for *int, *string, etc.)
217
- // Enhanced pattern: ptr.value!.value (when ptr itself is variable referenced)
218
- //
219
- // This function returns true when the pointer variable itself is variable referenced,
220
- // meaning we need an extra .value to access the actual pointer before dereferencing.
221
- //
222
- // Examples:
223
- // - ptr := &x (ptr not variable referenced): *ptr => ptr!.value
224
- // - ptrPtr := &ptr (ptr is variable referenced): *ptr => ptr.value!.value
225
- //
226
- // Args:
227
- //
228
- // ptrType: The type of the pointer being dereferenced
229
- //
230
- // Returns:
231
- //
232
- // true if additional .value access is needed due to the pointer being variable referenced
233
- func (a *Analysis) NeedsVarRefDeref(ptrType types.Type) bool {
234
- // For now, return false - this would need more sophisticated analysis
235
- // to track when pointer variables themselves are variable referenced
236
- return false
237
- }
238
-
239
- // NeedsVarRefFieldAccess determines whether a pointer variable needs the .value
240
- // access when performing field access through the pointer.
241
- //
242
- // In Go, field access through pointers is automatically dereferenced:
243
- //
244
- // ptr.Field // equivalent to (*ptr).Field
245
- //
246
- // In TypeScript, we need to determine if the pointer is:
247
- // 1. A simple pointer: ptr.Field (no .value needed)
248
- // 2. A variable-referenced pointer: ptr.value.Field (needs .value)
249
- //
250
- // Args:
251
- //
252
- // ptrType: The pointer type being used for field access
253
- //
254
- // Returns:
255
- //
256
- // true if .value access is needed before field access
257
- func (a *Analysis) NeedsVarRefFieldAccess(ptrType types.Type) bool {
258
- // This would require analysis of the specific pointer variable
259
- // For now, return false as a conservative default
260
- return false
240
+ // IsMethodValue returns whether the given SelectorExpr node is a method value that needs binding.
241
+ func (a *Analysis) IsMethodValue(node *ast.SelectorExpr) bool {
242
+ if node == nil {
243
+ return false
244
+ }
245
+ nodeInfo := a.NodeData[node]
246
+ if nodeInfo == nil {
247
+ return false
248
+ }
249
+ return nodeInfo.IsMethodValue
261
250
  }
262
251
 
263
252
  // analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
@@ -590,8 +579,20 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
590
579
  return v
591
580
 
592
581
  case *ast.SelectorExpr:
593
- // No need to track private field access since all fields are public
594
- return v
582
+ // Initialize NodeData for this selector expression
583
+ if v.analysis.NodeData[n] == nil {
584
+ v.analysis.NodeData[n] = &NodeInfo{}
585
+ }
586
+ v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
587
+
588
+ // Check if this is a method value (method being used as a value, not called immediately)
589
+ if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
590
+ if selection.Kind() == types.MethodVal {
591
+ // This is a method value - mark it for binding during code generation
592
+ v.analysis.NodeData[n].IsMethodValue = true
593
+ }
594
+ }
595
+ return v // Continue traversal
595
596
 
596
597
  case *ast.AssignStmt:
597
598
  for i, currentLHSExpr := range n.Lhs {
@@ -718,6 +719,25 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
718
719
  }
719
720
  }
720
721
  return v // Continue traversal
722
+
723
+ case *ast.DeclStmt:
724
+ // Handle declarations inside functions (const, var, type declarations within function bodies)
725
+ // These should not have export modifiers in TypeScript
726
+ if genDecl, ok := n.Decl.(*ast.GenDecl); ok {
727
+ // Check if we're inside a function (either FuncDecl or FuncLit)
728
+ isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
729
+
730
+ if isInsideFunction {
731
+ // Mark all specs in this declaration as being inside a function
732
+ for _, spec := range genDecl.Specs {
733
+ if v.analysis.NodeData[spec] == nil {
734
+ v.analysis.NodeData[spec] = &NodeInfo{}
735
+ }
736
+ v.analysis.NodeData[spec].IsInsideFunction = true
737
+ }
738
+ }
739
+ }
740
+ return v // Continue traversal
721
741
  }
722
742
 
723
743
  // For all other nodes, continue traversal
@@ -756,6 +776,37 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
756
776
  }
757
777
  }
758
778
 
779
+ // Check for method calls on imported types (e.g., sync.Mutex.Lock())
780
+ if selExpr, ok := s.Fun.(*ast.SelectorExpr); ok {
781
+ // Check if this is a method call on a variable (e.g., mu.Lock())
782
+ if ident, ok := selExpr.X.(*ast.Ident); ok {
783
+ // Get the type of the receiver
784
+ if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
785
+ if varObj, ok := obj.(*types.Var); ok {
786
+ // Get the type name and package
787
+ if namedType, ok := varObj.Type().(*types.Named); ok {
788
+ typeName := namedType.Obj().Name()
789
+ methodName := selExpr.Sel.Name
790
+
791
+ // Check if the type is from an imported package
792
+ if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
793
+ pkgPath := typePkg.Path()
794
+ // Extract package name from path (e.g., "sync" from "github.com/.../gs/sync")
795
+ parts := strings.Split(pkgPath, "/")
796
+ pkgName := parts[len(parts)-1]
797
+
798
+ // Check if this method is async based on metadata
799
+ if v.analysis.IsMethodAsync(pkgName, typeName, methodName) {
800
+ hasAsync = true
801
+ return false
802
+ }
803
+ }
804
+ }
805
+ }
806
+ }
807
+ }
808
+ }
809
+
759
810
  // TODO: Add detection of method calls on async types
760
811
  }
761
812
 
@@ -789,6 +840,9 @@ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap
789
840
  // Store the comment map in the analysis object
790
841
  analysis.Cmap = cmap
791
842
 
843
+ // Load package metadata for async function detection
844
+ analysis.LoadPackageMetadata()
845
+
792
846
  // Process imports from the file
793
847
  for _, imp := range file.Imports {
794
848
  path := ""
@@ -830,6 +884,20 @@ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap
830
884
 
831
885
  // Walk the AST with our visitor
832
886
  ast.Walk(visitor, file)
887
+
888
+ // Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
889
+ // This distinguishes between method calls (obj.Method()) and method values (obj.Method)
890
+ ast.Inspect(file, func(n ast.Node) bool {
891
+ if callExpr, ok := n.(*ast.CallExpr); ok {
892
+ if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
893
+ // This SelectorExpr is the function being called, so it's NOT a method value
894
+ if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
895
+ nodeInfo.IsMethodValue = false
896
+ }
897
+ }
898
+ }
899
+ return true
900
+ })
833
901
  }
834
902
 
835
903
  // getNamedReturns retrieves the named returns for a function
@@ -844,3 +912,178 @@ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
844
912
  }
845
913
  return namedReturns
846
914
  }
915
+
916
+ // AnalyzePackage performs package-level analysis to collect function definitions
917
+ // and calls across all files in the package for auto-import generation
918
+ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
919
+ analysis := NewPackageAnalysis()
920
+
921
+ // First pass: collect all function definitions per file
922
+ for i, syntax := range pkg.Syntax {
923
+ fileName := pkg.CompiledGoFiles[i]
924
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
925
+
926
+ var functions []string
927
+ for _, decl := range syntax.Decls {
928
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
929
+ // Only collect top-level functions (not methods)
930
+ if funcDecl.Recv == nil {
931
+ functions = append(functions, funcDecl.Name.Name)
932
+ }
933
+ }
934
+ }
935
+
936
+ if len(functions) > 0 {
937
+ analysis.FunctionDefs[baseFileName] = functions
938
+ }
939
+ }
940
+
941
+ // Second pass: analyze function calls and determine which need imports
942
+ for i, syntax := range pkg.Syntax {
943
+ fileName := pkg.CompiledGoFiles[i]
944
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
945
+
946
+ // Find all function calls in this file
947
+ callsFromOtherFiles := make(map[string][]string)
948
+
949
+ ast.Inspect(syntax, func(n ast.Node) bool {
950
+ if callExpr, ok := n.(*ast.CallExpr); ok {
951
+ if ident, ok := callExpr.Fun.(*ast.Ident); ok {
952
+ funcName := ident.Name
953
+
954
+ // Check if this function is defined in the current file
955
+ currentFileFuncs := analysis.FunctionDefs[baseFileName]
956
+ isDefinedInCurrentFile := false
957
+ for _, f := range currentFileFuncs {
958
+ if f == funcName {
959
+ isDefinedInCurrentFile = true
960
+ break
961
+ }
962
+ }
963
+
964
+ // If not defined in current file, find which file defines it
965
+ if !isDefinedInCurrentFile {
966
+ for sourceFile, funcs := range analysis.FunctionDefs {
967
+ if sourceFile == baseFileName {
968
+ continue // Skip current file
969
+ }
970
+ for _, f := range funcs {
971
+ if f == funcName {
972
+ // Found the function in another file
973
+ if callsFromOtherFiles[sourceFile] == nil {
974
+ callsFromOtherFiles[sourceFile] = []string{}
975
+ }
976
+ // Check if already added to avoid duplicates
977
+ found := false
978
+ for _, existing := range callsFromOtherFiles[sourceFile] {
979
+ if existing == funcName {
980
+ found = true
981
+ break
982
+ }
983
+ }
984
+ if !found {
985
+ callsFromOtherFiles[sourceFile] = append(callsFromOtherFiles[sourceFile], funcName)
986
+ }
987
+ break
988
+ }
989
+ }
990
+ }
991
+ }
992
+ }
993
+ }
994
+ return true
995
+ })
996
+
997
+ if len(callsFromOtherFiles) > 0 {
998
+ analysis.FunctionCalls[baseFileName] = callsFromOtherFiles
999
+ }
1000
+ }
1001
+
1002
+ return analysis
1003
+ }
1004
+
1005
+ // LoadPackageMetadata loads metadata from gs packages to determine which functions are async
1006
+ func (a *Analysis) LoadPackageMetadata() {
1007
+ // List of gs packages that have metadata
1008
+ metadataPackages := []string{
1009
+ "github.com/aperturerobotics/goscript/gs/sync",
1010
+ "github.com/aperturerobotics/goscript/gs/unicode",
1011
+ }
1012
+
1013
+ for _, pkgPath := range metadataPackages {
1014
+ cfg := &packages.Config{
1015
+ Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
1016
+ }
1017
+
1018
+ pkgs, err := packages.Load(cfg, pkgPath)
1019
+ if err != nil || len(pkgs) == 0 {
1020
+ continue // Skip if package can't be loaded
1021
+ }
1022
+
1023
+ pkg := pkgs[0]
1024
+ if pkg.Types == nil {
1025
+ continue
1026
+ }
1027
+
1028
+ // Extract the package name (e.g., "sync" from "github.com/aperturerobotics/goscript/gs/sync")
1029
+ parts := strings.Split(pkgPath, "/")
1030
+ pkgName := parts[len(parts)-1]
1031
+
1032
+ // Look for metadata variables in the package scope
1033
+ scope := pkg.Types.Scope()
1034
+ for _, name := range scope.Names() {
1035
+ obj := scope.Lookup(name)
1036
+ if obj == nil {
1037
+ continue
1038
+ }
1039
+
1040
+ // Check if this is a metadata variable (ends with "Info")
1041
+ if strings.HasSuffix(name, "Info") {
1042
+ if varObj, ok := obj.(*types.Var); ok {
1043
+ // Store the metadata with a key like "sync.MutexLock"
1044
+ methodName := strings.TrimSuffix(name, "Info")
1045
+ key := pkgName + "." + methodName
1046
+ a.PackageMetadata[key] = varObj
1047
+ }
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+
1053
+ // IsMethodAsync checks if a method call is async based on package metadata
1054
+ func (a *Analysis) IsMethodAsync(pkgName, typeName, methodName string) bool {
1055
+ // The metadata keys are stored as "sync.MutexLock", "sync.WaitGroupWait", etc.
1056
+ // We need to match "sync.Mutex.Lock" -> "sync.MutexLock"
1057
+ key := pkgName + "." + typeName + methodName
1058
+
1059
+ if metaObj, exists := a.PackageMetadata[key]; exists {
1060
+ if varObj, ok := metaObj.(*types.Var); ok {
1061
+ // Try to get the actual value of the variable
1062
+ // For now, we'll use the variable name to determine if it's async
1063
+ // The variable names follow the pattern: MutexLockInfo, WaitGroupWaitInfo, etc.
1064
+ // We can check if the corresponding metadata indicates IsAsync: true
1065
+ varName := varObj.Name()
1066
+
1067
+ // Based on our metadata definitions, these should be async:
1068
+ asyncMethods := map[string]bool{
1069
+ "MutexLockInfo": true,
1070
+ "RWMutexLockInfo": true,
1071
+ "RWMutexRLockInfo": true,
1072
+ "WaitGroupWaitInfo": true,
1073
+ "OnceDoInfo": true,
1074
+ "CondWaitInfo": true,
1075
+ "MapDeleteInfo": true,
1076
+ "MapLoadInfo": true,
1077
+ "MapLoadAndDeleteInfo": true,
1078
+ "MapLoadOrStoreInfo": true,
1079
+ "MapRangeInfo": true,
1080
+ "MapStoreInfo": true,
1081
+ }
1082
+
1083
+ isAsync := asyncMethods[varName]
1084
+ return isAsync
1085
+ }
1086
+ }
1087
+
1088
+ return false
1089
+ }
@@ -68,7 +68,7 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
68
68
  return nil
69
69
  }
70
70
 
71
- // Special handling for variable referenced variables in declarations
71
+ // Handle variable referenced variables in declarations
72
72
  if addDeclaration && tok == token.DEFINE {
73
73
  // Determine if LHS is variable referenced
74
74
  isLHSVarRefed := false
@@ -90,7 +90,7 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
90
90
  }
91
91
  }
92
92
 
93
- // Special handling for short declaration of variable referenced variables
93
+ // Handle short declaration of variable referenced variables
94
94
  if isLHSVarRefed && lhsIdent != nil {
95
95
  c.tsw.WriteLiterally("let ")
96
96
  // Just write the identifier name without .value
@@ -42,7 +42,7 @@ func TestEmitBuiltinOption(t *testing.T) {
42
42
  t.Fatalf("Compilation failed: %v", err)
43
43
  }
44
44
 
45
- // Check that the unsafe package wasn't emitted (we know it's handwritten)
45
+ // Check that handwritten packages like unsafe aren't emitted when DisableEmitBuiltin=true
46
46
  unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
47
47
  if _, err := os.Stat(unsafePath); !os.IsNotExist(err) {
48
48
  t.Errorf("unsafe package was emitted when DisableEmitBuiltin=true")