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.
- package/README.md +4 -4
- package/cmd/goscript/cmd_compile.go +0 -3
- package/cmd/goscript/deps.go +11 -0
- package/compiler/analysis.go +298 -55
- package/compiler/assignment.go +2 -2
- package/compiler/builtin_test.go +1 -1
- package/compiler/compiler.go +200 -68
- package/compiler/compiler_test.go +17 -24
- package/compiler/composite-lit.go +32 -8
- package/compiler/decl.go +6 -6
- package/compiler/expr-call.go +170 -15
- package/compiler/expr-selector.go +100 -0
- package/compiler/expr.go +1 -1
- package/compiler/protobuf.go +557 -0
- package/compiler/spec-struct.go +4 -0
- package/compiler/spec-value.go +89 -10
- package/compiler/spec.go +254 -1
- package/compiler/stmt-assign.go +35 -0
- package/compiler/type-assert.go +87 -0
- package/compiler/type.go +4 -1
- package/dist/gs/builtin/builtin.d.ts +20 -1
- package/dist/gs/builtin/builtin.js +95 -4
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +21 -2
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/errors/errors.d.ts +5 -6
- package/dist/gs/errors/errors.js.map +1 -1
- package/dist/gs/internal/oserror/errors.d.ts +6 -0
- package/dist/gs/internal/oserror/errors.js +7 -0
- package/dist/gs/internal/oserror/errors.js.map +1 -0
- package/dist/gs/internal/oserror/index.d.ts +1 -0
- package/dist/gs/internal/oserror/index.js +2 -0
- package/dist/gs/internal/oserror/index.js.map +1 -0
- package/dist/gs/io/fs/format.d.ts +3 -0
- package/dist/gs/io/fs/format.js +56 -0
- package/dist/gs/io/fs/format.js.map +1 -0
- package/dist/gs/io/fs/fs.d.ts +79 -0
- package/dist/gs/io/fs/fs.js +200 -0
- package/dist/gs/io/fs/fs.js.map +1 -0
- package/dist/gs/io/fs/glob.d.ts +10 -0
- package/dist/gs/io/fs/glob.js +141 -0
- package/dist/gs/io/fs/glob.js.map +1 -0
- package/dist/gs/io/fs/index.d.ts +8 -0
- package/dist/gs/io/fs/index.js +9 -0
- package/dist/gs/io/fs/index.js.map +1 -0
- package/dist/gs/io/fs/readdir.d.ts +7 -0
- package/dist/gs/io/fs/readdir.js +152 -0
- package/dist/gs/io/fs/readdir.js.map +1 -0
- package/dist/gs/io/fs/readfile.d.ts +6 -0
- package/dist/gs/io/fs/readfile.js +118 -0
- package/dist/gs/io/fs/readfile.js.map +1 -0
- package/dist/gs/io/fs/stat.d.ts +6 -0
- package/dist/gs/io/fs/stat.js +87 -0
- package/dist/gs/io/fs/stat.js.map +1 -0
- package/dist/gs/io/fs/sub.d.ts +6 -0
- package/dist/gs/io/fs/sub.js +172 -0
- package/dist/gs/io/fs/sub.js.map +1 -0
- package/dist/gs/io/fs/walk.d.ts +7 -0
- package/dist/gs/io/fs/walk.js +76 -0
- package/dist/gs/io/fs/walk.js.map +1 -0
- package/dist/gs/io/index.d.ts +1 -0
- package/dist/gs/io/index.js +2 -0
- package/dist/gs/io/index.js.map +1 -0
- package/dist/gs/io/io.d.ts +107 -0
- package/dist/gs/io/io.js +385 -0
- package/dist/gs/io/io.js.map +1 -0
- package/dist/gs/path/index.d.ts +2 -0
- package/dist/gs/path/index.js +3 -0
- package/dist/gs/path/index.js.map +1 -0
- package/dist/gs/path/match.d.ts +6 -0
- package/dist/gs/path/match.js +281 -0
- package/dist/gs/path/match.js.map +1 -0
- package/dist/gs/path/path.d.ts +7 -0
- package/dist/gs/path/path.js +256 -0
- package/dist/gs/path/path.js.map +1 -0
- package/dist/gs/strings/builder.d.ts +18 -0
- package/dist/gs/strings/builder.js +205 -0
- package/dist/gs/strings/builder.js.map +1 -0
- package/dist/gs/strings/clone.d.ts +1 -0
- package/dist/gs/strings/clone.js +16 -0
- package/dist/gs/strings/clone.js.map +1 -0
- package/dist/gs/strings/compare.d.ts +1 -0
- package/dist/gs/strings/compare.js +14 -0
- package/dist/gs/strings/compare.js.map +1 -0
- package/dist/gs/strings/index.d.ts +2 -0
- package/dist/gs/strings/index.js +3 -0
- package/dist/gs/strings/index.js.map +1 -0
- package/dist/gs/strings/iter.d.ts +8 -0
- package/dist/gs/strings/iter.js +160 -0
- package/dist/gs/strings/iter.js.map +1 -0
- package/dist/gs/strings/reader.d.ts +34 -0
- package/dist/gs/strings/reader.js +418 -0
- package/dist/gs/strings/reader.js.map +1 -0
- package/dist/gs/strings/replace.d.ts +106 -0
- package/dist/gs/strings/replace.js +1136 -0
- package/dist/gs/strings/replace.js.map +1 -0
- package/dist/gs/strings/search.d.ts +24 -0
- package/dist/gs/strings/search.js +169 -0
- package/dist/gs/strings/search.js.map +1 -0
- package/dist/gs/strings/strings.d.ts +47 -0
- package/dist/gs/strings/strings.js +418 -0
- package/dist/gs/strings/strings.js.map +1 -0
- package/dist/gs/stringslite/index.d.ts +1 -0
- package/dist/gs/stringslite/index.js +2 -0
- package/dist/gs/stringslite/index.js.map +1 -0
- package/dist/gs/stringslite/strings.d.ts +11 -0
- package/dist/gs/stringslite/strings.js +67 -0
- package/dist/gs/stringslite/strings.js.map +1 -0
- package/dist/gs/sync/index.d.ts +1 -0
- package/dist/gs/sync/index.js +2 -0
- package/dist/gs/sync/index.js.map +1 -0
- package/dist/gs/sync/sync.d.ts +79 -0
- package/dist/gs/sync/sync.js +392 -0
- package/dist/gs/sync/sync.js.map +1 -0
- package/dist/gs/time/time.d.ts +11 -2
- package/dist/gs/time/time.js +337 -12
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/index.d.ts +1 -0
- package/dist/gs/unicode/index.js +2 -0
- package/dist/gs/unicode/index.js.map +1 -0
- package/dist/gs/unicode/unicode.d.ts +105 -0
- package/dist/gs/unicode/unicode.js +332 -0
- package/dist/gs/unicode/unicode.js.map +1 -0
- package/dist/gs/unicode/utf8/index.d.ts +1 -0
- package/dist/gs/unicode/utf8/index.js +3 -0
- package/dist/gs/unicode/utf8/index.js.map +1 -0
- package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
- package/dist/gs/unicode/utf8/utf8.js +196 -0
- package/dist/gs/unicode/utf8/utf8.js.map +1 -0
- package/dist/gs/unsafe/index.d.ts +1 -0
- package/dist/gs/unsafe/index.js +2 -0
- package/dist/gs/unsafe/index.js.map +1 -0
- package/dist/gs/unsafe/unsafe.d.ts +11 -0
- package/dist/gs/unsafe/unsafe.js +44 -0
- package/dist/gs/unsafe/unsafe.js.map +1 -0
- package/go.mod +2 -1
- package/go.sum +6 -2
- package/gs/README.md +6 -0
- package/gs/builtin/builtin.ts +171 -0
- package/gs/builtin/channel.ts +683 -0
- package/gs/builtin/defer.ts +58 -0
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/io.ts +22 -0
- package/gs/builtin/map.ts +50 -0
- package/gs/builtin/slice.ts +1030 -0
- package/gs/builtin/type.ts +1106 -0
- package/gs/builtin/varRef.ts +25 -0
- package/gs/cmp/godoc.txt +8 -0
- package/gs/cmp/index.ts +29 -0
- package/gs/context/context.ts +401 -0
- package/gs/context/godoc.txt +69 -0
- package/gs/context/index.ts +1 -0
- package/gs/errors/errors.ts +223 -0
- package/gs/errors/godoc.txt +63 -0
- package/gs/errors/index.ts +1 -0
- package/gs/internal/goarch/godoc.txt +39 -0
- package/gs/internal/goarch/index.ts +18 -0
- package/gs/internal/oserror/errors.ts +14 -0
- package/gs/internal/oserror/index.ts +1 -0
- package/gs/io/fs/format.ts +65 -0
- package/gs/io/fs/fs.ts +359 -0
- package/gs/io/fs/glob.ts +167 -0
- package/gs/io/fs/godoc.txt +35 -0
- package/gs/io/fs/index.ts +8 -0
- package/gs/io/fs/readdir.ts +126 -0
- package/gs/io/fs/readfile.ts +77 -0
- package/gs/io/fs/stat.ts +38 -0
- package/gs/io/fs/sub.ts +208 -0
- package/gs/io/fs/walk.ts +89 -0
- package/gs/io/godoc.txt +61 -0
- package/gs/io/index.ts +1 -0
- package/gs/io/io.go +75 -0
- package/gs/io/io.ts +546 -0
- package/gs/iter/godoc.txt +203 -0
- package/gs/iter/index.ts +1 -0
- package/gs/iter/iter.ts +117 -0
- package/gs/math/bits/index.ts +356 -0
- package/gs/math/godoc.txt +76 -0
- package/gs/path/index.ts +2 -0
- package/gs/path/match.ts +307 -0
- package/gs/path/path.ts +301 -0
- package/gs/runtime/godoc.txt +331 -0
- package/gs/runtime/index.ts +1 -0
- package/gs/runtime/runtime.ts +178 -0
- package/gs/slices/godoc.txt +44 -0
- package/gs/slices/index.ts +1 -0
- package/gs/slices/slices.ts +22 -0
- package/gs/strings/builder.test.ts +121 -0
- package/gs/strings/builder.ts +223 -0
- package/gs/strings/clone.test.ts +43 -0
- package/gs/strings/clone.ts +17 -0
- package/gs/strings/compare.test.ts +84 -0
- package/gs/strings/compare.ts +13 -0
- package/gs/strings/godoc.txt +66 -0
- package/gs/strings/index.ts +2 -0
- package/gs/strings/iter.test.ts +343 -0
- package/gs/strings/iter.ts +171 -0
- package/gs/strings/reader.test.ts +242 -0
- package/gs/strings/reader.ts +451 -0
- package/gs/strings/replace.test.ts +181 -0
- package/gs/strings/replace.ts +1310 -0
- package/gs/strings/search.test.ts +214 -0
- package/gs/strings/search.ts +213 -0
- package/gs/strings/strings.test.ts +477 -0
- package/gs/strings/strings.ts +510 -0
- package/gs/stringslite/godoc.txt +17 -0
- package/gs/stringslite/index.ts +1 -0
- package/gs/stringslite/strings.ts +82 -0
- package/gs/sync/godoc.txt +21 -0
- package/gs/sync/index.ts +1 -0
- package/gs/sync/sync.go +64 -0
- package/gs/sync/sync.ts +449 -0
- package/gs/time/godoc.txt +116 -0
- package/gs/time/index.ts +1 -0
- package/gs/time/time.ts +585 -0
- package/gs/unicode/godoc.txt +52 -0
- package/gs/unicode/index.ts +1 -0
- package/gs/unicode/unicode.go +38 -0
- package/gs/unicode/unicode.ts +418 -0
- package/gs/unicode/utf8/godoc.txt +22 -0
- package/gs/unicode/utf8/index.ts +2 -0
- package/gs/unicode/utf8/utf8.ts +227 -0
- package/gs/unsafe/godoc.txt +19 -0
- package/gs/unsafe/index.ts +1 -0
- package/gs/unsafe/unsafe.test.ts +68 -0
- package/gs/unsafe/unsafe.ts +77 -0
- 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
|
-
- [
|
|
156
|
-
- [
|
|
157
|
-
- [
|
|
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:
|
|
@@ -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
|
+
)
|
package/compiler/analysis.go
CHANGED
|
@@ -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:
|
|
88
|
-
Imports:
|
|
89
|
-
FunctionData:
|
|
90
|
-
NodeData:
|
|
91
|
-
FuncLitData:
|
|
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
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
//
|
|
594
|
-
|
|
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
|
+
}
|
package/compiler/assignment.go
CHANGED
|
@@ -68,7 +68,7 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
68
68
|
return nil
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
//
|
|
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
|
-
//
|
|
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
|
package/compiler/builtin_test.go
CHANGED
|
@@ -42,7 +42,7 @@ func TestEmitBuiltinOption(t *testing.T) {
|
|
|
42
42
|
t.Fatalf("Compilation failed: %v", err)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// Check that
|
|
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")
|