goscript 0.0.48 → 0.0.50
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/cmd/goscript/deps.go +1 -4
- package/compiler/analysis.go +1120 -513
- package/compiler/analysis_test.go +113 -4
- package/compiler/compiler.go +88 -124
- package/compiler/decl.go +22 -0
- package/compiler/expr-call-async.go +46 -52
- package/compiler/expr-call-type-conversion.go +144 -59
- package/compiler/expr-call.go +235 -12
- package/compiler/expr.go +5 -82
- package/compiler/gs_dependencies_test.go +60 -1
- package/compiler/spec-value.go +73 -51
- package/compiler/spec.go +337 -151
- package/compiler/stmt-assign.go +7 -4
- package/compiler/stmt.go +250 -81
- package/compiler/type.go +13 -0
- package/dist/gs/builtin/builtin.d.ts +1 -5
- package/dist/gs/builtin/builtin.js +1 -34
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/context/context.d.ts +16 -18
- package/dist/gs/context/context.js +23 -13
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/fmt/fmt.js +22 -4
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +6 -12
- package/dist/gs/io/fs/fs.js +52 -67
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/os/index.d.ts +2 -1
- package/dist/gs/os/index.js +1 -1
- package/dist/gs/os/index.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +7 -1
- package/dist/gs/os/types_js.gs.js +16 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +2 -2
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +3 -3
- package/dist/gs/reflect/index.js +2 -2
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +2 -2
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +8 -9
- package/dist/gs/reflect/type.js +98 -103
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +1 -10
- package/dist/gs/reflect/types.js +3 -26
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.js +23 -23
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +3 -3
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/time/time.d.ts +13 -23
- package/dist/gs/time/time.js +57 -75
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/builtin.ts +3 -47
- package/gs/builtin/slice.ts +1 -1
- package/gs/bytes/meta.json +10 -0
- package/gs/context/context.ts +63 -45
- package/gs/fmt/fmt.ts +22 -4
- package/gs/fmt/meta.json +5 -0
- package/gs/internal/meta.json +5 -0
- package/gs/io/fs/fs.ts +58 -73
- package/gs/io/meta.json +9 -0
- package/gs/maps/meta.json +6 -0
- package/gs/math/meta.json +5 -0
- package/gs/os/index.ts +8 -1
- package/gs/os/meta.json +15 -0
- package/gs/os/types_js.gs.ts +22 -1
- package/gs/os/types_unix.gs.ts +2 -2
- package/gs/path/meta.json +6 -0
- package/gs/reflect/function-types.test.ts +10 -10
- package/gs/reflect/index.ts +6 -6
- package/gs/reflect/map.ts +2 -2
- package/gs/reflect/meta.json +5 -0
- package/gs/reflect/type.ts +105 -105
- package/gs/reflect/types.ts +2 -28
- package/gs/reflect/value.ts +23 -23
- package/gs/reflect/visiblefields.ts +3 -3
- package/gs/strconv/meta.json +5 -0
- package/gs/strings/meta.json +9 -0
- package/gs/sync/meta.json +19 -0
- package/gs/time/time.ts +65 -84
- package/package.json +2 -2
- package/dist/gs/builtin/io.d.ts +0 -16
- package/dist/gs/builtin/io.js +0 -15
- package/dist/gs/builtin/io.js.map +0 -1
- package/dist/gs/internal/testlog/index.d.ts +0 -1
- package/dist/gs/internal/testlog/index.js +0 -5
- package/dist/gs/internal/testlog/index.js.map +0 -1
- package/dist/gs/maps/iter.gs.d.ts +0 -7
- package/dist/gs/maps/iter.gs.js +0 -65
- package/dist/gs/maps/iter.gs.js.map +0 -1
- package/dist/gs/maps/maps.gs.d.ts +0 -7
- package/dist/gs/maps/maps.gs.js +0 -79
- package/dist/gs/maps/maps.gs.js.map +0 -1
- package/dist/gs/reflect/abi.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.js +0 -79
- package/dist/gs/reflect/abi.gs.js.map +0 -1
- package/dist/gs/reflect/abi.js +0 -79
- package/dist/gs/reflect/abi.js.map +0 -1
- package/dist/gs/reflect/badlinkname.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.js +0 -72
- package/dist/gs/reflect/badlinkname.gs.js.map +0 -1
- package/dist/gs/reflect/badlinkname.js +0 -72
- package/dist/gs/reflect/badlinkname.js.map +0 -1
- package/dist/gs/reflect/deepequal.gs.d.ts +0 -25
- package/dist/gs/reflect/deepequal.gs.js +0 -308
- package/dist/gs/reflect/deepequal.gs.js.map +0 -1
- package/dist/gs/reflect/float32reg_generic.gs.d.ts +0 -2
- package/dist/gs/reflect/float32reg_generic.gs.js +0 -10
- package/dist/gs/reflect/float32reg_generic.gs.js.map +0 -1
- package/dist/gs/reflect/index.gs.d.ts +0 -1
- package/dist/gs/reflect/index.gs.js +0 -3
- package/dist/gs/reflect/index.gs.js.map +0 -1
- package/dist/gs/reflect/iter.gs.d.ts +0 -3
- package/dist/gs/reflect/iter.gs.js +0 -24
- package/dist/gs/reflect/iter.gs.js.map +0 -1
- package/dist/gs/reflect/makefunc.gs.d.ts +0 -34
- package/dist/gs/reflect/makefunc.gs.js +0 -288
- package/dist/gs/reflect/makefunc.gs.js.map +0 -1
- package/dist/gs/reflect/map_swiss.gs.d.ts +0 -14
- package/dist/gs/reflect/map_swiss.gs.js +0 -70
- package/dist/gs/reflect/map_swiss.gs.js.map +0 -1
- package/dist/gs/reflect/reflect.gs.d.ts +0 -132
- package/dist/gs/reflect/reflect.gs.js +0 -437
- package/dist/gs/reflect/reflect.gs.js.map +0 -1
- package/dist/gs/reflect/swapper.gs.d.ts +0 -1
- package/dist/gs/reflect/swapper.gs.js +0 -32
- package/dist/gs/reflect/swapper.gs.js.map +0 -1
- package/dist/gs/reflect/type.gs.d.ts +0 -4
- package/dist/gs/reflect/type.gs.js +0 -21
- package/dist/gs/reflect/type.gs.js.map +0 -1
- package/dist/gs/reflect/value.gs.d.ts +0 -4
- package/dist/gs/reflect/value.gs.js +0 -12
- package/dist/gs/reflect/value.gs.js.map +0 -1
- package/dist/gs/reflect/visiblefields.gs.d.ts +0 -3
- package/dist/gs/reflect/visiblefields.gs.js +0 -123
- package/dist/gs/reflect/visiblefields.gs.js.map +0 -1
- package/dist/gs/stringslite/index.d.ts +0 -1
- package/dist/gs/stringslite/index.js +0 -2
- package/dist/gs/stringslite/index.js.map +0 -1
- package/dist/gs/stringslite/strings.d.ts +0 -11
- package/dist/gs/stringslite/strings.js +0 -67
- package/dist/gs/stringslite/strings.js.map +0 -1
- package/gs/bytes/metadata.go +0 -12
- package/gs/fmt/metadata.go +0 -7
- package/gs/internal/metadata.go +0 -7
- package/gs/io/io.go +0 -75
- package/gs/io/metadata.go +0 -11
- package/gs/maps/metadata.go +0 -8
- package/gs/math/metadata.go +0 -7
- package/gs/os/metadata.go +0 -17
- package/gs/path/metadata.go +0 -8
- package/gs/reflect/metadata.go +0 -7
- package/gs/strconv/metadata.go +0 -7
- package/gs/strings/metadata.go +0 -11
- package/gs/sync/metadata.go +0 -7
- package/gs/sync/sync.go +0 -64
|
@@ -5,6 +5,7 @@ import (
|
|
|
5
5
|
"go/parser"
|
|
6
6
|
"go/token"
|
|
7
7
|
"go/types"
|
|
8
|
+
"os"
|
|
8
9
|
"testing"
|
|
9
10
|
|
|
10
11
|
"golang.org/x/tools/go/packages"
|
|
@@ -159,6 +160,7 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
|
|
|
159
160
|
Defs: make(map[*ast.Ident]types.Object),
|
|
160
161
|
Uses: make(map[*ast.Ident]types.Object),
|
|
161
162
|
},
|
|
163
|
+
Fset: fset,
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
// Type check the package
|
|
@@ -169,10 +171,8 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
|
|
|
169
171
|
}
|
|
170
172
|
pkg.Types = typePkg
|
|
171
173
|
|
|
172
|
-
// Run analysis
|
|
173
|
-
analysis :=
|
|
174
|
-
cmap := ast.NewCommentMap(fset, file, file.Comments)
|
|
175
|
-
AnalyzeFile(file, pkg, analysis, cmap)
|
|
174
|
+
// Run package-level analysis
|
|
175
|
+
analysis := AnalyzePackageFiles(pkg, nil)
|
|
176
176
|
|
|
177
177
|
// Collect variable objects
|
|
178
178
|
objects := make(map[string]types.Object)
|
|
@@ -232,3 +232,112 @@ func main() {
|
|
|
232
232
|
t.Log("")
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
+
|
|
236
|
+
// TestWrapperTypeDetection verifies that the analysis correctly identifies wrapper types
|
|
237
|
+
func TestWrapperTypeDetection(t *testing.T) {
|
|
238
|
+
// Use the actual compliance test case
|
|
239
|
+
testPath := "../compliance/tests/wrapper_type_args"
|
|
240
|
+
|
|
241
|
+
// Load the package using the packages config like the main compiler does
|
|
242
|
+
cfg := &packages.Config{
|
|
243
|
+
Mode: packages.NeedName |
|
|
244
|
+
packages.NeedFiles |
|
|
245
|
+
packages.NeedCompiledGoFiles |
|
|
246
|
+
packages.NeedImports |
|
|
247
|
+
packages.NeedDeps |
|
|
248
|
+
packages.NeedExportFile |
|
|
249
|
+
packages.NeedTypes |
|
|
250
|
+
packages.NeedSyntax |
|
|
251
|
+
packages.NeedTypesInfo |
|
|
252
|
+
packages.NeedTypesSizes,
|
|
253
|
+
Tests: false,
|
|
254
|
+
Env: append(os.Environ(), "GOOS=js", "GOARCH=wasm"),
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
pkgs, err := packages.Load(cfg, testPath)
|
|
258
|
+
if err != nil {
|
|
259
|
+
t.Fatalf("Failed to load test package: %v", err)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if len(pkgs) != 1 {
|
|
263
|
+
t.Fatalf("Expected 1 package, got %d", len(pkgs))
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
pkg := pkgs[0]
|
|
267
|
+
if len(pkg.Errors) > 0 {
|
|
268
|
+
t.Fatalf("Package has errors: %v", pkg.Errors[0])
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Run package-level analysis
|
|
272
|
+
if len(pkg.Syntax) == 0 {
|
|
273
|
+
t.Fatal("No syntax files found")
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
analysis := AnalyzePackageFiles(pkg, nil)
|
|
277
|
+
|
|
278
|
+
// Verify the NamedBasicTypes map was initialized
|
|
279
|
+
if analysis.NamedBasicTypes == nil {
|
|
280
|
+
t.Error("NamedBasicTypes map was not initialized")
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Test some type lookups to verify wrapper type detection works
|
|
284
|
+
// We'll check if MyMode (which has methods) is detected as a wrapper type
|
|
285
|
+
scope := pkg.Types.Scope()
|
|
286
|
+
|
|
287
|
+
// Check if MyMode is detected as a wrapper type
|
|
288
|
+
if obj := scope.Lookup("MyMode"); obj != nil {
|
|
289
|
+
if typeName, ok := obj.(*types.TypeName); ok {
|
|
290
|
+
isWrapper := analysis.IsNamedBasicType(typeName.Type())
|
|
291
|
+
if !isWrapper {
|
|
292
|
+
t.Errorf("MyMode should be detected as wrapper type, got %v", isWrapper)
|
|
293
|
+
}
|
|
294
|
+
t.Logf("MyMode wrapper detection: %v (correct)", isWrapper)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Test that regular struct types are not detected as wrapper types
|
|
299
|
+
if obj := scope.Lookup("MyDir"); obj != nil {
|
|
300
|
+
if typeName, ok := obj.(*types.TypeName); ok {
|
|
301
|
+
isWrapper := analysis.IsNamedBasicType(typeName.Type())
|
|
302
|
+
if isWrapper {
|
|
303
|
+
t.Errorf("MyDir should not be detected as wrapper type, got %v", isWrapper)
|
|
304
|
+
}
|
|
305
|
+
t.Logf("MyDir wrapper detection: %v (correct)", isWrapper)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
t.Logf("Analysis completed successfully with %d named basic types tracked", len(analysis.NamedBasicTypes))
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// TestDiscoverGsPackages verifies that the discoverEmbeddedGsPackages function
|
|
313
|
+
// can find packages in the embedded gs/ directory
|
|
314
|
+
func TestDiscoverGsPackages(t *testing.T) {
|
|
315
|
+
analysis := NewAnalysis(nil)
|
|
316
|
+
|
|
317
|
+
// Test package discovery using the embedded filesystem
|
|
318
|
+
packages := analysis.discoverEmbeddedGsPackages()
|
|
319
|
+
t.Logf("Discovered %d packages:", len(packages))
|
|
320
|
+
for _, pkg := range packages {
|
|
321
|
+
t.Logf(" - %s", pkg)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// We should find at least some packages
|
|
325
|
+
if len(packages) == 0 {
|
|
326
|
+
t.Errorf("Expected to find at least one package in gs/ directory")
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Check for some known packages that should exist
|
|
330
|
+
expectedPackages := []string{"sync", "bytes", "strings"}
|
|
331
|
+
for _, expected := range expectedPackages {
|
|
332
|
+
found := false
|
|
333
|
+
for _, pkg := range packages {
|
|
334
|
+
if pkg == expected {
|
|
335
|
+
found = true
|
|
336
|
+
break
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if !found {
|
|
340
|
+
t.Logf("Expected package '%s' not found in discovered packages: %v", expected, packages)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
package/compiler/compiler.go
CHANGED
|
@@ -2,10 +2,10 @@ package compiler
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
|
+
"encoding/json"
|
|
5
6
|
"fmt"
|
|
6
7
|
"go/ast"
|
|
7
8
|
"go/constant"
|
|
8
|
-
"go/parser"
|
|
9
9
|
"go/token"
|
|
10
10
|
"go/types"
|
|
11
11
|
"os"
|
|
@@ -72,7 +72,11 @@ func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compil
|
|
|
72
72
|
packages.NeedTypesInfo |
|
|
73
73
|
packages.NeedTypesSizes
|
|
74
74
|
|
|
75
|
-
return &Compiler{
|
|
75
|
+
return &Compiler{
|
|
76
|
+
config: *conf,
|
|
77
|
+
le: le,
|
|
78
|
+
opts: *opts,
|
|
79
|
+
}, nil
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
// CompilationResult contains information about what was compiled
|
|
@@ -141,7 +145,6 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
141
145
|
// Check if this package has a handwritten equivalent
|
|
142
146
|
if hasHandwrittenEquivalent(pkg.PkgPath) {
|
|
143
147
|
// Add this package but don't visit its dependencies
|
|
144
|
-
// c.le.Debugf("Skipping dependencies of handwritten package: %s", pkg.PkgPath)
|
|
145
148
|
return
|
|
146
149
|
}
|
|
147
150
|
|
|
@@ -149,13 +152,11 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
149
152
|
for _, imp := range pkg.Imports {
|
|
150
153
|
// Skip protobuf-go-lite packages and their dependencies
|
|
151
154
|
if isProtobufGoLitePackage(imp.PkgPath) {
|
|
152
|
-
// c.le.Debugf("Skipping protobuf-go-lite package: %s", imp.PkgPath)
|
|
153
155
|
continue
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
// Skip packages that are only used by .pb.go files
|
|
157
159
|
if isPackageOnlyUsedByProtobufFiles(pkg, imp.PkgPath) {
|
|
158
|
-
// c.le.Debugf("Skipping package only used by .pb.go files: %s", imp.PkgPath)
|
|
159
160
|
continue
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -168,28 +169,39 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
168
169
|
visit(pkg)
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
//
|
|
172
|
-
|
|
172
|
+
// Now we have collected all dependencies, but they only have minimal information.
|
|
173
|
+
// We need to reload them with complete type information for compilation.
|
|
174
|
+
// Collect all package paths from the dependency graph
|
|
175
|
+
var pkgPaths []string
|
|
176
|
+
pkgPathSet := make(map[string]bool) // Use set to avoid duplicates
|
|
173
177
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if pkg.PkgPath != "" {
|
|
179
|
-
pkgPaths = append(pkgPaths, pkg.PkgPath)
|
|
180
|
-
}
|
|
178
|
+
for _, pkg := range allPkgs {
|
|
179
|
+
if pkg.PkgPath != "" && !pkgPathSet[pkg.PkgPath] {
|
|
180
|
+
pkgPaths = append(pkgPaths, pkg.PkgPath)
|
|
181
|
+
pkgPathSet[pkg.PkgPath] = true
|
|
181
182
|
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Reload all collected packages with complete type information
|
|
186
|
+
if len(pkgPaths) > 0 {
|
|
187
|
+
c.le.Debugf("Reloading %d packages with complete type information", len(pkgPaths))
|
|
182
188
|
|
|
183
|
-
// Reload all packages with full mode
|
|
184
|
-
// TODO: Can we get rid of this? This would be very slow!
|
|
185
189
|
fullOpts := c.opts
|
|
186
190
|
fullOpts.Context = ctx
|
|
191
|
+
// Use LoadAllSyntax to get complete type information, syntax trees, and type checking
|
|
187
192
|
fullOpts.Mode = packages.LoadAllSyntax
|
|
188
|
-
|
|
193
|
+
|
|
194
|
+
reloadedPkgs, err := packages.Load(&fullOpts, pkgPaths...)
|
|
189
195
|
if err != nil {
|
|
190
|
-
return fmt.Errorf("failed to reload packages with
|
|
196
|
+
return nil, fmt.Errorf("failed to reload packages with complete type information: %w", err)
|
|
191
197
|
}
|
|
192
|
-
|
|
198
|
+
|
|
199
|
+
// Replace the minimal packages with the fully loaded ones
|
|
200
|
+
pkgs = reloadedPkgs
|
|
201
|
+
} else {
|
|
202
|
+
// No packages to reload, use the original set
|
|
203
|
+
pkgs = allPkgs
|
|
204
|
+
}
|
|
193
205
|
}
|
|
194
206
|
|
|
195
207
|
// If DisableEmitBuiltin is false, we need to copy the builtin package to the output directory
|
|
@@ -206,6 +218,12 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
206
218
|
// Track which gs packages have been processed to avoid duplicates
|
|
207
219
|
processedGsPackages := make(map[string]bool)
|
|
208
220
|
|
|
221
|
+
// Create a map of all loaded packages for dependency analysis
|
|
222
|
+
allPackages := make(map[string]*packages.Package)
|
|
223
|
+
for _, pkg := range pkgs {
|
|
224
|
+
allPackages[pkg.PkgPath] = pkg
|
|
225
|
+
}
|
|
226
|
+
|
|
209
227
|
// Compile all packages
|
|
210
228
|
for _, pkg := range pkgs {
|
|
211
229
|
// Check if the package has a handwritten equivalent
|
|
@@ -237,7 +255,7 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
237
255
|
continue
|
|
238
256
|
}
|
|
239
257
|
|
|
240
|
-
pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
|
|
258
|
+
pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg, allPackages)
|
|
241
259
|
if err != nil {
|
|
242
260
|
return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
|
|
243
261
|
}
|
|
@@ -262,6 +280,7 @@ type PackageCompiler struct {
|
|
|
262
280
|
compilerConf *Config
|
|
263
281
|
outputPath string
|
|
264
282
|
pkg *packages.Package
|
|
283
|
+
allPackages map[string]*packages.Package
|
|
265
284
|
}
|
|
266
285
|
|
|
267
286
|
// NewPackageCompiler creates a new `PackageCompiler` for a given Go package.
|
|
@@ -272,12 +291,14 @@ func NewPackageCompiler(
|
|
|
272
291
|
le *logrus.Entry,
|
|
273
292
|
compilerConf *Config,
|
|
274
293
|
pkg *packages.Package,
|
|
294
|
+
allPackages map[string]*packages.Package,
|
|
275
295
|
) (*PackageCompiler, error) {
|
|
276
296
|
res := &PackageCompiler{
|
|
277
297
|
le: le,
|
|
278
298
|
pkg: pkg,
|
|
279
299
|
compilerConf: compilerConf,
|
|
280
300
|
outputPath: ComputeModulePath(compilerConf.OutputPath, pkg.PkgPath),
|
|
301
|
+
allPackages: allPackages,
|
|
281
302
|
}
|
|
282
303
|
|
|
283
304
|
return res, nil
|
|
@@ -304,7 +325,10 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
304
325
|
}
|
|
305
326
|
|
|
306
327
|
// Perform package-level analysis for auto-imports
|
|
307
|
-
packageAnalysis :=
|
|
328
|
+
packageAnalysis := AnalyzePackageImports(c.pkg)
|
|
329
|
+
|
|
330
|
+
// Perform comprehensive package-level analysis for code generation
|
|
331
|
+
analysis := AnalyzePackageFiles(c.pkg, c.allPackages)
|
|
308
332
|
|
|
309
333
|
// Track all compiled files for later generating the index.ts
|
|
310
334
|
compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
|
|
@@ -342,7 +366,7 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
342
366
|
|
|
343
367
|
// log just the filename
|
|
344
368
|
c.le.Debugf("GS: %s", filepath.Base(fileName))
|
|
345
|
-
if err := c.CompileFile(ctx, fileName, f, packageAnalysis); err != nil {
|
|
369
|
+
if err := c.CompileFile(ctx, fileName, f, analysis, packageAnalysis); err != nil {
|
|
346
370
|
return err
|
|
347
371
|
}
|
|
348
372
|
|
|
@@ -392,6 +416,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
392
416
|
// Find which symbols this file exports
|
|
393
417
|
var valueSymbols []string
|
|
394
418
|
var typeSymbols []string
|
|
419
|
+
var structSymbols []string // New: Track structs separately
|
|
395
420
|
|
|
396
421
|
// Find the corresponding syntax file
|
|
397
422
|
for i, syntax := range c.pkg.Syntax {
|
|
@@ -415,9 +440,15 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
415
440
|
switch s := spec.(type) {
|
|
416
441
|
case *ast.TypeSpec:
|
|
417
442
|
if s.Name.IsExported() {
|
|
418
|
-
//
|
|
419
|
-
|
|
420
|
-
|
|
443
|
+
// Check if this is a struct type
|
|
444
|
+
if _, isStruct := s.Type.(*ast.StructType); isStruct {
|
|
445
|
+
// Structs become TypeScript classes and need both type and value exports
|
|
446
|
+
structSymbols = append(structSymbols, s.Name.Name)
|
|
447
|
+
} else {
|
|
448
|
+
// Other type declarations (interfaces, type definitions, type aliases)
|
|
449
|
+
// become TypeScript types and must be exported with "export type"
|
|
450
|
+
typeSymbols = append(typeSymbols, s.Name.Name)
|
|
451
|
+
}
|
|
421
452
|
}
|
|
422
453
|
case *ast.ValueSpec:
|
|
423
454
|
for _, name := range s.Names {
|
|
@@ -442,6 +473,17 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
442
473
|
}
|
|
443
474
|
}
|
|
444
475
|
|
|
476
|
+
// Write struct exports (both as types and values)
|
|
477
|
+
if len(structSymbols) > 0 {
|
|
478
|
+
sort.Strings(structSymbols)
|
|
479
|
+
// Export classes as values (which makes them available as both types and values in TypeScript)
|
|
480
|
+
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
481
|
+
strings.Join(structSymbols, ", "), fileName)
|
|
482
|
+
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
483
|
+
return err
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
445
487
|
if len(typeSymbols) > 0 {
|
|
446
488
|
sort.Strings(typeSymbols)
|
|
447
489
|
exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
|
|
@@ -456,21 +498,11 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
456
498
|
}
|
|
457
499
|
|
|
458
500
|
// CompileFile handles the compilation of a single Go source file to TypeScript.
|
|
459
|
-
// It
|
|
460
|
-
//
|
|
461
|
-
// about varRefing, async functions, defer statements).
|
|
501
|
+
// It uses the pre-computed package-level analysis for accurate TypeScript generation
|
|
502
|
+
// (e.g., about varRefing, async functions, defer statements, receiver usage across files).
|
|
462
503
|
// Then, it creates a `FileCompiler` instance for the file and invokes its
|
|
463
504
|
// `Compile` method to generate the TypeScript code.
|
|
464
|
-
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, packageAnalysis *PackageAnalysis) error {
|
|
465
|
-
// Create a new analysis instance for per-file data
|
|
466
|
-
analysis := NewAnalysis()
|
|
467
|
-
|
|
468
|
-
// Create comment map for the file
|
|
469
|
-
cmap := ast.NewCommentMap(p.pkg.Fset, syntax, syntax.Comments)
|
|
470
|
-
|
|
471
|
-
// Analyze the file before compiling
|
|
472
|
-
AnalyzeFile(syntax, p.pkg, analysis, cmap)
|
|
473
|
-
|
|
505
|
+
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, analysis *Analysis, packageAnalysis *PackageAnalysis) error {
|
|
474
506
|
fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
|
|
475
507
|
if err != nil {
|
|
476
508
|
return err
|
|
@@ -644,9 +676,6 @@ type GoToTSCompiler struct {
|
|
|
644
676
|
pkg *packages.Package
|
|
645
677
|
|
|
646
678
|
analysis *Analysis
|
|
647
|
-
|
|
648
|
-
// shadowingContext tracks temporary variable mappings when we're in a shadowing context
|
|
649
|
-
shadowingContext map[string]string
|
|
650
679
|
}
|
|
651
680
|
|
|
652
681
|
// It initializes the compiler with a `TSCodeWriter` for output,
|
|
@@ -654,10 +683,9 @@ type GoToTSCompiler struct {
|
|
|
654
683
|
// analysis results (`Analysis`) to guide the translation process.
|
|
655
684
|
func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
|
|
656
685
|
return &GoToTSCompiler{
|
|
657
|
-
tsw:
|
|
658
|
-
pkg:
|
|
659
|
-
analysis:
|
|
660
|
-
shadowingContext: make(map[string]string),
|
|
686
|
+
tsw: tsw,
|
|
687
|
+
pkg: pkg,
|
|
688
|
+
analysis: analysis,
|
|
661
689
|
}
|
|
662
690
|
}
|
|
663
691
|
|
|
@@ -679,9 +707,9 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
|
|
|
679
707
|
return
|
|
680
708
|
}
|
|
681
709
|
|
|
682
|
-
// Check if
|
|
683
|
-
if
|
|
684
|
-
c.tsw.WriteLiterally(c.sanitizeIdentifier(
|
|
710
|
+
// Check if this identifier has a pre-computed mapping (e.g., wrapper function receiver)
|
|
711
|
+
if mappedName := c.analysis.GetIdentifierMapping(exp); mappedName != "" {
|
|
712
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(mappedName))
|
|
685
713
|
return
|
|
686
714
|
}
|
|
687
715
|
|
|
@@ -1036,97 +1064,33 @@ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) e
|
|
|
1036
1064
|
// GsPackageMetadata holds metadata about a gs/ package
|
|
1037
1065
|
type GsPackageMetadata struct {
|
|
1038
1066
|
// Dependencies lists the import paths that this gs/ package requires
|
|
1039
|
-
Dependencies []string
|
|
1067
|
+
Dependencies []string `json:"dependencies,omitempty"`
|
|
1068
|
+
AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
|
|
1040
1069
|
}
|
|
1041
1070
|
|
|
1042
|
-
// ReadGsPackageMetadata reads dependency metadata from .
|
|
1043
|
-
// It looks for a var variable named "GsDependencies" which should be a slice of strings
|
|
1044
|
-
// containing the import paths that this package depends on.
|
|
1071
|
+
// ReadGsPackageMetadata reads dependency metadata from meta.json file in a gs/ package
|
|
1045
1072
|
func (c *Compiler) ReadGsPackageMetadata(gsSourcePath string) (*GsPackageMetadata, error) {
|
|
1046
1073
|
metadata := &GsPackageMetadata{
|
|
1047
1074
|
Dependencies: []string{},
|
|
1075
|
+
AsyncMethods: make(map[string]bool),
|
|
1048
1076
|
}
|
|
1049
1077
|
|
|
1050
|
-
//
|
|
1051
|
-
|
|
1078
|
+
// Try to read meta.json file
|
|
1079
|
+
metaFilePath := filepath.Join(gsSourcePath, "meta.json")
|
|
1080
|
+
content, err := gs.GsOverrides.ReadFile(metaFilePath)
|
|
1052
1081
|
if err != nil {
|
|
1053
|
-
|
|
1082
|
+
// No meta.json file found, return empty metadata
|
|
1083
|
+
return metadata, nil
|
|
1054
1084
|
}
|
|
1055
1085
|
|
|
1056
|
-
//
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
metadataFilePath := filepath.Join(gsSourcePath, entry.Name())
|
|
1060
|
-
|
|
1061
|
-
// Read the .go file content
|
|
1062
|
-
content, err := gs.GsOverrides.ReadFile(metadataFilePath)
|
|
1063
|
-
if err != nil {
|
|
1064
|
-
continue // Skip files we can't read
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// Parse the file to extract metadata
|
|
1068
|
-
if deps, err := c.extractDependenciesFromGoFile(content); err == nil {
|
|
1069
|
-
metadata.Dependencies = append(metadata.Dependencies, deps...)
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1086
|
+
// Parse the JSON content
|
|
1087
|
+
if err := json.Unmarshal(content, metadata); err != nil {
|
|
1088
|
+
return metadata, fmt.Errorf("failed to parse meta.json in %s: %w", gsSourcePath, err)
|
|
1072
1089
|
}
|
|
1073
1090
|
|
|
1074
1091
|
return metadata, nil
|
|
1075
1092
|
}
|
|
1076
1093
|
|
|
1077
|
-
// extractDependenciesFromGoFile parses a .go file and extracts the GsDependencies var
|
|
1078
|
-
func (c *Compiler) extractDependenciesFromGoFile(content []byte) ([]string, error) {
|
|
1079
|
-
// Parse the Go file
|
|
1080
|
-
fset := token.NewFileSet()
|
|
1081
|
-
file, err := parser.ParseFile(fset, "metadata.go", content, 0)
|
|
1082
|
-
if err != nil {
|
|
1083
|
-
return nil, err
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
var dependencies []string
|
|
1087
|
-
|
|
1088
|
-
// Look for var declarations
|
|
1089
|
-
for _, decl := range file.Decls {
|
|
1090
|
-
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.VAR {
|
|
1091
|
-
for _, spec := range genDecl.Specs {
|
|
1092
|
-
if valueSpec, ok := spec.(*ast.ValueSpec); ok {
|
|
1093
|
-
for i, name := range valueSpec.Names {
|
|
1094
|
-
if name.Name == "GsDependencies" {
|
|
1095
|
-
// Found the GsDependencies var, extract its value
|
|
1096
|
-
if i < len(valueSpec.Values) {
|
|
1097
|
-
if deps := c.extractStringSliceFromExpr(valueSpec.Values[i]); deps != nil {
|
|
1098
|
-
dependencies = append(dependencies, deps...)
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
return dependencies, nil
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
// extractStringSliceFromExpr extracts string values from a composite literal expression
|
|
1112
|
-
func (c *Compiler) extractStringSliceFromExpr(expr ast.Expr) []string {
|
|
1113
|
-
var result []string
|
|
1114
|
-
|
|
1115
|
-
if compLit, ok := expr.(*ast.CompositeLit); ok {
|
|
1116
|
-
for _, elt := range compLit.Elts {
|
|
1117
|
-
if basicLit, ok := elt.(*ast.BasicLit); ok && basicLit.Kind == token.STRING {
|
|
1118
|
-
// Remove quotes from string literal
|
|
1119
|
-
value := basicLit.Value
|
|
1120
|
-
if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' {
|
|
1121
|
-
result = append(result, value[1:len(value)-1])
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
return result
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
1094
|
// copyGsPackageWithDependencies copies a gs/ package and all its dependencies recursively
|
|
1131
1095
|
// It tracks already processed packages to avoid infinite loops and duplicate work
|
|
1132
1096
|
func (c *Compiler) copyGsPackageWithDependencies(packagePath string, processedPackages map[string]bool, result *CompilationResult) error {
|
package/compiler/decl.go
CHANGED
|
@@ -3,6 +3,7 @@ package compiler
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
|
+
"go/types"
|
|
6
7
|
)
|
|
7
8
|
|
|
8
9
|
// WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
|
|
@@ -193,6 +194,27 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
|
|
|
193
194
|
var isAsync bool
|
|
194
195
|
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
195
196
|
isAsync = c.analysis.IsAsyncFunc(obj)
|
|
197
|
+
|
|
198
|
+
// Check if this method must be async due to interface constraints
|
|
199
|
+
if !isAsync && decl.Recv != nil && len(decl.Recv.List) > 0 {
|
|
200
|
+
// Get the receiver type
|
|
201
|
+
receiverType := decl.Recv.List[0].Type
|
|
202
|
+
if starExpr, ok := receiverType.(*ast.StarExpr); ok {
|
|
203
|
+
receiverType = starExpr.X
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if ident, ok := receiverType.(*ast.Ident); ok {
|
|
207
|
+
// Get the named type for this receiver
|
|
208
|
+
if receiverObj := c.pkg.TypesInfo.Uses[ident]; receiverObj != nil {
|
|
209
|
+
if namedType, ok := receiverObj.Type().(*types.Named); ok {
|
|
210
|
+
// Check if this method must be async due to interface constraints
|
|
211
|
+
if c.analysis.MustBeAsyncDueToInterface(namedType, decl.Name.Name) {
|
|
212
|
+
isAsync = true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
196
218
|
}
|
|
197
219
|
|
|
198
220
|
// Methods are typically public in the TS output
|
|
@@ -6,70 +6,64 @@ import (
|
|
|
6
6
|
"strings"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// writeAsyncCallIfNeeded writes the await prefix for async function or method calls
|
|
10
|
+
// Returns true if await was written, false otherwise
|
|
11
|
+
func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
12
|
+
switch fun := exp.Fun.(type) {
|
|
13
|
+
case *ast.Ident:
|
|
14
|
+
// Function call (e.g., func())
|
|
15
|
+
if obj := c.pkg.TypesInfo.Uses[fun]; obj != nil && c.analysis.IsAsyncFunc(obj) {
|
|
16
|
+
c.tsw.WriteLiterally("await ")
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
12
19
|
return false
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Check if this is an async function call
|
|
16
|
-
if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
|
|
17
|
-
c.tsw.WriteLiterally("await ")
|
|
18
|
-
return true
|
|
19
|
-
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
case *ast.SelectorExpr:
|
|
22
|
+
// Method call (e.g., obj.method())
|
|
23
|
+
ident, ok := fun.X.(*ast.Ident)
|
|
24
|
+
if !ok {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
23
27
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
28
|
+
// Get the type of the receiver
|
|
29
|
+
obj := c.pkg.TypesInfo.Uses[ident]
|
|
30
|
+
if obj == nil {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
34
|
+
varObj, ok := obj.(*types.Var)
|
|
35
|
+
if !ok {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
// Get the type name and package
|
|
40
|
+
namedType, ok := varObj.Type().(*types.Named)
|
|
41
|
+
if !ok {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
45
|
+
typeName := namedType.Obj().Name()
|
|
46
|
+
methodName := fun.Sel.Name
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
// Check if the type is from an imported package
|
|
49
|
+
typePkg := namedType.Obj().Pkg()
|
|
50
|
+
if typePkg == nil || typePkg == c.pkg.Types {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Use the full package path from the type information (not just the package name)
|
|
55
|
+
pkgPath := typePkg.Path()
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
// Check if this method is async based on metadata
|
|
58
|
+
if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
|
|
59
|
+
c.tsw.WriteLiterally("await ")
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
60
62
|
return false
|
|
61
|
-
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Check if this method is async based on metadata
|
|
67
|
-
if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
|
|
68
|
-
c.tsw.WriteLiterally("await ")
|
|
69
|
-
return true
|
|
64
|
+
default:
|
|
65
|
+
return false
|
|
70
66
|
}
|
|
71
|
-
|
|
72
|
-
return false
|
|
73
67
|
}
|
|
74
68
|
|
|
75
69
|
// addNonNullAssertion adds ! for function calls that might return null
|