goscript 0.2.2 → 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 (136) hide show
  1. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  2. package/compiler/lowering.go +223 -9
  3. package/compiler/override-registry_test.go +50 -0
  4. package/compiler/protobuf-ts-binding.go +154 -6
  5. package/compiler/protobuf-ts-binding_test.go +7 -2
  6. package/compiler/runtime-contract.go +2 -0
  7. package/compiler/runtime-contract_test.go +1 -0
  8. package/compiler/semantic-model.go +16 -0
  9. package/compiler/semantic-model_test.go +38 -0
  10. package/compiler/skeleton_test.go +473 -15
  11. package/compiler/typescript-emitter.go +4 -0
  12. package/dist/gs/builtin/builtin.js +7 -9
  13. package/dist/gs/builtin/builtin.js.map +1 -1
  14. package/dist/gs/builtin/defer.js +2 -2
  15. package/dist/gs/builtin/hostio.js +5 -5
  16. package/dist/gs/builtin/hostio.js.map +1 -1
  17. package/dist/gs/builtin/map.js +2 -1
  18. package/dist/gs/builtin/map.js.map +1 -1
  19. package/dist/gs/builtin/slice.d.ts +3 -0
  20. package/dist/gs/builtin/slice.js +39 -0
  21. package/dist/gs/builtin/slice.js.map +1 -1
  22. package/dist/gs/builtin/type.js +49 -0
  23. package/dist/gs/builtin/type.js.map +1 -1
  24. package/dist/gs/compress/zlib/index.js +5 -2
  25. package/dist/gs/compress/zlib/index.js.map +1 -1
  26. package/dist/gs/crypto/ecdh/index.js +27 -8
  27. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  28. package/dist/gs/crypto/ed25519/index.js +3 -3
  29. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  30. package/dist/gs/crypto/rand/index.js +6 -3
  31. package/dist/gs/crypto/rand/index.js.map +1 -1
  32. package/dist/gs/embed/index.js +9 -3
  33. package/dist/gs/embed/index.js.map +1 -1
  34. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  35. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  36. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  37. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  38. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  39. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  40. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  41. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  42. package/dist/gs/hash/fnv/index.js +13 -5
  43. package/dist/gs/hash/fnv/index.js.map +1 -1
  44. package/dist/gs/io/fs/glob.d.ts +3 -3
  45. package/dist/gs/io/fs/glob.js +8 -8
  46. package/dist/gs/io/fs/glob.js.map +1 -1
  47. package/dist/gs/io/fs/readdir.d.ts +2 -2
  48. package/dist/gs/io/fs/readdir.js +13 -74
  49. package/dist/gs/io/fs/readdir.js.map +1 -1
  50. package/dist/gs/io/fs/sub.js +4 -4
  51. package/dist/gs/io/fs/sub.js.map +1 -1
  52. package/dist/gs/io/fs/walk.js +1 -1
  53. package/dist/gs/io/fs/walk.js.map +1 -1
  54. package/dist/gs/maps/iter.js.map +1 -1
  55. package/dist/gs/maps/maps.js.map +1 -1
  56. package/dist/gs/mime/index.js +5 -2
  57. package/dist/gs/mime/index.js.map +1 -1
  58. package/dist/gs/net/http/httptest/index.js +6 -3
  59. package/dist/gs/net/http/httptest/index.js.map +1 -1
  60. package/dist/gs/net/http/index.d.ts +16 -4
  61. package/dist/gs/net/http/index.js +236 -40
  62. package/dist/gs/net/http/index.js.map +1 -1
  63. package/dist/gs/net/http/pprof/index.js.map +1 -1
  64. package/dist/gs/reflect/iter.js +1 -1
  65. package/dist/gs/reflect/iter.js.map +1 -1
  66. package/dist/gs/reflect/type.d.ts +2 -0
  67. package/dist/gs/reflect/type.js +53 -21
  68. package/dist/gs/reflect/type.js.map +1 -1
  69. package/dist/gs/runtime/pprof/index.js.map +1 -1
  70. package/dist/gs/runtime/runtime.js +2 -2
  71. package/dist/gs/runtime/runtime.js.map +1 -1
  72. package/dist/gs/runtime/trace/index.js.map +1 -1
  73. package/dist/gs/slices/slices.d.ts +1 -1
  74. package/dist/gs/slices/slices.js +37 -4
  75. package/dist/gs/slices/slices.js.map +1 -1
  76. package/gs/builtin/builtin.ts +11 -14
  77. package/gs/builtin/defer.ts +2 -2
  78. package/gs/builtin/hostio.ts +5 -5
  79. package/gs/builtin/map.ts +4 -1
  80. package/gs/builtin/slice.test.ts +14 -0
  81. package/gs/builtin/slice.ts +64 -0
  82. package/gs/builtin/type.ts +72 -0
  83. package/gs/bytes/bytes.test.ts +14 -13
  84. package/gs/compress/zlib/index.test.ts +19 -5
  85. package/gs/compress/zlib/index.ts +16 -7
  86. package/gs/context/context.test.ts +3 -1
  87. package/gs/crypto/ecdh/index.test.ts +6 -2
  88. package/gs/crypto/ecdh/index.ts +49 -12
  89. package/gs/crypto/ed25519/index.ts +20 -7
  90. package/gs/crypto/rand/index.ts +6 -3
  91. package/gs/embed/index.test.ts +3 -3
  92. package/gs/embed/index.ts +9 -3
  93. package/gs/fmt/fmt.test.ts +29 -4
  94. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  95. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  96. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  97. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  98. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  99. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  100. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  101. package/gs/hash/fnv/index.test.ts +1 -8
  102. package/gs/hash/fnv/index.ts +27 -10
  103. package/gs/io/fs/glob.ts +13 -10
  104. package/gs/io/fs/meta.json +2 -0
  105. package/gs/io/fs/readdir.test.ts +63 -2
  106. package/gs/io/fs/readdir.ts +33 -30
  107. package/gs/io/fs/sub.ts +4 -4
  108. package/gs/io/fs/walk.ts +1 -1
  109. package/gs/maps/iter.ts +9 -9
  110. package/gs/maps/maps.ts +4 -4
  111. package/gs/math/bits/index.test.ts +10 -1
  112. package/gs/mime/index.test.ts +33 -15
  113. package/gs/mime/index.ts +9 -2
  114. package/gs/net/http/httptest/index.test.ts +17 -3
  115. package/gs/net/http/httptest/index.ts +8 -3
  116. package/gs/net/http/index.test.ts +645 -123
  117. package/gs/net/http/index.ts +548 -113
  118. package/gs/net/http/pprof/index.ts +24 -6
  119. package/gs/os/file_unix_js.test.ts +22 -0
  120. package/gs/reflect/iter.ts +4 -2
  121. package/gs/reflect/map.test.ts +56 -1
  122. package/gs/reflect/type.ts +76 -37
  123. package/gs/runtime/pprof/index.test.ts +7 -1
  124. package/gs/runtime/pprof/index.ts +5 -1
  125. package/gs/runtime/runtime.test.ts +7 -0
  126. package/gs/runtime/runtime.ts +2 -4
  127. package/gs/runtime/trace/index.test.ts +9 -1
  128. package/gs/runtime/trace/index.ts +5 -1
  129. package/gs/slices/meta.json +3 -0
  130. package/gs/slices/slices.test.ts +59 -21
  131. package/gs/slices/slices.ts +61 -20
  132. package/gs/strconv/complex.test.ts +17 -3
  133. package/gs/sync/atomic/doc_64.test.ts +2 -9
  134. package/gs/sync/sync.test.ts +18 -8
  135. package/gs/syscall/js/index.test.ts +9 -4
  136. package/package.json +5 -4
@@ -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,
@@ -6799,7 +6851,7 @@ func (o *LoweringOwner) lowerFuncLit(ctx lowerFileContext, lit *ast.FuncLit) (st
6799
6851
  deferState := &loweredDeferState{}
6800
6852
  bodyCtx := ctx.withSignature(signature).withAsyncFunction(false).withDeferState(deferState).withoutRangeBranch()
6801
6853
  asyncCompatibleParams := funcLiteralNeedsAsyncFunctionParamCalls(signature)
6802
- if asyncCompatibleParams || funcLiteralUsesFunctionIdentifierCall(ctx, lit) {
6854
+ if asyncCompatibleParams || funcLiteralUsesAwaitableCall(ctx, lit) {
6803
6855
  bodyCtx = bodyCtx.withAsyncFunction(true)
6804
6856
  }
6805
6857
  var params []loweredParam
@@ -6816,7 +6868,7 @@ func (o *LoweringOwner) lowerFuncLit(ctx lowerFileContext, lit *ast.FuncLit) (st
6816
6868
  renderNamedResults(&rendered, o.lowerNamedResults(ctx, signature), 1)
6817
6869
  renderDeferStack(&rendered, deferState, 1)
6818
6870
  renderStmts(&rendered, body, 1)
6819
- async := stmtsContainAwait(body) || deferState.async
6871
+ async := bodyCtx.asyncFunction || stmtsContainAwait(body) || deferState.async
6820
6872
  prefix := ""
6821
6873
  if async {
6822
6874
  prefix = "async "
@@ -6839,7 +6891,7 @@ func renderLoweredParams(params []loweredParam) string {
6839
6891
  return strings.Join(rendered, ", ")
6840
6892
  }
6841
6893
 
6842
- func funcLiteralUsesFunctionIdentifierCall(ctx lowerFileContext, lit *ast.FuncLit) bool {
6894
+ func funcLiteralUsesAwaitableCall(ctx lowerFileContext, lit *ast.FuncLit) bool {
6843
6895
  if lit == nil || lit.Body == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
6844
6896
  return false
6845
6897
  }
@@ -6855,7 +6907,8 @@ func funcLiteralUsesFunctionIdentifierCall(ctx lowerFileContext, lit *ast.FuncLi
6855
6907
  if !ok {
6856
6908
  return true
6857
6909
  }
6858
- uses = callUsesFunctionIdentifier(ctx.semPkg.source, call.Fun)
6910
+ uses = callUsesFunctionIdentifier(ctx.semPkg.source, call.Fun) ||
6911
+ callUsesInterfaceMethod(ctx.semPkg.source, call.Fun)
6859
6912
  return !uses
6860
6913
  })
6861
6914
  return uses
@@ -7057,15 +7110,16 @@ func (o *LoweringOwner) lowerCallExpr(ctx lowerFileContext, expr *ast.CallExpr)
7057
7110
  }
7058
7111
  }
7059
7112
  }
7113
+ appendHelper := o.runtimeOwner.QualifiedHelper(RuntimeHelperAppend)
7060
7114
  if expr.Ellipsis != token.NoPos && len(args) > 1 {
7061
7115
  last := len(args) - 1
7062
7116
  spread := args[last]
7063
7117
  if isStringType(ctx.semPkg.source.TypesInfo.TypeOf(expr.Args[len(expr.Args)-1])) {
7064
7118
  spread = o.runtimeOwner.QualifiedHelper(RuntimeHelperStringToBytes) + "(" + spread + ")"
7065
7119
  }
7066
- args[last] = "...(" + spread + " ?? [])"
7120
+ args[last] = spread
7121
+ appendHelper = o.runtimeOwner.QualifiedHelper(RuntimeHelperAppendSlice)
7067
7122
  }
7068
- appendHelper := o.runtimeOwner.QualifiedHelper(RuntimeHelperAppend)
7069
7123
  if len(args) > 0 && args[0] == "null" {
7070
7124
  if slice, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(expr)).Underlying().(*types.Slice); ok {
7071
7125
  appendHelper += "<" + o.tsTypeFor(ctx, slice.Elem()) + ">"
@@ -7975,6 +8029,13 @@ func (o *LoweringOwner) lowerPointerReceiverMethodCall(
7975
8029
  methodMemberName(selector.Sel.Name) + "(" + strings.Join(args, ", ") + ")"
7976
8030
  return call, diagnostics, true
7977
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
+ }
7978
8039
  if crossPackageUnexportedNamedType(ctx, receiver) {
7979
8040
  call := o.runtimeOwner.QualifiedHelper(RuntimeHelperPointerValue) +
7980
8041
  "<any>(" + receiverExpr + ")." + methodMemberName(selector.Sel.Name) +
@@ -9786,12 +9847,61 @@ func (o *LoweringOwner) runtimeTypeInfoExpr(typ types.Type) string {
9786
9847
  }
9787
9848
 
9788
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 {
9789
9854
  typeParam, ok := types.Unalias(typ).(*types.TypeParam)
9790
- 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:
9791
9903
  return o.runtimeTypeInfoExpr(typ)
9792
9904
  }
9793
- return "__typeArgs?.[" + strconv.Quote(typeParam.Obj().Name()) + "]?.type ?? " +
9794
- o.runtimeTypeInfoExpr(typ)
9795
9905
  }
9796
9906
 
9797
9907
  func (o *LoweringOwner) runtimeTypeInfoExprWithSeen(typ types.Type, seen map[types.Type]bool) string {
@@ -9919,6 +10029,40 @@ func (o *LoweringOwner) runtimeStructFieldsExpr(structType *types.Struct, seen m
9919
10029
  return "[" + strings.Join(fields, ", ") + "]"
9920
10030
  }
9921
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
+
9922
10066
  func runtimeStructFieldInfoExpr(
9923
10067
  runtimeType string,
9924
10068
  storageKey string,
@@ -9983,6 +10127,26 @@ func (o *LoweringOwner) runtimeFunctionTypeInfoWithSeen(signature *types.Signatu
9983
10127
  return "({ " + strings.Join(parts, ", ") + " } as " + runtimePackage + ".FunctionTypeInfo)"
9984
10128
  }
9985
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
+
9986
10150
  func (o *LoweringOwner) runtimeSignatureTypes(tuple *types.Tuple, seen map[types.Type]bool) string {
9987
10151
  if tuple == nil || tuple.Len() == 0 {
9988
10152
  return "[]"
@@ -9994,6 +10158,17 @@ func (o *LoweringOwner) runtimeSignatureTypes(tuple *types.Tuple, seen map[types
9994
10158
  return "[" + strings.Join(types, ", ") + "]"
9995
10159
  }
9996
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
+
9997
10172
  func tsStructFieldName(name string, idx int) string {
9998
10173
  if name == "_" {
9999
10174
  return "_blank" + strconv.Itoa(idx)
@@ -10945,6 +11120,7 @@ func (o *LoweringOwner) callNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool
10945
11120
  return o.functionAsync(ctx, calledFunction(ctx.semPkg.source, fun)) ||
10946
11121
  o.overrideCallNeedsAwait(ctx, fun) ||
10947
11122
  callUsesFunctionValue(ctx.semPkg.source, fun) ||
11123
+ (ctx.asyncFunction && callUsesInterfaceMethod(ctx.semPkg.source, fun)) ||
10948
11124
  (ctx.asyncFunction && callUsesFunctionIdentifier(ctx.semPkg.source, fun))
10949
11125
  }
10950
11126
  if ctx.semPkg == nil || ctx.semPkg.source == nil {
@@ -10953,10 +11129,48 @@ func (o *LoweringOwner) callNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool
10953
11129
  return o.functionAsync(ctx, calledFunction(ctx.semPkg.source, fun)) ||
10954
11130
  o.overrideCallNeedsAwait(ctx, fun) ||
10955
11131
  callUsesFunctionValue(ctx.semPkg.source, fun) ||
11132
+ (ctx.asyncFunction && callUsesInterfaceMethod(ctx.semPkg.source, fun)) ||
10956
11133
  (ctx.asyncFunction && callUsesFunctionIdentifier(ctx.semPkg.source, fun))
10957
11134
  }
10958
11135
  }
10959
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
+
10960
11174
  func (o *LoweringOwner) overrideCallNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool {
10961
11175
  if o.overrideOwner == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
10962
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",
@@ -5,6 +5,7 @@ import (
5
5
  "go/ast"
6
6
  "os"
7
7
  "path/filepath"
8
+ "slices"
8
9
  "strings"
9
10
  )
10
11
 
@@ -16,6 +17,13 @@ type protobufTypeScriptBinding struct {
16
17
  hasOneof bool
17
18
  }
18
19
 
20
+ type protobufTypeScriptBindingOneofCase struct {
21
+ groupLocalName string
22
+ caseLocalName string
23
+ branchCtor string
24
+ valueCtor string
25
+ }
26
+
19
27
  func protobufTypeScriptBindings(semPkg *semanticPackage, options LoweringOptions) (map[string]protobufTypeScriptBinding, []Diagnostic) {
20
28
  if semPkg == nil || semPkg.source == nil || !options.ProtobufTypeScriptBinding {
21
29
  return nil, nil
@@ -291,6 +299,7 @@ func rewriteProtobufTypeScriptBindingFile(file *loweredFile, binding protobufTyp
291
299
  source: binding.importSource,
292
300
  sideEffect: true,
293
301
  })
302
+ oneofCases := protobufTypeScriptBindingOneofCases(file)
294
303
  var setupDecls []loweredDecl
295
304
  for _, decl := range file.decls {
296
305
  if decl.structType == nil {
@@ -306,7 +315,7 @@ func rewriteProtobufTypeScriptBindingFile(file *loweredFile, binding protobufTyp
306
315
  if !ok {
307
316
  continue
308
317
  }
309
- setup := protobufTypeScriptBindingStructSetupDecl(decl.structType, importAlias, messageName)
318
+ setup := protobufTypeScriptBindingStructSetupDecl(decl.structType, importAlias, messageName, oneofCases[decl.structType.name])
310
319
  if setup.code != "" {
311
320
  setupDecls = append(setupDecls, setup)
312
321
  }
@@ -314,6 +323,102 @@ func rewriteProtobufTypeScriptBindingFile(file *loweredFile, binding protobufTyp
314
323
  file.decls = append(file.decls, setupDecls...)
315
324
  }
316
325
 
326
+ func protobufTypeScriptBindingOneofCases(file *loweredFile) map[string][]protobufTypeScriptBindingOneofCase {
327
+ parentByName := make(map[string]*loweredStruct)
328
+ for _, decl := range file.decls {
329
+ if decl.structType != nil {
330
+ parentByName[decl.structType.name] = decl.structType
331
+ }
332
+ }
333
+ out := make(map[string][]protobufTypeScriptBindingOneofCase)
334
+ for _, decl := range file.decls {
335
+ branch := decl.structType
336
+ if branch == nil || !strings.Contains(branch.name, "_") {
337
+ continue
338
+ }
339
+ parent := protobufTypeScriptBindingOneofParent(branch.name, parentByName)
340
+ if parent == nil {
341
+ continue
342
+ }
343
+ groups := protobufTypeScriptBindingOneofGroups(parent)
344
+ if len(groups) == 0 {
345
+ continue
346
+ }
347
+ groupLocalName := protobufTypeScriptBindingOneofCaseGroup(branch, parent, groups)
348
+ if groupLocalName == "" {
349
+ continue
350
+ }
351
+ for _, field := range branch.fields {
352
+ if !strings.Contains(field.tag, "oneof") {
353
+ continue
354
+ }
355
+ out[parent.name] = append(out[parent.name], protobufTypeScriptBindingOneofCase{
356
+ groupLocalName: groupLocalName,
357
+ caseLocalName: protobufTypeScriptBindingFieldLocalName(field),
358
+ branchCtor: branch.name,
359
+ valueCtor: protobufTypeScriptBindingFieldCtor(field),
360
+ })
361
+ }
362
+ }
363
+ for parentName := range out {
364
+ slices.SortFunc(out[parentName], func(left, right protobufTypeScriptBindingOneofCase) int {
365
+ if left.groupLocalName != right.groupLocalName {
366
+ return strings.Compare(left.groupLocalName, right.groupLocalName)
367
+ }
368
+ return strings.Compare(left.caseLocalName, right.caseLocalName)
369
+ })
370
+ }
371
+ return out
372
+ }
373
+
374
+ func protobufTypeScriptBindingOneofParent(branchName string, parents map[string]*loweredStruct) *loweredStruct {
375
+ var parent *loweredStruct
376
+ for name, candidate := range parents {
377
+ if name == branchName || !strings.HasPrefix(branchName, name+"_") {
378
+ continue
379
+ }
380
+ if parent == nil || len(name) > len(parent.name) {
381
+ parent = candidate
382
+ }
383
+ }
384
+ return parent
385
+ }
386
+
387
+ func protobufTypeScriptBindingOneofGroups(parent *loweredStruct) map[string]string {
388
+ groups := make(map[string]string)
389
+ for _, field := range parent.fields {
390
+ if !strings.Contains(field.tag, "protobuf_oneof") {
391
+ continue
392
+ }
393
+ localName := protobufTypeScriptBindingTagValue(field.tag, "protobuf_oneof:\"")
394
+ if localName == "" {
395
+ groups[field.name] = protobufTypeScriptBindingFieldLocalName(field)
396
+ continue
397
+ }
398
+ groups[field.name] = protobufTypeScriptBindingProtoCamel(localName)
399
+ }
400
+ return groups
401
+ }
402
+
403
+ func protobufTypeScriptBindingOneofCaseGroup(branch, parent *loweredStruct, groups map[string]string) string {
404
+ if len(groups) == 1 {
405
+ for _, localName := range groups {
406
+ return localName
407
+ }
408
+ }
409
+ prefix := "is" + parent.name + "_"
410
+ for _, method := range branch.methods {
411
+ groupName, ok := strings.CutPrefix(method.name, prefix)
412
+ if !ok {
413
+ continue
414
+ }
415
+ if localName := groups[groupName]; localName != "" {
416
+ return localName
417
+ }
418
+ }
419
+ return ""
420
+ }
421
+
317
422
  func protobufTypeScriptBindingSyntheticMapEntry(name string) bool {
318
423
  return strings.Contains(name, "_") && strings.HasSuffix(name, "Entry")
319
424
  }
@@ -466,23 +571,66 @@ func protobufBindingParam(method *loweredFunction, idx int, fallback string) str
466
571
  return method.params[idx].name
467
572
  }
468
573
 
469
- func protobufTypeScriptBindingStructSetupDecl(structType *loweredStruct, importAlias, messageName string) loweredDecl {
574
+ func protobufTypeScriptBindingStructSetupDecl(structType *loweredStruct, importAlias, messageName string, oneofCases []protobufTypeScriptBindingOneofCase) loweredDecl {
470
575
  if structType == nil {
471
576
  return loweredDecl{}
472
577
  }
473
578
  if messageName == "" {
474
579
  messageName = structType.name
475
580
  }
476
- entries := make([]string, 0, len(structType.fields))
581
+ fieldEntries := make(map[string]string)
477
582
  for _, field := range structType.fields {
478
583
  ctor := protobufTypeScriptBindingFieldCtor(field)
479
584
  if ctor == "" {
480
585
  continue
481
586
  }
482
- entries = append(entries, strconvQuote(protobufTypeScriptBindingFieldLocalName(field))+": "+ctor)
587
+ fieldEntries[protobufTypeScriptBindingFieldLocalName(field)] = ctor
588
+ }
589
+ for _, oneofCase := range oneofCases {
590
+ if oneofCase.valueCtor != "" {
591
+ fieldEntries[oneofCase.caseLocalName] = oneofCase.valueCtor
592
+ }
593
+ }
594
+ fieldNames := make([]string, 0, len(fieldEntries))
595
+ for name := range fieldEntries {
596
+ fieldNames = append(fieldNames, name)
597
+ }
598
+ slices.Sort(fieldNames)
599
+ entries := make([]string, 0, len(fieldNames))
600
+ for _, name := range fieldNames {
601
+ entries = append(entries, strconvQuote(name)+": "+fieldEntries[name])
602
+ }
603
+ code := "(" + structType.name + " as any).__protobufTypeScriptMessage = " + importAlias + "." + messageName + ";\n" +
604
+ "(" + structType.name + " as any).__protobufTypeScriptFields = {" + strings.Join(entries, ", ") + "};"
605
+ if len(oneofCases) != 0 {
606
+ code += "\n(" + structType.name + " as any).__protobufTypeScriptOneofFields = " + protobufTypeScriptBindingOneofFieldsLiteral(oneofCases) + ";"
607
+ }
608
+ return loweredDecl{code: code}
609
+ }
610
+
611
+ func protobufTypeScriptBindingOneofFieldsLiteral(oneofCases []protobufTypeScriptBindingOneofCase) string {
612
+ groups := make(map[string][]protobufTypeScriptBindingOneofCase)
613
+ for _, oneofCase := range oneofCases {
614
+ groups[oneofCase.groupLocalName] = append(groups[oneofCase.groupLocalName], oneofCase)
615
+ }
616
+ groupNames := make([]string, 0, len(groups))
617
+ for name := range groups {
618
+ groupNames = append(groupNames, name)
619
+ }
620
+ slices.Sort(groupNames)
621
+ entries := make([]string, 0, len(groupNames))
622
+ for _, groupName := range groupNames {
623
+ cases := groups[groupName]
624
+ slices.SortFunc(cases, func(left, right protobufTypeScriptBindingOneofCase) int {
625
+ return strings.Compare(left.caseLocalName, right.caseLocalName)
626
+ })
627
+ caseEntries := make([]string, 0, len(cases))
628
+ for _, oneofCase := range cases {
629
+ caseEntries = append(caseEntries, strconvQuote(oneofCase.caseLocalName)+": "+oneofCase.branchCtor)
630
+ }
631
+ entries = append(entries, strconvQuote(groupName)+": {"+strings.Join(caseEntries, ", ")+"}")
483
632
  }
484
- return loweredDecl{code: "(" + structType.name + " as any).__protobufTypeScriptMessage = " + importAlias + "." + messageName + ";\n" +
485
- "(" + structType.name + " as any).__protobufTypeScriptFields = {" + strings.Join(entries, ", ") + "};"}
633
+ return "{" + strings.Join(entries, ", ") + "}"
486
634
  }
487
635
 
488
636
  func protobufTypeScriptBindingFieldLocalName(field loweredStructField) string {