goscript 0.2.1 → 0.2.3

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 (164) hide show
  1. package/compiler/gotest/runner.go +98 -0
  2. package/compiler/gotest/runner_test.go +45 -0
  3. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  4. package/compiler/lowering.go +227 -11
  5. package/compiler/override-registry_test.go +50 -0
  6. package/compiler/protobuf-ts-binding.go +155 -7
  7. package/compiler/protobuf-ts-binding_test.go +116 -2
  8. package/compiler/runtime-contract.go +2 -0
  9. package/compiler/runtime-contract_test.go +1 -0
  10. package/compiler/semantic-model.go +16 -0
  11. package/compiler/semantic-model_test.go +38 -0
  12. package/compiler/skeleton_test.go +477 -16
  13. package/compiler/typescript-emitter.go +4 -0
  14. package/dist/gs/builtin/builtin.js +7 -9
  15. package/dist/gs/builtin/builtin.js.map +1 -1
  16. package/dist/gs/builtin/defer.js +2 -2
  17. package/dist/gs/builtin/hostio.js +5 -5
  18. package/dist/gs/builtin/hostio.js.map +1 -1
  19. package/dist/gs/builtin/map.js +2 -1
  20. package/dist/gs/builtin/map.js.map +1 -1
  21. package/dist/gs/builtin/slice.d.ts +3 -0
  22. package/dist/gs/builtin/slice.js +39 -0
  23. package/dist/gs/builtin/slice.js.map +1 -1
  24. package/dist/gs/builtin/type.js +49 -0
  25. package/dist/gs/builtin/type.js.map +1 -1
  26. package/dist/gs/compress/zlib/index.js +5 -2
  27. package/dist/gs/compress/zlib/index.js.map +1 -1
  28. package/dist/gs/crypto/aes/index.d.ts +15 -0
  29. package/dist/gs/crypto/aes/index.js +57 -0
  30. package/dist/gs/crypto/aes/index.js.map +1 -0
  31. package/dist/gs/crypto/cipher/index.d.ts +41 -0
  32. package/dist/gs/crypto/cipher/index.js +255 -0
  33. package/dist/gs/crypto/cipher/index.js.map +1 -0
  34. package/dist/gs/crypto/ecdh/index.js +27 -8
  35. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  36. package/dist/gs/crypto/ed25519/index.js +3 -3
  37. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  38. package/dist/gs/crypto/rand/index.js +6 -3
  39. package/dist/gs/crypto/rand/index.js.map +1 -1
  40. package/dist/gs/embed/index.js +9 -3
  41. package/dist/gs/embed/index.js.map +1 -1
  42. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  43. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  44. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  45. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  46. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  47. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
  48. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
  49. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
  50. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  51. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  52. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  53. package/dist/gs/hash/fnv/index.js +13 -5
  54. package/dist/gs/hash/fnv/index.js.map +1 -1
  55. package/dist/gs/io/fs/glob.d.ts +3 -3
  56. package/dist/gs/io/fs/glob.js +8 -8
  57. package/dist/gs/io/fs/glob.js.map +1 -1
  58. package/dist/gs/io/fs/readdir.d.ts +2 -2
  59. package/dist/gs/io/fs/readdir.js +13 -74
  60. package/dist/gs/io/fs/readdir.js.map +1 -1
  61. package/dist/gs/io/fs/sub.js +4 -4
  62. package/dist/gs/io/fs/sub.js.map +1 -1
  63. package/dist/gs/io/fs/walk.js +1 -1
  64. package/dist/gs/io/fs/walk.js.map +1 -1
  65. package/dist/gs/io/io.js +18 -2
  66. package/dist/gs/io/io.js.map +1 -1
  67. package/dist/gs/maps/iter.js.map +1 -1
  68. package/dist/gs/maps/maps.js.map +1 -1
  69. package/dist/gs/mime/index.js +5 -2
  70. package/dist/gs/mime/index.js.map +1 -1
  71. package/dist/gs/net/http/httptest/index.js +6 -3
  72. package/dist/gs/net/http/httptest/index.js.map +1 -1
  73. package/dist/gs/net/http/index.d.ts +16 -4
  74. package/dist/gs/net/http/index.js +236 -40
  75. package/dist/gs/net/http/index.js.map +1 -1
  76. package/dist/gs/net/http/pprof/index.js.map +1 -1
  77. package/dist/gs/reflect/iter.js +1 -1
  78. package/dist/gs/reflect/iter.js.map +1 -1
  79. package/dist/gs/reflect/type.d.ts +2 -0
  80. package/dist/gs/reflect/type.js +53 -21
  81. package/dist/gs/reflect/type.js.map +1 -1
  82. package/dist/gs/runtime/debug/index.js +2 -1
  83. package/dist/gs/runtime/debug/index.js.map +1 -1
  84. package/dist/gs/runtime/pprof/index.js.map +1 -1
  85. package/dist/gs/runtime/runtime.js +2 -2
  86. package/dist/gs/runtime/runtime.js.map +1 -1
  87. package/dist/gs/runtime/trace/index.js.map +1 -1
  88. package/dist/gs/slices/slices.d.ts +1 -1
  89. package/dist/gs/slices/slices.js +37 -4
  90. package/dist/gs/slices/slices.js.map +1 -1
  91. package/go.mod +2 -2
  92. package/go.sum +2 -0
  93. package/gs/builtin/builtin.ts +11 -14
  94. package/gs/builtin/defer.ts +2 -2
  95. package/gs/builtin/hostio.test.ts +8 -3
  96. package/gs/builtin/hostio.ts +5 -7
  97. package/gs/builtin/map.ts +4 -1
  98. package/gs/builtin/slice.test.ts +14 -0
  99. package/gs/builtin/slice.ts +64 -0
  100. package/gs/builtin/type.ts +72 -0
  101. package/gs/bytes/bytes.test.ts +14 -13
  102. package/gs/compress/zlib/index.test.ts +19 -5
  103. package/gs/compress/zlib/index.ts +16 -7
  104. package/gs/context/context.test.ts +3 -1
  105. package/gs/crypto/aes/index.test.ts +120 -0
  106. package/gs/crypto/aes/index.ts +76 -0
  107. package/gs/crypto/cipher/index.ts +345 -0
  108. package/gs/crypto/cipher/meta.json +6 -0
  109. package/gs/crypto/ecdh/index.test.ts +6 -2
  110. package/gs/crypto/ecdh/index.ts +49 -12
  111. package/gs/crypto/ed25519/index.ts +20 -7
  112. package/gs/crypto/rand/index.ts +6 -3
  113. package/gs/embed/index.test.ts +3 -3
  114. package/gs/embed/index.ts +9 -3
  115. package/gs/fmt/fmt.test.ts +29 -4
  116. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  117. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  118. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  119. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  120. package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
  121. package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
  122. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  123. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  124. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  125. package/gs/hash/fnv/index.test.ts +1 -8
  126. package/gs/hash/fnv/index.ts +27 -10
  127. package/gs/io/fs/glob.ts +13 -10
  128. package/gs/io/fs/meta.json +2 -0
  129. package/gs/io/fs/readdir.test.ts +63 -2
  130. package/gs/io/fs/readdir.ts +33 -30
  131. package/gs/io/fs/sub.ts +4 -4
  132. package/gs/io/fs/walk.ts +1 -1
  133. package/gs/io/io.test.ts +56 -1
  134. package/gs/io/io.ts +19 -2
  135. package/gs/maps/iter.ts +9 -9
  136. package/gs/maps/maps.ts +4 -4
  137. package/gs/math/bits/index.test.ts +10 -1
  138. package/gs/mime/index.test.ts +33 -15
  139. package/gs/mime/index.ts +9 -2
  140. package/gs/net/http/httptest/index.test.ts +17 -3
  141. package/gs/net/http/httptest/index.ts +8 -3
  142. package/gs/net/http/index.test.ts +645 -123
  143. package/gs/net/http/index.ts +548 -113
  144. package/gs/net/http/pprof/index.ts +24 -6
  145. package/gs/os/file_unix_js.test.ts +22 -0
  146. package/gs/reflect/iter.ts +4 -2
  147. package/gs/reflect/map.test.ts +56 -1
  148. package/gs/reflect/type.ts +76 -37
  149. package/gs/runtime/debug/index.test.ts +32 -4
  150. package/gs/runtime/debug/index.ts +5 -2
  151. package/gs/runtime/pprof/index.test.ts +7 -1
  152. package/gs/runtime/pprof/index.ts +5 -1
  153. package/gs/runtime/runtime.test.ts +7 -0
  154. package/gs/runtime/runtime.ts +2 -4
  155. package/gs/runtime/trace/index.test.ts +9 -1
  156. package/gs/runtime/trace/index.ts +5 -1
  157. package/gs/slices/meta.json +3 -0
  158. package/gs/slices/slices.test.ts +59 -21
  159. package/gs/slices/slices.ts +61 -20
  160. package/gs/strconv/complex.test.ts +17 -3
  161. package/gs/sync/atomic/doc_64.test.ts +2 -9
  162. package/gs/sync/sync.test.ts +18 -8
  163. package/gs/syscall/js/index.test.ts +9 -4
  164. package/package.json +13 -5
@@ -141,6 +141,10 @@ func (r *Runner) runPackageTools(
141
141
  if len(indexes) == 0 {
142
142
  return
143
143
  }
144
+ if phase := materializeRuntimeModuleShims(req, outputRoots); phase.Failed() {
145
+ markRuntimeFailures(result, indexes, OwnerTestRunner, phase.Error)
146
+ return
147
+ }
144
148
  if len(indexes) == 1 {
145
149
  r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, outputRoots, indexes[0])
146
150
  return
@@ -286,6 +290,100 @@ func (r *Runner) runPackageRuntimes(
286
290
  r.runPackageRuntimesIndividually(ctx, req, workspace, result, outputRoots, indexes)
287
291
  }
288
292
 
293
+ func materializeRuntimeModuleShims(req *normalizedRequest, outputRoots []string) tsworkspace.Result {
294
+ if req.RuntimeBackend != RuntimeBackendBun {
295
+ return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace}
296
+ }
297
+ seen := make(map[string]bool)
298
+ shimRoots := runtimeModuleShimRoots(req, outputRoots)
299
+ for _, outputRoot := range outputRoots {
300
+ if outputRoot == "" {
301
+ continue
302
+ }
303
+ root := filepath.Join(outputRoot, "@goscript")
304
+ if _, err := os.Stat(root); err != nil {
305
+ if os.IsNotExist(err) {
306
+ continue
307
+ }
308
+ return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace, Error: errors.Wrap(err, "stat GoScript runtime output root").Error()}
309
+ }
310
+ err := filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
311
+ if err != nil {
312
+ return err
313
+ }
314
+ if entry.IsDir() || filepath.Ext(path) != ".ts" {
315
+ return nil
316
+ }
317
+ rel, err := filepath.Rel(root, path)
318
+ if err != nil {
319
+ return err
320
+ }
321
+ shimRel := filepath.Join("node_modules", "@goscript", strings.TrimSuffix(rel, ".ts")+".js")
322
+ for _, shimRoot := range shimRoots {
323
+ shimPath := filepath.Join(shimRoot, shimRel)
324
+ seenKey := shimPath
325
+ if seen[seenKey] {
326
+ continue
327
+ }
328
+ seen[seenKey] = true
329
+ shimDir := filepath.Dir(shimPath)
330
+ importPath, err := filepath.Rel(shimDir, path)
331
+ if err != nil {
332
+ return err
333
+ }
334
+ importPath = filepath.ToSlash(importPath)
335
+ if !strings.HasPrefix(importPath, ".") {
336
+ importPath = "./" + importPath
337
+ }
338
+ if err := writeRuntimeModuleShim(shimPath, "export * from "+strconv.Quote(importPath)+"\n"); err != nil {
339
+ return err
340
+ }
341
+ }
342
+ return nil
343
+ })
344
+ if err != nil {
345
+ return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace, Error: errors.Wrap(err, "materialize GoScript runtime module shims").Error()}
346
+ }
347
+ }
348
+ return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace}
349
+ }
350
+
351
+ func runtimeModuleShimRoots(req *normalizedRequest, outputRoots []string) []string {
352
+ seen := make(map[string]bool)
353
+ var roots []string
354
+ for _, root := range append([]string{req.WorkDir}, outputRoots...) {
355
+ if root == "" {
356
+ continue
357
+ }
358
+ clean := filepath.Clean(root)
359
+ if seen[clean] {
360
+ continue
361
+ }
362
+ seen[clean] = true
363
+ roots = append(roots, clean)
364
+ }
365
+ return roots
366
+ }
367
+
368
+ func writeRuntimeModuleShim(path string, data string) error {
369
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
370
+ return err
371
+ }
372
+ return os.WriteFile(path, []byte(data), 0o644)
373
+ }
374
+
375
+ func markRuntimeFailures(result *Result, indexes []int, owner Owner, message string) {
376
+ for _, idx := range indexes {
377
+ if idx < 0 || idx >= len(result.Packages) {
378
+ continue
379
+ }
380
+ result.Packages[idx].Action = ActionFail
381
+ result.Packages[idx].Owner = owner
382
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
383
+ result.Packages[idx].Error = message
384
+ }
385
+ }
386
+
289
387
  func (r *Runner) runPackageRuntimesIndividually(
290
388
  ctx context.Context,
291
389
  req *normalizedRequest,
@@ -1629,6 +1629,51 @@ func TestRenderRuntimeTypeScriptProjectDisablesEmit(t *testing.T) {
1629
1629
  }
1630
1630
  }
1631
1631
 
1632
+ func TestMaterializeRuntimeModuleShimsReexportsGeneratedTypeScript(t *testing.T) {
1633
+ workDir := t.TempDir()
1634
+ outputRoot := filepath.Join(workDir, "output")
1635
+ for _, name := range []string{
1636
+ "errors/index.ts",
1637
+ "github.com/s4wave/spacewave/core/plugin/space/config.gs.ts",
1638
+ } {
1639
+ path := filepath.Join(outputRoot, "@goscript", filepath.FromSlash(name))
1640
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
1641
+ t.Fatalf("create output parent: %v", err)
1642
+ }
1643
+ if err := os.WriteFile(path, []byte("export const value = 1\n"), 0o644); err != nil {
1644
+ t.Fatalf("write output file: %v", err)
1645
+ }
1646
+ }
1647
+
1648
+ req := &normalizedRequest{
1649
+ WorkDir: workDir,
1650
+ RuntimeBackend: RuntimeBackendBun,
1651
+ }
1652
+ phase := materializeRuntimeModuleShims(req, []string{outputRoot})
1653
+ if phase.Failed() {
1654
+ t.Fatalf("materialize shims: %s", phase.Error)
1655
+ }
1656
+ for _, root := range []string{workDir, outputRoot} {
1657
+ for _, name := range []string{
1658
+ "errors/index.js",
1659
+ "github.com/s4wave/spacewave/core/plugin/space/config.gs.js",
1660
+ } {
1661
+ path := filepath.Join(root, "node_modules", "@goscript", filepath.FromSlash(name))
1662
+ data, err := os.ReadFile(path)
1663
+ if err != nil {
1664
+ t.Fatalf("read shim %s: %v", name, err)
1665
+ }
1666
+ text := string(data)
1667
+ if !strings.Contains(text, ".ts\"") {
1668
+ t.Fatalf("shim should re-export generated TypeScript: %s", text)
1669
+ }
1670
+ if strings.Contains(text, workDir) {
1671
+ t.Fatalf("shim should use a portable relative import, got: %s", text)
1672
+ }
1673
+ }
1674
+ }
1675
+ }
1676
+
1632
1677
  func TestRenderTypeScriptProjectsCanUseIncrementalBuildInfo(t *testing.T) {
1633
1678
  req := &normalizedRequest{
1634
1679
  WorkDir: "/work",
@@ -3,6 +3,8 @@
3
3
  package browserapi
4
4
 
5
5
  import (
6
+ "fmt"
7
+ "os"
6
8
  "syscall/js"
7
9
  "testing"
8
10
  )
@@ -18,3 +20,37 @@ func TestBrowserAPI(t *testing.T) {
18
20
  t.Fatalf("div id = %q", got)
19
21
  }
20
22
  }
23
+
24
+ func TestBrowserStderrUsesConsoleLog(t *testing.T) {
25
+ console := js.Global().Get("console")
26
+ if console.IsUndefined() || console.IsNull() {
27
+ t.Fatal("missing browser console")
28
+ }
29
+
30
+ originalLog := console.Get("log")
31
+ originalError := console.Get("error")
32
+ calls := make([]string, 0, 1)
33
+ logFn := js.FuncOf(func(this js.Value, args []js.Value) any {
34
+ calls = append(calls, "log:"+args[0].String())
35
+ return nil
36
+ })
37
+ errorFn := js.FuncOf(func(this js.Value, args []js.Value) any {
38
+ calls = append(calls, "error:"+args[0].String())
39
+ return nil
40
+ })
41
+ defer logFn.Release()
42
+ defer errorFn.Release()
43
+ defer console.Set("log", originalLog)
44
+ defer console.Set("error", originalError)
45
+
46
+ console.Set("log", logFn)
47
+ console.Set("error", errorFn)
48
+
49
+ if _, err := fmt.Fprintln(os.Stderr, "goscript stderr proof"); err != nil {
50
+ t.Fatal(err)
51
+ }
52
+
53
+ if len(calls) != 1 || calls[0] != "log:goscript stderr proof" {
54
+ t.Fatalf("stderr console calls = %#v, want console.log", calls)
55
+ }
56
+ }
@@ -16,6 +16,8 @@ import (
16
16
  "strconv"
17
17
  "strings"
18
18
  "unicode/utf8"
19
+
20
+ "golang.org/x/tools/go/packages"
19
21
  )
20
22
 
21
23
  // LoweringOwner owns conversion from the semantic model to compiler IR.
@@ -2506,6 +2508,14 @@ func (o *LoweringOwner) runtimeMethodSignaturesWithSeen(iface *types.Interface,
2506
2508
  return "[" + strings.Join(methods, ", ") + "]"
2507
2509
  }
2508
2510
 
2511
+ func (o *LoweringOwner) runtimeMethodAssertSignaturesWithSeen(ctx lowerFileContext, iface *types.Interface, seen map[types.Type]bool) string {
2512
+ methods := make([]string, 0, iface.NumMethods())
2513
+ for method := range iface.Methods() {
2514
+ methods = append(methods, o.runtimeMethodAssertSignature(ctx, method, seen))
2515
+ }
2516
+ return "[" + strings.Join(methods, ", ") + "]"
2517
+ }
2518
+
2509
2519
  func (o *LoweringOwner) runtimeMethodSignature(method *types.Func, seen map[types.Type]bool) string {
2510
2520
  signature, _ := method.Type().(*types.Signature)
2511
2521
  if signature == nil {
@@ -2516,6 +2526,16 @@ func (o *LoweringOwner) runtimeMethodSignature(method *types.Func, seen map[type
2516
2526
  ", returns: " + o.runtimeMethodReturns(signature.Results(), seen) + " }"
2517
2527
  }
2518
2528
 
2529
+ func (o *LoweringOwner) runtimeMethodAssertSignature(ctx lowerFileContext, method *types.Func, seen map[types.Type]bool) string {
2530
+ signature, _ := method.Type().(*types.Signature)
2531
+ if signature == nil {
2532
+ return "{ name: " + strconv.Quote(method.Name()) + ", args: [], returns: [] }"
2533
+ }
2534
+ return "{ name: " + strconv.Quote(method.Name()) +
2535
+ ", args: " + o.runtimeMethodAssertArgs(ctx, signature.Params(), seen) +
2536
+ ", returns: " + o.runtimeMethodAssertReturns(ctx, signature.Results(), seen) + " }"
2537
+ }
2538
+
2519
2539
  func (o *LoweringOwner) runtimeMethodArgs(tuple *types.Tuple, seen map[types.Type]bool) string {
2520
2540
  if tuple == nil || tuple.Len() == 0 {
2521
2541
  return "[]"
@@ -2532,6 +2552,22 @@ func (o *LoweringOwner) runtimeMethodArgs(tuple *types.Tuple, seen map[types.Typ
2532
2552
  return "[" + strings.Join(args, ", ") + "]"
2533
2553
  }
2534
2554
 
2555
+ func (o *LoweringOwner) runtimeMethodAssertArgs(ctx lowerFileContext, tuple *types.Tuple, seen map[types.Type]bool) string {
2556
+ if tuple == nil || tuple.Len() == 0 {
2557
+ return "[]"
2558
+ }
2559
+ args := make([]string, 0, tuple.Len())
2560
+ for idx := range tuple.Len() {
2561
+ param := tuple.At(idx)
2562
+ name := param.Name()
2563
+ if name == "" {
2564
+ name = "_p" + strconv.Itoa(idx)
2565
+ }
2566
+ args = append(args, "{ name: "+strconv.Quote(name)+", type: "+o.runtimeTypeAssertInfoExprWithSeen(ctx, param.Type(), seen)+" }")
2567
+ }
2568
+ return "[" + strings.Join(args, ", ") + "]"
2569
+ }
2570
+
2535
2571
  func (o *LoweringOwner) runtimeMethodReturns(tuple *types.Tuple, seen map[types.Type]bool) string {
2536
2572
  if tuple == nil || tuple.Len() == 0 {
2537
2573
  return "[]"
@@ -2548,6 +2584,22 @@ func (o *LoweringOwner) runtimeMethodReturns(tuple *types.Tuple, seen map[types.
2548
2584
  return "[" + strings.Join(results, ", ") + "]"
2549
2585
  }
2550
2586
 
2587
+ func (o *LoweringOwner) runtimeMethodAssertReturns(ctx lowerFileContext, tuple *types.Tuple, seen map[types.Type]bool) string {
2588
+ if tuple == nil || tuple.Len() == 0 {
2589
+ return "[]"
2590
+ }
2591
+ results := make([]string, 0, tuple.Len())
2592
+ for idx := range tuple.Len() {
2593
+ result := tuple.At(idx)
2594
+ name := result.Name()
2595
+ if name == "" {
2596
+ name = "_r" + strconv.Itoa(idx)
2597
+ }
2598
+ results = append(results, "{ name: "+strconv.Quote(name)+", type: "+o.runtimeTypeAssertInfoExprWithSeen(ctx, result.Type(), seen)+" }")
2599
+ }
2600
+ return "[" + strings.Join(results, ", ") + "]"
2601
+ }
2602
+
2551
2603
  func (o *LoweringOwner) lowerStructType(ctx lowerFileContext, semType *semanticType) (*loweredStruct, []Diagnostic) {
2552
2604
  lowered := &loweredStruct{
2553
2605
  exported: ctx.topLevel,
@@ -4554,6 +4606,8 @@ func shortDeclNeedsTypeAnnotation(typ types.Type) bool {
4554
4606
  return true
4555
4607
  case *types.Slice:
4556
4608
  return true
4609
+ case *types.Chan:
4610
+ return true
4557
4611
  default:
4558
4612
  return false
4559
4613
  }
@@ -6797,7 +6851,7 @@ func (o *LoweringOwner) lowerFuncLit(ctx lowerFileContext, lit *ast.FuncLit) (st
6797
6851
  deferState := &loweredDeferState{}
6798
6852
  bodyCtx := ctx.withSignature(signature).withAsyncFunction(false).withDeferState(deferState).withoutRangeBranch()
6799
6853
  asyncCompatibleParams := funcLiteralNeedsAsyncFunctionParamCalls(signature)
6800
- if asyncCompatibleParams || funcLiteralUsesFunctionIdentifierCall(ctx, lit) {
6854
+ if asyncCompatibleParams || funcLiteralUsesAwaitableCall(ctx, lit) {
6801
6855
  bodyCtx = bodyCtx.withAsyncFunction(true)
6802
6856
  }
6803
6857
  var params []loweredParam
@@ -6814,7 +6868,7 @@ func (o *LoweringOwner) lowerFuncLit(ctx lowerFileContext, lit *ast.FuncLit) (st
6814
6868
  renderNamedResults(&rendered, o.lowerNamedResults(ctx, signature), 1)
6815
6869
  renderDeferStack(&rendered, deferState, 1)
6816
6870
  renderStmts(&rendered, body, 1)
6817
- async := stmtsContainAwait(body) || deferState.async
6871
+ async := bodyCtx.asyncFunction || stmtsContainAwait(body) || deferState.async
6818
6872
  prefix := ""
6819
6873
  if async {
6820
6874
  prefix = "async "
@@ -6837,7 +6891,7 @@ func renderLoweredParams(params []loweredParam) string {
6837
6891
  return strings.Join(rendered, ", ")
6838
6892
  }
6839
6893
 
6840
- func funcLiteralUsesFunctionIdentifierCall(ctx lowerFileContext, lit *ast.FuncLit) bool {
6894
+ func funcLiteralUsesAwaitableCall(ctx lowerFileContext, lit *ast.FuncLit) bool {
6841
6895
  if lit == nil || lit.Body == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
6842
6896
  return false
6843
6897
  }
@@ -6853,7 +6907,8 @@ func funcLiteralUsesFunctionIdentifierCall(ctx lowerFileContext, lit *ast.FuncLi
6853
6907
  if !ok {
6854
6908
  return true
6855
6909
  }
6856
- uses = callUsesFunctionIdentifier(ctx.semPkg.source, call.Fun)
6910
+ uses = callUsesFunctionIdentifier(ctx.semPkg.source, call.Fun) ||
6911
+ callUsesInterfaceMethod(ctx.semPkg.source, call.Fun)
6857
6912
  return !uses
6858
6913
  })
6859
6914
  return uses
@@ -7055,15 +7110,16 @@ func (o *LoweringOwner) lowerCallExpr(ctx lowerFileContext, expr *ast.CallExpr)
7055
7110
  }
7056
7111
  }
7057
7112
  }
7113
+ appendHelper := o.runtimeOwner.QualifiedHelper(RuntimeHelperAppend)
7058
7114
  if expr.Ellipsis != token.NoPos && len(args) > 1 {
7059
7115
  last := len(args) - 1
7060
7116
  spread := args[last]
7061
7117
  if isStringType(ctx.semPkg.source.TypesInfo.TypeOf(expr.Args[len(expr.Args)-1])) {
7062
7118
  spread = o.runtimeOwner.QualifiedHelper(RuntimeHelperStringToBytes) + "(" + spread + ")"
7063
7119
  }
7064
- args[last] = "...(" + spread + " ?? [])"
7120
+ args[last] = spread
7121
+ appendHelper = o.runtimeOwner.QualifiedHelper(RuntimeHelperAppendSlice)
7065
7122
  }
7066
- appendHelper := o.runtimeOwner.QualifiedHelper(RuntimeHelperAppend)
7067
7123
  if len(args) > 0 && args[0] == "null" {
7068
7124
  if slice, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(expr)).Underlying().(*types.Slice); ok {
7069
7125
  appendHelper += "<" + o.tsTypeFor(ctx, slice.Elem()) + ">"
@@ -7973,6 +8029,13 @@ func (o *LoweringOwner) lowerPointerReceiverMethodCall(
7973
8029
  methodMemberName(selector.Sel.Name) + "(" + strings.Join(args, ", ") + ")"
7974
8030
  return call, diagnostics, true
7975
8031
  }
8032
+ if receiver != nil && receiver.Obj() != nil && receiver.Obj().Pkg() != nil {
8033
+ pkgPath := receiver.Obj().Pkg().Path()
8034
+ if ctx.importPaths[pkgPath] == "" && !o.hasGeneratedImportPackage(ctx.model, pkgPath) {
8035
+ call := receiverExpr + "." + methodMemberName(selector.Sel.Name) + "(" + strings.Join(args, ", ") + ")"
8036
+ return call, diagnostics, true
8037
+ }
8038
+ }
7976
8039
  if crossPackageUnexportedNamedType(ctx, receiver) {
7977
8040
  call := o.runtimeOwner.QualifiedHelper(RuntimeHelperPointerValue) +
7978
8041
  "<any>(" + receiverExpr + ")." + methodMemberName(selector.Sel.Name) +
@@ -8688,7 +8751,7 @@ func (o *LoweringOwner) lowerPointerValueExpr(ctx lowerFileContext, expr ast.Exp
8688
8751
  return value, diagnostics
8689
8752
  }
8690
8753
  if ref, diagnostics, ok := o.lowerUnsafeArrayPointerRefExpr(ctx, expr); ok {
8691
- return ref + ".value", diagnostics
8754
+ return ref + "!.value", diagnostics
8692
8755
  }
8693
8756
  if ref, diagnostics, ok := o.lowerUnsafePointerRefExpr(ctx, expr); ok {
8694
8757
  return ref + ".value", diagnostics
@@ -8925,7 +8988,7 @@ func sameLoweredSourceExpr(ctx lowerFileContext, left ast.Expr, right ast.Expr)
8925
8988
 
8926
8989
  func (o *LoweringOwner) lowerPointerStorageExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
8927
8990
  if ref, diagnostics, ok := o.lowerUnsafeArrayPointerRefExpr(ctx, expr); ok {
8928
- return ref + ".value", diagnostics
8991
+ return ref + "!.value", diagnostics
8929
8992
  }
8930
8993
  if ref, diagnostics, ok := o.lowerUnsafePointerRefExpr(ctx, expr); ok {
8931
8994
  return ref + ".value", diagnostics
@@ -9784,12 +9847,61 @@ func (o *LoweringOwner) runtimeTypeInfoExpr(typ types.Type) string {
9784
9847
  }
9785
9848
 
9786
9849
  func (o *LoweringOwner) runtimeTypeAssertInfoExpr(ctx lowerFileContext, typ types.Type) string {
9850
+ return o.runtimeTypeAssertInfoExprWithSeen(ctx, typ, make(map[types.Type]bool))
9851
+ }
9852
+
9853
+ func (o *LoweringOwner) runtimeTypeAssertInfoExprWithSeen(ctx lowerFileContext, typ types.Type, seen map[types.Type]bool) string {
9787
9854
  typeParam, ok := types.Unalias(typ).(*types.TypeParam)
9788
- if !ok || !typeParamInScope(ctx, typeParam) {
9855
+ if ok && typeParamInScope(ctx, typeParam) {
9856
+ return "__typeArgs?.[" + strconv.Quote(typeParam.Obj().Name()) + "]?.type ?? " +
9857
+ o.runtimeTypeInfoExpr(typ)
9858
+ }
9859
+
9860
+ typeKind := o.runtimeOwner.QualifiedHelper(RuntimeHelperTypeKind)
9861
+ if typ == nil {
9862
+ return "{ kind: " + typeKind + ".Basic, name: \"unknown\" }"
9863
+ }
9864
+ typeKey := types.Unalias(typ)
9865
+ if typeKey != nil {
9866
+ if seen[typeKey] {
9867
+ return o.shallowRuntimeTypeInfoExpr(typ)
9868
+ }
9869
+ seen[typeKey] = true
9870
+ defer delete(seen, typeKey)
9871
+ }
9872
+ if named := namedFunctionType(typ); named != nil {
9873
+ return o.runtimeFunctionTypeAssertInfoWithSeen(ctx, named.Underlying().(*types.Signature), runtimeNamedTypeName(named), seen)
9874
+ }
9875
+ if named := namedStructType(typ); named != nil {
9876
+ return strconv.Quote(runtimeNamedTypeName(named))
9877
+ }
9878
+ if named := namedNonStructType(typ); named != nil {
9879
+ if basic, ok := types.Unalias(named.Underlying()).(*types.Basic); ok {
9880
+ return runtimeBasicTypeInfoExpr(typeKind, basic, runtimeNamedTypeName(named))
9881
+ }
9882
+ return strconv.Quote(runtimeNamedTypeName(named))
9883
+ }
9884
+ switch typed := types.Unalias(typ).Underlying().(type) {
9885
+ case *types.Pointer:
9886
+ return "{ kind: " + typeKind + ".Pointer, elemType: " + o.runtimeTypeAssertInfoExprWithSeen(ctx, typed.Elem(), seen) + " }"
9887
+ case *types.Struct:
9888
+ return "{ kind: " + typeKind + ".Struct, methods: [], fields: " + o.runtimeStructAssertFieldsExpr(ctx, typed, seen) + " }"
9889
+ case *types.Slice:
9890
+ return "{ kind: " + typeKind + ".Slice, elemType: " + o.runtimeTypeAssertInfoExprWithSeen(ctx, typed.Elem(), seen) + " }"
9891
+ case *types.Array:
9892
+ return "{ kind: " + typeKind + ".Array, elemType: " + o.runtimeTypeAssertInfoExprWithSeen(ctx, typed.Elem(), seen) + ", length: " + strconv.FormatInt(typed.Len(), 10) + " }"
9893
+ case *types.Map:
9894
+ return "{ kind: " + typeKind + ".Map, keyType: " + o.runtimeTypeAssertInfoExprWithSeen(ctx, typed.Key(), seen) + ", elemType: " + o.runtimeTypeAssertInfoExprWithSeen(ctx, typed.Elem(), seen) + " }"
9895
+ case *types.Chan:
9896
+ return "{ kind: " + typeKind + ".Channel, direction: " + strconv.Quote(channelDirectionString(typed.Dir())) + ", elemType: " + o.runtimeTypeAssertInfoExprWithSeen(ctx, typed.Elem(), seen) + " }"
9897
+ case *types.Interface:
9898
+ typed.Complete()
9899
+ return "{ kind: " + typeKind + ".Interface, methods: " + o.runtimeMethodAssertSignaturesWithSeen(ctx, typed, seen) + " }"
9900
+ case *types.Signature:
9901
+ return o.runtimeFunctionTypeAssertInfoWithSeen(ctx, typed, "", seen)
9902
+ default:
9789
9903
  return o.runtimeTypeInfoExpr(typ)
9790
9904
  }
9791
- return "__typeArgs?.[" + strconv.Quote(typeParam.Obj().Name()) + "]?.type ?? " +
9792
- o.runtimeTypeInfoExpr(typ)
9793
9905
  }
9794
9906
 
9795
9907
  func (o *LoweringOwner) runtimeTypeInfoExprWithSeen(typ types.Type, seen map[types.Type]bool) string {
@@ -9917,6 +10029,40 @@ func (o *LoweringOwner) runtimeStructFieldsExpr(structType *types.Struct, seen m
9917
10029
  return "[" + strings.Join(fields, ", ") + "]"
9918
10030
  }
9919
10031
 
10032
+ func (o *LoweringOwner) runtimeStructAssertFieldsExpr(ctx lowerFileContext, structType *types.Struct, seen map[types.Type]bool) string {
10033
+ fields := make([]string, 0, structType.NumFields())
10034
+ var vars []*types.Var
10035
+ for field := range structType.Fields() {
10036
+ vars = append(vars, field)
10037
+ }
10038
+ offsets := structFieldOffsets(goScriptTypeSizes(), vars)
10039
+ for idx := range structType.NumFields() {
10040
+ field := structType.Field(idx)
10041
+ fieldName := tsStructFieldName(field.Name(), idx)
10042
+ runtimeName := ""
10043
+ if fieldName != field.Name() {
10044
+ runtimeName = field.Name()
10045
+ }
10046
+ pkgPath := ""
10047
+ if !field.Exported() && field.Pkg() != nil {
10048
+ pkgPath = field.Pkg().Path()
10049
+ }
10050
+ fieldInfo := runtimeStructFieldInfoExpr(
10051
+ o.runtimeTypeAssertInfoExprWithSeen(ctx, field.Type(), seen),
10052
+ fieldName,
10053
+ runtimeName,
10054
+ structType.Tag(idx),
10055
+ pkgPath,
10056
+ field.Embedded(),
10057
+ []int{idx},
10058
+ offsets[idx],
10059
+ field.Exported(),
10060
+ )
10061
+ fields = append(fields, fieldInfo)
10062
+ }
10063
+ return "[" + strings.Join(fields, ", ") + "]"
10064
+ }
10065
+
9920
10066
  func runtimeStructFieldInfoExpr(
9921
10067
  runtimeType string,
9922
10068
  storageKey string,
@@ -9981,6 +10127,26 @@ func (o *LoweringOwner) runtimeFunctionTypeInfoWithSeen(signature *types.Signatu
9981
10127
  return "({ " + strings.Join(parts, ", ") + " } as " + runtimePackage + ".FunctionTypeInfo)"
9982
10128
  }
9983
10129
 
10130
+ func (o *LoweringOwner) runtimeFunctionTypeAssertInfoWithSeen(
10131
+ ctx lowerFileContext,
10132
+ signature *types.Signature,
10133
+ name string,
10134
+ seen map[types.Type]bool,
10135
+ ) string {
10136
+ typeKind := o.runtimeOwner.QualifiedHelper(RuntimeHelperTypeKind)
10137
+ parts := []string{"kind: " + typeKind + ".Function"}
10138
+ if name != "" {
10139
+ parts = append(parts, "name: "+strconv.Quote(name))
10140
+ }
10141
+ parts = append(parts, "params: "+o.runtimeTypeAssertSignatureTypes(ctx, signature.Params(), seen))
10142
+ parts = append(parts, "results: "+o.runtimeTypeAssertSignatureTypes(ctx, signature.Results(), seen))
10143
+ if signature.Variadic() {
10144
+ parts = append(parts, "isVariadic: true")
10145
+ }
10146
+ runtimePackage := strings.TrimSuffix(typeKind, ".TypeKind")
10147
+ return "({ " + strings.Join(parts, ", ") + " } as " + runtimePackage + ".FunctionTypeInfo)"
10148
+ }
10149
+
9984
10150
  func (o *LoweringOwner) runtimeSignatureTypes(tuple *types.Tuple, seen map[types.Type]bool) string {
9985
10151
  if tuple == nil || tuple.Len() == 0 {
9986
10152
  return "[]"
@@ -9992,6 +10158,17 @@ func (o *LoweringOwner) runtimeSignatureTypes(tuple *types.Tuple, seen map[types
9992
10158
  return "[" + strings.Join(types, ", ") + "]"
9993
10159
  }
9994
10160
 
10161
+ func (o *LoweringOwner) runtimeTypeAssertSignatureTypes(ctx lowerFileContext, tuple *types.Tuple, seen map[types.Type]bool) string {
10162
+ if tuple == nil || tuple.Len() == 0 {
10163
+ return "[]"
10164
+ }
10165
+ types := make([]string, 0, tuple.Len())
10166
+ for v := range tuple.Variables() {
10167
+ types = append(types, o.runtimeTypeAssertInfoExprWithSeen(ctx, v.Type(), seen))
10168
+ }
10169
+ return "[" + strings.Join(types, ", ") + "]"
10170
+ }
10171
+
9995
10172
  func tsStructFieldName(name string, idx int) string {
9996
10173
  if name == "_" {
9997
10174
  return "_blank" + strconv.Itoa(idx)
@@ -10943,6 +11120,7 @@ func (o *LoweringOwner) callNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool
10943
11120
  return o.functionAsync(ctx, calledFunction(ctx.semPkg.source, fun)) ||
10944
11121
  o.overrideCallNeedsAwait(ctx, fun) ||
10945
11122
  callUsesFunctionValue(ctx.semPkg.source, fun) ||
11123
+ (ctx.asyncFunction && callUsesInterfaceMethod(ctx.semPkg.source, fun)) ||
10946
11124
  (ctx.asyncFunction && callUsesFunctionIdentifier(ctx.semPkg.source, fun))
10947
11125
  }
10948
11126
  if ctx.semPkg == nil || ctx.semPkg.source == nil {
@@ -10951,10 +11129,48 @@ func (o *LoweringOwner) callNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool
10951
11129
  return o.functionAsync(ctx, calledFunction(ctx.semPkg.source, fun)) ||
10952
11130
  o.overrideCallNeedsAwait(ctx, fun) ||
10953
11131
  callUsesFunctionValue(ctx.semPkg.source, fun) ||
11132
+ (ctx.asyncFunction && callUsesInterfaceMethod(ctx.semPkg.source, fun)) ||
10954
11133
  (ctx.asyncFunction && callUsesFunctionIdentifier(ctx.semPkg.source, fun))
10955
11134
  }
10956
11135
  }
10957
11136
 
11137
+ func callUsesInterfaceMethod(pkg *packages.Package, fun ast.Expr) bool {
11138
+ if pkg == nil {
11139
+ return false
11140
+ }
11141
+ selector, ok := fun.(*ast.SelectorExpr)
11142
+ if !ok {
11143
+ return false
11144
+ }
11145
+ selection := pkg.TypesInfo.Selections[selector]
11146
+ if selection == nil || selection.Kind() != types.MethodVal {
11147
+ return false
11148
+ }
11149
+ if selectionUsesSyncErrorMethod(selection) {
11150
+ return false
11151
+ }
11152
+ return isInterfaceType(selection.Recv())
11153
+ }
11154
+
11155
+ func selectionUsesSyncErrorMethod(selection *types.Selection) bool {
11156
+ if selection == nil || selection.Kind() != types.MethodVal {
11157
+ return false
11158
+ }
11159
+ method, _ := selection.Obj().(*types.Func)
11160
+ return isSyncErrorMethodFunc(method)
11161
+ }
11162
+
11163
+ func isSyncErrorMethodFunc(fn *types.Func) bool {
11164
+ if fn == nil || fn.Name() != "Error" {
11165
+ return false
11166
+ }
11167
+ signature, _ := fn.Type().(*types.Signature)
11168
+ if signature == nil || signature.Params().Len() != 0 || signature.Results().Len() != 1 {
11169
+ return false
11170
+ }
11171
+ return types.Identical(signature.Results().At(0).Type(), types.Typ[types.String])
11172
+ }
11173
+
10958
11174
  func (o *LoweringOwner) overrideCallNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool {
10959
11175
  if o.overrideOwner == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
10960
11176
  return false
@@ -488,6 +488,56 @@ func TestCompilePackagesAwaitsOverrideAsyncFunctions(t *testing.T) {
488
488
  }
489
489
  }
490
490
 
491
+ func TestCompilePackagesAwaitsAsyncSlicesSortFuncComparator(t *testing.T) {
492
+ moduleDir := writePackageGraphFixture(t, map[string]string{
493
+ "go.mod": "module example.test/slicesasyncsort\n\ngo 1.25.3\n",
494
+ "main.go": strings.Join([]string{
495
+ "package main",
496
+ "import \"slices\"",
497
+ "type Comparator interface { Less(a, b int) bool }",
498
+ "func Use(c Comparator, values []int) {",
499
+ " slices.SortFunc(values, func(a, b int) int {",
500
+ " if c.Less(a, b) {",
501
+ " return -1",
502
+ " }",
503
+ " if c.Less(b, a) {",
504
+ " return 1",
505
+ " }",
506
+ " return 0",
507
+ " })",
508
+ "}",
509
+ "",
510
+ }, "\n"),
511
+ })
512
+ out := filepath.Join(t.TempDir(), "out")
513
+ comp, err := NewCompiler(&Config{
514
+ Dir: moduleDir,
515
+ OutputPath: out,
516
+ AllDependencies: true,
517
+ }, nil, nil)
518
+ if err != nil {
519
+ t.Fatal(err.Error())
520
+ }
521
+
522
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
523
+ t.Fatal(err.Error())
524
+ }
525
+ content, err := os.ReadFile(filepath.Join(out, "@goscript", "example.test", "slicesasyncsort", "main.gs.ts"))
526
+ if err != nil {
527
+ t.Fatal(err.Error())
528
+ }
529
+ text := string(content)
530
+ if !strings.Contains(text, "export async function Use") {
531
+ t.Fatalf("caller was not marked async for slices.SortFunc:\n%s", text)
532
+ }
533
+ if !strings.Contains(text, "await slices.SortFunc") {
534
+ t.Fatalf("slices.SortFunc call was not awaited:\n%s", text)
535
+ }
536
+ if !strings.Contains(text, "$.functionValue(async") {
537
+ t.Fatalf("SortFunc comparator was not lowered as async:\n%s", text)
538
+ }
539
+ }
540
+
491
541
  func TestCompilePackagesAwaitsReflectValueCall(t *testing.T) {
492
542
  moduleDir := writePackageGraphFixture(t, map[string]string{
493
543
  "go.mod": "module example.test/reflectcallasync\n\ngo 1.25.3\n",