goscript 0.0.26 → 0.0.28

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 (168) 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 +259 -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 +83 -0
  12. package/compiler/expr.go +1 -1
  13. package/compiler/protobuf.go +557 -0
  14. package/compiler/spec-struct.go +4 -0
  15. package/compiler/spec-value.go +10 -2
  16. package/compiler/spec.go +18 -1
  17. package/compiler/stmt-assign.go +35 -0
  18. package/compiler/type-assert.go +87 -0
  19. package/compiler/type.go +4 -1
  20. package/dist/gs/builtin/builtin.d.ts +19 -1
  21. package/dist/gs/builtin/builtin.js +84 -4
  22. package/dist/gs/builtin/builtin.js.map +1 -1
  23. package/dist/gs/builtin/slice.d.ts +1 -1
  24. package/dist/gs/builtin/slice.js +21 -2
  25. package/dist/gs/builtin/slice.js.map +1 -1
  26. package/dist/gs/errors/errors.d.ts +5 -6
  27. package/dist/gs/errors/errors.js.map +1 -1
  28. package/dist/gs/io/index.d.ts +1 -0
  29. package/dist/gs/io/index.js +2 -0
  30. package/dist/gs/io/index.js.map +1 -0
  31. package/dist/gs/io/io.d.ts +107 -0
  32. package/dist/gs/io/io.js +385 -0
  33. package/dist/gs/io/io.js.map +1 -0
  34. package/dist/gs/strings/builder.d.ts +18 -0
  35. package/dist/gs/strings/builder.js +205 -0
  36. package/dist/gs/strings/builder.js.map +1 -0
  37. package/dist/gs/strings/clone.d.ts +1 -0
  38. package/dist/gs/strings/clone.js +16 -0
  39. package/dist/gs/strings/clone.js.map +1 -0
  40. package/dist/gs/strings/compare.d.ts +1 -0
  41. package/dist/gs/strings/compare.js +14 -0
  42. package/dist/gs/strings/compare.js.map +1 -0
  43. package/dist/gs/strings/index.d.ts +2 -0
  44. package/dist/gs/strings/index.js +3 -0
  45. package/dist/gs/strings/index.js.map +1 -0
  46. package/dist/gs/strings/iter.d.ts +8 -0
  47. package/dist/gs/strings/iter.js +160 -0
  48. package/dist/gs/strings/iter.js.map +1 -0
  49. package/dist/gs/strings/reader.d.ts +34 -0
  50. package/dist/gs/strings/reader.js +418 -0
  51. package/dist/gs/strings/reader.js.map +1 -0
  52. package/dist/gs/strings/replace.d.ts +106 -0
  53. package/dist/gs/strings/replace.js +1136 -0
  54. package/dist/gs/strings/replace.js.map +1 -0
  55. package/dist/gs/strings/search.d.ts +24 -0
  56. package/dist/gs/strings/search.js +169 -0
  57. package/dist/gs/strings/search.js.map +1 -0
  58. package/dist/gs/strings/strings.d.ts +47 -0
  59. package/dist/gs/strings/strings.js +418 -0
  60. package/dist/gs/strings/strings.js.map +1 -0
  61. package/dist/gs/stringslite/index.d.ts +1 -0
  62. package/dist/gs/stringslite/index.js +2 -0
  63. package/dist/gs/stringslite/index.js.map +1 -0
  64. package/dist/gs/stringslite/strings.d.ts +11 -0
  65. package/dist/gs/stringslite/strings.js +67 -0
  66. package/dist/gs/stringslite/strings.js.map +1 -0
  67. package/dist/gs/sync/index.d.ts +1 -0
  68. package/dist/gs/sync/index.js +2 -0
  69. package/dist/gs/sync/index.js.map +1 -0
  70. package/dist/gs/sync/sync.d.ts +79 -0
  71. package/dist/gs/sync/sync.js +392 -0
  72. package/dist/gs/sync/sync.js.map +1 -0
  73. package/dist/gs/unicode/index.d.ts +1 -0
  74. package/dist/gs/unicode/index.js +2 -0
  75. package/dist/gs/unicode/index.js.map +1 -0
  76. package/dist/gs/unicode/unicode.d.ts +105 -0
  77. package/dist/gs/unicode/unicode.js +332 -0
  78. package/dist/gs/unicode/unicode.js.map +1 -0
  79. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  80. package/dist/gs/unicode/utf8/index.js +3 -0
  81. package/dist/gs/unicode/utf8/index.js.map +1 -0
  82. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  83. package/dist/gs/unicode/utf8/utf8.js +196 -0
  84. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  85. package/dist/gs/unsafe/index.d.ts +1 -0
  86. package/dist/gs/unsafe/index.js +2 -0
  87. package/dist/gs/unsafe/index.js.map +1 -0
  88. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  89. package/dist/gs/unsafe/unsafe.js +44 -0
  90. package/dist/gs/unsafe/unsafe.js.map +1 -0
  91. package/go.mod +2 -1
  92. package/go.sum +6 -2
  93. package/gs/README.md +6 -0
  94. package/gs/builtin/builtin.ts +158 -0
  95. package/gs/builtin/channel.ts +683 -0
  96. package/gs/builtin/defer.ts +58 -0
  97. package/gs/builtin/index.ts +1 -0
  98. package/gs/builtin/io.ts +22 -0
  99. package/gs/builtin/map.ts +50 -0
  100. package/gs/builtin/slice.ts +1030 -0
  101. package/gs/builtin/type.ts +1106 -0
  102. package/gs/builtin/varRef.ts +25 -0
  103. package/gs/cmp/godoc.txt +8 -0
  104. package/gs/cmp/index.ts +29 -0
  105. package/gs/context/context.ts +401 -0
  106. package/gs/context/godoc.txt +69 -0
  107. package/gs/context/index.ts +1 -0
  108. package/gs/errors/errors.ts +223 -0
  109. package/gs/errors/godoc.txt +63 -0
  110. package/gs/errors/index.ts +1 -0
  111. package/gs/internal/goarch/godoc.txt +39 -0
  112. package/gs/internal/goarch/index.ts +18 -0
  113. package/gs/io/godoc.txt +61 -0
  114. package/gs/io/index.ts +1 -0
  115. package/gs/io/io.go +75 -0
  116. package/gs/io/io.ts +546 -0
  117. package/gs/iter/godoc.txt +203 -0
  118. package/gs/iter/index.ts +1 -0
  119. package/gs/iter/iter.ts +117 -0
  120. package/gs/math/bits/index.ts +356 -0
  121. package/gs/math/godoc.txt +76 -0
  122. package/gs/runtime/godoc.txt +331 -0
  123. package/gs/runtime/index.ts +1 -0
  124. package/gs/runtime/runtime.ts +178 -0
  125. package/gs/slices/godoc.txt +44 -0
  126. package/gs/slices/index.ts +1 -0
  127. package/gs/slices/slices.ts +22 -0
  128. package/gs/strings/builder.test.ts +121 -0
  129. package/gs/strings/builder.ts +223 -0
  130. package/gs/strings/clone.test.ts +43 -0
  131. package/gs/strings/clone.ts +17 -0
  132. package/gs/strings/compare.test.ts +84 -0
  133. package/gs/strings/compare.ts +13 -0
  134. package/gs/strings/godoc.txt +66 -0
  135. package/gs/strings/index.ts +2 -0
  136. package/gs/strings/iter.test.ts +343 -0
  137. package/gs/strings/iter.ts +171 -0
  138. package/gs/strings/reader.test.ts +243 -0
  139. package/gs/strings/reader.ts +451 -0
  140. package/gs/strings/replace.test.ts +181 -0
  141. package/gs/strings/replace.ts +1310 -0
  142. package/gs/strings/search.test.ts +214 -0
  143. package/gs/strings/search.ts +213 -0
  144. package/gs/strings/strings.test.ts +477 -0
  145. package/gs/strings/strings.ts +510 -0
  146. package/gs/stringslite/godoc.txt +17 -0
  147. package/gs/stringslite/index.ts +1 -0
  148. package/gs/stringslite/strings.ts +82 -0
  149. package/gs/sync/godoc.txt +21 -0
  150. package/gs/sync/index.ts +1 -0
  151. package/gs/sync/sync.go +64 -0
  152. package/gs/sync/sync.ts +449 -0
  153. package/gs/time/godoc.md +116 -0
  154. package/gs/time/godoc.txt +116 -0
  155. package/gs/time/index.ts +1 -0
  156. package/gs/time/time.ts +272 -0
  157. package/gs/unicode/godoc.txt +52 -0
  158. package/gs/unicode/index.ts +1 -0
  159. package/gs/unicode/unicode.go +38 -0
  160. package/gs/unicode/unicode.ts +418 -0
  161. package/gs/unicode/utf8/godoc.txt +22 -0
  162. package/gs/unicode/utf8/index.ts +2 -0
  163. package/gs/unicode/utf8/utf8.ts +227 -0
  164. package/gs/unsafe/godoc.txt +19 -0
  165. package/gs/unsafe/index.ts +1 -0
  166. package/gs/unsafe/unsafe.test.ts +68 -0
  167. package/gs/unsafe/unsafe.ts +77 -0
  168. 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,7 @@ 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
57
60
  }
58
61
 
59
62
  // Analysis holds information gathered during the analysis phase of the Go code compilation.
@@ -79,16 +82,39 @@ type Analysis struct {
79
82
  // Keep specialized maps that serve different purposes
80
83
  // FuncLitData tracks function literal specific data since they don't have types.Object
81
84
  FuncLitData map[*ast.FuncLit]*FunctionInfo
85
+
86
+ // PackageMetadata holds package-level metadata
87
+ PackageMetadata map[string]interface{}
88
+ }
89
+
90
+ // PackageAnalysis holds cross-file analysis data for a package
91
+ type PackageAnalysis struct {
92
+ // FunctionDefs maps file names to the functions defined in that file
93
+ // Key: filename (without .go extension), Value: list of function names
94
+ FunctionDefs map[string][]string
95
+
96
+ // FunctionCalls maps file names to the functions they call from other files
97
+ // Key: filename (without .go extension), Value: map[sourceFile][]functionNames
98
+ FunctionCalls map[string]map[string][]string
82
99
  }
83
100
 
84
101
  // NewAnalysis creates a new Analysis instance.
85
102
  func NewAnalysis() *Analysis {
86
103
  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),
104
+ VariableUsage: make(map[types.Object]*VariableUsageInfo),
105
+ Imports: make(map[string]*fileImport),
106
+ FunctionData: make(map[types.Object]*FunctionInfo),
107
+ NodeData: make(map[ast.Node]*NodeInfo),
108
+ FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
109
+ PackageMetadata: make(map[string]interface{}),
110
+ }
111
+ }
112
+
113
+ // NewPackageAnalysis creates a new PackageAnalysis instance
114
+ func NewPackageAnalysis() *PackageAnalysis {
115
+ return &PackageAnalysis{
116
+ FunctionDefs: make(map[string][]string),
117
+ FunctionCalls: make(map[string]map[string][]string),
92
118
  }
93
119
  }
94
120
 
@@ -210,56 +236,6 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
210
236
  return false
211
237
  }
212
238
 
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
261
- }
262
-
263
239
  // analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
264
240
  type analysisVisitor struct {
265
241
  // analysis stores information gathered during the traversal
@@ -718,6 +694,25 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
718
694
  }
719
695
  }
720
696
  return v // Continue traversal
697
+
698
+ case *ast.DeclStmt:
699
+ // Handle declarations inside functions (const, var, type declarations within function bodies)
700
+ // These should not have export modifiers in TypeScript
701
+ if genDecl, ok := n.Decl.(*ast.GenDecl); ok {
702
+ // Check if we're inside a function (either FuncDecl or FuncLit)
703
+ isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
704
+
705
+ if isInsideFunction {
706
+ // Mark all specs in this declaration as being inside a function
707
+ for _, spec := range genDecl.Specs {
708
+ if v.analysis.NodeData[spec] == nil {
709
+ v.analysis.NodeData[spec] = &NodeInfo{}
710
+ }
711
+ v.analysis.NodeData[spec].IsInsideFunction = true
712
+ }
713
+ }
714
+ }
715
+ return v // Continue traversal
721
716
  }
722
717
 
723
718
  // For all other nodes, continue traversal
@@ -756,6 +751,37 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
756
751
  }
757
752
  }
758
753
 
754
+ // Check for method calls on imported types (e.g., sync.Mutex.Lock())
755
+ if selExpr, ok := s.Fun.(*ast.SelectorExpr); ok {
756
+ // Check if this is a method call on a variable (e.g., mu.Lock())
757
+ if ident, ok := selExpr.X.(*ast.Ident); ok {
758
+ // Get the type of the receiver
759
+ if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
760
+ if varObj, ok := obj.(*types.Var); ok {
761
+ // Get the type name and package
762
+ if namedType, ok := varObj.Type().(*types.Named); ok {
763
+ typeName := namedType.Obj().Name()
764
+ methodName := selExpr.Sel.Name
765
+
766
+ // Check if the type is from an imported package
767
+ if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
768
+ pkgPath := typePkg.Path()
769
+ // Extract package name from path (e.g., "sync" from "github.com/.../gs/sync")
770
+ parts := strings.Split(pkgPath, "/")
771
+ pkgName := parts[len(parts)-1]
772
+
773
+ // Check if this method is async based on metadata
774
+ if v.analysis.IsMethodAsync(pkgName, typeName, methodName) {
775
+ hasAsync = true
776
+ return false
777
+ }
778
+ }
779
+ }
780
+ }
781
+ }
782
+ }
783
+ }
784
+
759
785
  // TODO: Add detection of method calls on async types
760
786
  }
761
787
 
@@ -789,6 +815,9 @@ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap
789
815
  // Store the comment map in the analysis object
790
816
  analysis.Cmap = cmap
791
817
 
818
+ // Load package metadata for async function detection
819
+ analysis.LoadPackageMetadata()
820
+
792
821
  // Process imports from the file
793
822
  for _, imp := range file.Imports {
794
823
  path := ""
@@ -844,3 +873,178 @@ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
844
873
  }
845
874
  return namedReturns
846
875
  }
876
+
877
+ // AnalyzePackage performs package-level analysis to collect function definitions
878
+ // and calls across all files in the package for auto-import generation
879
+ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
880
+ analysis := NewPackageAnalysis()
881
+
882
+ // First pass: collect all function definitions per file
883
+ for i, syntax := range pkg.Syntax {
884
+ fileName := pkg.CompiledGoFiles[i]
885
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
886
+
887
+ var functions []string
888
+ for _, decl := range syntax.Decls {
889
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
890
+ // Only collect top-level functions (not methods)
891
+ if funcDecl.Recv == nil {
892
+ functions = append(functions, funcDecl.Name.Name)
893
+ }
894
+ }
895
+ }
896
+
897
+ if len(functions) > 0 {
898
+ analysis.FunctionDefs[baseFileName] = functions
899
+ }
900
+ }
901
+
902
+ // Second pass: analyze function calls and determine which need imports
903
+ for i, syntax := range pkg.Syntax {
904
+ fileName := pkg.CompiledGoFiles[i]
905
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
906
+
907
+ // Find all function calls in this file
908
+ callsFromOtherFiles := make(map[string][]string)
909
+
910
+ ast.Inspect(syntax, func(n ast.Node) bool {
911
+ if callExpr, ok := n.(*ast.CallExpr); ok {
912
+ if ident, ok := callExpr.Fun.(*ast.Ident); ok {
913
+ funcName := ident.Name
914
+
915
+ // Check if this function is defined in the current file
916
+ currentFileFuncs := analysis.FunctionDefs[baseFileName]
917
+ isDefinedInCurrentFile := false
918
+ for _, f := range currentFileFuncs {
919
+ if f == funcName {
920
+ isDefinedInCurrentFile = true
921
+ break
922
+ }
923
+ }
924
+
925
+ // If not defined in current file, find which file defines it
926
+ if !isDefinedInCurrentFile {
927
+ for sourceFile, funcs := range analysis.FunctionDefs {
928
+ if sourceFile == baseFileName {
929
+ continue // Skip current file
930
+ }
931
+ for _, f := range funcs {
932
+ if f == funcName {
933
+ // Found the function in another file
934
+ if callsFromOtherFiles[sourceFile] == nil {
935
+ callsFromOtherFiles[sourceFile] = []string{}
936
+ }
937
+ // Check if already added to avoid duplicates
938
+ found := false
939
+ for _, existing := range callsFromOtherFiles[sourceFile] {
940
+ if existing == funcName {
941
+ found = true
942
+ break
943
+ }
944
+ }
945
+ if !found {
946
+ callsFromOtherFiles[sourceFile] = append(callsFromOtherFiles[sourceFile], funcName)
947
+ }
948
+ break
949
+ }
950
+ }
951
+ }
952
+ }
953
+ }
954
+ }
955
+ return true
956
+ })
957
+
958
+ if len(callsFromOtherFiles) > 0 {
959
+ analysis.FunctionCalls[baseFileName] = callsFromOtherFiles
960
+ }
961
+ }
962
+
963
+ return analysis
964
+ }
965
+
966
+ // LoadPackageMetadata loads metadata from gs packages to determine which functions are async
967
+ func (a *Analysis) LoadPackageMetadata() {
968
+ // List of gs packages that have metadata
969
+ metadataPackages := []string{
970
+ "github.com/aperturerobotics/goscript/gs/sync",
971
+ "github.com/aperturerobotics/goscript/gs/unicode",
972
+ }
973
+
974
+ for _, pkgPath := range metadataPackages {
975
+ cfg := &packages.Config{
976
+ Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
977
+ }
978
+
979
+ pkgs, err := packages.Load(cfg, pkgPath)
980
+ if err != nil || len(pkgs) == 0 {
981
+ continue // Skip if package can't be loaded
982
+ }
983
+
984
+ pkg := pkgs[0]
985
+ if pkg.Types == nil {
986
+ continue
987
+ }
988
+
989
+ // Extract the package name (e.g., "sync" from "github.com/aperturerobotics/goscript/gs/sync")
990
+ parts := strings.Split(pkgPath, "/")
991
+ pkgName := parts[len(parts)-1]
992
+
993
+ // Look for metadata variables in the package scope
994
+ scope := pkg.Types.Scope()
995
+ for _, name := range scope.Names() {
996
+ obj := scope.Lookup(name)
997
+ if obj == nil {
998
+ continue
999
+ }
1000
+
1001
+ // Check if this is a metadata variable (ends with "Info")
1002
+ if strings.HasSuffix(name, "Info") {
1003
+ if varObj, ok := obj.(*types.Var); ok {
1004
+ // Store the metadata with a key like "sync.MutexLock"
1005
+ methodName := strings.TrimSuffix(name, "Info")
1006
+ key := pkgName + "." + methodName
1007
+ a.PackageMetadata[key] = varObj
1008
+ }
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+
1014
+ // IsMethodAsync checks if a method call is async based on package metadata
1015
+ func (a *Analysis) IsMethodAsync(pkgName, typeName, methodName string) bool {
1016
+ // The metadata keys are stored as "sync.MutexLock", "sync.WaitGroupWait", etc.
1017
+ // We need to match "sync.Mutex.Lock" -> "sync.MutexLock"
1018
+ key := pkgName + "." + typeName + methodName
1019
+
1020
+ if metaObj, exists := a.PackageMetadata[key]; exists {
1021
+ if varObj, ok := metaObj.(*types.Var); ok {
1022
+ // Try to get the actual value of the variable
1023
+ // For now, we'll use the variable name to determine if it's async
1024
+ // The variable names follow the pattern: MutexLockInfo, WaitGroupWaitInfo, etc.
1025
+ // We can check if the corresponding metadata indicates IsAsync: true
1026
+ varName := varObj.Name()
1027
+
1028
+ // Based on our metadata definitions, these should be async:
1029
+ asyncMethods := map[string]bool{
1030
+ "MutexLockInfo": true,
1031
+ "RWMutexLockInfo": true,
1032
+ "RWMutexRLockInfo": true,
1033
+ "WaitGroupWaitInfo": true,
1034
+ "OnceDoInfo": true,
1035
+ "CondWaitInfo": true,
1036
+ "MapDeleteInfo": true,
1037
+ "MapLoadInfo": true,
1038
+ "MapLoadAndDeleteInfo": true,
1039
+ "MapLoadOrStoreInfo": true,
1040
+ "MapRangeInfo": true,
1041
+ "MapStoreInfo": true,
1042
+ }
1043
+
1044
+ isAsync := asyncMethods[varName]
1045
+ return isAsync
1046
+ }
1047
+ }
1048
+
1049
+ return false
1050
+ }
@@ -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")