goscript 0.1.4 → 0.2.1

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 (295) hide show
  1. package/README.md +5 -2
  2. package/cmd/go_js_wasm_exec/main.go +201 -0
  3. package/cmd/go_js_wasm_exec/main_test.go +83 -0
  4. package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +7 -0
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/cmd/goscript-wasm/main.go +38 -6
  8. package/compiler/compile-request.go +12 -9
  9. package/compiler/compliance_test.go +0 -1
  10. package/compiler/config.go +2 -0
  11. package/compiler/diagnostic.go +104 -12
  12. package/compiler/diagnostic_test.go +106 -0
  13. package/compiler/gotest/request.go +28 -0
  14. package/compiler/gotest/runner.go +354 -44
  15. package/compiler/gotest/runner_test.go +293 -1
  16. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  17. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  18. package/compiler/index.test.ts +23 -0
  19. package/compiler/lowered-program.go +33 -24
  20. package/compiler/lowering.go +746 -194
  21. package/compiler/lowering_bench_test.go +42 -27
  22. package/compiler/lowering_internal_test.go +18 -0
  23. package/compiler/override-facts.go +15 -0
  24. package/compiler/override-parity-verifier.go +450 -0
  25. package/compiler/override-parity.go +122 -0
  26. package/compiler/override-registry_test.go +559 -0
  27. package/compiler/protobuf-ts-binding.go +567 -0
  28. package/compiler/protobuf-ts-binding_test.go +402 -0
  29. package/compiler/runtime-contract.go +4 -0
  30. package/compiler/runtime-contract_test.go +2 -0
  31. package/compiler/semantic-model-types.go +9 -4
  32. package/compiler/semantic-model.go +282 -70
  33. package/compiler/semantic-model_test.go +82 -1
  34. package/compiler/service.go +21 -1
  35. package/compiler/skeleton_test.go +118 -10
  36. package/compiler/typescript-emitter.go +128 -13
  37. package/compiler/wasm/compile_test.go +37 -4
  38. package/compiler/{wasm_api.go → wasm-api.go} +57 -7
  39. package/dist/gs/builtin/hostio.js +5 -0
  40. package/dist/gs/builtin/hostio.js.map +1 -1
  41. package/dist/gs/builtin/slice.d.ts +13 -2
  42. package/dist/gs/builtin/slice.js +187 -6
  43. package/dist/gs/builtin/slice.js.map +1 -1
  44. package/dist/gs/builtin/type.d.ts +13 -5
  45. package/dist/gs/builtin/type.js +153 -60
  46. package/dist/gs/builtin/type.js.map +1 -1
  47. package/dist/gs/builtin/varRef.d.ts +11 -0
  48. package/dist/gs/builtin/varRef.js +57 -2
  49. package/dist/gs/builtin/varRef.js.map +1 -1
  50. package/dist/gs/bytes/buffer.gs.js +1 -1
  51. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  52. package/dist/gs/bytes/reader.gs.js +1 -1
  53. package/dist/gs/bytes/reader.gs.js.map +1 -1
  54. package/dist/gs/compress/zlib/index.d.ts +10 -3
  55. package/dist/gs/compress/zlib/index.js +50 -16
  56. package/dist/gs/compress/zlib/index.js.map +1 -1
  57. package/dist/gs/encoding/json/index.d.ts +114 -0
  58. package/dist/gs/encoding/json/index.js +544 -36
  59. package/dist/gs/encoding/json/index.js.map +1 -1
  60. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +101 -0
  61. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +589 -0
  62. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  63. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
  64. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
  65. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
  66. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  67. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  68. package/dist/gs/go/scanner/index.d.ts +2 -0
  69. package/dist/gs/go/scanner/index.js +29 -5
  70. package/dist/gs/go/scanner/index.js.map +1 -1
  71. package/dist/gs/go/token/index.js +22 -6
  72. package/dist/gs/go/token/index.js.map +1 -1
  73. package/dist/gs/hash/index.d.ts +6 -0
  74. package/dist/gs/hash/index.js +20 -0
  75. package/dist/gs/hash/index.js.map +1 -1
  76. package/dist/gs/internal/byteorder/index.js +2 -2
  77. package/dist/gs/internal/byteorder/index.js.map +1 -1
  78. package/dist/gs/internal/goarch/index.d.ts +43 -3
  79. package/dist/gs/internal/goarch/index.js +42 -10
  80. package/dist/gs/internal/goarch/index.js.map +1 -1
  81. package/dist/gs/io/fs/fs.js +26 -14
  82. package/dist/gs/io/fs/fs.js.map +1 -1
  83. package/dist/gs/io/fs/readdir.js +4 -2
  84. package/dist/gs/io/fs/readdir.js.map +1 -1
  85. package/dist/gs/io/fs/sub.js +8 -1
  86. package/dist/gs/io/fs/sub.js.map +1 -1
  87. package/dist/gs/io/io.d.ts +2 -0
  88. package/dist/gs/io/io.js.map +1 -1
  89. package/dist/gs/math/bits/index.d.ts +5 -0
  90. package/dist/gs/math/bits/index.js +16 -4
  91. package/dist/gs/math/bits/index.js.map +1 -1
  92. package/dist/gs/mime/index.d.ts +16 -0
  93. package/dist/gs/mime/index.js +315 -6
  94. package/dist/gs/mime/index.js.map +1 -1
  95. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  96. package/dist/gs/net/http/httptest/index.js +85 -6
  97. package/dist/gs/net/http/httptest/index.js.map +1 -1
  98. package/dist/gs/net/http/index.d.ts +300 -5
  99. package/dist/gs/net/http/index.js +1598 -58
  100. package/dist/gs/net/http/index.js.map +1 -1
  101. package/dist/gs/os/dir_unix.gs.js +1 -1
  102. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  103. package/dist/gs/os/error.gs.js +1 -1
  104. package/dist/gs/os/error.gs.js.map +1 -1
  105. package/dist/gs/os/exec.gs.d.ts +1 -0
  106. package/dist/gs/os/exec.gs.js +4 -8
  107. package/dist/gs/os/exec.gs.js.map +1 -1
  108. package/dist/gs/os/exec_posix.gs.js +1 -1
  109. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  110. package/dist/gs/os/index.d.ts +1 -1
  111. package/dist/gs/os/index.js +1 -1
  112. package/dist/gs/os/index.js.map +1 -1
  113. package/dist/gs/os/proc.gs.d.ts +4 -0
  114. package/dist/gs/os/proc.gs.js +12 -6
  115. package/dist/gs/os/proc.gs.js.map +1 -1
  116. package/dist/gs/os/root_js.gs.js +1 -1
  117. package/dist/gs/os/root_js.gs.js.map +1 -1
  118. package/dist/gs/os/types.gs.js +1 -1
  119. package/dist/gs/os/types.gs.js.map +1 -1
  120. package/dist/gs/os/types_js.gs.js +1 -1
  121. package/dist/gs/os/types_js.gs.js.map +1 -1
  122. package/dist/gs/os/types_unix.gs.js +1 -1
  123. package/dist/gs/os/types_unix.gs.js.map +1 -1
  124. package/dist/gs/path/path.js +11 -7
  125. package/dist/gs/path/path.js.map +1 -1
  126. package/dist/gs/reflect/index.d.ts +5 -4
  127. package/dist/gs/reflect/index.js +4 -3
  128. package/dist/gs/reflect/index.js.map +1 -1
  129. package/dist/gs/reflect/map.js +15 -0
  130. package/dist/gs/reflect/map.js.map +1 -1
  131. package/dist/gs/reflect/type.d.ts +25 -6
  132. package/dist/gs/reflect/type.js +1475 -228
  133. package/dist/gs/reflect/type.js.map +1 -1
  134. package/dist/gs/reflect/types.d.ts +14 -6
  135. package/dist/gs/reflect/types.js +35 -1
  136. package/dist/gs/reflect/types.js.map +1 -1
  137. package/dist/gs/reflect/value.d.ts +1 -0
  138. package/dist/gs/reflect/value.js +83 -41
  139. package/dist/gs/reflect/value.js.map +1 -1
  140. package/dist/gs/reflect/visiblefields.js +4 -140
  141. package/dist/gs/reflect/visiblefields.js.map +1 -1
  142. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  143. package/dist/gs/runtime/pprof/index.js +50 -30
  144. package/dist/gs/runtime/pprof/index.js.map +1 -1
  145. package/dist/gs/runtime/runtime.js +5 -4
  146. package/dist/gs/runtime/runtime.js.map +1 -1
  147. package/dist/gs/runtime/trace/index.js +5 -19
  148. package/dist/gs/runtime/trace/index.js.map +1 -1
  149. package/dist/gs/strconv/atoi.gs.js +1 -1
  150. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  151. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  152. package/dist/gs/strconv/complex.gs.js +148 -0
  153. package/dist/gs/strconv/complex.gs.js.map +1 -0
  154. package/dist/gs/strconv/index.d.ts +1 -0
  155. package/dist/gs/strconv/index.js +1 -0
  156. package/dist/gs/strconv/index.js.map +1 -1
  157. package/dist/gs/strings/builder.js +1 -1
  158. package/dist/gs/strings/reader.js +9 -5
  159. package/dist/gs/strings/reader.js.map +1 -1
  160. package/dist/gs/strings/replace.js +15 -7
  161. package/dist/gs/strings/replace.js.map +1 -1
  162. package/dist/gs/strings/strings.d.ts +5 -0
  163. package/dist/gs/strings/strings.js +57 -5
  164. package/dist/gs/strings/strings.js.map +1 -1
  165. package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
  166. package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
  167. package/dist/gs/sync/atomic/type.gs.js +9 -9
  168. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  169. package/dist/gs/sync/atomic/value.gs.js +2 -2
  170. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  171. package/dist/gs/syscall/env.js +22 -14
  172. package/dist/gs/syscall/env.js.map +1 -1
  173. package/dist/gs/testing/testing.js +55 -13
  174. package/dist/gs/testing/testing.js.map +1 -1
  175. package/dist/gs/time/time.d.ts +24 -1
  176. package/dist/gs/time/time.js +43 -3
  177. package/dist/gs/time/time.js.map +1 -1
  178. package/dist/gs/unique/index.js +7 -1
  179. package/dist/gs/unique/index.js.map +1 -1
  180. package/go.mod +3 -3
  181. package/go.sum +16 -0
  182. package/gs/builtin/hostio.test.ts +16 -0
  183. package/gs/builtin/hostio.ts +7 -0
  184. package/gs/builtin/runtime-contract.test.ts +246 -21
  185. package/gs/builtin/slice.ts +269 -24
  186. package/gs/builtin/type.ts +226 -59
  187. package/gs/builtin/varRef.ts +85 -2
  188. package/gs/bytes/buffer.gs.ts +1 -1
  189. package/gs/bytes/reader.gs.ts +1 -1
  190. package/gs/compress/zlib/index.test.ts +62 -1
  191. package/gs/compress/zlib/index.ts +53 -16
  192. package/gs/compress/zlib/parity.json +51 -0
  193. package/gs/encoding/json/index.test.ts +360 -6
  194. package/gs/encoding/json/index.ts +679 -38
  195. package/gs/encoding/json/parity.json +81 -0
  196. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +373 -3
  197. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +893 -1
  198. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
  199. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
  200. package/gs/github.com/pkg/errors/errors.ts +54 -30
  201. package/gs/go/scanner/index.test.ts +39 -56
  202. package/gs/go/scanner/index.ts +33 -5
  203. package/gs/go/scanner/parity.json +27 -0
  204. package/gs/go/token/index.ts +22 -6
  205. package/gs/hash/index.test.ts +20 -33
  206. package/gs/hash/index.ts +28 -0
  207. package/gs/hash/parity.json +21 -0
  208. package/gs/internal/byteorder/index.test.ts +2 -2
  209. package/gs/internal/byteorder/index.ts +2 -2
  210. package/gs/internal/goarch/index.test.ts +32 -0
  211. package/gs/internal/goarch/index.ts +45 -13
  212. package/gs/internal/goarch/parity.json +144 -0
  213. package/gs/io/fs/fs.ts +26 -14
  214. package/gs/io/fs/readdir.ts +4 -4
  215. package/gs/io/fs/sub.ts +8 -1
  216. package/gs/io/io.ts +1 -0
  217. package/gs/io/parity.json +162 -0
  218. package/gs/math/bits/index.test.ts +14 -1
  219. package/gs/math/bits/index.ts +23 -4
  220. package/gs/math/bits/parity.json +156 -0
  221. package/gs/mime/index.test.ts +90 -0
  222. package/gs/mime/index.ts +369 -6
  223. package/gs/mime/parity.json +36 -0
  224. package/gs/net/http/httptest/index.test.ts +98 -2
  225. package/gs/net/http/httptest/index.ts +101 -6
  226. package/gs/net/http/httptest/parity.json +15 -0
  227. package/gs/net/http/index.test.ts +781 -12
  228. package/gs/net/http/index.ts +1860 -139
  229. package/gs/net/http/meta.json +16 -1
  230. package/gs/net/http/parity.json +193 -0
  231. package/gs/os/dir_unix.gs.ts +1 -1
  232. package/gs/os/error.gs.ts +1 -1
  233. package/gs/os/exec.gs.ts +4 -8
  234. package/gs/os/exec_posix.gs.ts +1 -1
  235. package/gs/os/index.test.ts +9 -0
  236. package/gs/os/index.ts +1 -0
  237. package/gs/os/parity.json +9 -0
  238. package/gs/os/proc.gs.ts +18 -5
  239. package/gs/os/proc.test.ts +26 -0
  240. package/gs/os/root_js.gs.ts +1 -1
  241. package/gs/os/types.gs.ts +1 -1
  242. package/gs/os/types_js.gs.ts +1 -1
  243. package/gs/os/types_unix.gs.ts +1 -1
  244. package/gs/path/path.ts +11 -7
  245. package/gs/reflect/field.test.ts +37 -15
  246. package/gs/reflect/function-types.test.ts +518 -22
  247. package/gs/reflect/index.ts +8 -6
  248. package/gs/reflect/map.ts +20 -0
  249. package/gs/reflect/meta.json +6 -4
  250. package/gs/reflect/parity.json +234 -0
  251. package/gs/reflect/sliceat.test.ts +156 -0
  252. package/gs/reflect/structof.test.ts +401 -0
  253. package/gs/reflect/type.ts +1961 -317
  254. package/gs/reflect/typefor.test.ts +530 -10
  255. package/gs/reflect/types.ts +43 -18
  256. package/gs/reflect/value.ts +105 -45
  257. package/gs/reflect/visiblefields.ts +5 -168
  258. package/gs/runtime/parity.json +24 -0
  259. package/gs/runtime/pprof/index.test.ts +29 -7
  260. package/gs/runtime/pprof/index.ts +56 -30
  261. package/gs/runtime/pprof/parity.json +27 -0
  262. package/gs/runtime/runtime.test.ts +3 -1
  263. package/gs/runtime/runtime.ts +4 -3
  264. package/gs/runtime/trace/index.test.ts +5 -3
  265. package/gs/runtime/trace/index.ts +8 -20
  266. package/gs/runtime/trace/parity.json +36 -0
  267. package/gs/strconv/atoi.gs.ts +1 -1
  268. package/gs/strconv/complex.gs.ts +174 -0
  269. package/gs/strconv/complex.test.ts +65 -0
  270. package/gs/strconv/index.ts +1 -0
  271. package/gs/strconv/parity.json +120 -0
  272. package/gs/strings/builder.ts +1 -1
  273. package/gs/strings/parity.json +186 -0
  274. package/gs/strings/reader.ts +9 -5
  275. package/gs/strings/replace.ts +15 -7
  276. package/gs/strings/strings.test.ts +22 -2
  277. package/gs/strings/strings.ts +64 -6
  278. package/gs/sync/atomic/doc_64.gs.ts +6 -7
  279. package/gs/sync/atomic/doc_64.test.ts +43 -0
  280. package/gs/sync/atomic/type.gs.ts +9 -9
  281. package/gs/sync/atomic/value.gs.ts +2 -2
  282. package/gs/syscall/env.ts +29 -14
  283. package/gs/testing/testing.test.ts +67 -0
  284. package/gs/testing/testing.ts +87 -19
  285. package/gs/time/parity.json +225 -0
  286. package/gs/time/time.test.ts +20 -2
  287. package/gs/time/time.ts +49 -7
  288. package/gs/unique/index.ts +7 -1
  289. package/package.json +4 -2
  290. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  291. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
  292. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  293. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
  294. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
  295. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
@@ -242,6 +242,87 @@ func TestSemanticModelRecordsInterfaceAssertionAndNilFacts(t *testing.T) {
242
242
  }
243
243
  }
244
244
 
245
+ func TestSemanticModelRejectsInterfaceMethodSignatureMismatch(t *testing.T) {
246
+ moduleDir := writePackageGraphFixture(t, map[string]string{
247
+ "go.mod": "module example.test/signaturemismatch\n\ngo 1.25.3\n",
248
+ "main.go": strings.Join([]string{
249
+ "package signaturemismatch",
250
+ "type Reader interface { Read() int }",
251
+ "type Impl struct{}",
252
+ "func (Impl) Read() string { return \"bad\" }",
253
+ "",
254
+ }, "\n"),
255
+ })
256
+ graph := loadPackageGraph(t, &CompileRequest{
257
+ Patterns: []string{"."},
258
+ Dir: moduleDir,
259
+ OutputPath: filepath.Join(t.TempDir(), "out"),
260
+ DependencyMode: DependencyModeRequested,
261
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
262
+ })
263
+ model := buildSemanticModel(t, graph)
264
+
265
+ if hasInterfaceImplementation(model, "Impl", "Reader", false) {
266
+ t.Fatalf("unexpected Impl -> Reader implementation: %#v", model.interfaceImplementations)
267
+ }
268
+ }
269
+
270
+ func TestSemanticModelAcceptsPromotedInterfaceMethods(t *testing.T) {
271
+ moduleDir := writePackageGraphFixture(t, map[string]string{
272
+ "go.mod": "module example.test/promoted\n\ngo 1.25.3\n",
273
+ "main.go": strings.Join([]string{
274
+ "package promoted",
275
+ "type Reader interface { Read() int }",
276
+ "type Base struct{}",
277
+ "func (Base) Read() int { return 1 }",
278
+ "type Impl struct { Base }",
279
+ "",
280
+ }, "\n"),
281
+ })
282
+ graph := loadPackageGraph(t, &CompileRequest{
283
+ Patterns: []string{"."},
284
+ Dir: moduleDir,
285
+ OutputPath: filepath.Join(t.TempDir(), "out"),
286
+ DependencyMode: DependencyModeRequested,
287
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
288
+ })
289
+ model := buildSemanticModel(t, graph)
290
+
291
+ if !hasInterfaceImplementation(model, "Impl", "Reader", false) {
292
+ t.Fatalf("missing promoted Impl -> Reader implementation: %#v", model.interfaceImplementations)
293
+ }
294
+ }
295
+
296
+ func TestSemanticModelKeepsUnexportedInterfaceMethodsPackageScoped(t *testing.T) {
297
+ moduleDir := writePackageGraphFixture(t, map[string]string{
298
+ "go.mod": "module example.test/unexportediface\n\ngo 1.25.3\n",
299
+ "dep/dep.go": strings.Join([]string{
300
+ "package dep",
301
+ "type private interface { read() int }",
302
+ "",
303
+ }, "\n"),
304
+ "main.go": strings.Join([]string{
305
+ "package unexportediface",
306
+ "import _ \"example.test/unexportediface/dep\"",
307
+ "type Impl struct{}",
308
+ "func (Impl) read() int { return 1 }",
309
+ "",
310
+ }, "\n"),
311
+ })
312
+ graph := loadPackageGraph(t, &CompileRequest{
313
+ Patterns: []string{"."},
314
+ Dir: moduleDir,
315
+ OutputPath: filepath.Join(t.TempDir(), "out"),
316
+ DependencyMode: DependencyModeAll,
317
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
318
+ })
319
+ model := buildSemanticModel(t, graph)
320
+
321
+ if hasInterfaceImplementation(model, "Impl", "private", false) {
322
+ t.Fatalf("unexpected cross-package unexported implementation: %#v", model.interfaceImplementations)
323
+ }
324
+ }
325
+
245
326
  func TestSemanticModelColorsAsyncFunctionsAndOverrides(t *testing.T) {
246
327
  moduleDir := writePackageGraphFixture(t, map[string]string{
247
328
  "go.mod": "module example.test/async\n\ngo 1.25.3\n",
@@ -356,7 +437,7 @@ func TestSemanticModelPropagatesAsyncToOverrideInterfaceMethods(t *testing.T) {
356
437
  if implementation.typ != nil && implementation.typ.Obj().Name() == "asyncWriter" &&
357
438
  implementation.iface != nil && implementation.iface.Obj().Pkg().Path() == "io" &&
358
439
  implementation.iface.Obj().Name() == "Writer" &&
359
- implementation.pointer && implementation.asyncMethods["Write"] {
440
+ implementation.pointer {
360
441
  found = true
361
442
  break
362
443
  }
@@ -14,6 +14,7 @@ type CompileService struct {
14
14
  emitterOwner *TypeScriptEmitOwner
15
15
  runtimeOwner *RuntimeContractOwner
16
16
  overrideOwner *OverrideRegistryOwner
17
+ parityOwner *OverrideParityVerifier
17
18
  }
18
19
 
19
20
  // NewCompileService creates a compile service with every pipeline owner.
@@ -28,6 +29,7 @@ func NewCompileService(overrideDirs ...string) *CompileService {
28
29
  emitterOwner: NewTypeScriptEmitOwner(runtimeOwner),
29
30
  runtimeOwner: runtimeOwner,
30
31
  overrideOwner: overrideOwner,
32
+ parityOwner: NewOverrideParityVerifier(),
31
33
  }
32
34
  }
33
35
 
@@ -96,6 +98,19 @@ func (s *CompileService) Compile(ctx context.Context, req *CompileRequest) (*Com
96
98
  return result, NewCompileError(diagnostics)
97
99
  }
98
100
 
101
+ overrideFacts, factsDiagnostics := s.overrideOwner.Facts(ctx)
102
+ diagnostics = append(diagnostics, factsDiagnostics...)
103
+ if diagnosticsHaveErrors(diagnostics) {
104
+ result.Diagnostics = diagnostics
105
+ return result, NewCompileError(diagnostics)
106
+ }
107
+ parityDiagnostics := s.parityOwner.Verify(ctx, graph, overrideFacts)
108
+ diagnostics = append(diagnostics, parityDiagnostics...)
109
+ if diagnosticsHaveErrors(diagnostics) {
110
+ result.Diagnostics = diagnostics
111
+ return result, NewCompileError(diagnostics)
112
+ }
113
+
99
114
  semanticModel, semanticDiagnostics := s.semanticOwner.Build(ctx, graph)
100
115
  diagnostics = append(diagnostics, semanticDiagnostics...)
101
116
  if diagnosticsHaveErrors(diagnostics) {
@@ -110,7 +125,12 @@ func (s *CompileService) Compile(ctx context.Context, req *CompileRequest) (*Com
110
125
  return result, NewCompileError(diagnostics)
111
126
  }
112
127
 
113
- loweredProgram, loweringDiagnostics := s.loweringOwner.Build(ctx, semanticModel)
128
+ loweredProgram, loweringDiagnostics := s.loweringOwner.Build(ctx, semanticModel, LoweringOptions{
129
+ SourceRoot: protobufTypeScriptBindingRoot(req.Dir),
130
+ DisplayRoot: req.Dir,
131
+ OutputPath: req.OutputPath,
132
+ ProtobufTypeScriptBinding: req.ProtobufTypeScriptBinding,
133
+ })
114
134
  diagnostics = append(diagnostics, loweringDiagnostics...)
115
135
  if diagnosticsHaveErrors(diagnostics) {
116
136
  result.Diagnostics = diagnostics
@@ -911,6 +911,49 @@ func TestCompilePackagesAnnotatesNewPointerShortDecls(t *testing.T) {
911
911
  }
912
912
  }
913
913
 
914
+ func TestCompilePackagesAnnotatesNewArrayPointerShortDecls(t *testing.T) {
915
+ moduleDir := writePackageGraphFixture(t, map[string]string{
916
+ "go.mod": "module example.test/newarrayptrdecl\n\ngo 1.25.3\n",
917
+ "main.go": strings.Join([]string{
918
+ "package main",
919
+ "func use(*[32]byte) {}",
920
+ "func main() {",
921
+ " buf := new([32]byte)",
922
+ " use(buf)",
923
+ " if true {",
924
+ " buf = nil",
925
+ " }",
926
+ " use(buf)",
927
+ "}",
928
+ "",
929
+ }, "\n"),
930
+ })
931
+ outputDir := filepath.Join(t.TempDir(), "output")
932
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
933
+ if err != nil {
934
+ t.Fatal(err.Error())
935
+ }
936
+
937
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
938
+ t.Fatal(err.Error())
939
+ }
940
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "newarrayptrdecl", "main.gs.ts")
941
+ content, err := os.ReadFile(outputFile)
942
+ if err != nil {
943
+ t.Fatal(err.Error())
944
+ }
945
+ text := string(content)
946
+ for _, want := range []string{
947
+ "let buf: $.VarRef<Uint8Array> | null = $.varRef<Uint8Array>(new Uint8Array(32))",
948
+ "buf = null",
949
+ "use(buf)",
950
+ } {
951
+ if !strings.Contains(text, want) {
952
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
953
+ }
954
+ }
955
+ }
956
+
914
957
  func TestCompilePackagesEmitsShadowedBuiltinCalls(t *testing.T) {
915
958
  moduleDir := writePackageGraphFixture(t, map[string]string{
916
959
  "go.mod": "module example.test/shadowbuiltin\n\ngo 1.25.3\n",
@@ -1453,6 +1496,53 @@ func TestCompilePackagesEmitsIndexAddressRefs(t *testing.T) {
1453
1496
  }
1454
1497
  }
1455
1498
 
1499
+ func TestCompilePackagesLowersUnsafeBytePointerArithmetic(t *testing.T) {
1500
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1501
+ "go.mod": "module example.test/unsafeaddr\n\ngo 1.25.3\n",
1502
+ "main.go": strings.Join([]string{
1503
+ "package main",
1504
+ "import \"unsafe\"",
1505
+ "func Set(bits []uint64, idx uint64, mask []uint8) uint8 {",
1506
+ " ptr := unsafe.Pointer(uintptr(unsafe.Pointer(&bits[idx>>6])) + uintptr((idx%64)>>3))",
1507
+ " *(*uint8)(ptr) |= mask[idx%8]",
1508
+ " return *(*uint8)(ptr)",
1509
+ "}",
1510
+ "func CopyBlock(dst []byte, words *[16]uint32) {",
1511
+ " *(*[64]byte)(unsafe.Pointer(&dst[0])) = *(*[64]byte)(unsafe.Pointer(&words[0]))",
1512
+ "}",
1513
+ "",
1514
+ }, "\n"),
1515
+ })
1516
+ outputDir := filepath.Join(t.TempDir(), "output")
1517
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1518
+ if err != nil {
1519
+ t.Fatal(err.Error())
1520
+ }
1521
+
1522
+ _, err = comp.CompilePackages(context.Background(), ".")
1523
+ if err != nil {
1524
+ t.Fatal(err.Error())
1525
+ }
1526
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "unsafeaddr", "main.gs.ts")
1527
+ content, err := os.ReadFile(outputFile)
1528
+ if err != nil {
1529
+ t.Fatal(err.Error())
1530
+ }
1531
+ text := string(content)
1532
+ if !strings.Contains(text, "$.indexByteAddress(bits!, $.uint64Shr(idx, 6), 8)") {
1533
+ t.Fatalf("missing byte-addressed unsafe pointer root:\n%s", text)
1534
+ }
1535
+ if !strings.Contains(text, "$.unsafePointerRef<number>(ptr).value =") {
1536
+ t.Fatalf("missing unsafe pointer storage ref:\n%s", text)
1537
+ }
1538
+ if !strings.Contains(text, "return $.uint($.unsafePointerRef<number>(ptr).value, 8)") {
1539
+ t.Fatalf("missing unsafe pointer value ref:\n%s", text)
1540
+ }
1541
+ if !strings.Contains(text, "$.arrayPointerFromIndexRef<number>($.indexRef($.pointerValue<number[]>(words), 0), 64, 4, 1)") {
1542
+ t.Fatalf("missing byte-view array pointer conversion:\n%s", text)
1543
+ }
1544
+ }
1545
+
1456
1546
  func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
1457
1547
  moduleDir := writePackageGraphFixture(t, map[string]string{
1458
1548
  "go.mod": "module example.test/structs\n\ngo 1.25.3\n",
@@ -1516,8 +1606,8 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
1516
1606
  "Counter.prototype.Set.call(pointer, 2)",
1517
1607
  "Counter.prototype.Set.call(NewCounter(), 5)",
1518
1608
  "let [, ok] = $.typeAssertTuple<Counter | $.VarRef<Counter> | null>(iface, { kind: $.TypeKind.Pointer, elemType: \"main.Counter\" })",
1519
- "\"Value\": { type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\" }",
1520
- "\"ID\": { kind: $.TypeKind.Basic, name: \"int32\", typeName: \"main.ObjectID\" }",
1609
+ "{ name: \"Value\", key: \"Value\", type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\", index: [0], offset: 0, exported: true }",
1610
+ "{ name: \"ID\", key: \"ID\", type: { kind: $.TypeKind.Basic, name: \"int32\", typeName: \"main.ObjectID\" }, index: [1], offset: 8, exported: true }",
1521
1611
  } {
1522
1612
  if !strings.Contains(text, want) {
1523
1613
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -2345,10 +2435,13 @@ func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssert
2345
2435
  "Read(): string",
2346
2436
  "Close(): string",
2347
2437
  "$.registerInterfaceType(\n\t\"main.ReadCloser\"",
2438
+ "{ name: \"Close\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }]\n);",
2348
2439
  "((__receiver) => () => __receiver.Inc())($.pointerValue<Counter>(counter))",
2349
- "$.namedFunction(greet, \"main.Greeter\")",
2440
+ "$.namedFunction(greet, \"main.Greeter\", ({ kind: $.TypeKind.Function, name: \"main.Greeter\"",
2441
+ "params: [{ kind: $.TypeKind.Basic, name: \"string\" }]",
2442
+ "results: [{ kind: $.TypeKind.Basic, name: \"string\" }]",
2350
2443
  "$.interfaceValue<any>(null, \"*struct{Name string}\")",
2351
- "elemType: { kind: $.TypeKind.Struct, methods: [], fields: {\"Name\": { kind: $.TypeKind.Basic, name: \"string\" }} }",
2444
+ "elemType: { kind: $.TypeKind.Struct, methods: [], fields: [{ name: \"Name\", key: \"Name\", type: { kind: $.TypeKind.Basic, name: \"string\" }",
2352
2445
  "let fn = __goscriptTuple",
2353
2446
  "switch (true)",
2354
2447
  "case $.typeAssert<Exclude<ReadCloser, null>>(__goscriptTypeSwitchValue, \"main.ReadCloser\").ok",
@@ -2612,9 +2705,9 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
2612
2705
  "$.mapSet(seen, 1, {})",
2613
2706
  "$.genericZero(__typeArgs, \"T\", null)",
2614
2707
  "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
2615
- "ZeroValue({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)} }})",
2616
- "CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)} }}, zero)",
2617
- "Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)} }}, null)",
2708
+ "ZeroValue({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }})",
2709
+ "CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }}, zero)",
2710
+ "Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }}, null)",
2618
2711
  } {
2619
2712
  if !strings.Contains(text, want) {
2620
2713
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -2761,7 +2854,7 @@ func TestCompilePackagesEmitsRecursiveFunctionTypeInfo(t *testing.T) {
2761
2854
  text := string(content)
2762
2855
  for _, want := range []string{
2763
2856
  "export type Handler = ((_p0: ((_p0: Handler | null) => Handler | null | globalThis.Promise<Handler | null>) | null) => Handler | null | globalThis.Promise<Handler | null>) | null",
2764
- "\"Next\": ({ kind: $.TypeKind.Function, name: \"main.Handler\"",
2857
+ "{ name: \"Next\", key: \"Next\", type: ({ kind: $.TypeKind.Function, name: \"main.Handler\"",
2765
2858
  "params: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
2766
2859
  "results: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
2767
2860
  } {
@@ -4127,6 +4220,12 @@ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
4127
4220
  " if value > 0 {",
4128
4221
  " goto Again",
4129
4222
  " }",
4223
+ "Drive:",
4224
+ " window := value + 1",
4225
+ " if window < 0 {",
4226
+ " goto Drive",
4227
+ " }",
4228
+ " println(window)",
4130
4229
  " release := func() { println(\"release\") }",
4131
4230
  " rel := &release",
4132
4231
  " (*rel)()",
@@ -4163,6 +4262,9 @@ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
4163
4262
  "break Block",
4164
4263
  "Again: while (true)",
4165
4264
  "continue Again",
4265
+ "Drive: while (true)",
4266
+ "var window = value + 1",
4267
+ "$.println(window)",
4166
4268
  "($.pointerValue<(() => void) | null>(rel))!()",
4167
4269
  "$.functionValue((): void => {\n\t\tusing __defer = new $.DisposableStack()",
4168
4270
  "__defer.defer(() => { $.println(\"wrapped deferred\") })",
@@ -4299,10 +4401,15 @@ func TestCompilePackagesLowersUnaryBitwiseComplement(t *testing.T) {
4299
4401
  "main.go": strings.Join([]string{
4300
4402
  "package main",
4301
4403
  "var value = 1",
4404
+ "var wide uint64 = 1",
4405
+ "var signed int64 = 1",
4406
+ "func invert(crc uint64) uint64 {",
4407
+ " return ^crc",
4408
+ "}",
4302
4409
  "func main() {",
4303
4410
  " mask := 7",
4304
4411
  " mask &^= 3",
4305
- " println(^value, value &^ 3, mask, 0700)",
4412
+ " println(^value, ^wide, ^signed, invert(wide), value &^ 3, mask, 0700)",
4306
4413
  "}",
4307
4414
  "",
4308
4415
  }, "\n"),
@@ -4323,8 +4430,9 @@ func TestCompilePackagesLowersUnaryBitwiseComplement(t *testing.T) {
4323
4430
  }
4324
4431
  text := string(content)
4325
4432
  for _, want := range []string{
4433
+ "return $.uint($.uint64Xor(crc, -1n), 64)",
4326
4434
  "mask = mask & ~((3))",
4327
- "$.println(~value, value & ~(3), mask, 0o700)",
4435
+ "$.println($.int64Xor(value, -1n), $.uint($.uint64Xor(wide, -1n), 64), $.int($.int64Xor(signed, -1n)), $.uint(invert($.uint(wide, 64)), 64), value & ~(3), mask, 0o700)",
4328
4436
  } {
4329
4437
  if !strings.Contains(text, want) {
4330
4438
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -134,6 +134,7 @@ func (o *TypeScriptEmitOwner) EmitToMemory(
134
134
 
135
135
  func (o *TypeScriptEmitOwner) renderLoweredFile(pkg *loweredPackage, file *loweredFile) string {
136
136
  var b strings.Builder
137
+ b.Grow(estimateLoweredFileSize(file))
137
138
  if file.sourcePath != "" {
138
139
  b.WriteString("// Generated file based on ")
139
140
  b.WriteString(filepath.Base(file.sourcePath))
@@ -219,6 +220,104 @@ func (o *TypeScriptEmitOwner) renderLoweredFile(pkg *loweredPackage, file *lower
219
220
  return b.String()
220
221
  }
221
222
 
223
+ func estimateLoweredFileSize(file *loweredFile) int {
224
+ if file == nil {
225
+ return 0
226
+ }
227
+ size := 128 + len(file.imports)*96 + len(file.decls)*32
228
+ for _, imp := range file.imports {
229
+ size += len(imp.alias) + len(imp.source)
230
+ }
231
+ for _, decl := range file.decls {
232
+ size += len(decl.code)
233
+ if decl.function != nil {
234
+ size += estimateLoweredFunctionSize(decl.function)
235
+ }
236
+ if decl.structType != nil {
237
+ size += estimateLoweredStructSize(decl.structType)
238
+ }
239
+ }
240
+ return size
241
+ }
242
+
243
+ func estimateLoweredStructSize(structType *loweredStruct) int {
244
+ if structType == nil {
245
+ return 0
246
+ }
247
+ size := 256 + len(structType.name) + len(structType.typeName) + len(structType.fields)*128
248
+ for _, field := range structType.fields {
249
+ size += len(field.name) + len(field.runtimeName) + len(field.typ) + len(field.zero) + len(field.runtimeType) +
250
+ len(field.doc) + len(field.tag)
251
+ }
252
+ for idx := range structType.methods {
253
+ size += estimateLoweredFunctionSize(&structType.methods[idx])
254
+ }
255
+ return size
256
+ }
257
+
258
+ func estimateLoweredFunctionSize(fn *loweredFunction) int {
259
+ if fn == nil {
260
+ return 0
261
+ }
262
+ size := 192 + len(fn.name) + len(fn.result) + len(fn.receiverAlias) + len(fn.receiverType) + len(fn.receiverValue)
263
+ for _, typeParam := range fn.typeParams {
264
+ size += len(typeParam) + 2
265
+ }
266
+ for _, param := range fn.params {
267
+ size += len(param.name) + len(param.typ) + 4
268
+ }
269
+ for _, result := range fn.namedResults {
270
+ size += len(result.name) + len(result.typ) + len(result.zero) + len(result.returnExpr) + 16
271
+ }
272
+ size += estimateLoweredStmtsSize(fn.paramBindings)
273
+ size += estimateLoweredStmtsSize(fn.body)
274
+ return size
275
+ }
276
+
277
+ func estimateLoweredStmtsSize(stmts []loweredStmt) int {
278
+ size := len(stmts) * 24
279
+ for _, stmt := range stmts {
280
+ size += len(stmt.text) + len(stmt.leading)*16
281
+ for _, line := range stmt.leading {
282
+ size += len(line)
283
+ }
284
+ size += estimateLoweredStmtsSize(stmt.children)
285
+ size += estimateLoweredStmtsSize(stmt.elseBody)
286
+ if stmt.rangeFunc != nil {
287
+ size += len(stmt.rangeFunc.value) + len(stmt.rangeFunc.params)*16 + estimateLoweredStmtsSize(stmt.rangeFunc.body)
288
+ }
289
+ if stmt.switchStmt != nil {
290
+ size += len(stmt.switchStmt.value)
291
+ for _, switchCase := range stmt.switchStmt.cases {
292
+ size += len(switchCase.values)*16 + estimateLoweredStmtsSize(switchCase.body)
293
+ for _, value := range switchCase.values {
294
+ size += len(value)
295
+ }
296
+ }
297
+ }
298
+ if stmt.selectStmt != nil {
299
+ size += len(stmt.selectStmt.hasReturn) + len(stmt.selectStmt.value) + len(stmt.selectStmt.result) + len(stmt.selectStmt.resultType)
300
+ for _, selectCase := range stmt.selectStmt.cases {
301
+ size += len(selectCase.channel) + len(selectCase.value) + estimateLoweredStmtsSize(selectCase.prelude) +
302
+ estimateLoweredStmtsSize(selectCase.body)
303
+ }
304
+ }
305
+ if stmt.typeSwitch != nil {
306
+ size += len(stmt.typeSwitch.value) + len(stmt.typeSwitch.varName) + estimateLoweredStmtsSize(stmt.typeSwitch.defaultBody)
307
+ for _, switchCase := range stmt.typeSwitch.cases {
308
+ for _, typ := range switchCase.types {
309
+ size += len(typ)
310
+ }
311
+ for _, typ := range switchCase.tsTypes {
312
+ size += len(typ)
313
+ }
314
+ size += estimateLoweredStmtsSize(switchCase.body)
315
+ }
316
+ }
317
+ }
318
+ return size
319
+ }
320
+
222
321
  func sortedStructDecls(decls []loweredDecl) []loweredDecl {
223
322
  structs := make([]loweredDecl, 0)
224
323
  names := make(map[string]bool)
@@ -421,29 +520,42 @@ func renderStruct(b *strings.Builder, structType *loweredStruct, runtimeOwner *R
421
520
  if idx != 0 {
422
521
  b.WriteString(", ")
423
522
  }
424
- methodName := method.name
425
- if method.runtimeName != "" {
426
- methodName = method.runtimeName
427
- }
428
- b.WriteString("{ name: ")
429
- b.WriteString(strconvQuote(methodName))
430
- b.WriteString(", args: [], returns: [] }")
523
+ b.WriteString(runtimeMethodSignatureExpr(method))
431
524
  }
432
525
  b.WriteString("],\n\t\t")
433
526
  b.WriteString(structType.name)
434
- b.WriteString(",\n\t\t{")
527
+ b.WriteString(",\n\t\t[")
435
528
  for idx, field := range structType.fields {
436
529
  if idx != 0 {
437
530
  b.WriteString(", ")
438
531
  }
439
- b.WriteString(strconvQuote(field.name))
440
- b.WriteString(": ")
441
- b.WriteString(runtimeStructFieldInfoExpr(field.runtimeType, field.runtimeName, field.tag))
442
- }
443
- b.WriteString("}\n\t)\n")
532
+ b.WriteString(runtimeStructFieldInfoExpr(
533
+ field.runtimeType,
534
+ field.name,
535
+ field.runtimeName,
536
+ field.tag,
537
+ field.pkgPath,
538
+ field.anonymous,
539
+ field.index,
540
+ field.offset,
541
+ field.exported,
542
+ ))
543
+ }
544
+ b.WriteString("]\n\t)\n")
444
545
  b.WriteString("}\n")
445
546
  }
446
547
 
548
+ func runtimeMethodSignatureExpr(method loweredFunction) string {
549
+ if method.runtimeSignature != "" {
550
+ return method.runtimeSignature
551
+ }
552
+ methodName := method.name
553
+ if method.runtimeName != "" {
554
+ methodName = method.runtimeName
555
+ }
556
+ return "{ name: " + strconvQuote(methodName) + ", args: [], returns: [] }"
557
+ }
558
+
447
559
  func writeLineComment(b *strings.Builder, indent string, comment string) {
448
560
  comment = strings.TrimSpace(comment)
449
561
  if comment == "" {
@@ -1158,6 +1270,9 @@ func renderIndex(pkg *loweredPackage) string {
1158
1270
  if file.sideEffect {
1159
1271
  lines = append(lines, "import \"./"+file.outputName+"\"")
1160
1272
  }
1273
+ if file.exportAll {
1274
+ lines = append(lines, "export * from \"./"+file.outputName+"\"")
1275
+ }
1161
1276
  exports := slices.Clone(file.exports)
1162
1277
  slices.Sort(exports)
1163
1278
  if len(exports) != 0 {
@@ -49,7 +49,8 @@ func TestCompileSourceReportsParseErrors(t *testing.T) {
49
49
  if err == nil {
50
50
  t.Fatal("expected compile error")
51
51
  }
52
- requireCompileDiagnostic(t, err, "goscript/wasm:parse")
52
+ diag := requireCompileDiagnostic(t, err, "goscript/wasm:parse")
53
+ requireDiagnosticPosition(t, diag, "main.go", 2, 12)
53
54
  }
54
55
 
55
56
  func TestCompileSourceReportsUnsupportedImports(t *testing.T) {
@@ -57,10 +58,27 @@ func TestCompileSourceReportsUnsupportedImports(t *testing.T) {
57
58
  if err == nil {
58
59
  t.Fatal("expected compile error")
59
60
  }
60
- requireCompileDiagnostic(t, err, "goscript/wasm:imports-unsupported")
61
+ diag := requireCompileDiagnostic(t, err, "goscript/wasm:imports-unsupported")
62
+ requireDiagnosticPosition(t, diag, "main.go", 3, 8)
61
63
  }
62
64
 
63
- func requireCompileDiagnostic(t *testing.T, err error, code string) {
65
+ func TestCompileSourceReportsTypecheckPositions(t *testing.T) {
66
+ _, err := CompileSource(strings.Join([]string{
67
+ "package main",
68
+ "",
69
+ "func main() {",
70
+ " missing()",
71
+ "}",
72
+ "",
73
+ }, "\n"), "main")
74
+ if err == nil {
75
+ t.Fatal("expected compile error")
76
+ }
77
+ diag := requireCompileDiagnostic(t, err, "goscript/wasm:typecheck")
78
+ requireDiagnosticPosition(t, diag, "main.go", 4, 5)
79
+ }
80
+
81
+ func requireCompileDiagnostic(t *testing.T, err error, code string) compiler.Diagnostic {
64
82
  t.Helper()
65
83
 
66
84
  var compileErr *compiler.CompileError
@@ -72,8 +90,23 @@ func requireCompileDiagnostic(t *testing.T, err error, code string) {
72
90
  if diag.Severity != compiler.DiagnosticSeverityError {
73
91
  t.Fatalf("expected error severity, got %s", diag.Severity)
74
92
  }
75
- return
93
+ return diag
76
94
  }
77
95
  }
78
96
  t.Fatalf("missing diagnostic %q in %#v", code, compileErr.Diagnostics)
97
+ return compiler.Diagnostic{}
98
+ }
99
+
100
+ func requireDiagnosticPosition(t *testing.T, diag compiler.Diagnostic, displayFile string, line int, column int) {
101
+ t.Helper()
102
+
103
+ if diag.Position == nil {
104
+ t.Fatalf("expected position in %#v", diag)
105
+ }
106
+ if diag.Position.DisplayFile != displayFile {
107
+ t.Fatalf("DisplayFile = %q, want %q", diag.Position.DisplayFile, displayFile)
108
+ }
109
+ if diag.Position.Line != line || diag.Position.Column != column {
110
+ t.Fatalf("position = %d:%d, want %d:%d", diag.Position.Line, diag.Position.Column, line, column)
111
+ }
79
112
  }