goscript 0.0.83 → 0.1.0

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 (197) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +27 -7
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +86 -0
  39. package/dist/gs/builtin/hostio.js +266 -0
  40. package/dist/gs/builtin/hostio.js.map +1 -0
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/print.d.ts +8 -0
  45. package/dist/gs/builtin/print.js +111 -0
  46. package/dist/gs/builtin/print.js.map +1 -0
  47. package/dist/gs/builtin/slice.d.ts +1 -1
  48. package/dist/gs/builtin/slice.js.map +1 -1
  49. package/dist/gs/builtin/type.d.ts +11 -0
  50. package/dist/gs/builtin/type.js +55 -1
  51. package/dist/gs/builtin/type.js.map +1 -1
  52. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  53. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  54. package/dist/gs/bytes/reader.gs.js.map +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/crypto/rand/index.d.ts +5 -0
  57. package/dist/gs/crypto/rand/index.js +77 -0
  58. package/dist/gs/crypto/rand/index.js.map +1 -0
  59. package/dist/gs/encoding/json/index.d.ts +3 -0
  60. package/dist/gs/encoding/json/index.js +160 -0
  61. package/dist/gs/encoding/json/index.js.map +1 -0
  62. package/dist/gs/fmt/fmt.js +2 -22
  63. package/dist/gs/fmt/fmt.js.map +1 -1
  64. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  65. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  66. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  67. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  68. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  69. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  70. package/dist/gs/go/scanner/index.d.ts +29 -0
  71. package/dist/gs/go/scanner/index.js +120 -0
  72. package/dist/gs/go/scanner/index.js.map +1 -0
  73. package/dist/gs/go/token/index.d.ts +31 -0
  74. package/dist/gs/go/token/index.js +82 -0
  75. package/dist/gs/go/token/index.js.map +1 -0
  76. package/dist/gs/internal/abi/index.js.map +1 -1
  77. package/dist/gs/io/fs/fs.js.map +1 -1
  78. package/dist/gs/io/fs/readdir.js.map +1 -1
  79. package/dist/gs/io/fs/readfile.js.map +1 -1
  80. package/dist/gs/io/fs/stat.js.map +1 -1
  81. package/dist/gs/io/fs/sub.js.map +1 -1
  82. package/dist/gs/io/io.js.map +1 -1
  83. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  84. package/dist/gs/os/error.gs.js +2 -4
  85. package/dist/gs/os/error.gs.js.map +1 -1
  86. package/dist/gs/os/exec.gs.js.map +1 -1
  87. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  88. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  89. package/dist/gs/os/root_js.gs.js.map +1 -1
  90. package/dist/gs/os/tempfile.gs.js +66 -9
  91. package/dist/gs/os/tempfile.gs.js.map +1 -1
  92. package/dist/gs/os/types.gs.js.map +1 -1
  93. package/dist/gs/os/types_js.gs.d.ts +2 -51
  94. package/dist/gs/os/types_js.gs.js +67 -105
  95. package/dist/gs/os/types_js.gs.js.map +1 -1
  96. package/dist/gs/os/types_unix.gs.js.map +1 -1
  97. package/dist/gs/path/filepath/match.js.map +1 -1
  98. package/dist/gs/path/match.js.map +1 -1
  99. package/dist/gs/path/path.js.map +1 -1
  100. package/dist/gs/reflect/index.d.ts +2 -2
  101. package/dist/gs/reflect/index.js +1 -1
  102. package/dist/gs/reflect/index.js.map +1 -1
  103. package/dist/gs/reflect/map.js.map +1 -1
  104. package/dist/gs/reflect/type.d.ts +2 -1
  105. package/dist/gs/reflect/type.js +85 -14
  106. package/dist/gs/reflect/type.js.map +1 -1
  107. package/dist/gs/reflect/types.js.map +1 -1
  108. package/dist/gs/reflect/visiblefields.js.map +1 -1
  109. package/dist/gs/runtime/runtime.js.map +1 -1
  110. package/dist/gs/sort/sort.gs.js.map +1 -1
  111. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  112. package/dist/gs/strconv/quote.gs.js.map +1 -1
  113. package/dist/gs/strings/builder.js.map +1 -1
  114. package/dist/gs/strings/reader.js.map +1 -1
  115. package/dist/gs/strings/replace.js.map +1 -1
  116. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  117. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  118. package/dist/gs/sync/sync.d.ts +1 -0
  119. package/dist/gs/sync/sync.js +12 -0
  120. package/dist/gs/sync/sync.js.map +1 -1
  121. package/dist/gs/time/time.js.map +1 -1
  122. package/dist/gs/unicode/unicode.js.map +1 -1
  123. package/go.mod +2 -2
  124. package/gs/builtin/builtin.ts +31 -6
  125. package/gs/builtin/hostio.test.ts +246 -0
  126. package/gs/builtin/hostio.ts +413 -0
  127. package/gs/builtin/index.ts +1 -0
  128. package/gs/builtin/print.test.ts +48 -0
  129. package/gs/builtin/print.ts +154 -0
  130. package/gs/builtin/runtime-contract.test.ts +230 -0
  131. package/gs/builtin/type.ts +84 -1
  132. package/gs/crypto/rand/index.test.ts +32 -0
  133. package/gs/crypto/rand/index.ts +90 -0
  134. package/gs/crypto/rand/meta.json +5 -0
  135. package/gs/encoding/json/index.test.ts +65 -0
  136. package/gs/encoding/json/index.ts +186 -0
  137. package/gs/fmt/fmt.test.ts +41 -30
  138. package/gs/fmt/fmt.ts +2 -22
  139. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  140. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  141. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  142. package/gs/go/scanner/index.test.ts +50 -0
  143. package/gs/go/scanner/index.ts +157 -0
  144. package/gs/go/token/index.test.ts +21 -0
  145. package/gs/go/token/index.ts +120 -0
  146. package/gs/os/file_unix_js.test.ts +103 -0
  147. package/gs/os/meta.json +1 -2
  148. package/gs/os/tempfile.gs.test.ts +85 -0
  149. package/gs/os/tempfile.gs.ts +71 -11
  150. package/gs/os/types_js.gs.ts +74 -153
  151. package/gs/reflect/index.ts +1 -1
  152. package/gs/reflect/type.ts +106 -17
  153. package/gs/reflect/typefor.test.ts +75 -0
  154. package/gs/sync/sync.test.ts +24 -0
  155. package/gs/sync/sync.ts +12 -0
  156. package/package.json +13 -13
  157. package/compiler/analysis.go +0 -3475
  158. package/compiler/analysis_test.go +0 -338
  159. package/compiler/assignment.go +0 -580
  160. package/compiler/builtin_test.go +0 -92
  161. package/compiler/code-writer.go +0 -115
  162. package/compiler/compiler_test.go +0 -149
  163. package/compiler/composite-lit.go +0 -779
  164. package/compiler/config_test.go +0 -62
  165. package/compiler/constraint.go +0 -86
  166. package/compiler/decl.go +0 -801
  167. package/compiler/expr-call-async.go +0 -188
  168. package/compiler/expr-call-builtins.go +0 -208
  169. package/compiler/expr-call-helpers.go +0 -382
  170. package/compiler/expr-call-make.go +0 -318
  171. package/compiler/expr-call-type-conversion.go +0 -520
  172. package/compiler/expr-call.go +0 -413
  173. package/compiler/expr-selector.go +0 -343
  174. package/compiler/expr-star.go +0 -82
  175. package/compiler/expr-type.go +0 -442
  176. package/compiler/expr-value.go +0 -89
  177. package/compiler/expr.go +0 -773
  178. package/compiler/field.go +0 -183
  179. package/compiler/gs_dependencies_test.go +0 -298
  180. package/compiler/lit.go +0 -322
  181. package/compiler/output.go +0 -72
  182. package/compiler/primitive.go +0 -149
  183. package/compiler/protobuf.go +0 -697
  184. package/compiler/sanitize.go +0 -100
  185. package/compiler/spec-struct.go +0 -995
  186. package/compiler/spec-value.go +0 -540
  187. package/compiler/spec.go +0 -725
  188. package/compiler/stmt-assign.go +0 -664
  189. package/compiler/stmt-for.go +0 -266
  190. package/compiler/stmt-range.go +0 -475
  191. package/compiler/stmt-select.go +0 -262
  192. package/compiler/stmt-type-switch.go +0 -147
  193. package/compiler/stmt.go +0 -1308
  194. package/compiler/type-assert.go +0 -386
  195. package/compiler/type-info.go +0 -156
  196. package/compiler/type-utils.go +0 -207
  197. package/compiler/type.go +0 -892
@@ -0,0 +1,3576 @@
1
+ package compiler
2
+
3
+ import (
4
+ "cmp"
5
+ "context"
6
+ "go/ast"
7
+ "go/constant"
8
+ "go/token"
9
+ "go/types"
10
+ "path/filepath"
11
+ "slices"
12
+ "strconv"
13
+ "strings"
14
+ )
15
+
16
+ // LoweringOwner owns conversion from the semantic model to compiler IR.
17
+ type LoweringOwner struct {
18
+ runtimeOwner *RuntimeContractOwner
19
+ overrideOwner *OverrideRegistryOwner
20
+ }
21
+
22
+ // NewLoweringOwner creates the lowering owner.
23
+ func NewLoweringOwner(runtimeOwner *RuntimeContractOwner, overrideOwner *OverrideRegistryOwner) *LoweringOwner {
24
+ if runtimeOwner == nil {
25
+ runtimeOwner = NewRuntimeContractOwner()
26
+ }
27
+ if overrideOwner == nil {
28
+ overrideOwner = NewOverrideRegistryOwner()
29
+ }
30
+ return &LoweringOwner{
31
+ runtimeOwner: runtimeOwner,
32
+ overrideOwner: overrideOwner,
33
+ }
34
+ }
35
+
36
+ // Build converts the semantic model into the compiler IR.
37
+ func (o *LoweringOwner) Build(ctx context.Context, model *SemanticModel) (*LoweredProgram, []Diagnostic) {
38
+ if err := ctx.Err(); err != nil {
39
+ return nil, []Diagnostic{{
40
+ Severity: DiagnosticSeverityError,
41
+ Code: "goscript/context:canceled",
42
+ Message: err.Error(),
43
+ }}
44
+ }
45
+ if model == nil {
46
+ return nil, []Diagnostic{{
47
+ Severity: DiagnosticSeverityError,
48
+ Code: "goscript/lowering:no-model",
49
+ Message: "lowering requires a semantic model",
50
+ }}
51
+ }
52
+
53
+ program := &LoweredProgram{}
54
+ semPkgs := make([]*semanticPackage, 0, len(model.packages))
55
+ for _, semPkg := range model.packages {
56
+ semPkgs = append(semPkgs, semPkg)
57
+ }
58
+ slices.SortFunc(semPkgs, func(a, b *semanticPackage) int {
59
+ return cmp.Compare(a.pkgPath, b.pkgPath)
60
+ })
61
+
62
+ var diagnostics []Diagnostic
63
+ for _, semPkg := range semPkgs {
64
+ if semPkg.source == nil {
65
+ diagnostics = append(diagnostics, loweringUnsupported("package", semPkg.pkgPath, "missing semantic source package"))
66
+ continue
67
+ }
68
+ loweredPkg, pkgDiagnostics := o.lowerPackage(model, semPkg)
69
+ diagnostics = append(diagnostics, pkgDiagnostics...)
70
+ if loweredPkg != nil {
71
+ program.packages = append(program.packages, loweredPkg)
72
+ }
73
+ }
74
+ if diagnosticsHaveErrors(diagnostics) {
75
+ return nil, diagnostics
76
+ }
77
+ return program, nil
78
+ }
79
+
80
+ func (o *LoweringOwner) lowerPackage(model *SemanticModel, semPkg *semanticPackage) (*loweredPackage, []Diagnostic) {
81
+ loweredPkg := &loweredPackage{
82
+ pkgPath: semPkg.pkgPath,
83
+ name: semPkg.name,
84
+ }
85
+ var diagnostics []Diagnostic
86
+ for idx, file := range semPkg.source.Syntax {
87
+ sourcePath := sourceFilePath(semPkg, idx, file)
88
+ loweredFile, fileDiagnostics := o.lowerFile(model, semPkg, file, sourcePath)
89
+ diagnostics = append(diagnostics, fileDiagnostics...)
90
+ if loweredFile != nil {
91
+ loweredPkg.files = append(loweredPkg.files, loweredFile)
92
+ }
93
+ }
94
+ slices.SortFunc(loweredPkg.files, func(a, b *loweredFile) int {
95
+ return cmp.Compare(a.outputName, b.outputName)
96
+ })
97
+ return loweredPkg, diagnostics
98
+ }
99
+
100
+ func sourceFilePath(semPkg *semanticPackage, idx int, file *ast.File) string {
101
+ if idx < len(semPkg.source.CompiledGoFiles) {
102
+ return semPkg.source.CompiledGoFiles[idx]
103
+ }
104
+ pos := sourcePos(semPkg.source, file.Package)
105
+ return pos.file
106
+ }
107
+
108
+ func sourceOutputName(sourcePath string) string {
109
+ return strings.TrimSuffix(filepath.Base(sourcePath), ".go") + ".gs.ts"
110
+ }
111
+
112
+ func (o *LoweringOwner) lowerFile(
113
+ model *SemanticModel,
114
+ semPkg *semanticPackage,
115
+ file *ast.File,
116
+ sourcePath string,
117
+ ) (*loweredFile, []Diagnostic) {
118
+ loweredFile := &loweredFile{
119
+ sourcePath: sourcePath,
120
+ outputName: sourceOutputName(sourcePath),
121
+ imports: []loweredImport{{
122
+ alias: o.runtimeOwner.BuiltinImport().Alias,
123
+ source: o.runtimeOwner.BuiltinImport().Source,
124
+ }},
125
+ }
126
+ importAliases := make(map[string]string)
127
+ importPaths := make(map[string]string)
128
+ for _, importSpec := range file.Imports {
129
+ pkgName, _ := semPkg.source.TypesInfo.Implicits[importSpec].(*types.PkgName)
130
+ if importSpec.Name != nil {
131
+ pkgName, _ = semPkg.source.TypesInfo.Defs[importSpec.Name].(*types.PkgName)
132
+ }
133
+ if pkgName == nil || pkgName.Imported() == nil {
134
+ continue
135
+ }
136
+ alias := pkgName.Name()
137
+ if importSpec.Name != nil {
138
+ alias = importSpec.Name.Name
139
+ }
140
+ if alias == "." || alias == "_" {
141
+ continue
142
+ }
143
+ importAliases[alias] = pkgName.Imported().Path()
144
+ importPaths[pkgName.Imported().Path()] = alias
145
+ loweredFile.imports = append(loweredFile.imports, loweredImport{
146
+ alias: alias,
147
+ source: "@goscript/" + pkgName.Imported().Path() + "/index.js",
148
+ })
149
+ }
150
+ localAliases, localAliasSources := o.localFileAliases(semPkg, file, sourcePath)
151
+ localImports := make([]loweredImport, 0, len(localAliases))
152
+ seenLocalImport := make(map[string]bool)
153
+ for _, alias := range localAliases {
154
+ if seenLocalImport[alias] {
155
+ continue
156
+ }
157
+ seenLocalImport[alias] = true
158
+ source := localAliasSources[alias]
159
+ localImports = append(localImports, loweredImport{alias: alias, source: source})
160
+ }
161
+ slices.SortFunc(localImports, func(a, b loweredImport) int {
162
+ return cmp.Compare(a.alias, b.alias)
163
+ })
164
+ loweredFile.imports = append(loweredFile.imports, localImports...)
165
+
166
+ ctx := lowerFileContext{
167
+ model: model,
168
+ semPkg: semPkg,
169
+ file: file,
170
+ importAliases: importAliases,
171
+ importPaths: importPaths,
172
+ localAliases: localAliases,
173
+ topLevel: true,
174
+ }
175
+ var diagnostics []Diagnostic
176
+ for _, decl := range file.Decls {
177
+ loweredDecls, declDiagnostics := o.lowerDecl(ctx, decl)
178
+ diagnostics = append(diagnostics, declDiagnostics...)
179
+ for _, decl := range loweredDecls {
180
+ loweredFile.decls = append(loweredFile.decls, decl)
181
+ if decl.indexExport != "" {
182
+ loweredFile.exports = append(loweredFile.exports, decl.indexExport)
183
+ }
184
+ if decl.typeIndexExport != "" {
185
+ loweredFile.typeExports = append(loweredFile.typeExports, decl.typeIndexExport)
186
+ }
187
+ if decl.function != nil && decl.function.indexExported && decl.function.name != "main" {
188
+ loweredFile.exports = append(loweredFile.exports, decl.function.name)
189
+ }
190
+ if decl.structType != nil && decl.structType.indexExported {
191
+ loweredFile.exports = append(loweredFile.exports, decl.structType.name)
192
+ }
193
+ }
194
+ }
195
+ return loweredFile, diagnostics
196
+ }
197
+
198
+ func (o *LoweringOwner) localFileAliases(
199
+ semPkg *semanticPackage,
200
+ file *ast.File,
201
+ sourcePath string,
202
+ ) (map[types.Object]string, map[string]string) {
203
+ declFiles := make(map[types.Object]string)
204
+ for _, decl := range semPkg.declarations {
205
+ if decl.object == nil || decl.position.file == "" {
206
+ continue
207
+ }
208
+ declFiles[decl.object] = decl.position.file
209
+ }
210
+ outputNames := make(map[string]string)
211
+ for idx, syntax := range semPkg.source.Syntax {
212
+ outputSourcePath := sourceFilePath(semPkg, idx, syntax)
213
+ outputNames[outputSourcePath] = sourceOutputName(outputSourcePath)
214
+ }
215
+ aliases := make(map[types.Object]string)
216
+ aliasSources := make(map[string]string)
217
+ seenTypes := make(map[types.Type]bool)
218
+ var addTypeDeps func(typ types.Type)
219
+ addObject := func(obj types.Object) {
220
+ if obj == nil || obj.Pkg() == nil || obj.Pkg().Path() != semPkg.pkgPath {
221
+ return
222
+ }
223
+ declFile := declFiles[obj]
224
+ if declFile == "" || declFile == sourcePath {
225
+ return
226
+ }
227
+ outputName := outputNames[declFile]
228
+ if outputName == "" {
229
+ return
230
+ }
231
+ alias := "__goscript_" + safeIdentifier(strings.TrimSuffix(outputName, ".gs.ts"))
232
+ aliases[obj] = alias
233
+ aliasSources[alias] = "./" + outputName
234
+ if typeName, ok := obj.(*types.TypeName); ok {
235
+ addTypeDeps(typeName.Type())
236
+ }
237
+ }
238
+ addTypeDeps = func(typ types.Type) {
239
+ if typ == nil || seenTypes[typ] {
240
+ return
241
+ }
242
+ seenTypes[typ] = true
243
+ if named, ok := types.Unalias(typ).(*types.Named); ok {
244
+ addObject(named.Obj())
245
+ if args := named.TypeArgs(); args != nil {
246
+ for t := range args.Types() {
247
+ addTypeDeps(t)
248
+ }
249
+ }
250
+ addTypeDeps(named.Underlying())
251
+ return
252
+ }
253
+ switch typed := types.Unalias(typ).Underlying().(type) {
254
+ case *types.Pointer:
255
+ addTypeDeps(typed.Elem())
256
+ case *types.Slice:
257
+ addTypeDeps(typed.Elem())
258
+ case *types.Array:
259
+ addTypeDeps(typed.Elem())
260
+ case *types.Map:
261
+ addTypeDeps(typed.Key())
262
+ addTypeDeps(typed.Elem())
263
+ case *types.Chan:
264
+ addTypeDeps(typed.Elem())
265
+ case *types.Struct:
266
+ for field := range typed.Fields() {
267
+ addTypeDeps(field.Type())
268
+ }
269
+ }
270
+ }
271
+ ast.Inspect(file, func(node ast.Node) bool {
272
+ switch typed := node.(type) {
273
+ case *ast.Ident:
274
+ addObject(semPkg.source.TypesInfo.Uses[typed])
275
+ case *ast.SelectorExpr:
276
+ if selection := semPkg.source.TypesInfo.Selections[typed]; selection != nil {
277
+ addObject(selection.Obj())
278
+ }
279
+ }
280
+ return true
281
+ })
282
+ return aliases, aliasSources
283
+ }
284
+
285
+ func safeIdentifier(value string) string {
286
+ var b strings.Builder
287
+ for idx, r := range value {
288
+ valid := r == '_' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || idx != 0 && r >= '0' && r <= '9'
289
+ if valid {
290
+ b.WriteRune(r)
291
+ continue
292
+ }
293
+ b.WriteByte('_')
294
+ }
295
+ if b.Len() == 0 {
296
+ return "_"
297
+ }
298
+ identifier := b.String()
299
+ switch identifier {
300
+ case "abstract", "any", "as", "asserts", "async", "await", "boolean", "break", "case",
301
+ "catch", "class", "const", "constructor", "continue", "debugger", "declare", "default",
302
+ "delete", "do", "else", "enum", "export", "extends", "false", "finally", "for",
303
+ "from", "function", "get", "if", "implements", "import", "in", "infer", "instanceof",
304
+ "interface", "is", "keyof", "let", "module", "namespace", "never", "new", "null",
305
+ "number", "object", "of", "package", "private", "protected", "public", "readonly",
306
+ "require", "return", "set", "static", "string", "super", "switch", "symbol", "this",
307
+ "throw", "true", "try", "type", "typeof", "undefined", "unique", "unknown", "var",
308
+ "void", "while", "with", "yield":
309
+ return "_" + identifier
310
+ default:
311
+ return identifier
312
+ }
313
+ }
314
+
315
+ func safeParamName(param *types.Var, idx int) string {
316
+ if param == nil || param.Name() == "" {
317
+ return "_p" + strconv.Itoa(idx)
318
+ }
319
+ return safeIdentifier(param.Name())
320
+ }
321
+
322
+ type lowerFileContext struct {
323
+ model *SemanticModel
324
+ semPkg *semanticPackage
325
+ file *ast.File
326
+ importAliases map[string]string
327
+ importPaths map[string]string
328
+ localAliases map[types.Object]string
329
+ signature *types.Signature
330
+ deferState *loweredDeferState
331
+ topLevel bool
332
+ }
333
+
334
+ func (o *LoweringOwner) lowerDecl(ctx lowerFileContext, decl ast.Decl) ([]loweredDecl, []Diagnostic) {
335
+ switch typed := decl.(type) {
336
+ case *ast.GenDecl:
337
+ if typed.Tok == token.IMPORT {
338
+ return nil, nil
339
+ }
340
+ return o.lowerGenDecl(ctx, typed)
341
+ case *ast.FuncDecl:
342
+ if typed.Recv != nil {
343
+ if receiver := receiverNamedTypeFromDecl(ctx, typed); receiver != nil && namedStructType(receiver) == nil {
344
+ fn, diagnostics := o.lowerNamedReceiverMethodDecl(ctx, typed, receiver)
345
+ if fn == nil {
346
+ return nil, []Diagnostic{loweringUnsupported("function", typed.Name.Name, "missing type information")}
347
+ }
348
+ return []loweredDecl{{function: fn}}, diagnostics
349
+ }
350
+ return nil, nil
351
+ }
352
+ fn, diagnostics := o.lowerFuncDecl(ctx, typed)
353
+ if fn == nil {
354
+ return nil, []Diagnostic{loweringUnsupported("function", typed.Name.Name, "missing type information")}
355
+ }
356
+ return []loweredDecl{{function: fn}}, diagnostics
357
+ default:
358
+ return nil, []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported declaration kind")}
359
+ }
360
+ }
361
+
362
+ func (o *LoweringOwner) lowerGenDecl(ctx lowerFileContext, decl *ast.GenDecl) ([]loweredDecl, []Diagnostic) {
363
+ var decls []loweredDecl
364
+ var diagnostics []Diagnostic
365
+ for _, spec := range decl.Specs {
366
+ switch typed := spec.(type) {
367
+ case *ast.TypeSpec:
368
+ decl, specDiagnostics := o.lowerTypeSpec(ctx, typed)
369
+ diagnostics = append(diagnostics, specDiagnostics...)
370
+ if decl.code != "" || decl.structType != nil {
371
+ decls = append(decls, decl)
372
+ }
373
+ case *ast.ValueSpec:
374
+ for idx, name := range typed.Names {
375
+ obj := ctx.semPkg.source.TypesInfo.Defs[name]
376
+ if obj == nil {
377
+ continue
378
+ }
379
+ value := o.lowerDeclarationZeroValueExpr(ctx, obj.Type())
380
+ if idx < len(typed.Values) {
381
+ lowered, exprDiagnostics := o.lowerExpr(ctx, typed.Values[idx])
382
+ diagnostics = append(diagnostics, exprDiagnostics...)
383
+ value = o.lowerValueForTarget(ctx, typed.Values[idx], obj.Type(), lowered)
384
+ }
385
+ if _, ok := obj.(*types.Const); !ok && ctx.model.needsVarRef[obj] {
386
+ value = o.runtimeOwner.QualifiedHelper(RuntimeHelperVarRef) + "(" + value + ")"
387
+ }
388
+ keyword := "let"
389
+ if _, ok := obj.(*types.Const); ok || decl.Tok == token.CONST {
390
+ keyword = "const"
391
+ }
392
+ code := keyword + " " + o.lowerIdent(ctx, name, true) + ": " + o.tsVariableTypeFor(ctx, obj.Type(), ctx.model.needsVarRef[obj]) + " = " + value
393
+ indexExport := ""
394
+ if ctx.topLevel {
395
+ code = "export " + code
396
+ if ast.IsExported(name.Name) {
397
+ indexExport = name.Name
398
+ }
399
+ }
400
+ decls = append(decls, loweredDecl{code: code, indexExport: indexExport})
401
+ }
402
+ default:
403
+ diagnostics = append(diagnostics, loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported general declaration"))
404
+ }
405
+ }
406
+ return decls, diagnostics
407
+ }
408
+
409
+ func (o *LoweringOwner) lowerTypeSpec(ctx lowerFileContext, spec *ast.TypeSpec) (loweredDecl, []Diagnostic) {
410
+ obj, _ := ctx.semPkg.source.TypesInfo.Defs[spec.Name].(*types.TypeName)
411
+ if obj == nil {
412
+ return loweredDecl{}, nil
413
+ }
414
+ named, _ := obj.Type().(*types.Named)
415
+ if named == nil {
416
+ return loweredDecl{}, nil
417
+ }
418
+ semType := ctx.model.types[named]
419
+ if semType == nil {
420
+ return loweredDecl{}, nil
421
+ }
422
+ if _, ok := named.Underlying().(*types.Struct); ok {
423
+ lowered, diagnostics := o.lowerStructType(ctx, semType)
424
+ return loweredDecl{structType: lowered}, diagnostics
425
+ }
426
+ if iface, ok := named.Underlying().(*types.Interface); ok {
427
+ return o.lowerInterfaceType(ctx, semType, iface), nil
428
+ }
429
+ code := "type " + semType.name + " = " + o.tsTypeFor(ctx, named.Underlying())
430
+ typeIndexExport := ""
431
+ if ctx.topLevel {
432
+ code = "export " + code
433
+ if ast.IsExported(semType.name) {
434
+ typeIndexExport = semType.name
435
+ }
436
+ }
437
+ return loweredDecl{code: code, typeIndexExport: typeIndexExport}, nil
438
+ }
439
+
440
+ func (o *LoweringOwner) lowerInterfaceType(ctx lowerFileContext, semType *semanticType, iface *types.Interface) loweredDecl {
441
+ iface.Complete()
442
+ code := "type " + semType.name + " = " + o.tsInterfaceType(ctx, iface)
443
+ typeIndexExport := ""
444
+ if ctx.topLevel {
445
+ code = "export " + code
446
+ if ast.IsExported(semType.name) {
447
+ typeIndexExport = semType.name
448
+ }
449
+ }
450
+ code = code + "\n\n" + o.runtimeOwner.QualifiedHelper(RuntimeHelperRegisterInterfaceType) +
451
+ "(\n\t" + strconv.Quote(runtimeNamedTypeName(semType.named)) +
452
+ ",\n\tnull,\n\t" + o.runtimeMethodSignatures(iface) + "\n)"
453
+ return loweredDecl{code: code, typeIndexExport: typeIndexExport}
454
+ }
455
+
456
+ func (o *LoweringOwner) tsInterfaceType(ctx lowerFileContext, iface *types.Interface) string {
457
+ if iface.NumMethods() == 0 {
458
+ return "any"
459
+ }
460
+ methods := make([]string, 0, iface.NumMethods())
461
+ for method := range iface.Methods() {
462
+ methods = append(methods, o.tsMethodSignature(ctx, method))
463
+ }
464
+ return "null | {\n\t" + strings.Join(methods, "\n\t") + "\n}"
465
+ }
466
+
467
+ func (o *LoweringOwner) tsMethodSignature(ctx lowerFileContext, method *types.Func) string {
468
+ signature, _ := method.Type().(*types.Signature)
469
+ if signature == nil {
470
+ return method.Name() + "(): unknown"
471
+ }
472
+ async := o.functionAsync(ctx, method)
473
+ return method.Name() + "(" + o.tsSignatureParamsFor(ctx, signature) + "): " +
474
+ asyncResultType(o.tsSignatureResultFor(ctx, signature), async)
475
+ }
476
+
477
+ func (o *LoweringOwner) runtimeMethodSignatures(iface *types.Interface) string {
478
+ return o.runtimeMethodSignaturesWithSeen(iface, make(map[string]bool))
479
+ }
480
+
481
+ func (o *LoweringOwner) runtimeMethodSignaturesWithSeen(iface *types.Interface, seen map[string]bool) string {
482
+ methods := make([]string, 0, iface.NumMethods())
483
+ for method := range iface.Methods() {
484
+ methods = append(methods, o.runtimeMethodSignature(method, seen))
485
+ }
486
+ return "[" + strings.Join(methods, ", ") + "]"
487
+ }
488
+
489
+ func (o *LoweringOwner) runtimeMethodSignature(method *types.Func, seen map[string]bool) string {
490
+ signature, _ := method.Type().(*types.Signature)
491
+ if signature == nil {
492
+ return "{ name: " + strconv.Quote(method.Name()) + ", args: [], returns: [] }"
493
+ }
494
+ return "{ name: " + strconv.Quote(method.Name()) +
495
+ ", args: " + o.runtimeMethodArgs(signature.Params(), seen) +
496
+ ", returns: " + o.runtimeMethodReturns(signature.Results(), seen) + " }"
497
+ }
498
+
499
+ func (o *LoweringOwner) runtimeMethodArgs(tuple *types.Tuple, seen map[string]bool) string {
500
+ if tuple == nil || tuple.Len() == 0 {
501
+ return "[]"
502
+ }
503
+ args := make([]string, 0, tuple.Len())
504
+ for idx := range tuple.Len() {
505
+ param := tuple.At(idx)
506
+ name := param.Name()
507
+ if name == "" {
508
+ name = "_p" + strconv.Itoa(idx)
509
+ }
510
+ args = append(args, "{ name: "+strconv.Quote(name)+", type: "+o.runtimeTypeInfoExprWithSeen(param.Type(), seen)+" }")
511
+ }
512
+ return "[" + strings.Join(args, ", ") + "]"
513
+ }
514
+
515
+ func (o *LoweringOwner) runtimeMethodReturns(tuple *types.Tuple, seen map[string]bool) string {
516
+ if tuple == nil || tuple.Len() == 0 {
517
+ return "[]"
518
+ }
519
+ results := make([]string, 0, tuple.Len())
520
+ for idx := range tuple.Len() {
521
+ result := tuple.At(idx)
522
+ name := result.Name()
523
+ if name == "" {
524
+ name = "_r" + strconv.Itoa(idx)
525
+ }
526
+ results = append(results, "{ name: "+strconv.Quote(name)+", type: "+o.runtimeTypeInfoExprWithSeen(result.Type(), seen)+" }")
527
+ }
528
+ return "[" + strings.Join(results, ", ") + "]"
529
+ }
530
+
531
+ func (o *LoweringOwner) lowerStructType(ctx lowerFileContext, semType *semanticType) (*loweredStruct, []Diagnostic) {
532
+ lowered := &loweredStruct{
533
+ exported: ctx.topLevel,
534
+ indexExported: ctx.topLevel && ast.IsExported(semType.name),
535
+ name: semType.name,
536
+ typeName: runtimeNamedTypeName(semType.named),
537
+ }
538
+ for _, field := range semType.fields {
539
+ lowered.fields = append(lowered.fields, loweredStructField{
540
+ name: field.name,
541
+ typ: o.tsTypeFor(ctx, field.typ),
542
+ zero: o.lowerZeroValueExprFor(ctx, field.typ),
543
+ runtimeType: o.runtimeTypeInfoExpr(field.typ),
544
+ doc: field.doc,
545
+ tag: field.tag,
546
+ structValue: isStructValueType(field.typ),
547
+ })
548
+ }
549
+
550
+ methodDecls := o.methodDeclsForType(ctx, semType.named)
551
+ var diagnostics []Diagnostic
552
+ for _, methodDecl := range methodDecls {
553
+ method, methodDiagnostics := o.lowerFuncDecl(ctx, methodDecl)
554
+ diagnostics = append(diagnostics, methodDiagnostics...)
555
+ if method != nil {
556
+ lowered.methods = append(lowered.methods, *method)
557
+ }
558
+ }
559
+ return lowered, diagnostics
560
+ }
561
+
562
+ func (o *LoweringOwner) methodDeclsForType(ctx lowerFileContext, named *types.Named) []*ast.FuncDecl {
563
+ if named == nil {
564
+ return nil
565
+ }
566
+ var methods []*ast.FuncDecl
567
+ for _, file := range ctx.semPkg.source.Syntax {
568
+ for _, decl := range file.Decls {
569
+ fnDecl, ok := decl.(*ast.FuncDecl)
570
+ if !ok || fnDecl.Recv == nil {
571
+ continue
572
+ }
573
+ fnObj, _ := ctx.semPkg.source.TypesInfo.Defs[fnDecl.Name].(*types.Func)
574
+ if fnObj == nil {
575
+ continue
576
+ }
577
+ signature, _ := fnObj.Type().(*types.Signature)
578
+ if signature == nil || signature.Recv() == nil {
579
+ continue
580
+ }
581
+ if sameNamedTypeOrigin(receiverNamedType(signature.Recv().Type()), named) {
582
+ methods = append(methods, fnDecl)
583
+ }
584
+ }
585
+ }
586
+ slices.SortFunc(methods, func(a, b *ast.FuncDecl) int {
587
+ return cmp.Compare(a.Name.Name, b.Name.Name)
588
+ })
589
+ return methods
590
+ }
591
+
592
+ func receiverNamedTypeFromDecl(ctx lowerFileContext, decl *ast.FuncDecl) *types.Named {
593
+ if decl == nil || decl.Recv == nil {
594
+ return nil
595
+ }
596
+ fnObj, _ := ctx.semPkg.source.TypesInfo.Defs[decl.Name].(*types.Func)
597
+ if fnObj == nil {
598
+ return nil
599
+ }
600
+ signature, _ := fnObj.Type().(*types.Signature)
601
+ if signature == nil || signature.Recv() == nil {
602
+ return nil
603
+ }
604
+ return receiverNamedType(signature.Recv().Type())
605
+ }
606
+
607
+ func (o *LoweringOwner) lowerNamedReceiverMethodDecl(
608
+ ctx lowerFileContext,
609
+ decl *ast.FuncDecl,
610
+ receiver *types.Named,
611
+ ) (*loweredFunction, []Diagnostic) {
612
+ fnObj, _ := ctx.semPkg.source.TypesInfo.Defs[decl.Name].(*types.Func)
613
+ if fnObj == nil {
614
+ return nil, nil
615
+ }
616
+ signature, _ := fnObj.Type().(*types.Signature)
617
+ if signature == nil || signature.Recv() == nil {
618
+ return nil, nil
619
+ }
620
+ result := o.tsSignatureResultFor(ctx, signature)
621
+ async := ctx.model.functions[fnObj] != nil && ctx.model.functions[fnObj].async
622
+ receiverName := "recv"
623
+ if len(decl.Recv.List) != 0 && len(decl.Recv.List[0].Names) != 0 {
624
+ receiverName = safeIdentifier(decl.Recv.List[0].Names[0].Name)
625
+ }
626
+ deferState := &loweredDeferState{}
627
+ lowered := &loweredFunction{
628
+ exported: ctx.topLevel,
629
+ indexExported: ctx.topLevel && (ast.IsExported(receiver.Obj().Name()) || ast.IsExported(decl.Name.Name)),
630
+ async: async,
631
+ name: methodFunctionName(receiver, decl.Name.Name),
632
+ result: asyncResultType(result, async),
633
+ deferState: deferState,
634
+ params: []loweredParam{{
635
+ name: receiverName,
636
+ typ: o.tsReceiverTypeFor(ctx, signature.Recv().Type()),
637
+ }},
638
+ }
639
+ for idx := range signature.Params().Len() {
640
+ param := signature.Params().At(idx)
641
+ lowered.params = append(lowered.params, loweredParam{
642
+ name: safeParamName(param, idx),
643
+ typ: o.tsTypeFor(ctx, param.Type()),
644
+ })
645
+ }
646
+ if decl.Body != nil {
647
+ body, diagnostics := o.lowerBlock(ctx.withSignature(signature).withDeferState(deferState), decl.Body)
648
+ lowered.body = body
649
+ if deferState.async && !lowered.async {
650
+ lowered.async = true
651
+ lowered.result = asyncResultType(result, true)
652
+ }
653
+ return lowered, diagnostics
654
+ }
655
+ return lowered, nil
656
+ }
657
+
658
+ func (o *LoweringOwner) lowerFuncDecl(ctx lowerFileContext, decl *ast.FuncDecl) (*loweredFunction, []Diagnostic) {
659
+ fnObj, _ := ctx.semPkg.source.TypesInfo.Defs[decl.Name].(*types.Func)
660
+ if fnObj == nil {
661
+ return nil, nil
662
+ }
663
+ signature, _ := fnObj.Type().(*types.Signature)
664
+ if signature == nil {
665
+ return nil, nil
666
+ }
667
+ result := o.tsSignatureResultFor(ctx, signature)
668
+ async := ctx.model.functions[fnObj] != nil && ctx.model.functions[fnObj].async
669
+ deferState := &loweredDeferState{}
670
+ lowered := &loweredFunction{
671
+ exported: ctx.topLevel,
672
+ indexExported: ctx.topLevel && (ast.IsExported(decl.Name.Name) || decl.Name.Name == "main"),
673
+ async: async,
674
+ name: decl.Name.Name,
675
+ result: asyncResultType(result, async),
676
+ deferState: deferState,
677
+ }
678
+ if signature.TypeParams() != nil && signature.TypeParams().Len() != 0 {
679
+ lowered.params = append(lowered.params, loweredParam{
680
+ name: "__typeArgs",
681
+ typ: "$.GenericTypeArgs | undefined",
682
+ })
683
+ }
684
+ if decl.Recv != nil && len(decl.Recv.List) != 0 && len(decl.Recv.List[0].Names) != 0 {
685
+ lowered.receiverAlias = safeIdentifier(decl.Recv.List[0].Names[0].Name)
686
+ }
687
+ if decl.Name.Name == "main" {
688
+ lowered.async = true
689
+ lowered.result = asyncResultType(result, true)
690
+ }
691
+ for idx := range signature.Params().Len() {
692
+ param := signature.Params().At(idx)
693
+ lowered.params = append(lowered.params, loweredParam{
694
+ name: safeParamName(param, idx),
695
+ typ: o.tsTypeFor(ctx, param.Type()),
696
+ })
697
+ }
698
+ if decl.Body != nil {
699
+ body, diagnostics := o.lowerBlock(ctx.withSignature(signature).withDeferState(deferState), decl.Body)
700
+ lowered.body = body
701
+ if deferState.async && !lowered.async {
702
+ lowered.async = true
703
+ lowered.result = asyncResultType(result, true)
704
+ }
705
+ return lowered, diagnostics
706
+ }
707
+ return lowered, nil
708
+ }
709
+
710
+ func (ctx lowerFileContext) withSignature(signature *types.Signature) lowerFileContext {
711
+ ctx.signature = signature
712
+ return ctx
713
+ }
714
+
715
+ func (ctx lowerFileContext) withDeferState(deferState *loweredDeferState) lowerFileContext {
716
+ ctx.deferState = deferState
717
+ return ctx
718
+ }
719
+
720
+ func (ctx lowerFileContext) withLocalScope() lowerFileContext {
721
+ ctx.topLevel = false
722
+ return ctx
723
+ }
724
+
725
+ func (o *LoweringOwner) lowerBlock(ctx lowerFileContext, block *ast.BlockStmt) ([]loweredStmt, []Diagnostic) {
726
+ if block == nil {
727
+ return nil, nil
728
+ }
729
+ return o.lowerStmtListAfter(ctx.withLocalScope(), block.List, sourceLine(ctx, block.Lbrace))
730
+ }
731
+
732
+ func (o *LoweringOwner) lowerStmt(ctx lowerFileContext, stmt ast.Stmt) ([]loweredStmt, []Diagnostic) {
733
+ switch typed := stmt.(type) {
734
+ case *ast.DeclStmt:
735
+ decls, diagnostics := o.lowerDecl(ctx, typed.Decl)
736
+ stmts := make([]loweredStmt, 0, len(decls))
737
+ for _, decl := range decls {
738
+ if decl.code != "" {
739
+ stmts = append(stmts, loweredStmt{text: decl.code})
740
+ }
741
+ }
742
+ return stmts, diagnostics
743
+ case *ast.AssignStmt:
744
+ return o.lowerAssignStmt(ctx, typed)
745
+ case *ast.SendStmt:
746
+ text, diagnostics := o.lowerSendStmt(ctx, typed)
747
+ return []loweredStmt{{text: text}}, diagnostics
748
+ case *ast.GoStmt:
749
+ text, diagnostics := o.lowerGoStmt(ctx, typed)
750
+ return []loweredStmt{{text: text}}, diagnostics
751
+ case *ast.DeferStmt:
752
+ text, diagnostics := o.lowerDeferStmt(ctx, typed)
753
+ return []loweredStmt{{text: text}}, diagnostics
754
+ case *ast.ExprStmt:
755
+ text, diagnostics := o.lowerExpr(ctx, typed.X)
756
+ return []loweredStmt{{text: text}}, diagnostics
757
+ case *ast.ReturnStmt:
758
+ text, diagnostics := o.lowerReturnStmt(ctx, typed)
759
+ return []loweredStmt{{text: text}}, diagnostics
760
+ case *ast.IfStmt:
761
+ var diagnostics []Diagnostic
762
+ var init []loweredStmt
763
+ if typed.Init != nil {
764
+ initStmts, initDiagnostics := o.lowerStmt(ctx, typed.Init)
765
+ diagnostics = append(diagnostics, initDiagnostics...)
766
+ init = append(init, initStmts...)
767
+ }
768
+ cond, condDiagnostics := o.lowerExpr(ctx, typed.Cond)
769
+ diagnostics = append(diagnostics, condDiagnostics...)
770
+ body, bodyDiagnostics := o.lowerBlock(ctx, typed.Body)
771
+ diagnostics = append(diagnostics, bodyDiagnostics...)
772
+ stmt := loweredStmt{
773
+ text: "if (" + cond + ")",
774
+ children: body,
775
+ }
776
+ if typed.Else != nil {
777
+ elseBody, elseDiagnostics := o.lowerElse(ctx, typed.Else)
778
+ diagnostics = append(diagnostics, elseDiagnostics...)
779
+ stmt.elseBody = elseBody
780
+ }
781
+ if len(init) != 0 {
782
+ init = append(init, stmt)
783
+ return []loweredStmt{{children: init}}, diagnostics
784
+ }
785
+ return []loweredStmt{stmt}, diagnostics
786
+ case *ast.ForStmt:
787
+ lowered, diagnostics := o.lowerForStmt(ctx, typed)
788
+ return []loweredStmt{lowered}, diagnostics
789
+ case *ast.RangeStmt:
790
+ lowered, diagnostics := o.lowerRangeStmt(ctx, typed)
791
+ return []loweredStmt{lowered}, diagnostics
792
+ case *ast.SelectStmt:
793
+ lowered, diagnostics := o.lowerSelectStmt(ctx, typed)
794
+ return []loweredStmt{{selectStmt: lowered}}, diagnostics
795
+ case *ast.SwitchStmt:
796
+ return o.lowerSwitchStmt(ctx, typed)
797
+ case *ast.TypeSwitchStmt:
798
+ return o.lowerTypeSwitchStmt(ctx, typed)
799
+ case *ast.IncDecStmt:
800
+ expr, diagnostics := o.lowerExpr(ctx, typed.X)
801
+ return []loweredStmt{{text: expr + typed.Tok.String()}}, diagnostics
802
+ case *ast.BranchStmt:
803
+ if typed.Label != nil {
804
+ return nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported labeled branch")}
805
+ }
806
+ switch typed.Tok {
807
+ case token.BREAK, token.CONTINUE:
808
+ return []loweredStmt{{text: typed.Tok.String()}}, nil
809
+ default:
810
+ return nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported branch")}
811
+ }
812
+ default:
813
+ return nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported statement kind")}
814
+ }
815
+ }
816
+
817
+ func (o *LoweringOwner) lowerElse(ctx lowerFileContext, stmt ast.Stmt) ([]loweredStmt, []Diagnostic) {
818
+ switch typed := stmt.(type) {
819
+ case *ast.BlockStmt:
820
+ return o.lowerBlock(ctx, typed)
821
+ case *ast.IfStmt:
822
+ return o.lowerStmt(ctx, typed)
823
+ default:
824
+ return nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported else statement")}
825
+ }
826
+ }
827
+
828
+ func (o *LoweringOwner) lowerStmtList(ctx lowerFileContext, stmts []ast.Stmt) ([]loweredStmt, []Diagnostic) {
829
+ return o.lowerStmtListAfter(ctx, stmts, 0)
830
+ }
831
+
832
+ func (o *LoweringOwner) lowerStmtListAfter(
833
+ ctx lowerFileContext,
834
+ stmts []ast.Stmt,
835
+ prevEndLine int,
836
+ ) ([]loweredStmt, []Diagnostic) {
837
+ lowered := make([]loweredStmt, 0, len(stmts))
838
+ var diagnostics []Diagnostic
839
+ for _, stmt := range stmts {
840
+ startLine := sourceLine(ctx, stmt.Pos())
841
+ leading := leadingStmtLines(ctx, prevEndLine, startLine)
842
+ stmtLowered, stmtDiagnostics := o.lowerStmt(ctx, stmt)
843
+ diagnostics = append(diagnostics, stmtDiagnostics...)
844
+ if len(stmtLowered) != 0 && len(leading) != 0 {
845
+ stmtLowered[0].leading = append(leading, stmtLowered[0].leading...)
846
+ }
847
+ lowered = append(lowered, stmtLowered...)
848
+ if endLine := sourceLine(ctx, stmt.End()); endLine != 0 {
849
+ prevEndLine = endLine
850
+ }
851
+ }
852
+ return lowered, diagnostics
853
+ }
854
+
855
+ func leadingStmtLines(ctx lowerFileContext, prevEndLine int, startLine int) []string {
856
+ if prevEndLine == 0 || startLine == 0 || startLine <= prevEndLine+1 {
857
+ return nil
858
+ }
859
+ if ctx.file == nil || ctx.semPkg == nil || ctx.semPkg.source == nil || ctx.semPkg.source.Fset == nil {
860
+ return []string{""}
861
+ }
862
+
863
+ var lines []string
864
+ lastLine := prevEndLine
865
+ for _, group := range ctx.file.Comments {
866
+ groupStart := sourceLine(ctx, group.Pos())
867
+ groupEnd := sourceLine(ctx, group.End())
868
+ if groupStart <= prevEndLine || groupEnd >= startLine {
869
+ continue
870
+ }
871
+ if groupStart > lastLine+1 {
872
+ lines = append(lines, "")
873
+ }
874
+ for _, comment := range group.List {
875
+ for line := range strings.SplitSeq(comment.Text, "\n") {
876
+ lines = append(lines, line)
877
+ }
878
+ }
879
+ lastLine = groupEnd
880
+ }
881
+ if startLine > lastLine+1 {
882
+ lines = append(lines, "")
883
+ }
884
+ return lines
885
+ }
886
+
887
+ func sourceLine(ctx lowerFileContext, pos token.Pos) int {
888
+ if ctx.semPkg == nil || ctx.semPkg.source == nil || ctx.semPkg.source.Fset == nil || !pos.IsValid() {
889
+ return 0
890
+ }
891
+ return ctx.semPkg.source.Fset.Position(pos).Line
892
+ }
893
+
894
+ func (o *LoweringOwner) lowerSendStmt(ctx lowerFileContext, stmt *ast.SendStmt) (string, []Diagnostic) {
895
+ channel, channelDiagnostics := o.lowerExpr(ctx, stmt.Chan)
896
+ value, valueDiagnostics := o.lowerExpr(ctx, stmt.Value)
897
+ diagnostics := append(channelDiagnostics, valueDiagnostics...)
898
+ return "await " + o.runtimeOwner.QualifiedHelper(RuntimeHelperChanSend) + "(" + channel + ", " + value + ")", diagnostics
899
+ }
900
+
901
+ func (o *LoweringOwner) lowerGoStmt(ctx lowerFileContext, stmt *ast.GoStmt) (string, []Diagnostic) {
902
+ call, diagnostics := o.lowerCallExpr(ctx, stmt.Call)
903
+ return "queueMicrotask(async () => { " + call + " })", diagnostics
904
+ }
905
+
906
+ func (o *LoweringOwner) lowerDeferStmt(ctx lowerFileContext, stmt *ast.DeferStmt) (string, []Diagnostic) {
907
+ call, diagnostics := o.lowerCallExpr(ctx, stmt.Call)
908
+ async := strings.Contains(call, "await ")
909
+ if ctx.deferState != nil {
910
+ ctx.deferState.used = true
911
+ if async {
912
+ ctx.deferState.async = true
913
+ }
914
+ }
915
+ if async {
916
+ return "__defer.defer(async () => { " + call + " })", diagnostics
917
+ }
918
+ return "__defer.defer(() => { " + call + " })", diagnostics
919
+ }
920
+
921
+ func (o *LoweringOwner) lowerAssignStmt(ctx lowerFileContext, stmt *ast.AssignStmt) ([]loweredStmt, []Diagnostic) {
922
+ if len(stmt.Rhs) == 1 && isChannelReceiveExpr(stmt.Rhs[0]) {
923
+ return o.lowerChannelReceiveAssignStmt(ctx, stmt)
924
+ }
925
+ if len(stmt.Lhs) == 1 && len(stmt.Rhs) == 1 {
926
+ if ident, ok := stmt.Lhs[0].(*ast.Ident); ok && ident.Name == "_" {
927
+ right, diagnostics := o.lowerExpr(ctx, stmt.Rhs[0])
928
+ return []loweredStmt{{text: right}}, diagnostics
929
+ }
930
+ }
931
+ if len(stmt.Rhs) == 1 && len(stmt.Lhs) > 1 {
932
+ if allBlankIdents(stmt.Lhs) {
933
+ right, diagnostics := o.lowerTupleExpr(ctx, stmt.Rhs[0])
934
+ return []loweredStmt{{text: right}}, diagnostics
935
+ }
936
+ right, diagnostics := o.lowerTupleExpr(ctx, stmt.Rhs[0])
937
+ lefts := make([]string, 0, len(stmt.Lhs))
938
+ for _, lhs := range stmt.Lhs {
939
+ if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "_" {
940
+ lefts = append(lefts, "")
941
+ continue
942
+ }
943
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, lhs, stmt.Tok == token.DEFINE)
944
+ diagnostics = append(diagnostics, leftDiagnostics...)
945
+ lefts = append(lefts, left)
946
+ }
947
+ prefix := ""
948
+ if stmt.Tok == token.DEFINE {
949
+ prefix = "let "
950
+ if !allShortAssignTargetsNew(ctx, stmt.Lhs) || tupleDeclarationNeedsElementStatements(ctx, stmt.Lhs) {
951
+ return o.lowerTupleReassignmentStmt(ctx, stmt, right, diagnostics)
952
+ }
953
+ return []loweredStmt{{text: prefix + "[" + strings.Join(lefts, ", ") + "] = " + right}}, diagnostics
954
+ }
955
+ return o.lowerTupleReassignmentStmt(ctx, stmt, right, diagnostics)
956
+ }
957
+ if len(stmt.Rhs) > 1 && len(stmt.Lhs) == len(stmt.Rhs) && stmt.Tok == token.ASSIGN {
958
+ return o.lowerParallelAssignStmt(ctx, stmt)
959
+ }
960
+
961
+ stmts := make([]loweredStmt, 0, len(stmt.Lhs))
962
+ var diagnostics []Diagnostic
963
+ for idx, lhs := range stmt.Lhs {
964
+ if idx >= len(stmt.Rhs) {
965
+ break
966
+ }
967
+ if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "_" {
968
+ continue
969
+ }
970
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, lhs, stmt.Tok == token.DEFINE)
971
+ right, rightDiagnostics := o.lowerExpr(ctx, stmt.Rhs[idx])
972
+ diagnostics = append(diagnostics, leftDiagnostics...)
973
+ diagnostics = append(diagnostics, rightDiagnostics...)
974
+ targetType := ctx.semPkg.source.TypesInfo.TypeOf(lhs)
975
+ right = o.lowerValueForTarget(ctx, stmt.Rhs[idx], targetType, right)
976
+ if index, ok := lhs.(*ast.IndexExpr); ok && isMapType(ctx.semPkg.source.TypesInfo.TypeOf(index.X)) && stmt.Tok == token.ASSIGN {
977
+ mapExpr, mapDiagnostics := o.lowerExpr(ctx, index.X)
978
+ keyExpr, keyDiagnostics := o.lowerExpr(ctx, index.Index)
979
+ diagnostics = append(diagnostics, mapDiagnostics...)
980
+ diagnostics = append(diagnostics, keyDiagnostics...)
981
+ stmts = append(stmts, loweredStmt{text: o.runtimeOwner.QualifiedHelper(RuntimeHelperMapSet) + "(" + mapExpr + ", " + keyExpr + ", " + right + ")"})
982
+ continue
983
+ }
984
+ if star, ok := lhs.(*ast.StarExpr); ok && stmt.Tok == token.ASSIGN && isStructValueType(targetType) {
985
+ pointer, pointerDiagnostics := o.lowerPointerValueExpr(ctx, star.X)
986
+ diagnostics = append(diagnostics, pointerDiagnostics...)
987
+ stmts = append(stmts, loweredStmt{text: o.runtimeOwner.QualifiedHelper(RuntimeHelperAssignStruct) + "(" + pointer + ", " + right + ")"})
988
+ continue
989
+ }
990
+ if star, ok := lhs.(*ast.StarExpr); ok && stmt.Tok == token.ASSIGN {
991
+ pointer, pointerDiagnostics := o.lowerPointerStorageExpr(ctx, star.X)
992
+ diagnostics = append(diagnostics, pointerDiagnostics...)
993
+ stmts = append(stmts, loweredStmt{text: pointer + " = " + right})
994
+ continue
995
+ }
996
+ if stmt.Tok == token.DEFINE {
997
+ if ident, ok := lhs.(*ast.Ident); ok {
998
+ right = o.lowerDeclaredValue(ctx, ident, right)
999
+ }
1000
+ stmts = append(stmts, loweredStmt{text: "let " + left + " = " + right})
1001
+ continue
1002
+ }
1003
+ stmts = append(stmts, loweredStmt{text: left + " " + stmt.Tok.String() + " " + right})
1004
+ }
1005
+ return stmts, diagnostics
1006
+ }
1007
+
1008
+ func (o *LoweringOwner) lowerChannelReceiveAssignStmt(
1009
+ ctx lowerFileContext,
1010
+ stmt *ast.AssignStmt,
1011
+ ) ([]loweredStmt, []Diagnostic) {
1012
+ if len(stmt.Rhs) != 1 {
1013
+ return nil, nil
1014
+ }
1015
+ receive, ok := stmt.Rhs[0].(*ast.UnaryExpr)
1016
+ if !ok || receive.Op != token.ARROW {
1017
+ return nil, nil
1018
+ }
1019
+ channel, diagnostics := o.lowerExpr(ctx, receive.X)
1020
+ if len(stmt.Lhs) == 1 {
1021
+ if ident, ok := stmt.Lhs[0].(*ast.Ident); ok && ident.Name == "_" {
1022
+ return []loweredStmt{{text: "await " + o.runtimeOwner.QualifiedHelper(RuntimeHelperChanRecv) + "(" + channel + ")"}}, diagnostics
1023
+ }
1024
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, stmt.Lhs[0], stmt.Tok == token.DEFINE)
1025
+ diagnostics = append(diagnostics, leftDiagnostics...)
1026
+ prefix := ""
1027
+ value := "await " + o.runtimeOwner.QualifiedHelper(RuntimeHelperChanRecv) + "(" + channel + ")"
1028
+ if stmt.Tok == token.DEFINE {
1029
+ prefix = "let "
1030
+ value = o.lowerDeclaredValue(ctx, stmt.Lhs[0], value)
1031
+ }
1032
+ return []loweredStmt{{text: prefix + left + " = " + value}}, diagnostics
1033
+ }
1034
+ tempName := "__goscriptRecv" + strconv.Itoa(int(stmt.Pos()))
1035
+ stmts := []loweredStmt{{text: "let " + tempName + " = await " + o.runtimeOwner.QualifiedHelper(RuntimeHelperChanRecvWithOk) + "(" + channel + ")"}}
1036
+ if allBlankIdents(stmt.Lhs) {
1037
+ return stmts, diagnostics
1038
+ }
1039
+ fields := []string{".value", ".ok"}
1040
+ for idx, lhs := range stmt.Lhs {
1041
+ if idx >= len(fields) {
1042
+ break
1043
+ }
1044
+ if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "_" {
1045
+ continue
1046
+ }
1047
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, lhs, stmt.Tok == token.DEFINE && isShortAssignTargetNew(ctx, lhs))
1048
+ diagnostics = append(diagnostics, leftDiagnostics...)
1049
+ prefix := ""
1050
+ value := tempName + fields[idx]
1051
+ if stmt.Tok == token.DEFINE && isShortAssignTargetNew(ctx, lhs) {
1052
+ prefix = "let "
1053
+ value = o.lowerDeclaredValue(ctx, lhs, value)
1054
+ }
1055
+ stmts = append(stmts, loweredStmt{text: prefix + left + " = " + value})
1056
+ }
1057
+ return stmts, diagnostics
1058
+ }
1059
+
1060
+ func (o *LoweringOwner) lowerTupleReassignmentStmt(
1061
+ ctx lowerFileContext,
1062
+ stmt *ast.AssignStmt,
1063
+ right string,
1064
+ diagnostics []Diagnostic,
1065
+ ) ([]loweredStmt, []Diagnostic) {
1066
+ tempName := "__goscriptTuple" + strconv.Itoa(int(stmt.Pos()))
1067
+ stmts := []loweredStmt{{text: "let " + tempName + " = " + right}}
1068
+ for idx, lhs := range stmt.Lhs {
1069
+ if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "_" {
1070
+ continue
1071
+ }
1072
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, lhs, stmt.Tok == token.DEFINE && isShortAssignTargetNew(ctx, lhs))
1073
+ diagnostics = append(diagnostics, leftDiagnostics...)
1074
+ prefix := ""
1075
+ value := tempName + "[" + strconv.Itoa(idx) + "]"
1076
+ if stmt.Tok == token.DEFINE && isShortAssignTargetNew(ctx, lhs) {
1077
+ prefix = "let "
1078
+ value = o.lowerDeclaredValue(ctx, lhs, value)
1079
+ }
1080
+ stmts = append(stmts, loweredStmt{text: prefix + left + " = " + value})
1081
+ }
1082
+ return stmts, diagnostics
1083
+ }
1084
+
1085
+ func (o *LoweringOwner) lowerDeclaredValue(ctx lowerFileContext, lhs ast.Expr, value string) string {
1086
+ ident, ok := lhs.(*ast.Ident)
1087
+ if !ok {
1088
+ return value
1089
+ }
1090
+ obj := ctx.semPkg.source.TypesInfo.Defs[ident]
1091
+ if obj == nil || !ctx.model.needsVarRef[obj] {
1092
+ return value
1093
+ }
1094
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperVarRef) + "(" + value + ")"
1095
+ }
1096
+
1097
+ func (o *LoweringOwner) lowerParallelAssignStmt(ctx lowerFileContext, stmt *ast.AssignStmt) ([]loweredStmt, []Diagnostic) {
1098
+ stmts := make([]loweredStmt, 0, len(stmt.Rhs)*2)
1099
+ var diagnostics []Diagnostic
1100
+ tempPrefix := "__goscriptAssign" + strconv.Itoa(int(stmt.Pos())) + "_"
1101
+ for idx, rhs := range stmt.Rhs {
1102
+ right, rightDiagnostics := o.lowerExpr(ctx, rhs)
1103
+ diagnostics = append(diagnostics, rightDiagnostics...)
1104
+ stmts = append(stmts, loweredStmt{text: "let " + tempPrefix + strconv.Itoa(idx) + " = " + right})
1105
+ }
1106
+ for idx, lhs := range stmt.Lhs {
1107
+ if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "_" {
1108
+ continue
1109
+ }
1110
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, lhs, false)
1111
+ diagnostics = append(diagnostics, leftDiagnostics...)
1112
+ stmts = append(stmts, loweredStmt{text: left + " = " + tempPrefix + strconv.Itoa(idx)})
1113
+ }
1114
+ return stmts, diagnostics
1115
+ }
1116
+
1117
+ func allShortAssignTargetsNew(ctx lowerFileContext, exprs []ast.Expr) bool {
1118
+ for _, expr := range exprs {
1119
+ if ident, ok := expr.(*ast.Ident); ok && ident.Name == "_" {
1120
+ continue
1121
+ }
1122
+ if !isShortAssignTargetNew(ctx, expr) {
1123
+ return false
1124
+ }
1125
+ }
1126
+ return true
1127
+ }
1128
+
1129
+ func tupleDeclarationNeedsElementStatements(ctx lowerFileContext, exprs []ast.Expr) bool {
1130
+ for _, expr := range exprs {
1131
+ ident, ok := expr.(*ast.Ident)
1132
+ if !ok || ident.Name == "_" {
1133
+ continue
1134
+ }
1135
+ obj := ctx.semPkg.source.TypesInfo.Defs[ident]
1136
+ if obj != nil && ctx.model.needsVarRef[obj] {
1137
+ return true
1138
+ }
1139
+ }
1140
+ return false
1141
+ }
1142
+
1143
+ func isShortAssignTargetNew(ctx lowerFileContext, expr ast.Expr) bool {
1144
+ ident, ok := expr.(*ast.Ident)
1145
+ if !ok {
1146
+ return false
1147
+ }
1148
+ return ctx.semPkg.source.TypesInfo.Defs[ident] != nil
1149
+ }
1150
+
1151
+ func allBlankIdents(exprs []ast.Expr) bool {
1152
+ if len(exprs) == 0 {
1153
+ return false
1154
+ }
1155
+ for _, expr := range exprs {
1156
+ ident, ok := expr.(*ast.Ident)
1157
+ if !ok || ident.Name != "_" {
1158
+ return false
1159
+ }
1160
+ }
1161
+ return true
1162
+ }
1163
+
1164
+ func isChannelReceiveExpr(expr ast.Expr) bool {
1165
+ receive, ok := expr.(*ast.UnaryExpr)
1166
+ return ok && receive.Op == token.ARROW
1167
+ }
1168
+
1169
+ func (o *LoweringOwner) lowerReturnStmt(ctx lowerFileContext, stmt *ast.ReturnStmt) (string, []Diagnostic) {
1170
+ if len(stmt.Results) == 0 {
1171
+ return "return", nil
1172
+ }
1173
+ if len(stmt.Results) == 1 {
1174
+ expr, diagnostics := o.lowerExpr(ctx, stmt.Results[0])
1175
+ if returnType := singleReturnType(ctx); returnType != nil {
1176
+ expr = o.lowerValueForTarget(ctx, stmt.Results[0], returnType, expr)
1177
+ }
1178
+ return "return " + expr, diagnostics
1179
+ }
1180
+ parts := make([]string, 0, len(stmt.Results))
1181
+ var diagnostics []Diagnostic
1182
+ for _, result := range stmt.Results {
1183
+ expr, exprDiagnostics := o.lowerExpr(ctx, result)
1184
+ diagnostics = append(diagnostics, exprDiagnostics...)
1185
+ parts = append(parts, expr)
1186
+ }
1187
+ return "return [" + strings.Join(parts, ", ") + "]", diagnostics
1188
+ }
1189
+
1190
+ func singleReturnType(ctx lowerFileContext) types.Type {
1191
+ if ctx.signature == nil || ctx.signature.Results() == nil || ctx.signature.Results().Len() != 1 {
1192
+ return nil
1193
+ }
1194
+ return ctx.signature.Results().At(0).Type()
1195
+ }
1196
+
1197
+ func (o *LoweringOwner) lowerForStmt(ctx lowerFileContext, stmt *ast.ForStmt) (loweredStmt, []Diagnostic) {
1198
+ if stmt.Init == nil && stmt.Post == nil {
1199
+ cond := "true"
1200
+ var diagnostics []Diagnostic
1201
+ if stmt.Cond != nil {
1202
+ var condDiagnostics []Diagnostic
1203
+ cond, condDiagnostics = o.lowerExpr(ctx, stmt.Cond)
1204
+ diagnostics = append(diagnostics, condDiagnostics...)
1205
+ }
1206
+ body, bodyDiagnostics := o.lowerBlock(ctx, stmt.Body)
1207
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1208
+ return loweredStmt{
1209
+ text: "while (" + cond + ")",
1210
+ children: body,
1211
+ }, diagnostics
1212
+ }
1213
+
1214
+ init := ""
1215
+ var diagnostics []Diagnostic
1216
+ if stmt.Init != nil {
1217
+ lowered, initDiagnostics := o.lowerStmt(ctx, stmt.Init)
1218
+ diagnostics = append(diagnostics, initDiagnostics...)
1219
+ if len(lowered) != 0 {
1220
+ init = strings.TrimSuffix(lowered[0].text, ";")
1221
+ }
1222
+ }
1223
+ cond := ""
1224
+ if stmt.Cond != nil {
1225
+ var condDiagnostics []Diagnostic
1226
+ cond, condDiagnostics = o.lowerExpr(ctx, stmt.Cond)
1227
+ diagnostics = append(diagnostics, condDiagnostics...)
1228
+ }
1229
+ post := ""
1230
+ if stmt.Post != nil {
1231
+ lowered, postDiagnostics := o.lowerStmt(ctx, stmt.Post)
1232
+ diagnostics = append(diagnostics, postDiagnostics...)
1233
+ if len(lowered) != 0 {
1234
+ post = strings.TrimSuffix(lowered[0].text, ";")
1235
+ }
1236
+ }
1237
+ body, bodyDiagnostics := o.lowerBlock(ctx, stmt.Body)
1238
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1239
+ return loweredStmt{
1240
+ text: "for (" + init + "; " + cond + "; " + post + ")",
1241
+ children: body,
1242
+ }, diagnostics
1243
+ }
1244
+
1245
+ func (o *LoweringOwner) lowerRangeStmt(ctx lowerFileContext, stmt *ast.RangeStmt) (loweredStmt, []Diagnostic) {
1246
+ rangeValue, diagnostics := o.lowerExpr(ctx, stmt.X)
1247
+ body, bodyDiagnostics := o.lowerBlock(ctx, stmt.Body)
1248
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1249
+
1250
+ keyName := rangeKeyName(stmt.Key)
1251
+ valueName := rangeKeyName(stmt.Value)
1252
+ rangeType := ctx.semPkg.source.TypesInfo.TypeOf(stmt.X)
1253
+ if isChannelType(rangeType) {
1254
+ tempName := "__goscriptRange" + strconv.Itoa(int(stmt.Pos()))
1255
+ children := []loweredStmt{
1256
+ {text: "let " + tempName + " = await " + o.runtimeOwner.QualifiedHelper(RuntimeHelperChanRecvWithOk) + "(" + rangeValue + ")"},
1257
+ {text: "if (!" + tempName + ".ok)", children: []loweredStmt{{text: "break"}}},
1258
+ }
1259
+ if keyName != "" {
1260
+ prefix := ""
1261
+ if stmt.Tok == token.DEFINE {
1262
+ prefix = "let "
1263
+ }
1264
+ children = append(children, loweredStmt{text: prefix + keyName + " = " + tempName + ".value"})
1265
+ }
1266
+ children = append(children, body...)
1267
+ return loweredStmt{
1268
+ text: "while (true)",
1269
+ children: children,
1270
+ }, diagnostics
1271
+ }
1272
+ if isIntegerRangeType(rangeType) {
1273
+ if keyName == "" {
1274
+ keyName = "__rangeIndex"
1275
+ }
1276
+ return loweredStmt{
1277
+ text: "for (let " + keyName + " = 0; " + keyName + " < " + rangeValue + "; " + keyName + "++)",
1278
+ children: body,
1279
+ }, diagnostics
1280
+ }
1281
+ if isMapType(rangeType) {
1282
+ key := keyName
1283
+ if key == "" {
1284
+ key = "__rangeKey"
1285
+ }
1286
+ value := valueName
1287
+ if value == "" {
1288
+ value = "__rangeValue"
1289
+ }
1290
+ return loweredStmt{
1291
+ text: "for (const [" + key + ", " + value + "] of " + rangeValue + "?.entries() ?? [])",
1292
+ children: body,
1293
+ }, diagnostics
1294
+ }
1295
+ if isFunctionType(rangeType) {
1296
+ signature := rangeFunctionSignature(rangeType)
1297
+ if signature == nil {
1298
+ return loweredStmt{}, append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported function range signature"))
1299
+ }
1300
+ if rangeFuncBodyHasUnsupportedControl(stmt.Body) {
1301
+ return loweredStmt{}, append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported control flow in function range body"))
1302
+ }
1303
+ lowered, funcDiagnostics := o.lowerRangeFuncStmt(ctx, stmt, rangeValue, signature)
1304
+ diagnostics = append(diagnostics, funcDiagnostics...)
1305
+ return lowered, diagnostics
1306
+ }
1307
+
1308
+ indexName := keyName
1309
+ if indexName == "" {
1310
+ indexName = "__rangeIndex"
1311
+ }
1312
+ children := body
1313
+ if valueName != "" {
1314
+ indexTarget := lowerIndexTarget(rangeValue, rangeType)
1315
+ children = append([]loweredStmt{{text: "let " + valueName + " = " + indexTarget + "[" + indexName + "]"}}, body...)
1316
+ }
1317
+ return loweredStmt{
1318
+ text: "for (let " + indexName + " = 0; " + indexName + " < " + o.runtimeOwner.QualifiedHelper(RuntimeHelperLen) + "(" + rangeValue + "); " + indexName + "++)",
1319
+ children: children,
1320
+ }, diagnostics
1321
+ }
1322
+
1323
+ func (o *LoweringOwner) lowerRangeFuncStmt(
1324
+ ctx lowerFileContext,
1325
+ stmt *ast.RangeStmt,
1326
+ rangeValue string,
1327
+ signature *types.Signature,
1328
+ ) (loweredStmt, []Diagnostic) {
1329
+ yieldSignature, ok := types.Unalias(signature.Params().At(0).Type()).Underlying().(*types.Signature)
1330
+ if !ok {
1331
+ return loweredStmt{}, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported function range yield signature")}
1332
+ }
1333
+ keyName := rangeKeyName(stmt.Key)
1334
+ valueName := rangeKeyName(stmt.Value)
1335
+ paramKeyName := keyName
1336
+ paramValueName := valueName
1337
+ if stmt.Tok != token.DEFINE {
1338
+ paramKeyName = ""
1339
+ paramValueName = ""
1340
+ }
1341
+ paramNames := rangeFuncParamNames(paramKeyName, paramValueName, yieldSignature.Params().Len(), int(stmt.Pos()))
1342
+
1343
+ body, diagnostics := o.lowerBlock(ctx, stmt.Body)
1344
+ if stmt.Tok != token.DEFINE {
1345
+ assignments, assignmentDiagnostics := o.lowerRangeFuncAssignments(ctx, stmt, paramNames)
1346
+ diagnostics = append(diagnostics, assignmentDiagnostics...)
1347
+ body = append(assignments, body...)
1348
+ }
1349
+
1350
+ return loweredStmt{rangeFunc: &loweredRangeFunc{
1351
+ value: rangeValue,
1352
+ params: paramNames,
1353
+ body: body,
1354
+ }}, diagnostics
1355
+ }
1356
+
1357
+ func (o *LoweringOwner) lowerRangeFuncAssignments(
1358
+ ctx lowerFileContext,
1359
+ stmt *ast.RangeStmt,
1360
+ paramNames []string,
1361
+ ) ([]loweredStmt, []Diagnostic) {
1362
+ var diagnostics []Diagnostic
1363
+ var assignments []loweredStmt
1364
+ for idx, expr := range []ast.Expr{stmt.Key, stmt.Value} {
1365
+ if expr == nil || idx >= len(paramNames) {
1366
+ continue
1367
+ }
1368
+ if ident, ok := expr.(*ast.Ident); ok && ident.Name == "_" {
1369
+ continue
1370
+ }
1371
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, expr, false)
1372
+ diagnostics = append(diagnostics, leftDiagnostics...)
1373
+ assignments = append(assignments, loweredStmt{text: left + " = " + paramNames[idx]})
1374
+ }
1375
+ return assignments, diagnostics
1376
+ }
1377
+
1378
+ func (o *LoweringOwner) lowerSelectStmt(ctx lowerFileContext, stmt *ast.SelectStmt) (*loweredSelect, []Diagnostic) {
1379
+ resultType := "void"
1380
+ if ctx.signature != nil {
1381
+ resultType = o.tsSignatureResultFor(ctx, ctx.signature)
1382
+ }
1383
+ lowered := &loweredSelect{
1384
+ hasReturn: "__goscriptSelectHasReturn" + strconv.Itoa(int(stmt.Pos())),
1385
+ value: "__goscriptSelectValue" + strconv.Itoa(int(stmt.Pos())),
1386
+ resultType: resultType,
1387
+ }
1388
+ var diagnostics []Diagnostic
1389
+ caseID := 0
1390
+ for _, raw := range stmt.Body.List {
1391
+ clause, ok := raw.(*ast.CommClause)
1392
+ if !ok {
1393
+ diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported select clause"))
1394
+ continue
1395
+ }
1396
+ switch comm := clause.Comm.(type) {
1397
+ case nil:
1398
+ body, bodyDiagnostics := o.lowerStmtList(ctx.withLocalScope(), clause.Body)
1399
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1400
+ lowered.hasDefault = true
1401
+ lowered.cases = append(lowered.cases, loweredSelectCase{
1402
+ id: -1,
1403
+ channel: "null",
1404
+ body: body,
1405
+ })
1406
+ case *ast.SendStmt:
1407
+ channel, channelDiagnostics := o.lowerExpr(ctx, comm.Chan)
1408
+ value, valueDiagnostics := o.lowerExpr(ctx, comm.Value)
1409
+ body, bodyDiagnostics := o.lowerStmtList(ctx.withLocalScope(), clause.Body)
1410
+ diagnostics = append(diagnostics, channelDiagnostics...)
1411
+ diagnostics = append(diagnostics, valueDiagnostics...)
1412
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1413
+ lowered.cases = append(lowered.cases, loweredSelectCase{
1414
+ id: caseID,
1415
+ isSend: true,
1416
+ channel: channel,
1417
+ value: value,
1418
+ body: body,
1419
+ })
1420
+ caseID++
1421
+ case *ast.ExprStmt:
1422
+ channel, prelude, receiveDiagnostics := o.lowerSelectReceiveComm(ctx, nil, comm.X)
1423
+ body, bodyDiagnostics := o.lowerStmtList(ctx.withLocalScope(), clause.Body)
1424
+ diagnostics = append(diagnostics, receiveDiagnostics...)
1425
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1426
+ lowered.cases = append(lowered.cases, loweredSelectCase{
1427
+ id: caseID,
1428
+ channel: channel,
1429
+ prelude: prelude,
1430
+ body: body,
1431
+ })
1432
+ caseID++
1433
+ case *ast.AssignStmt:
1434
+ channel, prelude, receiveDiagnostics := o.lowerSelectReceiveComm(ctx, comm, nil)
1435
+ body, bodyDiagnostics := o.lowerStmtList(ctx.withLocalScope(), clause.Body)
1436
+ diagnostics = append(diagnostics, receiveDiagnostics...)
1437
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1438
+ lowered.cases = append(lowered.cases, loweredSelectCase{
1439
+ id: caseID,
1440
+ channel: channel,
1441
+ prelude: prelude,
1442
+ body: body,
1443
+ })
1444
+ caseID++
1445
+ default:
1446
+ diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported select communication"))
1447
+ }
1448
+ }
1449
+ return lowered, diagnostics
1450
+ }
1451
+
1452
+ func (o *LoweringOwner) lowerSelectReceiveComm(
1453
+ ctx lowerFileContext,
1454
+ assign *ast.AssignStmt,
1455
+ expr ast.Expr,
1456
+ ) (string, []loweredStmt, []Diagnostic) {
1457
+ receiveExpr := expr
1458
+ if assign != nil && len(assign.Rhs) == 1 {
1459
+ receiveExpr = assign.Rhs[0]
1460
+ }
1461
+ receive, ok := receiveExpr.(*ast.UnaryExpr)
1462
+ if !ok || receive.Op != token.ARROW {
1463
+ return "null", nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported select receive")}
1464
+ }
1465
+ channel, diagnostics := o.lowerExpr(ctx, receive.X)
1466
+ if assign == nil {
1467
+ return channel, nil, diagnostics
1468
+ }
1469
+ prelude := make([]loweredStmt, 0, len(assign.Lhs))
1470
+ fields := []string{".value", ".ok"}
1471
+ for idx, lhs := range assign.Lhs {
1472
+ if idx >= len(fields) {
1473
+ break
1474
+ }
1475
+ if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "_" {
1476
+ continue
1477
+ }
1478
+ left, leftDiagnostics := o.lowerAssignmentTarget(ctx, lhs, assign.Tok == token.DEFINE && isShortAssignTargetNew(ctx, lhs))
1479
+ diagnostics = append(diagnostics, leftDiagnostics...)
1480
+ prefix := ""
1481
+ if assign.Tok == token.DEFINE && isShortAssignTargetNew(ctx, lhs) {
1482
+ prefix = "let "
1483
+ }
1484
+ prelude = append(prelude, loweredStmt{text: prefix + left + " = result" + fields[idx]})
1485
+ }
1486
+ return channel, prelude, diagnostics
1487
+ }
1488
+
1489
+ func (o *LoweringOwner) lowerSwitchStmt(ctx lowerFileContext, stmt *ast.SwitchStmt) ([]loweredStmt, []Diagnostic) {
1490
+ var diagnostics []Diagnostic
1491
+ var init []loweredStmt
1492
+ if stmt.Init != nil {
1493
+ lowered, initDiagnostics := o.lowerStmt(ctx, stmt.Init)
1494
+ diagnostics = append(diagnostics, initDiagnostics...)
1495
+ init = append(init, lowered...)
1496
+ }
1497
+
1498
+ value := "true"
1499
+ if stmt.Tag != nil {
1500
+ var valueDiagnostics []Diagnostic
1501
+ value, valueDiagnostics = o.lowerExpr(ctx, stmt.Tag)
1502
+ diagnostics = append(diagnostics, valueDiagnostics...)
1503
+ }
1504
+
1505
+ switchIR := &loweredSwitch{value: value}
1506
+ for _, raw := range stmt.Body.List {
1507
+ clause, ok := raw.(*ast.CaseClause)
1508
+ if !ok {
1509
+ diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported switch clause"))
1510
+ continue
1511
+ }
1512
+ body, bodyDiagnostics := o.lowerStmtList(ctx.withLocalScope(), clause.Body)
1513
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1514
+ if len(clause.List) == 0 {
1515
+ switchIR.defaultBody = body
1516
+ continue
1517
+ }
1518
+
1519
+ values := make([]string, 0, len(clause.List))
1520
+ for _, expr := range clause.List {
1521
+ lowered, exprDiagnostics := o.lowerExpr(ctx, expr)
1522
+ diagnostics = append(diagnostics, exprDiagnostics...)
1523
+ values = append(values, lowered)
1524
+ }
1525
+ switchIR.cases = append(switchIR.cases, loweredSwitchCase{
1526
+ values: values,
1527
+ body: body,
1528
+ })
1529
+ }
1530
+
1531
+ lowered := loweredStmt{switchStmt: switchIR}
1532
+ if len(init) == 0 {
1533
+ return []loweredStmt{lowered}, diagnostics
1534
+ }
1535
+ init = append(init, lowered)
1536
+ return []loweredStmt{{children: init}}, diagnostics
1537
+ }
1538
+
1539
+ func (o *LoweringOwner) lowerTypeSwitchStmt(ctx lowerFileContext, stmt *ast.TypeSwitchStmt) ([]loweredStmt, []Diagnostic) {
1540
+ var lowered []loweredStmt
1541
+ var diagnostics []Diagnostic
1542
+ if stmt.Init != nil {
1543
+ init, initDiagnostics := o.lowerStmt(ctx, stmt.Init)
1544
+ diagnostics = append(diagnostics, initDiagnostics...)
1545
+ lowered = append(lowered, init...)
1546
+ }
1547
+
1548
+ valueExpr, varName, assignDiagnostics := o.lowerTypeSwitchAssign(ctx, stmt.Assign)
1549
+ diagnostics = append(diagnostics, assignDiagnostics...)
1550
+ if valueExpr == "" {
1551
+ return lowered, append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported type switch assignment"))
1552
+ }
1553
+
1554
+ switchIR := &loweredTypeSwitch{
1555
+ value: valueExpr,
1556
+ varName: varName,
1557
+ }
1558
+ for _, clauseStmt := range stmt.Body.List {
1559
+ clause, ok := clauseStmt.(*ast.CaseClause)
1560
+ if !ok {
1561
+ diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported type switch clause"))
1562
+ continue
1563
+ }
1564
+ body, bodyDiagnostics := o.lowerStmtList(ctx, clause.Body)
1565
+ diagnostics = append(diagnostics, bodyDiagnostics...)
1566
+ if len(clause.List) == 0 {
1567
+ switchIR.defaultBody = body
1568
+ continue
1569
+ }
1570
+ types := make([]string, 0, len(clause.List))
1571
+ for _, expr := range clause.List {
1572
+ types = append(types, o.runtimeTypeInfoExpr(ctx.semPkg.source.TypesInfo.TypeOf(expr)))
1573
+ }
1574
+ switchIR.cases = append(switchIR.cases, loweredTypeSwitchCase{
1575
+ types: types,
1576
+ body: body,
1577
+ })
1578
+ }
1579
+ lowered = append(lowered, loweredStmt{typeSwitch: switchIR})
1580
+ return lowered, diagnostics
1581
+ }
1582
+
1583
+ func (o *LoweringOwner) lowerTypeSwitchAssign(ctx lowerFileContext, stmt ast.Stmt) (string, string, []Diagnostic) {
1584
+ switch typed := stmt.(type) {
1585
+ case *ast.ExprStmt:
1586
+ return o.lowerTypeSwitchGuard(ctx, typed.X, "")
1587
+ case *ast.AssignStmt:
1588
+ if len(typed.Lhs) != 1 || len(typed.Rhs) != 1 {
1589
+ return "", "", nil
1590
+ }
1591
+ varName := ""
1592
+ if ident, ok := typed.Lhs[0].(*ast.Ident); ok && ident.Name != "_" {
1593
+ varName = safeIdentifier(ident.Name)
1594
+ }
1595
+ return o.lowerTypeSwitchGuard(ctx, typed.Rhs[0], varName)
1596
+ default:
1597
+ return "", "", nil
1598
+ }
1599
+ }
1600
+
1601
+ func (o *LoweringOwner) lowerTypeSwitchGuard(ctx lowerFileContext, expr ast.Expr, varName string) (string, string, []Diagnostic) {
1602
+ assertion, ok := expr.(*ast.TypeAssertExpr)
1603
+ if !ok || assertion.Type != nil {
1604
+ return "", "", nil
1605
+ }
1606
+ value, diagnostics := o.lowerExpr(ctx, assertion.X)
1607
+ return value, varName, diagnostics
1608
+ }
1609
+
1610
+ func rangeKeyName(expr ast.Expr) string {
1611
+ ident, ok := expr.(*ast.Ident)
1612
+ if !ok || ident.Name == "_" {
1613
+ return ""
1614
+ }
1615
+ return safeIdentifier(ident.Name)
1616
+ }
1617
+
1618
+ func rangeFunctionSignature(typ types.Type) *types.Signature {
1619
+ signature, ok := types.Unalias(typ).Underlying().(*types.Signature)
1620
+ if !ok || signature.Params() == nil || signature.Params().Len() != 1 {
1621
+ return nil
1622
+ }
1623
+ yieldSignature, ok := types.Unalias(signature.Params().At(0).Type()).Underlying().(*types.Signature)
1624
+ if !ok || yieldSignature.Params() == nil || yieldSignature.Results() == nil {
1625
+ return nil
1626
+ }
1627
+ if yieldSignature.Params().Len() > 2 || yieldSignature.Results().Len() != 1 {
1628
+ return nil
1629
+ }
1630
+ if !isBoolType(yieldSignature.Results().At(0).Type()) {
1631
+ return nil
1632
+ }
1633
+ return signature
1634
+ }
1635
+
1636
+ func isFunctionType(typ types.Type) bool {
1637
+ if typ == nil {
1638
+ return false
1639
+ }
1640
+ _, ok := types.Unalias(typ).Underlying().(*types.Signature)
1641
+ return ok
1642
+ }
1643
+
1644
+ func isBoolType(typ types.Type) bool {
1645
+ basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
1646
+ return ok && basic.Kind() == types.Bool
1647
+ }
1648
+
1649
+ func rangeFuncParamNames(keyName, valueName string, arity int, pos int) []string {
1650
+ names := make([]string, 0, arity)
1651
+ if arity >= 1 {
1652
+ name := keyName
1653
+ if name == "" {
1654
+ name = "__goscriptRange" + strconv.Itoa(pos) + "_0"
1655
+ }
1656
+ names = append(names, name)
1657
+ }
1658
+ if arity >= 2 {
1659
+ name := valueName
1660
+ if name == "" {
1661
+ name = "__goscriptRange" + strconv.Itoa(pos) + "_1"
1662
+ }
1663
+ names = append(names, name)
1664
+ }
1665
+ return names
1666
+ }
1667
+
1668
+ func rangeFuncBodyHasUnsupportedControl(body *ast.BlockStmt) bool {
1669
+ unsupported := false
1670
+ ast.Inspect(body, func(node ast.Node) bool {
1671
+ if node == nil || unsupported {
1672
+ return !unsupported
1673
+ }
1674
+ if _, ok := node.(*ast.FuncLit); ok {
1675
+ return false
1676
+ }
1677
+ switch node.(type) {
1678
+ case *ast.BranchStmt, *ast.ReturnStmt:
1679
+ unsupported = true
1680
+ return false
1681
+ default:
1682
+ return true
1683
+ }
1684
+ })
1685
+ return unsupported
1686
+ }
1687
+
1688
+ func isIntegerRangeType(typ types.Type) bool {
1689
+ basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
1690
+ return ok && basic.Info()&types.IsInteger != 0
1691
+ }
1692
+
1693
+ func (o *LoweringOwner) lowerExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
1694
+ switch typed := expr.(type) {
1695
+ case *ast.BasicLit:
1696
+ return lowerBasicLit(typed), nil
1697
+ case *ast.Ident:
1698
+ return o.lowerIdent(ctx, typed, false), nil
1699
+ case *ast.BinaryExpr:
1700
+ left, leftDiagnostics := o.lowerExpr(ctx, typed.X)
1701
+ right, rightDiagnostics := o.lowerExpr(ctx, typed.Y)
1702
+ return left + " " + typed.Op.String() + " " + right, append(leftDiagnostics, rightDiagnostics...)
1703
+ case *ast.UnaryExpr:
1704
+ if typed.Op == token.AND {
1705
+ return o.lowerAddressExpr(ctx, typed.X)
1706
+ }
1707
+ if typed.Op == token.ARROW {
1708
+ value, diagnostics := o.lowerExpr(ctx, typed.X)
1709
+ return "await " + o.runtimeOwner.QualifiedHelper(RuntimeHelperChanRecv) + "(" + value + ")", diagnostics
1710
+ }
1711
+ value, diagnostics := o.lowerExpr(ctx, typed.X)
1712
+ if typed.Op == token.NOT || typed.Op == token.SUB || typed.Op == token.ADD {
1713
+ return typed.Op.String() + value, diagnostics
1714
+ }
1715
+ return value, append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported unary operator"))
1716
+ case *ast.StarExpr:
1717
+ return o.lowerPointerValueExpr(ctx, typed.X)
1718
+ case *ast.ParenExpr:
1719
+ value, diagnostics := o.lowerExpr(ctx, typed.X)
1720
+ return "(" + value + ")", diagnostics
1721
+ case *ast.CallExpr:
1722
+ return o.lowerCallExpr(ctx, typed)
1723
+ case *ast.FuncLit:
1724
+ value, _, diagnostics := o.lowerFuncLit(ctx, typed)
1725
+ return value, diagnostics
1726
+ case *ast.SelectorExpr:
1727
+ return o.lowerSelectorExpr(ctx, typed)
1728
+ case *ast.IndexExpr:
1729
+ return o.lowerIndexExpr(ctx, typed)
1730
+ case *ast.SliceExpr:
1731
+ return o.lowerSliceExpr(ctx, typed)
1732
+ case *ast.CompositeLit:
1733
+ return o.lowerCompositeLit(ctx, typed, true)
1734
+ case *ast.TypeAssertExpr:
1735
+ return o.lowerTypeAssertExpr(ctx, typed)
1736
+ case *ast.IndexListExpr:
1737
+ return o.lowerExpr(ctx, typed.X)
1738
+ default:
1739
+ return "undefined", []Diagnostic{loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported expression kind")}
1740
+ }
1741
+ }
1742
+
1743
+ func lowerBasicLit(lit *ast.BasicLit) string {
1744
+ if lit.Kind == token.CHAR {
1745
+ value, err := strconv.Unquote(lit.Value)
1746
+ if err != nil || value == "" {
1747
+ return "0"
1748
+ }
1749
+ return strconv.FormatInt(int64([]rune(value)[0]), 10)
1750
+ }
1751
+ return lit.Value
1752
+ }
1753
+
1754
+ func (o *LoweringOwner) lowerFuncLit(ctx lowerFileContext, lit *ast.FuncLit) (string, bool, []Diagnostic) {
1755
+ signature, _ := ctx.semPkg.source.TypesInfo.TypeOf(lit).(*types.Signature)
1756
+ deferState := &loweredDeferState{}
1757
+ body, diagnostics := o.lowerBlock(ctx.withSignature(signature).withDeferState(deferState), lit.Body)
1758
+ var rendered strings.Builder
1759
+ renderDeferStack(&rendered, deferState, 1)
1760
+ renderStmts(&rendered, body, 1)
1761
+ async := stmtsContainAwait(body) || deferState.async
1762
+ prefix := ""
1763
+ if async {
1764
+ prefix = "async "
1765
+ }
1766
+ function := prefix + "(" + o.tsSignatureParamsFor(ctx, signature) + "): " +
1767
+ asyncResultType(o.tsSignatureResultFor(ctx, signature), async) + " => {\n" +
1768
+ rendered.String() + "}"
1769
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperFunctionValue) +
1770
+ "(" + function + ", " + o.runtimeFunctionTypeInfo(signature, "") + ")", async, diagnostics
1771
+ }
1772
+
1773
+ func stmtsContainAwait(stmts []loweredStmt) bool {
1774
+ for _, stmt := range stmts {
1775
+ if strings.Contains(stmt.text, "await ") ||
1776
+ stmtsContainAwait(stmt.children) ||
1777
+ stmtsContainAwait(stmt.elseBody) {
1778
+ return true
1779
+ }
1780
+ if stmt.selectStmt != nil {
1781
+ for _, switchCase := range stmt.selectStmt.cases {
1782
+ if stmtsContainAwait(switchCase.prelude) || stmtsContainAwait(switchCase.body) {
1783
+ return true
1784
+ }
1785
+ }
1786
+ }
1787
+ if stmt.typeSwitch != nil {
1788
+ for _, switchCase := range stmt.typeSwitch.cases {
1789
+ if stmtsContainAwait(switchCase.body) {
1790
+ return true
1791
+ }
1792
+ }
1793
+ if stmtsContainAwait(stmt.typeSwitch.defaultBody) {
1794
+ return true
1795
+ }
1796
+ }
1797
+ }
1798
+ return false
1799
+ }
1800
+
1801
+ func lowerIdent(ident *ast.Ident) string {
1802
+ switch ident.Name {
1803
+ case "nil":
1804
+ return "null"
1805
+ case "true", "false":
1806
+ return ident.Name
1807
+ default:
1808
+ return safeIdentifier(ident.Name)
1809
+ }
1810
+ }
1811
+
1812
+ func (o *LoweringOwner) lowerIdent(ctx lowerFileContext, ident *ast.Ident, raw bool) string {
1813
+ value := lowerIdent(ident)
1814
+ if raw || ident.Name == "nil" || ident.Name == "true" || ident.Name == "false" {
1815
+ return value
1816
+ }
1817
+ obj := objectForIdent(ctx, ident)
1818
+ if alias := ctx.localAliases[obj]; alias != "" {
1819
+ return alias + "." + value
1820
+ }
1821
+ if obj != nil && ctx.model.needsVarRef[obj] {
1822
+ return value + ".value"
1823
+ }
1824
+ return value
1825
+ }
1826
+
1827
+ func objectForIdent(ctx lowerFileContext, ident *ast.Ident) types.Object {
1828
+ if ctx.semPkg == nil || ctx.semPkg.source == nil {
1829
+ return nil
1830
+ }
1831
+ if obj := ctx.semPkg.source.TypesInfo.Uses[ident]; obj != nil {
1832
+ return obj
1833
+ }
1834
+ return ctx.semPkg.source.TypesInfo.Defs[ident]
1835
+ }
1836
+
1837
+ func (o *LoweringOwner) lowerCallExpr(ctx lowerFileContext, expr *ast.CallExpr) (string, []Diagnostic) {
1838
+ if ident, ok := expr.Fun.(*ast.Ident); ok {
1839
+ switch ident.Name {
1840
+ case "make":
1841
+ return o.lowerMakeExpr(ctx, expr)
1842
+ case "new":
1843
+ return o.lowerNewExpr(ctx, expr)
1844
+ }
1845
+ }
1846
+ if targetType := typeFromExpr(ctx, expr.Fun); targetType != nil {
1847
+ return o.lowerConversionExpr(ctx, expr, targetType)
1848
+ }
1849
+
1850
+ args, diagnostics := o.lowerCallArgs(ctx, expr, callTargetSignature(ctx, expr.Fun))
1851
+
1852
+ switch fun := expr.Fun.(type) {
1853
+ case *ast.Ident:
1854
+ switch fun.Name {
1855
+ case "println", "print":
1856
+ helper := RuntimeHelperPrintln
1857
+ if fun.Name == "print" {
1858
+ helper = RuntimeHelperPrint
1859
+ }
1860
+ return o.runtimeOwner.QualifiedHelper(helper) + "(" + strings.Join(args, ", ") + ")", diagnostics
1861
+ case "byte":
1862
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperByte) + "(" + strings.Join(args, ", ") + ")", diagnostics
1863
+ case "append":
1864
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperAppend) + "(" + strings.Join(args, ", ") + ")", diagnostics
1865
+ case "cap":
1866
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperCap) + "(" + strings.Join(args, ", ") + ")", diagnostics
1867
+ case "clear":
1868
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperClear) + "(" + strings.Join(args, ", ") + ")", diagnostics
1869
+ case "copy":
1870
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperCopy) + "(" + strings.Join(args, ", ") + ")", diagnostics
1871
+ case "delete":
1872
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperDeleteMapEntry) + "(" + strings.Join(args, ", ") + ")", diagnostics
1873
+ case "int":
1874
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperInt) + "(" + strings.Join(args, ", ") + ")", diagnostics
1875
+ case "len":
1876
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperLen) + "(" + strings.Join(args, ", ") + ")", diagnostics
1877
+ case "max":
1878
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMax) + "(" + strings.Join(args, ", ") + ")", diagnostics
1879
+ case "min":
1880
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMin) + "(" + strings.Join(args, ", ") + ")", diagnostics
1881
+ case "panic":
1882
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperPanic) + "(" + strings.Join(args, ", ") + ")", diagnostics
1883
+ case "recover":
1884
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperRecover) + "(" + strings.Join(args, ", ") + ")", diagnostics
1885
+ case "string":
1886
+ return "String(" + strings.Join(args, ", ") + ")", diagnostics
1887
+ case "close":
1888
+ if len(args) != 1 {
1889
+ return "undefined", append(diagnostics, loweringUnsupported("call", ctx.semPkg.pkgPath, "close requires one argument"))
1890
+ }
1891
+ return args[0] + "!.close()", diagnostics
1892
+ default:
1893
+ if signature := genericFunctionSignature(ctx, fun); signature != nil {
1894
+ args = append([]string{o.inferredGenericTypeArgsExpr(ctx, signature, expr.Args)}, args...)
1895
+ }
1896
+ callee := o.lowerCallableExpr(ctx, fun, o.lowerIdent(ctx, fun, false))
1897
+ call := callee + "(" + strings.Join(args, ", ") + ")"
1898
+ return o.awaitCallIfNeeded(ctx, fun, call), diagnostics
1899
+ }
1900
+ case *ast.SelectorExpr:
1901
+ if selection := ctx.semPkg.source.TypesInfo.Selections[fun]; selection != nil && selection.Kind() == types.MethodVal {
1902
+ if typeParam := receiverTypeParam(selection.Recv()); typeParam != nil {
1903
+ receiverExpr, receiverDiagnostics := o.lowerExpr(ctx, fun.X)
1904
+ diagnostics = append(diagnostics, receiverDiagnostics...)
1905
+ methodArgs := append([]string{"__typeArgs", strconv.Quote(typeParam.Obj().Name()), strconv.Quote(fun.Sel.Name), receiverExpr}, args...)
1906
+ call := o.runtimeOwner.QualifiedHelper(RuntimeHelperCallGenericMethod) + "(" + strings.Join(methodArgs, ", ") + ")"
1907
+ return o.awaitCallIfNeeded(ctx, fun, call), diagnostics
1908
+ }
1909
+ receiver := receiverNamedType(selection.Recv())
1910
+ if namedNonInterfaceNonStructType(receiver) {
1911
+ return o.lowerNamedReceiverMethodCall(ctx, fun, args, diagnostics)
1912
+ }
1913
+ receiverExpr, receiverDiagnostics := o.lowerMethodReceiverExpr(ctx, fun.X, selection)
1914
+ diagnostics = append(diagnostics, receiverDiagnostics...)
1915
+ call := receiverExpr + "." + fun.Sel.Name + "(" + strings.Join(args, ", ") + ")"
1916
+ return o.awaitCallIfNeeded(ctx, fun, call), diagnostics
1917
+ }
1918
+ selector, selectorDiagnostics := o.lowerSelectorExpr(ctx, fun)
1919
+ call := o.lowerCallableExpr(ctx, fun, selector) + "(" + strings.Join(args, ", ") + ")"
1920
+ return o.awaitCallIfNeeded(ctx, fun, call), append(diagnostics, selectorDiagnostics...)
1921
+ case *ast.IndexExpr:
1922
+ if signature, _ := ctx.semPkg.source.TypesInfo.TypeOf(fun).(*types.Signature); signature != nil {
1923
+ callee, calleeDiagnostics := o.lowerExpr(ctx, fun.X)
1924
+ args = append([]string{o.genericTypeArgsExpr(ctx, fun.X, []ast.Expr{fun.Index})}, args...)
1925
+ call := o.lowerCallableExpr(ctx, fun.X, callee) + "(" + strings.Join(args, ", ") + ")"
1926
+ return o.awaitCallIfNeeded(ctx, fun, call), append(diagnostics, calleeDiagnostics...)
1927
+ }
1928
+ case *ast.IndexListExpr:
1929
+ if signature, _ := ctx.semPkg.source.TypesInfo.TypeOf(fun).(*types.Signature); signature != nil {
1930
+ callee, calleeDiagnostics := o.lowerExpr(ctx, fun.X)
1931
+ args = append([]string{o.genericTypeArgsExpr(ctx, fun.X, fun.Indices)}, args...)
1932
+ call := o.lowerCallableExpr(ctx, fun.X, callee) + "(" + strings.Join(args, ", ") + ")"
1933
+ return o.awaitCallIfNeeded(ctx, fun, call), append(diagnostics, calleeDiagnostics...)
1934
+ }
1935
+ case *ast.CallExpr:
1936
+ callee, calleeDiagnostics := o.lowerCallExpr(ctx, fun)
1937
+ if strings.HasPrefix(callee, "await ") {
1938
+ callee = "(" + callee + ")"
1939
+ }
1940
+ callee = o.lowerCallableExpr(ctx, fun, callee)
1941
+ return callee + "(" + strings.Join(args, ", ") + ")", append(diagnostics, calleeDiagnostics...)
1942
+ case *ast.FuncLit:
1943
+ callee, async, calleeDiagnostics := o.lowerFuncLit(ctx, fun)
1944
+ call := "(" + callee + ")(" + strings.Join(args, ", ") + ")"
1945
+ if async {
1946
+ call = "await " + call
1947
+ }
1948
+ return call, append(diagnostics, calleeDiagnostics...)
1949
+ default:
1950
+ if callTargetSignature(ctx, expr.Fun) != nil {
1951
+ callee, calleeDiagnostics := o.lowerExpr(ctx, expr.Fun)
1952
+ if strings.HasPrefix(callee, "await ") {
1953
+ callee = "(" + callee + ")"
1954
+ }
1955
+ call := o.lowerCallableExpr(ctx, expr.Fun, callee) + "(" + strings.Join(args, ", ") + ")"
1956
+ return o.awaitCallIfNeeded(ctx, expr.Fun, call), append(diagnostics, calleeDiagnostics...)
1957
+ }
1958
+ return "undefined", append(diagnostics, loweringUnsupported("call", ctx.semPkg.pkgPath, "unsupported call target"))
1959
+ }
1960
+ return "undefined", append(diagnostics, loweringUnsupported("call", ctx.semPkg.pkgPath, "unsupported call target"))
1961
+ }
1962
+
1963
+ func (o *LoweringOwner) lowerCallableExpr(ctx lowerFileContext, expr ast.Expr, callee string) string {
1964
+ if callTargetNeedsNonNull(ctx, expr) {
1965
+ return callee + "!"
1966
+ }
1967
+ return callee
1968
+ }
1969
+
1970
+ func (o *LoweringOwner) lowerCallArgs(
1971
+ ctx lowerFileContext,
1972
+ expr *ast.CallExpr,
1973
+ signature *types.Signature,
1974
+ ) ([]string, []Diagnostic) {
1975
+ if signature == nil || !signature.Variadic() ||
1976
+ isBuiltinCallTarget(ctx, expr.Fun) ||
1977
+ o.variadicCallUsesOverrideRest(ctx, expr.Fun) {
1978
+ return o.lowerExprList(ctx, expr.Args)
1979
+ }
1980
+ params := signature.Params()
1981
+ if params == nil || params.Len() == 0 {
1982
+ return o.lowerExprList(ctx, expr.Args)
1983
+ }
1984
+
1985
+ fixedCount := params.Len() - 1
1986
+ args := make([]string, 0, params.Len())
1987
+ var variadicArgs []string
1988
+ var diagnostics []Diagnostic
1989
+ for idx, arg := range expr.Args {
1990
+ lowered, argDiagnostics := o.lowerExpr(ctx, arg)
1991
+ diagnostics = append(diagnostics, argDiagnostics...)
1992
+ if idx < fixedCount {
1993
+ args = append(args, lowered)
1994
+ continue
1995
+ }
1996
+ if expr.Ellipsis != token.NoPos && idx == len(expr.Args)-1 {
1997
+ args = append(args, lowered)
1998
+ continue
1999
+ }
2000
+ variadicArgs = append(variadicArgs, lowered)
2001
+ }
2002
+ if len(expr.Args) < fixedCount || (expr.Ellipsis != token.NoPos && len(args) == params.Len()) {
2003
+ return args, diagnostics
2004
+ }
2005
+ if len(variadicArgs) == 0 {
2006
+ args = append(args, "null")
2007
+ return args, diagnostics
2008
+ }
2009
+
2010
+ elemType := "any"
2011
+ if slice, ok := types.Unalias(params.At(fixedCount).Type()).Underlying().(*types.Slice); ok {
2012
+ elemType = o.tsTypeFor(ctx, slice.Elem())
2013
+ }
2014
+ args = append(args, o.runtimeOwner.QualifiedHelper(RuntimeHelperArrayToSlice)+
2015
+ "<"+elemType+">(["+strings.Join(variadicArgs, ", ")+"])")
2016
+ return args, diagnostics
2017
+ }
2018
+
2019
+ func (o *LoweringOwner) lowerExprList(ctx lowerFileContext, exprs []ast.Expr) ([]string, []Diagnostic) {
2020
+ args := make([]string, 0, len(exprs))
2021
+ var diagnostics []Diagnostic
2022
+ for _, expr := range exprs {
2023
+ lowered, exprDiagnostics := o.lowerExpr(ctx, expr)
2024
+ diagnostics = append(diagnostics, exprDiagnostics...)
2025
+ args = append(args, lowered)
2026
+ }
2027
+ return args, diagnostics
2028
+ }
2029
+
2030
+ func callTargetSignature(ctx lowerFileContext, expr ast.Expr) *types.Signature {
2031
+ if ctx.semPkg == nil || ctx.semPkg.source == nil {
2032
+ return nil
2033
+ }
2034
+ typ := ctx.semPkg.source.TypesInfo.TypeOf(expr)
2035
+ if typ == nil {
2036
+ return nil
2037
+ }
2038
+ if signature, ok := typ.(*types.Signature); ok {
2039
+ return signature
2040
+ }
2041
+ signature, _ := types.Unalias(typ).Underlying().(*types.Signature)
2042
+ return signature
2043
+ }
2044
+
2045
+ func callTargetNeedsNonNull(ctx lowerFileContext, expr ast.Expr) bool {
2046
+ if callTargetSignature(ctx, expr) == nil {
2047
+ return false
2048
+ }
2049
+ switch typed := expr.(type) {
2050
+ case *ast.FuncLit:
2051
+ return false
2052
+ case *ast.Ident:
2053
+ switch objectForIdent(ctx, typed).(type) {
2054
+ case *types.Func, *types.Builtin:
2055
+ return false
2056
+ default:
2057
+ return true
2058
+ }
2059
+ case *ast.SelectorExpr:
2060
+ if selection := ctx.semPkg.source.TypesInfo.Selections[typed]; selection != nil && selection.Kind() == types.MethodVal {
2061
+ return false
2062
+ }
2063
+ if _, ok := objectForIdent(ctx, typed.Sel).(*types.Func); ok {
2064
+ return false
2065
+ }
2066
+ return true
2067
+ default:
2068
+ return true
2069
+ }
2070
+ }
2071
+
2072
+ func isBuiltinCallTarget(ctx lowerFileContext, expr ast.Expr) bool {
2073
+ ident, ok := expr.(*ast.Ident)
2074
+ if !ok {
2075
+ return false
2076
+ }
2077
+ _, ok = objectForIdent(ctx, ident).(*types.Builtin)
2078
+ return ok
2079
+ }
2080
+
2081
+ func (o *LoweringOwner) variadicCallUsesOverrideRest(ctx lowerFileContext, expr ast.Expr) bool {
2082
+ if o.overrideOwner == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
2083
+ return false
2084
+ }
2085
+ fn := calledFunction(ctx.semPkg.source, expr)
2086
+ if fn == nil || fn.Pkg() == nil {
2087
+ return false
2088
+ }
2089
+ _, ok := o.overrideOwner.importPackageRoot(fn.Pkg().Path())
2090
+ return ok
2091
+ }
2092
+
2093
+ func (o *LoweringOwner) lowerMakeExpr(ctx lowerFileContext, expr *ast.CallExpr) (string, []Diagnostic) {
2094
+ if len(expr.Args) < 1 {
2095
+ return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "make requires a type argument")}
2096
+ }
2097
+ targetType := typeFromExpr(ctx, expr.Args[0])
2098
+ if targetType == nil {
2099
+ return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "make requires a type expression")}
2100
+ }
2101
+ switch typed := types.Unalias(targetType).Underlying().(type) {
2102
+ case *types.Slice:
2103
+ length := "0"
2104
+ capacity := ""
2105
+ var diagnostics []Diagnostic
2106
+ if len(expr.Args) >= 2 {
2107
+ var lengthDiagnostics []Diagnostic
2108
+ length, lengthDiagnostics = o.lowerExpr(ctx, expr.Args[1])
2109
+ diagnostics = append(diagnostics, lengthDiagnostics...)
2110
+ }
2111
+ if len(expr.Args) >= 3 {
2112
+ var capacityDiagnostics []Diagnostic
2113
+ capacity, capacityDiagnostics = o.lowerExpr(ctx, expr.Args[2])
2114
+ diagnostics = append(diagnostics, capacityDiagnostics...)
2115
+ }
2116
+ args := []string{length}
2117
+ if capacity != "" {
2118
+ args = append(args, capacity)
2119
+ }
2120
+ if hint := sliceTypeHint(typed.Elem()); hint != "" {
2121
+ if capacity == "" {
2122
+ args = append(args, "undefined")
2123
+ }
2124
+ args = append(args, strconv.Quote(hint))
2125
+ }
2126
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMakeSlice) +
2127
+ "<" + o.tsTypeFor(ctx, typed.Elem()) + ">(" + strings.Join(args, ", ") + ")", diagnostics
2128
+ case *types.Map:
2129
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMakeMap) +
2130
+ "<" + o.tsTypeFor(ctx, typed.Key()) + ", " + o.tsTypeFor(ctx, typed.Elem()) + ">()", nil
2131
+ case *types.Chan:
2132
+ capacity := "0"
2133
+ var diagnostics []Diagnostic
2134
+ if len(expr.Args) >= 2 {
2135
+ var capacityDiagnostics []Diagnostic
2136
+ capacity, capacityDiagnostics = o.lowerExpr(ctx, expr.Args[1])
2137
+ diagnostics = append(diagnostics, capacityDiagnostics...)
2138
+ }
2139
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMakeChannel) +
2140
+ "<" + o.tsTypeFor(ctx, typed.Elem()) + ">(" + capacity + ", " +
2141
+ o.lowerZeroValueExprFor(ctx, typed.Elem()) + ", " + strconv.Quote(channelDirectionString(typed.Dir())) + ")", diagnostics
2142
+ default:
2143
+ return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "unsupported make type")}
2144
+ }
2145
+ }
2146
+
2147
+ func (o *LoweringOwner) lowerNewExpr(ctx lowerFileContext, expr *ast.CallExpr) (string, []Diagnostic) {
2148
+ if len(expr.Args) != 1 {
2149
+ return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "new requires one type argument")}
2150
+ }
2151
+ typ := typeFromExpr(ctx, expr.Args[0])
2152
+ if named := namedStructType(typ); named != nil {
2153
+ return "new " + o.namedTypeExpr(ctx, named) + "()", nil
2154
+ }
2155
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperVarRef) + "(" + o.lowerZeroValueExprFor(ctx, typ) + ")", nil
2156
+ }
2157
+
2158
+ func (o *LoweringOwner) lowerConversionExpr(
2159
+ ctx lowerFileContext,
2160
+ expr *ast.CallExpr,
2161
+ targetType types.Type,
2162
+ ) (string, []Diagnostic) {
2163
+ if len(expr.Args) != 1 {
2164
+ return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "unsupported conversion arity")}
2165
+ }
2166
+ value, diagnostics := o.lowerExpr(ctx, expr.Args[0])
2167
+ sourceType := ctx.semPkg.source.TypesInfo.TypeOf(expr.Args[0])
2168
+ if isNilExpr(expr.Args[0]) && isPointerType(targetType) {
2169
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperTypedNil) +
2170
+ "(" + strconv.Quote(goRuntimeTypeString(targetType)) + ")", diagnostics
2171
+ }
2172
+ if isInterfaceType(targetType) {
2173
+ return o.lowerValueForTarget(ctx, expr.Args[0], targetType, value), diagnostics
2174
+ }
2175
+ if isStringType(targetType) {
2176
+ switch {
2177
+ case isRuneSliceType(sourceType):
2178
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperRunesToString) + "(" + value + ")", diagnostics
2179
+ case isByteSliceType(sourceType):
2180
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperBytesToString) + "(" + value + ")", diagnostics
2181
+ case isStringType(sourceType):
2182
+ return value, diagnostics
2183
+ case isNumericType(sourceType):
2184
+ return "String.fromCodePoint(" + value + ")", diagnostics
2185
+ default:
2186
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperGenericBytesOrStringToString) + "(" + value + ")", diagnostics
2187
+ }
2188
+ }
2189
+ if isRuneSliceType(targetType) && isStringType(sourceType) {
2190
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperStringToRunes) + "(" + value + ")", diagnostics
2191
+ }
2192
+ if isByteSliceType(targetType) && isStringType(sourceType) {
2193
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperStringToBytes) + "(" + value + ")", diagnostics
2194
+ }
2195
+ if named := namedFunctionType(targetType); named != nil {
2196
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperNamedFunction) +
2197
+ "(" + value + ", " + strconv.Quote(runtimeNamedTypeName(named)) + ")", diagnostics
2198
+ }
2199
+ if named := namedNonStructType(targetType); named != nil {
2200
+ return value, diagnostics
2201
+ }
2202
+ if isNumericType(targetType) {
2203
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperInt) + "(" + value + ")", diagnostics
2204
+ }
2205
+ return value, diagnostics
2206
+ }
2207
+
2208
+ func (o *LoweringOwner) lowerNamedReceiverMethodCall(
2209
+ ctx lowerFileContext,
2210
+ selector *ast.SelectorExpr,
2211
+ args []string,
2212
+ diagnostics []Diagnostic,
2213
+ ) (string, []Diagnostic) {
2214
+ selection := ctx.semPkg.source.TypesInfo.Selections[selector]
2215
+ receiver := receiverNamedType(selection.Recv())
2216
+ receiverExpr, receiverDiagnostics := o.lowerNamedReceiverForMethod(ctx, selector.X, selection)
2217
+ diagnostics = append(diagnostics, receiverDiagnostics...)
2218
+ allArgs := append([]string{receiverExpr}, args...)
2219
+ call := o.methodFunctionExpr(ctx, receiver, selection.Obj(), selector.Sel.Name) + "(" + strings.Join(allArgs, ", ") + ")"
2220
+ return o.awaitCallIfNeeded(ctx, selector, call), diagnostics
2221
+ }
2222
+
2223
+ func (o *LoweringOwner) lowerNamedReceiverForMethod(
2224
+ ctx lowerFileContext,
2225
+ expr ast.Expr,
2226
+ selection *types.Selection,
2227
+ ) (string, []Diagnostic) {
2228
+ method, _ := selection.Obj().(*types.Func)
2229
+ receiverPointer := false
2230
+ if method != nil {
2231
+ signature, _ := method.Type().(*types.Signature)
2232
+ if signature != nil && signature.Recv() != nil {
2233
+ _, receiverPointer = signature.Recv().Type().(*types.Pointer)
2234
+ }
2235
+ }
2236
+ if receiverPointer {
2237
+ if ident, ok := expr.(*ast.Ident); ok {
2238
+ if obj := objectForIdent(ctx, ident); obj != nil && ctx.model.needsVarRef[obj] {
2239
+ return o.lowerIdent(ctx, ident, true), nil
2240
+ }
2241
+ }
2242
+ return o.lowerAddressExpr(ctx, expr)
2243
+ }
2244
+ return o.lowerExpr(ctx, expr)
2245
+ }
2246
+
2247
+ func (o *LoweringOwner) lowerSelectorExpr(ctx lowerFileContext, expr *ast.SelectorExpr) (string, []Diagnostic) {
2248
+ if ident, ok := expr.X.(*ast.Ident); ok {
2249
+ if _, ok := ctx.importAliases[ident.Name]; ok {
2250
+ return ident.Name + "." + expr.Sel.Name, nil
2251
+ }
2252
+ }
2253
+ if selection := ctx.semPkg.source.TypesInfo.Selections[expr]; selection != nil {
2254
+ switch selection.Kind() {
2255
+ case types.MethodVal:
2256
+ if receiver := receiverNamedType(selection.Recv()); namedNonInterfaceNonStructType(receiver) {
2257
+ receiverExpr, diagnostics := o.lowerNamedReceiverForMethod(ctx, expr.X, selection)
2258
+ methodExpr := o.methodFunctionExpr(ctx, receiver, selection.Obj(), expr.Sel.Name)
2259
+ return o.lowerMethodValueClosure(ctx, selection, receiverExpr, methodExpr, true), diagnostics
2260
+ }
2261
+ receiver, diagnostics := o.lowerMethodReceiverExpr(ctx, expr.X, selection)
2262
+ return o.lowerMethodValueClosure(ctx, selection, receiver, "__receiver."+expr.Sel.Name, false), diagnostics
2263
+ case types.FieldVal:
2264
+ receiver, diagnostics := o.lowerFieldReceiverExpr(ctx, expr.X)
2265
+ return receiver + "." + expr.Sel.Name, diagnostics
2266
+ }
2267
+ }
2268
+ left, diagnostics := o.lowerExpr(ctx, expr.X)
2269
+ return left + "." + expr.Sel.Name, diagnostics
2270
+ }
2271
+
2272
+ func (o *LoweringOwner) lowerMethodValueClosure(
2273
+ ctx lowerFileContext,
2274
+ selection *types.Selection,
2275
+ receiver string,
2276
+ callee string,
2277
+ includeReceiver bool,
2278
+ ) string {
2279
+ signature, _ := selection.Type().(*types.Signature)
2280
+ var params []string
2281
+ var args []string
2282
+ if signature != nil && signature.Params() != nil {
2283
+ params = make([]string, 0, signature.Params().Len())
2284
+ args = make([]string, 0, signature.Params().Len())
2285
+ for idx := range signature.Params().Len() {
2286
+ param := signature.Params().At(idx)
2287
+ name := safeParamName(param, idx)
2288
+ params = append(params, name+": "+o.tsTypeFor(ctx, param.Type()))
2289
+ args = append(args, name)
2290
+ }
2291
+ }
2292
+ if includeReceiver {
2293
+ args = append([]string{"__receiver"}, args...)
2294
+ }
2295
+ return "((__receiver) => (" + strings.Join(params, ", ") + ") => " + callee + "(" + strings.Join(args, ", ") + "))(" + receiver + ")"
2296
+ }
2297
+
2298
+ func (o *LoweringOwner) lowerFieldReceiverExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
2299
+ if isPointerToStructType(ctx.semPkg.source.TypesInfo.TypeOf(expr)) {
2300
+ return o.lowerPointerValueExpr(ctx, expr)
2301
+ }
2302
+ return o.lowerExpr(ctx, expr)
2303
+ }
2304
+
2305
+ func (o *LoweringOwner) lowerMethodReceiverExpr(
2306
+ ctx lowerFileContext,
2307
+ expr ast.Expr,
2308
+ selection *types.Selection,
2309
+ ) (string, []Diagnostic) {
2310
+ fn, _ := selection.Obj().(*types.Func)
2311
+ receiverPointer := false
2312
+ if fn != nil {
2313
+ signature, _ := fn.Type().(*types.Signature)
2314
+ if signature != nil && signature.Recv() != nil {
2315
+ _, receiverPointer = signature.Recv().Type().(*types.Pointer)
2316
+ }
2317
+ }
2318
+
2319
+ var receiver string
2320
+ var diagnostics []Diagnostic
2321
+ if isPointerToStructType(ctx.semPkg.source.TypesInfo.TypeOf(expr)) {
2322
+ receiver, diagnostics = o.lowerPointerValueExpr(ctx, expr)
2323
+ } else {
2324
+ receiver, diagnostics = o.lowerExpr(ctx, expr)
2325
+ }
2326
+ if receiverPointer {
2327
+ return receiver, diagnostics
2328
+ }
2329
+ if isStructValueType(ctx.semPkg.source.TypesInfo.TypeOf(expr)) ||
2330
+ isPointerToStructType(ctx.semPkg.source.TypesInfo.TypeOf(expr)) {
2331
+ return o.lowerStructClone(receiver), diagnostics
2332
+ }
2333
+ if isInterfaceType(ctx.semPkg.source.TypesInfo.TypeOf(expr)) {
2334
+ return receiver + "!", diagnostics
2335
+ }
2336
+ return receiver, diagnostics
2337
+ }
2338
+
2339
+ func (o *LoweringOwner) lowerAssignmentTarget(
2340
+ ctx lowerFileContext,
2341
+ expr ast.Expr,
2342
+ declare bool,
2343
+ ) (string, []Diagnostic) {
2344
+ switch typed := expr.(type) {
2345
+ case *ast.Ident:
2346
+ if declare {
2347
+ return o.lowerIdent(ctx, typed, true), nil
2348
+ }
2349
+ return o.lowerIdent(ctx, typed, false), nil
2350
+ case *ast.StarExpr:
2351
+ return o.lowerPointerValueExpr(ctx, typed.X)
2352
+ default:
2353
+ return o.lowerExpr(ctx, expr)
2354
+ }
2355
+ }
2356
+
2357
+ func (o *LoweringOwner) lowerAddressExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
2358
+ switch typed := expr.(type) {
2359
+ case *ast.Ident:
2360
+ return o.lowerIdent(ctx, typed, true), nil
2361
+ case *ast.CompositeLit:
2362
+ return o.lowerCompositeLit(ctx, typed, false)
2363
+ case *ast.SelectorExpr:
2364
+ receiver, diagnostics := o.lowerFieldReceiverExpr(ctx, typed.X)
2365
+ return receiver + "._fields." + typed.Sel.Name, diagnostics
2366
+ default:
2367
+ return "undefined", []Diagnostic{loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported address expression")}
2368
+ }
2369
+ }
2370
+
2371
+ func (o *LoweringOwner) lowerPointerValueExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
2372
+ base, diagnostics := o.lowerExpr(ctx, expr)
2373
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperPointerValue) + "(" + base + ")", diagnostics
2374
+ }
2375
+
2376
+ func (o *LoweringOwner) lowerPointerStorageExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
2377
+ base, diagnostics := o.lowerExpr(ctx, expr)
2378
+ return base + "!.value", diagnostics
2379
+ }
2380
+
2381
+ func (o *LoweringOwner) lowerIndexExpr(ctx lowerFileContext, expr *ast.IndexExpr) (string, []Diagnostic) {
2382
+ target, targetDiagnostics := o.lowerExpr(ctx, expr.X)
2383
+ index, indexDiagnostics := o.lowerExpr(ctx, expr.Index)
2384
+ diagnostics := append(targetDiagnostics, indexDiagnostics...)
2385
+ targetType := ctx.semPkg.source.TypesInfo.TypeOf(expr.X)
2386
+ switch {
2387
+ case isStringType(targetType):
2388
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperIndexStringOrBytes) + "(" + target + ", " + index + ")", diagnostics
2389
+ case isMapType(targetType):
2390
+ return o.lowerMapGetValue(ctx, expr, target, index), diagnostics
2391
+ default:
2392
+ return lowerIndexTarget(target, targetType) + "[" + index + "]", diagnostics
2393
+ }
2394
+ }
2395
+
2396
+ func lowerIndexTarget(target string, typ types.Type) string {
2397
+ if isNilableType(typ) {
2398
+ return target + "!"
2399
+ }
2400
+ return target
2401
+ }
2402
+
2403
+ func (o *LoweringOwner) lowerSliceExpr(ctx lowerFileContext, expr *ast.SliceExpr) (string, []Diagnostic) {
2404
+ target, diagnostics := o.lowerExpr(ctx, expr.X)
2405
+ low, lowDiagnostics := o.lowerOptionalExpr(ctx, expr.Low)
2406
+ high, highDiagnostics := o.lowerOptionalExpr(ctx, expr.High)
2407
+ max, maxDiagnostics := o.lowerOptionalExpr(ctx, expr.Max)
2408
+ diagnostics = append(diagnostics, lowDiagnostics...)
2409
+ diagnostics = append(diagnostics, highDiagnostics...)
2410
+ diagnostics = append(diagnostics, maxDiagnostics...)
2411
+ helper := RuntimeHelperGoSlice
2412
+ if isStringType(ctx.semPkg.source.TypesInfo.TypeOf(expr.X)) {
2413
+ helper = RuntimeHelperSliceStringOrBytes
2414
+ }
2415
+ args := []string{target, low, high}
2416
+ if expr.Slice3 {
2417
+ args = append(args, max)
2418
+ }
2419
+ return o.runtimeOwner.QualifiedHelper(helper) + "(" + strings.Join(args, ", ") + ")", diagnostics
2420
+ }
2421
+
2422
+ func (o *LoweringOwner) lowerOptionalExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
2423
+ if expr == nil {
2424
+ return "undefined", nil
2425
+ }
2426
+ return o.lowerExpr(ctx, expr)
2427
+ }
2428
+
2429
+ func (o *LoweringOwner) lowerCompositeLit(
2430
+ ctx lowerFileContext,
2431
+ lit *ast.CompositeLit,
2432
+ markStruct bool,
2433
+ ) (string, []Diagnostic) {
2434
+ named := namedStructType(ctx.semPkg.source.TypesInfo.TypeOf(lit))
2435
+ if named != nil {
2436
+ return o.lowerStructCompositeLit(ctx, lit, named, markStruct)
2437
+ }
2438
+ if structType, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(lit)).Underlying().(*types.Struct); ok {
2439
+ return o.lowerAnonymousStructCompositeLit(ctx, lit, structType)
2440
+ }
2441
+ if array, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(lit)).Underlying().(*types.Array); ok {
2442
+ return o.lowerArrayCompositeLit(ctx, lit, array)
2443
+ }
2444
+ if slice, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(lit)).Underlying().(*types.Slice); ok {
2445
+ return o.lowerSliceCompositeLit(ctx, lit, slice)
2446
+ }
2447
+ if mapType, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(lit)).Underlying().(*types.Map); ok {
2448
+ return o.lowerMapCompositeLit(ctx, lit, mapType)
2449
+ }
2450
+ return "undefined", []Diagnostic{loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported composite literal")}
2451
+ }
2452
+
2453
+ func (o *LoweringOwner) lowerStructCompositeLit(
2454
+ ctx lowerFileContext,
2455
+ lit *ast.CompositeLit,
2456
+ named *types.Named,
2457
+ markStruct bool,
2458
+ ) (string, []Diagnostic) {
2459
+ structType, _ := named.Underlying().(*types.Struct)
2460
+ fields := make([]string, 0, len(lit.Elts))
2461
+ var diagnostics []Diagnostic
2462
+ for idx, elt := range lit.Elts {
2463
+ fieldName := ""
2464
+ fieldType := types.Type(nil)
2465
+ valueExpr := elt
2466
+ if keyed, ok := elt.(*ast.KeyValueExpr); ok {
2467
+ valueExpr = keyed.Value
2468
+ if ident, ok := keyed.Key.(*ast.Ident); ok {
2469
+ fieldName = ident.Name
2470
+ if field := fieldByName(structType, fieldName); field != nil {
2471
+ fieldType = field.Type()
2472
+ }
2473
+ }
2474
+ } else if idx < structType.NumFields() {
2475
+ field := structType.Field(idx)
2476
+ fieldName = field.Name()
2477
+ fieldType = field.Type()
2478
+ }
2479
+ if fieldName == "" {
2480
+ diagnostics = append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported struct literal field"))
2481
+ continue
2482
+ }
2483
+ value, valueDiagnostics := o.lowerExpr(ctx, valueExpr)
2484
+ diagnostics = append(diagnostics, valueDiagnostics...)
2485
+ value = o.lowerValueForTarget(ctx, valueExpr, fieldType, value)
2486
+ fields = append(fields, fieldName+": "+value)
2487
+ }
2488
+
2489
+ expr := "new " + o.namedTypeExpr(ctx, named) + "()"
2490
+ if len(fields) != 0 {
2491
+ expr = "new " + o.namedTypeExpr(ctx, named) + "({" + strings.Join(fields, ", ") + "})"
2492
+ }
2493
+ if markStruct {
2494
+ expr = o.runtimeOwner.QualifiedHelper(RuntimeHelperMarkAsStructValue) + "(" + expr + ")"
2495
+ }
2496
+ return expr, diagnostics
2497
+ }
2498
+
2499
+ func (o *LoweringOwner) lowerAnonymousStructCompositeLit(
2500
+ ctx lowerFileContext,
2501
+ lit *ast.CompositeLit,
2502
+ structType *types.Struct,
2503
+ ) (string, []Diagnostic) {
2504
+ fields := make([]string, 0, len(lit.Elts))
2505
+ var diagnostics []Diagnostic
2506
+ for idx, elt := range lit.Elts {
2507
+ fieldName := ""
2508
+ fieldType := types.Type(nil)
2509
+ valueExpr := elt
2510
+ if keyed, ok := elt.(*ast.KeyValueExpr); ok {
2511
+ valueExpr = keyed.Value
2512
+ if ident, ok := keyed.Key.(*ast.Ident); ok {
2513
+ fieldName = ident.Name
2514
+ if field := fieldByName(structType, fieldName); field != nil {
2515
+ fieldType = field.Type()
2516
+ }
2517
+ }
2518
+ }
2519
+ if fieldName == "" && idx < structType.NumFields() {
2520
+ field := structType.Field(idx)
2521
+ fieldName = field.Name()
2522
+ fieldType = field.Type()
2523
+ }
2524
+ if fieldName == "" {
2525
+ diagnostics = append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported anonymous struct literal field"))
2526
+ continue
2527
+ }
2528
+ value, valueDiagnostics := o.lowerExpr(ctx, valueExpr)
2529
+ diagnostics = append(diagnostics, valueDiagnostics...)
2530
+ value = o.lowerValueForTarget(ctx, valueExpr, fieldType, value)
2531
+ fields = append(fields, fieldName+": "+value)
2532
+ }
2533
+ return "{" + strings.Join(fields, ", ") + "}", diagnostics
2534
+ }
2535
+
2536
+ func (o *LoweringOwner) lowerArrayCompositeLit(
2537
+ ctx lowerFileContext,
2538
+ lit *ast.CompositeLit,
2539
+ array *types.Array,
2540
+ ) (string, []Diagnostic) {
2541
+ values := make([]string, int(array.Len()))
2542
+ for idx := range values {
2543
+ values[idx] = o.lowerZeroValueExprFor(ctx, array.Elem())
2544
+ }
2545
+ nextIndex := 0
2546
+ var diagnostics []Diagnostic
2547
+ for _, elt := range lit.Elts {
2548
+ index := nextIndex
2549
+ valueExpr := elt
2550
+ if keyed, ok := elt.(*ast.KeyValueExpr); ok {
2551
+ valueExpr = keyed.Value
2552
+ parsed, keyConst := constIntExpr(ctx, keyed.Key)
2553
+ if keyConst {
2554
+ index = parsed
2555
+ }
2556
+ if !keyConst {
2557
+ key, keyDiagnostics := o.lowerExpr(ctx, keyed.Key)
2558
+ diagnostics = append(diagnostics, keyDiagnostics...)
2559
+ parsed, err := strconv.Atoi(key)
2560
+ if err == nil {
2561
+ index = parsed
2562
+ }
2563
+ }
2564
+ }
2565
+ if index >= 0 && index < len(values) {
2566
+ value, valueDiagnostics := o.lowerExpr(ctx, valueExpr)
2567
+ diagnostics = append(diagnostics, valueDiagnostics...)
2568
+ values[index] = o.lowerValueForTarget(ctx, valueExpr, array.Elem(), value)
2569
+ }
2570
+ nextIndex = index + 1
2571
+ }
2572
+ return "[" + strings.Join(values, ", ") + "]", diagnostics
2573
+ }
2574
+
2575
+ func (o *LoweringOwner) lowerSliceCompositeLit(
2576
+ ctx lowerFileContext,
2577
+ lit *ast.CompositeLit,
2578
+ slice *types.Slice,
2579
+ ) (string, []Diagnostic) {
2580
+ values := make([]string, 0, len(lit.Elts))
2581
+ nextIndex := 0
2582
+ var diagnostics []Diagnostic
2583
+ for _, elt := range lit.Elts {
2584
+ index := nextIndex
2585
+ valueExpr := elt
2586
+ if keyed, ok := elt.(*ast.KeyValueExpr); ok {
2587
+ valueExpr = keyed.Value
2588
+ parsed, keyConst := constIntExpr(ctx, keyed.Key)
2589
+ if keyConst {
2590
+ index = parsed
2591
+ }
2592
+ if !keyConst {
2593
+ key, keyDiagnostics := o.lowerExpr(ctx, keyed.Key)
2594
+ diagnostics = append(diagnostics, keyDiagnostics...)
2595
+ parsed, err := strconv.Atoi(key)
2596
+ if err == nil {
2597
+ index = parsed
2598
+ }
2599
+ }
2600
+ }
2601
+ for len(values) <= index {
2602
+ values = append(values, o.lowerZeroValueExprFor(ctx, slice.Elem()))
2603
+ }
2604
+ value, valueDiagnostics := o.lowerExpr(ctx, valueExpr)
2605
+ diagnostics = append(diagnostics, valueDiagnostics...)
2606
+ values[index] = o.lowerValueForTarget(ctx, valueExpr, slice.Elem(), value)
2607
+ nextIndex = index + 1
2608
+ }
2609
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperArrayToSlice) +
2610
+ "<" + o.tsTypeFor(ctx, slice.Elem()) + ">([" + strings.Join(values, ", ") + "])", diagnostics
2611
+ }
2612
+
2613
+ func (o *LoweringOwner) lowerMapCompositeLit(
2614
+ ctx lowerFileContext,
2615
+ lit *ast.CompositeLit,
2616
+ mapType *types.Map,
2617
+ ) (string, []Diagnostic) {
2618
+ entries := make([]string, 0, len(lit.Elts))
2619
+ var diagnostics []Diagnostic
2620
+ for _, elt := range lit.Elts {
2621
+ keyed, ok := elt.(*ast.KeyValueExpr)
2622
+ if !ok {
2623
+ diagnostics = append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported map literal entry"))
2624
+ continue
2625
+ }
2626
+ key, keyDiagnostics := o.lowerExpr(ctx, keyed.Key)
2627
+ value, valueDiagnostics := o.lowerExpr(ctx, keyed.Value)
2628
+ diagnostics = append(diagnostics, keyDiagnostics...)
2629
+ diagnostics = append(diagnostics, valueDiagnostics...)
2630
+ value = o.lowerValueForTarget(ctx, keyed.Value, mapType.Elem(), value)
2631
+ entries = append(entries, "["+key+", "+value+"]")
2632
+ }
2633
+ return "new Map<" + o.tsTypeFor(ctx, mapType.Key()) + ", " + o.tsTypeFor(ctx, mapType.Elem()) + ">([" + strings.Join(entries, ", ") + "])", diagnostics
2634
+ }
2635
+
2636
+ func (o *LoweringOwner) lowerTypeAssertExpr(ctx lowerFileContext, expr *ast.TypeAssertExpr) (string, []Diagnostic) {
2637
+ value, diagnostics := o.lowerExpr(ctx, expr.X)
2638
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMustTypeAssert) +
2639
+ "<" + o.tsTypeFor(ctx, ctx.semPkg.source.TypesInfo.TypeOf(expr.Type)) + ">(" +
2640
+ value + ", " + o.runtimeTypeInfoExpr(ctx.semPkg.source.TypesInfo.TypeOf(expr.Type)) + ")", diagnostics
2641
+ }
2642
+
2643
+ func (o *LoweringOwner) lowerTupleExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
2644
+ switch typed := expr.(type) {
2645
+ case *ast.TypeAssertExpr:
2646
+ value, diagnostics := o.lowerExpr(ctx, typed.X)
2647
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperTypeAssertTuple) +
2648
+ "<" + o.tsTypeFor(ctx, ctx.semPkg.source.TypesInfo.TypeOf(typed.Type)) + ">(" +
2649
+ value + ", " + o.runtimeTypeInfoExpr(ctx.semPkg.source.TypesInfo.TypeOf(typed.Type)) + ")", diagnostics
2650
+ case *ast.IndexExpr:
2651
+ if isMapType(ctx.semPkg.source.TypesInfo.TypeOf(typed.X)) {
2652
+ target, targetDiagnostics := o.lowerExpr(ctx, typed.X)
2653
+ index, indexDiagnostics := o.lowerExpr(ctx, typed.Index)
2654
+ return o.lowerMapGetTuple(ctx, typed, target, index), append(targetDiagnostics, indexDiagnostics...)
2655
+ }
2656
+ }
2657
+ return o.lowerExpr(ctx, expr)
2658
+ }
2659
+
2660
+ func (o *LoweringOwner) lowerMapGetValue(ctx lowerFileContext, expr *ast.IndexExpr, target string, index string) string {
2661
+ return o.lowerMapGetTuple(ctx, expr, target, index) + "[0]"
2662
+ }
2663
+
2664
+ func (o *LoweringOwner) lowerMapGetTuple(ctx lowerFileContext, expr *ast.IndexExpr, target string, index string) string {
2665
+ mapType, _ := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(expr.X)).Underlying().(*types.Map)
2666
+ defaultValue := "undefined"
2667
+ if mapType != nil {
2668
+ defaultValue = o.lowerZeroValueExprFor(ctx, mapType.Elem())
2669
+ }
2670
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMapGet) + "(" + target + ", " + index + ", " + defaultValue + ")"
2671
+ }
2672
+
2673
+ func (o *LoweringOwner) lowerValueForTarget(
2674
+ ctx lowerFileContext,
2675
+ expr ast.Expr,
2676
+ targetType types.Type,
2677
+ value string,
2678
+ ) string {
2679
+ sourceType := ctx.semPkg.source.TypesInfo.TypeOf(expr)
2680
+ if isInterfaceType(targetType) && isStructValueType(sourceType) {
2681
+ return o.lowerStructClone(value)
2682
+ }
2683
+ if isInterfaceType(targetType) && !isInterfaceType(sourceType) && isNilableType(sourceType) {
2684
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperInterfaceValue) +
2685
+ "<" + o.tsTypeFor(ctx, targetType) + ">(" + value + ", " + strconv.Quote(goRuntimeTypeString(sourceType)) + ")"
2686
+ }
2687
+ if isStructValueType(targetType) && shouldCloneStructValue(expr) {
2688
+ return o.lowerStructClone(value)
2689
+ }
2690
+ return value
2691
+ }
2692
+
2693
+ func (o *LoweringOwner) lowerStructClone(value string) string {
2694
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMarkAsStructValue) + "(" + value + ".clone())"
2695
+ }
2696
+
2697
+ func (o *LoweringOwner) lowerZeroValueExpr(typ types.Type) string {
2698
+ if named := namedStructType(typ); named != nil && isStructValueType(typ) {
2699
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMarkAsStructValue) + "(new " + named.Obj().Name() + "())"
2700
+ }
2701
+ return zeroValueExpr(typ)
2702
+ }
2703
+
2704
+ func (o *LoweringOwner) lowerZeroValueExprFor(ctx lowerFileContext, typ types.Type) string {
2705
+ if named := namedStructType(typ); named != nil && isStructValueType(typ) {
2706
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperMarkAsStructValue) + "(new " + o.namedTypeExpr(ctx, named) + "())"
2707
+ }
2708
+ switch typed := types.Unalias(typ).Underlying().(type) {
2709
+ case *types.Basic:
2710
+ if typed.Info()&types.IsBoolean != 0 {
2711
+ return "false"
2712
+ }
2713
+ if typed.Info()&types.IsString != 0 {
2714
+ return "\"\""
2715
+ }
2716
+ if typed.Info()&types.IsNumeric != 0 {
2717
+ return "0"
2718
+ }
2719
+ return "undefined"
2720
+ case *types.Array:
2721
+ return "Array.from({ length: " + strconv.FormatInt(typed.Len(), 10) + " }, () => " + o.lowerZeroValueExprFor(ctx, typed.Elem()) + ")"
2722
+ case *types.Struct:
2723
+ return "{}"
2724
+ default:
2725
+ return "null"
2726
+ }
2727
+ }
2728
+
2729
+ func (o *LoweringOwner) lowerDeclarationZeroValueExpr(ctx lowerFileContext, typ types.Type) string {
2730
+ typeParam, ok := types.Unalias(typ).(*types.TypeParam)
2731
+ if !ok {
2732
+ return o.lowerZeroValueExprFor(ctx, typ)
2733
+ }
2734
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperGenericZero) +
2735
+ "(__typeArgs, " + strconv.Quote(typeParam.Obj().Name()) + ", " + zeroValueExpr(typ) + ")"
2736
+ }
2737
+
2738
+ func (o *LoweringOwner) runtimeTypeInfoExpr(typ types.Type) string {
2739
+ return o.runtimeTypeInfoExprWithSeen(typ, make(map[string]bool))
2740
+ }
2741
+
2742
+ func (o *LoweringOwner) runtimeTypeInfoExprWithSeen(typ types.Type, seen map[string]bool) string {
2743
+ typeKind := o.runtimeOwner.QualifiedHelper(RuntimeHelperTypeKind)
2744
+ if typ == nil {
2745
+ return "{ kind: " + typeKind + ".Basic, name: \"unknown\" }"
2746
+ }
2747
+ typeKey := goRuntimeTypeString(typ)
2748
+ if typeKey != "" {
2749
+ if seen[typeKey] {
2750
+ return o.shallowRuntimeTypeInfoExpr(typ)
2751
+ }
2752
+ seen[typeKey] = true
2753
+ defer delete(seen, typeKey)
2754
+ }
2755
+ if named := namedStructType(typ); named != nil {
2756
+ return strconv.Quote(runtimeNamedTypeName(named))
2757
+ }
2758
+ if named := namedFunctionType(typ); named != nil {
2759
+ return o.runtimeFunctionTypeInfo(named.Underlying().(*types.Signature), runtimeNamedTypeName(named))
2760
+ }
2761
+ if named := namedNonStructType(typ); named != nil {
2762
+ return strconv.Quote(runtimeNamedTypeName(named))
2763
+ }
2764
+ switch typed := types.Unalias(typ).Underlying().(type) {
2765
+ case *types.Basic:
2766
+ switch {
2767
+ case typed.Info()&types.IsBoolean != 0:
2768
+ return "{ kind: " + typeKind + ".Basic, name: \"bool\" }"
2769
+ case typed.Info()&types.IsString != 0:
2770
+ return "{ kind: " + typeKind + ".Basic, name: \"string\" }"
2771
+ case typed.Info()&types.IsNumeric != 0:
2772
+ return "{ kind: " + typeKind + ".Basic, name: \"" + basicRuntimeName(typed) + "\" }"
2773
+ default:
2774
+ return "{ kind: " + typeKind + ".Basic, name: \"unknown\" }"
2775
+ }
2776
+ case *types.Pointer:
2777
+ return "{ kind: " + typeKind + ".Pointer, elemType: " + o.runtimeTypeInfoExprWithSeen(typed.Elem(), seen) + " }"
2778
+ case *types.Struct:
2779
+ return "{ kind: " + typeKind + ".Struct, methods: [], fields: " + o.runtimeStructFieldsExpr(typed, seen) + " }"
2780
+ case *types.Slice:
2781
+ return "{ kind: " + typeKind + ".Slice, elemType: " + o.runtimeTypeInfoExprWithSeen(typed.Elem(), seen) + " }"
2782
+ case *types.Array:
2783
+ return "{ kind: " + typeKind + ".Array, elemType: " + o.runtimeTypeInfoExprWithSeen(typed.Elem(), seen) + ", length: " + strconv.FormatInt(typed.Len(), 10) + " }"
2784
+ case *types.Map:
2785
+ return "{ kind: " + typeKind + ".Map, keyType: " + o.runtimeTypeInfoExprWithSeen(typed.Key(), seen) + ", elemType: " + o.runtimeTypeInfoExprWithSeen(typed.Elem(), seen) + " }"
2786
+ case *types.Chan:
2787
+ return "{ kind: " + typeKind + ".Channel, direction: " + strconv.Quote(channelDirectionString(typed.Dir())) + ", elemType: " + o.runtimeTypeInfoExprWithSeen(typed.Elem(), seen) + " }"
2788
+ case *types.Interface:
2789
+ typed.Complete()
2790
+ return "{ kind: " + typeKind + ".Interface, methods: " + o.runtimeMethodSignaturesWithSeen(typed, seen) + " }"
2791
+ case *types.Signature:
2792
+ return o.runtimeFunctionTypeInfoWithSeen(typed, "", seen)
2793
+ default:
2794
+ return "{ kind: " + typeKind + ".Basic, name: \"unknown\" }"
2795
+ }
2796
+ }
2797
+
2798
+ func (o *LoweringOwner) shallowRuntimeTypeInfoExpr(typ types.Type) string {
2799
+ typeKind := o.runtimeOwner.QualifiedHelper(RuntimeHelperTypeKind)
2800
+ switch types.Unalias(typ).Underlying().(type) {
2801
+ case *types.Interface:
2802
+ return "{ kind: " + typeKind + ".Interface, methods: [] }"
2803
+ case *types.Signature:
2804
+ return "{ kind: " + typeKind + ".Function, params: [], results: [] }"
2805
+ case *types.Pointer:
2806
+ return "{ kind: " + typeKind + ".Pointer, elemType: { kind: " + typeKind + ".Basic, name: \"unknown\" } }"
2807
+ case *types.Struct:
2808
+ return "{ kind: " + typeKind + ".Struct, methods: [], fields: {} }"
2809
+ case *types.Slice:
2810
+ return "{ kind: " + typeKind + ".Slice, elemType: { kind: " + typeKind + ".Basic, name: \"unknown\" } }"
2811
+ case *types.Array:
2812
+ return "{ kind: " + typeKind + ".Array, elemType: { kind: " + typeKind + ".Basic, name: \"unknown\" }, length: 0 }"
2813
+ case *types.Map:
2814
+ return "{ kind: " + typeKind + ".Map, keyType: { kind: " + typeKind + ".Basic, name: \"unknown\" }, elemType: { kind: " + typeKind + ".Basic, name: \"unknown\" } }"
2815
+ case *types.Chan:
2816
+ return "{ kind: " + typeKind + ".Channel, direction: \"both\", elemType: { kind: " + typeKind + ".Basic, name: \"unknown\" } }"
2817
+ default:
2818
+ return "{ kind: " + typeKind + ".Basic, name: \"unknown\" }"
2819
+ }
2820
+ }
2821
+
2822
+ func (o *LoweringOwner) runtimeStructFieldsExpr(structType *types.Struct, seen map[string]bool) string {
2823
+ fields := make([]string, 0, structType.NumFields())
2824
+ for idx := range structType.NumFields() {
2825
+ field := structType.Field(idx)
2826
+ fieldInfo := runtimeStructFieldInfoExpr(
2827
+ o.runtimeTypeInfoExprWithSeen(field.Type(), seen),
2828
+ structType.Tag(idx),
2829
+ )
2830
+ fields = append(fields, strconv.Quote(field.Name())+": "+fieldInfo)
2831
+ }
2832
+ return "{" + strings.Join(fields, ", ") + "}"
2833
+ }
2834
+
2835
+ func runtimeStructFieldInfoExpr(runtimeType string, tag string) string {
2836
+ if tag == "" {
2837
+ return runtimeType
2838
+ }
2839
+ return "{ type: " + runtimeType + ", tag: " + strconv.Quote(tag) + " }"
2840
+ }
2841
+
2842
+ func (o *LoweringOwner) runtimeFunctionTypeInfo(signature *types.Signature, name string) string {
2843
+ return o.runtimeFunctionTypeInfoWithSeen(signature, name, make(map[string]bool))
2844
+ }
2845
+
2846
+ func (o *LoweringOwner) runtimeFunctionTypeInfoWithSeen(signature *types.Signature, name string, seen map[string]bool) string {
2847
+ typeKind := o.runtimeOwner.QualifiedHelper(RuntimeHelperTypeKind)
2848
+ parts := []string{"kind: " + typeKind + ".Function"}
2849
+ if name != "" {
2850
+ parts = append(parts, "name: "+strconv.Quote(name))
2851
+ }
2852
+ parts = append(parts, "params: "+o.runtimeSignatureTypes(signature.Params(), seen))
2853
+ parts = append(parts, "results: "+o.runtimeSignatureTypes(signature.Results(), seen))
2854
+ if signature.Variadic() {
2855
+ parts = append(parts, "isVariadic: true")
2856
+ }
2857
+ return "{ " + strings.Join(parts, ", ") + " }"
2858
+ }
2859
+
2860
+ func (o *LoweringOwner) runtimeSignatureTypes(tuple *types.Tuple, seen map[string]bool) string {
2861
+ if tuple == nil || tuple.Len() == 0 {
2862
+ return "[]"
2863
+ }
2864
+ types := make([]string, 0, tuple.Len())
2865
+ for v := range tuple.Variables() {
2866
+ types = append(types, o.runtimeTypeInfoExprWithSeen(v.Type(), seen))
2867
+ }
2868
+ return "[" + strings.Join(types, ", ") + "]"
2869
+ }
2870
+
2871
+ func fieldByName(structType *types.Struct, name string) *types.Var {
2872
+ for field := range structType.Fields() {
2873
+ if field.Name() == name {
2874
+ return field
2875
+ }
2876
+ }
2877
+ return nil
2878
+ }
2879
+
2880
+ func shouldCloneStructValue(expr ast.Expr) bool {
2881
+ switch expr.(type) {
2882
+ case *ast.CompositeLit:
2883
+ return false
2884
+ default:
2885
+ return true
2886
+ }
2887
+ }
2888
+
2889
+ func constIntExpr(ctx lowerFileContext, expr ast.Expr) (int, bool) {
2890
+ tv, ok := ctx.semPkg.source.TypesInfo.Types[expr]
2891
+ if !ok || tv.Value == nil {
2892
+ return 0, false
2893
+ }
2894
+ value, ok := constant.Int64Val(tv.Value)
2895
+ if !ok {
2896
+ return 0, false
2897
+ }
2898
+ return int(value), true
2899
+ }
2900
+
2901
+ func tsType(typ types.Type) string {
2902
+ if typ == nil {
2903
+ return "unknown"
2904
+ }
2905
+ if isBuiltinErrorType(typ) {
2906
+ return "$.GoError"
2907
+ }
2908
+ if named, ok := types.Unalias(typ).(*types.Named); ok {
2909
+ if _, ok := named.Underlying().(*types.Struct); ok {
2910
+ return named.Obj().Name()
2911
+ }
2912
+ return named.Obj().Name()
2913
+ }
2914
+ switch typed := types.Unalias(typ).Underlying().(type) {
2915
+ case *types.Basic:
2916
+ if typed.Kind() == types.UntypedNil {
2917
+ return "null"
2918
+ }
2919
+ if typed.Info()&types.IsBoolean != 0 {
2920
+ return "boolean"
2921
+ }
2922
+ if typed.Info()&types.IsString != 0 {
2923
+ return "string"
2924
+ }
2925
+ if typed.Info()&types.IsNumeric != 0 {
2926
+ return "number"
2927
+ }
2928
+ return "unknown"
2929
+ case *types.Struct:
2930
+ return "Record<string, unknown>"
2931
+ case *types.Array:
2932
+ return tsType(typed.Elem()) + "[]"
2933
+ case *types.Slice:
2934
+ return "$.Slice<" + tsType(typed.Elem()) + ">"
2935
+ case *types.Map:
2936
+ return "Map<" + tsType(typed.Key()) + ", " + tsType(typed.Elem()) + "> | null"
2937
+ case *types.Chan:
2938
+ return "$.Channel<" + tsType(typed.Elem()) + "> | null"
2939
+ case *types.Pointer:
2940
+ if named := namedNonStructType(typed.Elem()); named != nil {
2941
+ return "$.VarRef<" + named.Obj().Name() + "> | null"
2942
+ }
2943
+ if named := namedStructType(typed.Elem()); named != nil {
2944
+ return named.Obj().Name() + " | $.VarRef<" + named.Obj().Name() + "> | null"
2945
+ }
2946
+ return "$.VarRef<" + tsType(typed.Elem()) + "> | null"
2947
+ case *types.Interface:
2948
+ return "any"
2949
+ case *types.Signature:
2950
+ return "(" + tsSignatureParams(typed) + ") => " + tsSignatureResult(typed)
2951
+ default:
2952
+ return "unknown"
2953
+ }
2954
+ }
2955
+
2956
+ func tsSignatureParams(signature *types.Signature) string {
2957
+ if signature == nil || signature.Params() == nil || signature.Params().Len() == 0 {
2958
+ return ""
2959
+ }
2960
+ params := make([]string, 0, signature.Params().Len())
2961
+ for idx := range signature.Params().Len() {
2962
+ param := signature.Params().At(idx)
2963
+ params = append(params, safeParamName(param, idx)+": "+tsType(param.Type()))
2964
+ }
2965
+ return strings.Join(params, ", ")
2966
+ }
2967
+
2968
+ func tsSignatureResult(signature *types.Signature) string {
2969
+ if signature == nil || signature.Results() == nil || signature.Results().Len() == 0 {
2970
+ return "void"
2971
+ }
2972
+ if signature.Results().Len() == 1 {
2973
+ return tsType(signature.Results().At(0).Type())
2974
+ }
2975
+ results := make([]string, 0, signature.Results().Len())
2976
+ for result := range signature.Results().Variables() {
2977
+ results = append(results, tsType(result.Type()))
2978
+ }
2979
+ return "[" + strings.Join(results, ", ") + "]"
2980
+ }
2981
+
2982
+ func (o *LoweringOwner) tsSignatureParamsFor(ctx lowerFileContext, signature *types.Signature) string {
2983
+ if signature == nil || signature.Params() == nil || signature.Params().Len() == 0 {
2984
+ return ""
2985
+ }
2986
+ params := make([]string, 0, signature.Params().Len())
2987
+ for idx := range signature.Params().Len() {
2988
+ param := signature.Params().At(idx)
2989
+ params = append(params, safeParamName(param, idx)+": "+o.tsTypeFor(ctx, param.Type()))
2990
+ }
2991
+ return strings.Join(params, ", ")
2992
+ }
2993
+
2994
+ func (o *LoweringOwner) tsSignatureResultFor(ctx lowerFileContext, signature *types.Signature) string {
2995
+ if signature == nil || signature.Results() == nil || signature.Results().Len() == 0 {
2996
+ return "void"
2997
+ }
2998
+ if signature.Results().Len() == 1 {
2999
+ return o.tsTypeFor(ctx, signature.Results().At(0).Type())
3000
+ }
3001
+ results := make([]string, 0, signature.Results().Len())
3002
+ for result := range signature.Results().Variables() {
3003
+ results = append(results, o.tsTypeFor(ctx, result.Type()))
3004
+ }
3005
+ return "[" + strings.Join(results, ", ") + "]"
3006
+ }
3007
+
3008
+ func asyncResultType(result string, async bool) string {
3009
+ if !async {
3010
+ return result
3011
+ }
3012
+ return "Promise<" + result + ">"
3013
+ }
3014
+
3015
+ func (o *LoweringOwner) tsVariableTypeFor(ctx lowerFileContext, typ types.Type, needsVarRef bool) string {
3016
+ valueType := o.tsTypeFor(ctx, typ)
3017
+ if needsVarRef {
3018
+ return "$.VarRef<" + valueType + ">"
3019
+ }
3020
+ return valueType
3021
+ }
3022
+
3023
+ func (o *LoweringOwner) tsTypeFor(ctx lowerFileContext, typ types.Type) string {
3024
+ if typ == nil {
3025
+ return "unknown"
3026
+ }
3027
+ if isBuiltinErrorType(typ) {
3028
+ return "$.GoError"
3029
+ }
3030
+ if named, ok := types.Unalias(typ).(*types.Named); ok {
3031
+ return o.namedTypeExpr(ctx, named)
3032
+ }
3033
+ switch typed := types.Unalias(typ).Underlying().(type) {
3034
+ case *types.Array:
3035
+ return o.tsTypeFor(ctx, typed.Elem()) + "[]"
3036
+ case *types.Slice:
3037
+ return "$.Slice<" + o.tsTypeFor(ctx, typed.Elem()) + ">"
3038
+ case *types.Map:
3039
+ return "Map<" + o.tsTypeFor(ctx, typed.Key()) + ", " + o.tsTypeFor(ctx, typed.Elem()) + "> | null"
3040
+ case *types.Chan:
3041
+ return "$.Channel<" + o.tsTypeFor(ctx, typed.Elem()) + "> | null"
3042
+ case *types.Pointer:
3043
+ if named := namedNonStructType(typed.Elem()); named != nil {
3044
+ return "$.VarRef<" + o.namedTypeExpr(ctx, named) + "> | null"
3045
+ }
3046
+ if named := namedStructType(typed.Elem()); named != nil {
3047
+ name := o.namedTypeExpr(ctx, named)
3048
+ return name + " | $.VarRef<" + name + "> | null"
3049
+ }
3050
+ return "$.VarRef<" + o.tsTypeFor(ctx, typed.Elem()) + "> | null"
3051
+ case *types.Signature:
3052
+ return "((" + o.tsSignatureParamsFor(ctx, typed) + ") => " + o.tsSignatureResultFor(ctx, typed) + ") | null"
3053
+ default:
3054
+ return tsType(typ)
3055
+ }
3056
+ }
3057
+
3058
+ func zeroValueExpr(typ types.Type) string {
3059
+ if typ == nil {
3060
+ return "undefined"
3061
+ }
3062
+ if named := namedStructType(typ); named != nil && isStructValueType(typ) {
3063
+ return "new " + named.Obj().Name() + "()"
3064
+ }
3065
+ switch typed := types.Unalias(typ).Underlying().(type) {
3066
+ case *types.Basic:
3067
+ if typed.Info()&types.IsBoolean != 0 {
3068
+ return "false"
3069
+ }
3070
+ if typed.Info()&types.IsString != 0 {
3071
+ return "\"\""
3072
+ }
3073
+ if typed.Info()&types.IsNumeric != 0 {
3074
+ return "0"
3075
+ }
3076
+ return "undefined"
3077
+ case *types.Array:
3078
+ return "Array.from({ length: " + strconv.FormatInt(typed.Len(), 10) + " }, () => " + zeroValueExpr(typed.Elem()) + ")"
3079
+ case *types.Struct:
3080
+ return "{}"
3081
+ default:
3082
+ return "null"
3083
+ }
3084
+ }
3085
+
3086
+ func namedStructType(typ types.Type) *types.Named {
3087
+ named, _ := types.Unalias(typ).(*types.Named)
3088
+ if named == nil {
3089
+ return nil
3090
+ }
3091
+ if _, ok := named.Underlying().(*types.Struct); !ok {
3092
+ return nil
3093
+ }
3094
+ return named
3095
+ }
3096
+
3097
+ func namedNonStructType(typ types.Type) *types.Named {
3098
+ named, _ := types.Unalias(typ).(*types.Named)
3099
+ if named == nil {
3100
+ return nil
3101
+ }
3102
+ if _, ok := named.Underlying().(*types.Struct); ok {
3103
+ return nil
3104
+ }
3105
+ return named
3106
+ }
3107
+
3108
+ func namedFunctionType(typ types.Type) *types.Named {
3109
+ named, _ := types.Unalias(typ).(*types.Named)
3110
+ if named == nil {
3111
+ return nil
3112
+ }
3113
+ if _, ok := named.Underlying().(*types.Signature); !ok {
3114
+ return nil
3115
+ }
3116
+ return named
3117
+ }
3118
+
3119
+ func isBuiltinErrorType(typ types.Type) bool {
3120
+ if typ == nil {
3121
+ return false
3122
+ }
3123
+ named, _ := types.Unalias(typ).(*types.Named)
3124
+ if named == nil || named.Obj() == nil {
3125
+ return false
3126
+ }
3127
+ return named.Obj().Pkg() == nil && named.Obj().Name() == "error"
3128
+ }
3129
+
3130
+ func receiverTypeParam(typ types.Type) *types.TypeParam {
3131
+ for {
3132
+ pointer, ok := typ.(*types.Pointer)
3133
+ if !ok {
3134
+ break
3135
+ }
3136
+ typ = pointer.Elem()
3137
+ }
3138
+ typeParam, _ := types.Unalias(typ).(*types.TypeParam)
3139
+ return typeParam
3140
+ }
3141
+
3142
+ func sameNamedTypeOrigin(a *types.Named, b *types.Named) bool {
3143
+ if a == nil || b == nil {
3144
+ return false
3145
+ }
3146
+ return a == b || a.Origin() == b.Origin()
3147
+ }
3148
+
3149
+ func namedNonInterfaceNonStructType(named *types.Named) bool {
3150
+ if named == nil {
3151
+ return false
3152
+ }
3153
+ switch named.Underlying().(type) {
3154
+ case *types.Interface, *types.Struct:
3155
+ return false
3156
+ default:
3157
+ return true
3158
+ }
3159
+ }
3160
+
3161
+ func isStructValueType(typ types.Type) bool {
3162
+ return namedStructType(typ) != nil
3163
+ }
3164
+
3165
+ func isPointerToStructType(typ types.Type) bool {
3166
+ pointer, ok := types.Unalias(typ).Underlying().(*types.Pointer)
3167
+ if !ok {
3168
+ return false
3169
+ }
3170
+ return namedStructType(pointer.Elem()) != nil
3171
+ }
3172
+
3173
+ func isMapType(typ types.Type) bool {
3174
+ if typ == nil {
3175
+ return false
3176
+ }
3177
+ _, ok := types.Unalias(typ).Underlying().(*types.Map)
3178
+ return ok
3179
+ }
3180
+
3181
+ func isChannelType(typ types.Type) bool {
3182
+ if typ == nil {
3183
+ return false
3184
+ }
3185
+ _, ok := types.Unalias(typ).Underlying().(*types.Chan)
3186
+ return ok
3187
+ }
3188
+
3189
+ func isPointerType(typ types.Type) bool {
3190
+ if typ == nil {
3191
+ return false
3192
+ }
3193
+ _, ok := types.Unalias(typ).Underlying().(*types.Pointer)
3194
+ return ok
3195
+ }
3196
+
3197
+ func channelDirectionString(dir types.ChanDir) string {
3198
+ switch dir {
3199
+ case types.SendOnly:
3200
+ return "send"
3201
+ case types.RecvOnly:
3202
+ return "receive"
3203
+ default:
3204
+ return "both"
3205
+ }
3206
+ }
3207
+
3208
+ func isNilExpr(expr ast.Expr) bool {
3209
+ ident, ok := expr.(*ast.Ident)
3210
+ return ok && ident.Name == "nil"
3211
+ }
3212
+
3213
+ func isStringType(typ types.Type) bool {
3214
+ basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
3215
+ return ok && basic.Info()&types.IsString != 0
3216
+ }
3217
+
3218
+ func isNumericType(typ types.Type) bool {
3219
+ basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
3220
+ return ok && basic.Info()&types.IsNumeric != 0
3221
+ }
3222
+
3223
+ func isRuneSliceType(typ types.Type) bool {
3224
+ slice, ok := types.Unalias(typ).Underlying().(*types.Slice)
3225
+ return ok && isRuneType(slice.Elem())
3226
+ }
3227
+
3228
+ func isByteSliceType(typ types.Type) bool {
3229
+ slice, ok := types.Unalias(typ).Underlying().(*types.Slice)
3230
+ return ok && isByteType(slice.Elem())
3231
+ }
3232
+
3233
+ func isRuneType(typ types.Type) bool {
3234
+ basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
3235
+ return ok && basic.Kind() == types.Int32
3236
+ }
3237
+
3238
+ func isByteType(typ types.Type) bool {
3239
+ basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
3240
+ return ok && basic.Kind() == types.Uint8
3241
+ }
3242
+
3243
+ func sliceTypeHint(typ types.Type) string {
3244
+ switch {
3245
+ case isByteType(typ):
3246
+ return "byte"
3247
+ case isStringType(typ):
3248
+ return "string"
3249
+ case isNumericType(typ):
3250
+ return "number"
3251
+ default:
3252
+ return ""
3253
+ }
3254
+ }
3255
+
3256
+ func typeFromExpr(ctx lowerFileContext, expr ast.Expr) types.Type {
3257
+ if expr == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
3258
+ return nil
3259
+ }
3260
+ if tv, ok := ctx.semPkg.source.TypesInfo.Types[expr]; ok && tv.IsType() {
3261
+ return tv.Type
3262
+ }
3263
+ return nil
3264
+ }
3265
+
3266
+ func genericFunctionSignature(ctx lowerFileContext, expr ast.Expr) *types.Signature {
3267
+ if ctx.semPkg == nil || ctx.semPkg.source == nil {
3268
+ return nil
3269
+ }
3270
+ var fn *types.Func
3271
+ switch typed := expr.(type) {
3272
+ case *ast.Ident:
3273
+ fn, _ = ctx.semPkg.source.TypesInfo.Uses[typed].(*types.Func)
3274
+ case *ast.SelectorExpr:
3275
+ fn, _ = ctx.semPkg.source.TypesInfo.Uses[typed.Sel].(*types.Func)
3276
+ if selection := ctx.semPkg.source.TypesInfo.Selections[typed]; selection != nil {
3277
+ fn, _ = selection.Obj().(*types.Func)
3278
+ }
3279
+ }
3280
+ if fn == nil {
3281
+ return nil
3282
+ }
3283
+ signature, _ := fn.Type().(*types.Signature)
3284
+ if signature == nil || signature.TypeParams() == nil || signature.TypeParams().Len() == 0 {
3285
+ return nil
3286
+ }
3287
+ return signature
3288
+ }
3289
+
3290
+ func (o *LoweringOwner) functionAsync(ctx lowerFileContext, fn *types.Func) bool {
3291
+ if fn == nil || ctx.model == nil {
3292
+ return false
3293
+ }
3294
+ semFn := ctx.model.functions[fn]
3295
+ return semFn != nil && semFn.async
3296
+ }
3297
+
3298
+ func (o *LoweringOwner) callNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool {
3299
+ for {
3300
+ switch typed := fun.(type) {
3301
+ case *ast.IndexExpr:
3302
+ fun = typed.X
3303
+ case *ast.IndexListExpr:
3304
+ fun = typed.X
3305
+ default:
3306
+ if ctx.semPkg == nil || ctx.semPkg.source == nil {
3307
+ return false
3308
+ }
3309
+ return o.functionAsync(ctx, calledFunction(ctx.semPkg.source, fun)) ||
3310
+ o.overrideCallNeedsAwait(ctx, fun)
3311
+ }
3312
+ }
3313
+ }
3314
+
3315
+ func (o *LoweringOwner) overrideCallNeedsAwait(ctx lowerFileContext, fun ast.Expr) bool {
3316
+ if o.overrideOwner == nil || ctx.semPkg == nil || ctx.semPkg.source == nil {
3317
+ return false
3318
+ }
3319
+ selector, ok := fun.(*ast.SelectorExpr)
3320
+ if !ok {
3321
+ return false
3322
+ }
3323
+ selection := ctx.semPkg.source.TypesInfo.Selections[selector]
3324
+ if selection == nil {
3325
+ return false
3326
+ }
3327
+ method, _ := selection.Obj().(*types.Func)
3328
+ if method == nil {
3329
+ return false
3330
+ }
3331
+ named := receiverNamedType(selection.Recv())
3332
+ if named == nil || named.Obj() == nil || named.Obj().Pkg() == nil {
3333
+ return false
3334
+ }
3335
+ async, err := o.overrideOwner.IsMethodAsync(
3336
+ named.Obj().Pkg().Path(),
3337
+ named.Obj().Name()+"."+method.Name(),
3338
+ )
3339
+ return err == nil && async
3340
+ }
3341
+
3342
+ func (o *LoweringOwner) awaitCallIfNeeded(ctx lowerFileContext, fun ast.Expr, call string) string {
3343
+ if o.callNeedsAwait(ctx, fun) {
3344
+ return "await " + call
3345
+ }
3346
+ return call
3347
+ }
3348
+
3349
+ func (o *LoweringOwner) genericTypeArgsExpr(ctx lowerFileContext, callee ast.Expr, typeArgExprs []ast.Expr) string {
3350
+ signature := genericFunctionSignature(ctx, callee)
3351
+ if signature == nil {
3352
+ return "undefined"
3353
+ }
3354
+ typeParams := signature.TypeParams()
3355
+ entries := make([]string, 0, typeParams.Len())
3356
+ for idx := range typeParams.Len() {
3357
+ if idx >= len(typeArgExprs) {
3358
+ break
3359
+ }
3360
+ typ := ctx.semPkg.source.TypesInfo.TypeOf(typeArgExprs[idx])
3361
+ if typ == nil {
3362
+ continue
3363
+ }
3364
+ entries = append(entries, typeParams.At(idx).Obj().Name()+": "+o.genericTypeDescriptorExpr(typ))
3365
+ }
3366
+ if len(entries) == 0 {
3367
+ return "undefined"
3368
+ }
3369
+ return "{" + strings.Join(entries, ", ") + "}"
3370
+ }
3371
+
3372
+ func (o *LoweringOwner) inferredGenericTypeArgsExpr(
3373
+ ctx lowerFileContext,
3374
+ signature *types.Signature,
3375
+ args []ast.Expr,
3376
+ ) string {
3377
+ typeParams := signature.TypeParams()
3378
+ if typeParams == nil || typeParams.Len() == 0 {
3379
+ return "undefined"
3380
+ }
3381
+ inferred := make(map[*types.TypeParam]types.Type)
3382
+ params := signature.Params()
3383
+ if params != nil {
3384
+ for idx := range params.Len() {
3385
+ if idx >= len(args) {
3386
+ break
3387
+ }
3388
+ o.inferGenericTypeArg(inferred, params.At(idx).Type(), ctx.semPkg.source.TypesInfo.TypeOf(args[idx]))
3389
+ }
3390
+ }
3391
+ entries := make([]string, 0, typeParams.Len())
3392
+ for typeParam := range typeParams.TypeParams() {
3393
+ typ := inferred[typeParam]
3394
+ if typ == nil {
3395
+ continue
3396
+ }
3397
+ entries = append(entries, typeParam.Obj().Name()+": "+o.genericTypeDescriptorExpr(typ))
3398
+ }
3399
+ if len(entries) == 0 {
3400
+ return "undefined"
3401
+ }
3402
+ return "{" + strings.Join(entries, ", ") + "}"
3403
+ }
3404
+
3405
+ func (o *LoweringOwner) inferGenericTypeArg(
3406
+ inferred map[*types.TypeParam]types.Type,
3407
+ paramType types.Type,
3408
+ argType types.Type,
3409
+ ) {
3410
+ if paramType == nil || argType == nil {
3411
+ return
3412
+ }
3413
+ if typeParam, ok := types.Unalias(paramType).(*types.TypeParam); ok {
3414
+ if inferred[typeParam] == nil {
3415
+ inferred[typeParam] = argType
3416
+ }
3417
+ return
3418
+ }
3419
+ switch param := types.Unalias(paramType).Underlying().(type) {
3420
+ case *types.Slice:
3421
+ if arg, ok := types.Unalias(argType).Underlying().(*types.Slice); ok {
3422
+ o.inferGenericTypeArg(inferred, param.Elem(), arg.Elem())
3423
+ }
3424
+ case *types.Pointer:
3425
+ if arg, ok := types.Unalias(argType).Underlying().(*types.Pointer); ok {
3426
+ o.inferGenericTypeArg(inferred, param.Elem(), arg.Elem())
3427
+ }
3428
+ }
3429
+ }
3430
+
3431
+ func (o *LoweringOwner) genericTypeDescriptorExpr(typ types.Type) string {
3432
+ parts := []string{
3433
+ "type: " + o.runtimeTypeInfoExpr(typ),
3434
+ "zero: () => " + zeroValueExpr(typ),
3435
+ }
3436
+ if methods := o.genericMethodDescriptors(typ); methods != "" {
3437
+ parts = append(parts, "methods: "+methods)
3438
+ }
3439
+ return "{ " + strings.Join(parts, ", ") + " }"
3440
+ }
3441
+
3442
+ func (o *LoweringOwner) genericMethodDescriptors(typ types.Type) string {
3443
+ named, _ := types.Unalias(typ).(*types.Named)
3444
+ if named == nil {
3445
+ return ""
3446
+ }
3447
+ methodSet := types.NewMethodSet(named)
3448
+ methods := make([]string, 0, methodSet.Len())
3449
+ for method := range methodSet.Methods() {
3450
+ method, _ := method.Obj().(*types.Func)
3451
+ if method == nil {
3452
+ continue
3453
+ }
3454
+ if namedStructType(named) != nil || isInterfaceType(named) {
3455
+ methods = append(methods, method.Name()+": (receiver: any, ...args: any[]) => receiver."+method.Name()+"(...args)")
3456
+ continue
3457
+ }
3458
+ methods = append(methods, method.Name()+": "+methodFunctionName(named.Origin(), method.Name()))
3459
+ }
3460
+ if len(methods) == 0 {
3461
+ return ""
3462
+ }
3463
+ return "{" + strings.Join(methods, ", ") + "}"
3464
+ }
3465
+
3466
+ func methodFunctionName(receiver *types.Named, method string) string {
3467
+ if receiver == nil || receiver.Obj() == nil {
3468
+ return method
3469
+ }
3470
+ return receiver.Obj().Name() + "_" + method
3471
+ }
3472
+
3473
+ func (o *LoweringOwner) methodFunctionExpr(
3474
+ ctx lowerFileContext,
3475
+ receiver *types.Named,
3476
+ obj types.Object,
3477
+ method string,
3478
+ ) string {
3479
+ name := methodFunctionName(receiver, method)
3480
+ if alias := ctx.localAliases[obj]; alias != "" {
3481
+ return alias + "." + name
3482
+ }
3483
+ if receiver != nil && receiver.Obj() != nil && receiver.Obj().Pkg() != nil {
3484
+ if alias := ctx.importPaths[receiver.Obj().Pkg().Path()]; alias != "" {
3485
+ return alias + "." + name
3486
+ }
3487
+ }
3488
+ return name
3489
+ }
3490
+
3491
+ func (o *LoweringOwner) namedTypeExpr(ctx lowerFileContext, named *types.Named) string {
3492
+ if named == nil || named.Obj() == nil {
3493
+ return "unknown"
3494
+ }
3495
+ baseName := named.Obj().Name()
3496
+ if alias := ctx.localAliases[named.Obj()]; alias != "" {
3497
+ baseName = alias + "." + baseName
3498
+ } else if named.Obj().Pkg() != nil {
3499
+ if alias := ctx.importPaths[named.Obj().Pkg().Path()]; alias != "" {
3500
+ baseName = alias + "." + baseName
3501
+ }
3502
+ }
3503
+ if args := o.overrideTypeArgsExpr(ctx, named); args != "" {
3504
+ return baseName + "<" + args + ">"
3505
+ }
3506
+ return baseName
3507
+ }
3508
+
3509
+ func (o *LoweringOwner) overrideTypeArgsExpr(ctx lowerFileContext, named *types.Named) string {
3510
+ if o.overrideOwner == nil || named == nil || named.Obj() == nil || named.Obj().Pkg() == nil {
3511
+ return ""
3512
+ }
3513
+ args := named.TypeArgs()
3514
+ if args == nil || args.Len() == 0 {
3515
+ return ""
3516
+ }
3517
+ if _, ok := o.overrideOwner.importPackageRoot(named.Obj().Pkg().Path()); !ok {
3518
+ return ""
3519
+ }
3520
+ parts := make([]string, 0, args.Len())
3521
+ for typ := range args.Types() {
3522
+ parts = append(parts, o.tsTypeFor(ctx, typ))
3523
+ }
3524
+ return strings.Join(parts, ", ")
3525
+ }
3526
+
3527
+ func (o *LoweringOwner) tsReceiverTypeFor(ctx lowerFileContext, typ types.Type) string {
3528
+ if pointer, ok := types.Unalias(typ).Underlying().(*types.Pointer); ok {
3529
+ if named := namedNonStructType(pointer.Elem()); named != nil {
3530
+ return "$.VarRef<" + o.namedTypeExpr(ctx, named) + ">"
3531
+ }
3532
+ }
3533
+ return o.tsTypeFor(ctx, typ)
3534
+ }
3535
+
3536
+ func runtimeNamedTypeName(named *types.Named) string {
3537
+ if named == nil || named.Obj() == nil {
3538
+ return ""
3539
+ }
3540
+ if named.Obj().Pkg() == nil {
3541
+ return named.Obj().Name()
3542
+ }
3543
+ return named.Obj().Pkg().Name() + "." + named.Obj().Name()
3544
+ }
3545
+
3546
+ func goRuntimeTypeString(typ types.Type) string {
3547
+ return types.TypeString(typ, func(pkg *types.Package) string {
3548
+ return pkg.Name()
3549
+ })
3550
+ }
3551
+
3552
+ func basicRuntimeName(basic *types.Basic) string {
3553
+ if basic == nil {
3554
+ return "unknown"
3555
+ }
3556
+ switch basic.Kind() {
3557
+ case types.Bool:
3558
+ return "bool"
3559
+ case types.String:
3560
+ return "string"
3561
+ default:
3562
+ if basic.Info()&types.IsNumeric != 0 {
3563
+ return "int"
3564
+ }
3565
+ return basic.Name()
3566
+ }
3567
+ }
3568
+
3569
+ func loweringUnsupported(kind string, subject string, detail string) Diagnostic {
3570
+ return Diagnostic{
3571
+ Severity: DiagnosticSeverityError,
3572
+ Code: "goscript/lowering:unsupported",
3573
+ Message: "unsupported " + kind + " in v2 lowering seed",
3574
+ Detail: subject + ": " + detail,
3575
+ }
3576
+ }