goscript 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/compiler/lowered-program.go +1 -0
  2. package/compiler/lowering.go +715 -44
  3. package/compiler/override-registry_test.go +43 -0
  4. package/compiler/skeleton_test.go +464 -12
  5. package/compiler/typescript-emitter.go +28 -2
  6. package/dist/gs/builtin/channel.js +36 -9
  7. package/dist/gs/builtin/channel.js.map +1 -1
  8. package/dist/gs/builtin/type.js +8 -3
  9. package/dist/gs/builtin/type.js.map +1 -1
  10. package/dist/gs/bytes/bytes.gs.d.ts +7 -5
  11. package/dist/gs/bytes/bytes.gs.js +10 -4
  12. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  13. package/dist/gs/crypto/sha1/index.d.ts +5 -0
  14. package/dist/gs/crypto/sha1/index.js +106 -0
  15. package/dist/gs/crypto/sha1/index.js.map +1 -0
  16. package/dist/gs/fmt/fmt.d.ts +1 -1
  17. package/dist/gs/fmt/fmt.js +64 -3
  18. package/dist/gs/fmt/fmt.js.map +1 -1
  19. package/dist/gs/io/io.d.ts +8 -5
  20. package/dist/gs/io/io.js +20 -2
  21. package/dist/gs/io/io.js.map +1 -1
  22. package/dist/gs/net/http/httptest/index.js +7 -5
  23. package/dist/gs/net/http/httptest/index.js.map +1 -1
  24. package/dist/gs/net/http/index.d.ts +8 -0
  25. package/dist/gs/net/http/index.js +139 -10
  26. package/dist/gs/net/http/index.js.map +1 -1
  27. package/dist/gs/os/zero_copy_posix.gs.js +1 -1
  28. package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
  29. package/gs/builtin/channel.ts +47 -9
  30. package/gs/builtin/runtime-contract.test.ts +33 -0
  31. package/gs/builtin/type.ts +12 -3
  32. package/gs/bytes/bytes.gs.ts +19 -10
  33. package/gs/bytes/bytes.test.ts +17 -0
  34. package/gs/context/context.test.ts +5 -1
  35. package/gs/crypto/sha1/index.test.ts +28 -0
  36. package/gs/crypto/sha1/index.ts +130 -0
  37. package/gs/crypto/sha1/meta.json +8 -0
  38. package/gs/fmt/fmt.test.ts +20 -0
  39. package/gs/fmt/fmt.ts +75 -5
  40. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +1 -1
  41. package/gs/io/io.test.ts +64 -0
  42. package/gs/io/io.ts +30 -12
  43. package/gs/net/http/httptest/index.test.ts +34 -2
  44. package/gs/net/http/httptest/index.ts +23 -8
  45. package/gs/net/http/index.test.ts +30 -0
  46. package/gs/net/http/index.ts +159 -10
  47. package/gs/os/zero_copy_posix.gs.ts +1 -2
  48. package/gs/sync/meta.json +1 -0
  49. package/package.json +1 -1
@@ -341,6 +341,49 @@ func TestCompilePackagesPropagatesOverrideAsyncInterfaceMethods(t *testing.T) {
341
341
  }
342
342
  }
343
343
 
344
+ func TestCompilePackagesAwaitsOverrideAsyncInterfaceMethodCalls(t *testing.T) {
345
+ moduleDir := writePackageGraphFixture(t, map[string]string{
346
+ "go.mod": "module example.test/overrideasyncinterfacemethod\n\ngo 1.25.3\n",
347
+ "main.go": strings.Join([]string{
348
+ "package main",
349
+ "import \"sync\"",
350
+ "func Use(l sync.Locker) {",
351
+ " l.Lock()",
352
+ " l.Unlock()",
353
+ "}",
354
+ "func main() {}",
355
+ "",
356
+ }, "\n"),
357
+ })
358
+ out := filepath.Join(t.TempDir(), "out")
359
+ comp, err := NewCompiler(&Config{
360
+ Dir: moduleDir,
361
+ OutputPath: out,
362
+ AllDependencies: true,
363
+ }, nil, nil)
364
+ if err != nil {
365
+ t.Fatal(err.Error())
366
+ }
367
+
368
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
369
+ t.Fatal(err.Error())
370
+ }
371
+ content, err := os.ReadFile(filepath.Join(out, "@goscript", "example.test", "overrideasyncinterfacemethod", "main.gs.ts"))
372
+ if err != nil {
373
+ t.Fatal(err.Error())
374
+ }
375
+ text := string(content)
376
+ for _, want := range []string{
377
+ "export async function Use(l: sync.Locker | null): globalThis.Promise<void>",
378
+ "await $.pointerValue<Exclude<sync.Locker, null>>(l).Lock()",
379
+ "$.pointerValue<Exclude<sync.Locker, null>>(l).Unlock()",
380
+ } {
381
+ if !strings.Contains(text, want) {
382
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
383
+ }
384
+ }
385
+ }
386
+
344
387
  func TestCompilePackagesAwaitsOverrideAsyncFunctions(t *testing.T) {
345
388
  moduleDir := writePackageGraphFixture(t, map[string]string{
346
389
  "go.mod": "module example.test/overrideasyncfunc\n\ngo 1.25.3\n",
@@ -215,11 +215,11 @@ func TestCompilePackagesLazilyInitializesCrossFilePackageVars(t *testing.T) {
215
215
  }
216
216
  text := string(content)
217
217
  for _, want := range []string{
218
- "export let two: holder = undefined as unknown as holder",
218
+ "export var two: holder = undefined as unknown as holder",
219
219
  "export function __goscript_get_two(): holder",
220
- "export let remoteZero: __goscript_a.remote = undefined as unknown as __goscript_a.remote",
220
+ "export var remoteZero: __goscript_a.remote = undefined as unknown as __goscript_a.remote",
221
221
  "export function __goscript_get_remoteZero(): __goscript_a.remote",
222
- "__goscript_a.one",
222
+ "__goscript_a.__goscript_get_one()",
223
223
  } {
224
224
  if !strings.Contains(text, want) {
225
225
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -263,7 +263,7 @@ func TestCompilePackagesLazilyInitializesSameFileLaterPackageVars(t *testing.T)
263
263
  }
264
264
  text := string(content)
265
265
  for _, want := range []string{
266
- "export let table: $.Slice<detail> = undefined as unknown as $.Slice<detail>",
266
+ "export var table: $.Slice<detail> = undefined as unknown as $.Slice<detail>",
267
267
  "export function __goscript_get_table(): $.Slice<detail>",
268
268
  "export let later: detail = $.markAsStructValue(new detail({n: 7}))",
269
269
  } {
@@ -305,7 +305,7 @@ func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *t
305
305
  }
306
306
  text := string(content)
307
307
  for _, want := range []string{
308
- "export let first: Point | $.VarRef<Point> | null = undefined as unknown as Point | $.VarRef<Point> | null",
308
+ "export var first: Point | $.VarRef<Point> | null = undefined as unknown as Point | $.VarRef<Point> | null",
309
309
  "export function __goscript_get_first(): Point | $.VarRef<Point> | null",
310
310
  "function __goscript_get___goscriptTuple",
311
311
  "export let later: number = 7",
@@ -316,6 +316,67 @@ func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *t
316
316
  }
317
317
  }
318
318
 
319
+ func TestCompilePackagesLazilyInitializesEffectFreeTypeForFromCrossFileInit(t *testing.T) {
320
+ moduleDir := writePackageGraphFixture(t, map[string]string{
321
+ "go.mod": "module example.test/lazytypefor\n\ngo 1.25.3\n",
322
+ "a.go": strings.Join([]string{
323
+ "package main",
324
+ "import \"reflect\"",
325
+ "var stringType = reflect.TypeFor[string]()",
326
+ "var _ = marker",
327
+ "",
328
+ }, "\n"),
329
+ "b.go": strings.Join([]string{
330
+ "package main",
331
+ "func readStringType() { println(stringType != nil) }",
332
+ "func main() {}",
333
+ "",
334
+ }, "\n"),
335
+ "c.go": strings.Join([]string{
336
+ "package main",
337
+ "var marker = 1",
338
+ "func init() { readStringType() }",
339
+ "",
340
+ }, "\n"),
341
+ })
342
+ outputDir := filepath.Join(t.TempDir(), "output")
343
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
344
+ if err != nil {
345
+ t.Fatal(err.Error())
346
+ }
347
+
348
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
349
+ t.Fatal(err.Error())
350
+ }
351
+ aFile := filepath.Join(outputDir, "@goscript", "example.test", "lazytypefor", "a.gs.ts")
352
+ aContent, err := os.ReadFile(aFile)
353
+ if err != nil {
354
+ t.Fatal(err.Error())
355
+ }
356
+ aText := string(aContent)
357
+ for _, want := range []string{
358
+ "export var stringType: reflect.Type | null = undefined as unknown as reflect.Type | null",
359
+ "export function __goscript_get_stringType(): reflect.Type | null",
360
+ "stringType = reflect.TypeFor",
361
+ } {
362
+ if !strings.Contains(aText, want) {
363
+ t.Fatalf("missing %q in generated a.go output:\n%s", want, aText)
364
+ }
365
+ }
366
+ bFile := filepath.Join(outputDir, "@goscript", "example.test", "lazytypefor", "b.gs.ts")
367
+ bContent, err := os.ReadFile(bFile)
368
+ if err != nil {
369
+ t.Fatal(err.Error())
370
+ }
371
+ bText := string(bContent)
372
+ if !strings.Contains(bText, "__goscript_a.__goscript_get_stringType() != null") {
373
+ t.Fatalf("missing lazy TypeFor package var getter use:\n%s", bText)
374
+ }
375
+ if strings.Contains(bText, "__goscript_a.stringType != null") {
376
+ t.Fatalf("cross-file init still reads TypeFor package var directly:\n%s", bText)
377
+ }
378
+ }
379
+
319
380
  func TestCompilePackagesInitializesLazyAsyncPackageVarsBeforeInit(t *testing.T) {
320
381
  moduleDir := writePackageGraphFixture(t, map[string]string{
321
382
  "go.mod": "module example.test/lazyasyncvars\n\ngo 1.25.3\n",
@@ -392,8 +453,8 @@ func TestCompilePackagesAssignsLazyPackageVarsDirectly(t *testing.T) {
392
453
  t.Fatal(err.Error())
393
454
  }
394
455
  text := string(content)
395
- if !strings.Contains(text, "table = $.append(__goscript_get_table(), 2)") {
396
- t.Fatalf("missing direct lazy package var assignment:\n%s", text)
456
+ if !strings.Contains(text, "__goscript_set_table($.append(__goscript_get_table(), 2))") {
457
+ t.Fatalf("missing lazy package var assignment through setter:\n%s", text)
397
458
  }
398
459
  if strings.Contains(text, "__goscript_get_table() =") {
399
460
  t.Fatalf("lazy getter used as assignment target:\n%s", text)
@@ -889,6 +950,49 @@ func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
889
950
  }
890
951
  }
891
952
 
953
+ func TestCompilePackagesEmitsTypeOnlyLocalImports(t *testing.T) {
954
+ moduleDir := writePackageGraphFixture(t, map[string]string{
955
+ "go.mod": "module example.test/typeonlylocal\n\ngo 1.25.3\n",
956
+ "a.go": strings.Join([]string{
957
+ "package main",
958
+ "type Acceptor interface {",
959
+ " Accept(Payload)",
960
+ "}",
961
+ "",
962
+ }, "\n"),
963
+ "payload.go": strings.Join([]string{
964
+ "package main",
965
+ "type Payload struct {",
966
+ " Value string",
967
+ "}",
968
+ "",
969
+ }, "\n"),
970
+ })
971
+ outputDir := filepath.Join(t.TempDir(), "output")
972
+ comp, err := NewCompiler(&Config{
973
+ Dir: moduleDir,
974
+ OutputPath: outputDir,
975
+ }, nil, nil)
976
+ if err != nil {
977
+ t.Fatal(err.Error())
978
+ }
979
+
980
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
981
+ t.Fatal(err.Error())
982
+ }
983
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "typeonlylocal", "a.gs.ts"))
984
+ if err != nil {
985
+ t.Fatal(err.Error())
986
+ }
987
+ text := string(content)
988
+ if !strings.Contains(text, "import type * as __goscript_payload from \"./payload.gs.ts\"") {
989
+ t.Fatalf("missing type-only same-package import:\n%s", text)
990
+ }
991
+ if strings.Contains(text, "import \"./payload.gs.ts\"") {
992
+ t.Fatalf("type-only same-package import should not force sibling module execution:\n%s", text)
993
+ }
994
+ }
995
+
892
996
  func TestCompilePackagesPreservesSourceImportAliasesForAssociatedMethods(t *testing.T) {
893
997
  moduleDir := writePackageGraphFixture(t, map[string]string{
894
998
  "go.mod": "module example.test/sourcealiases\n\ngo 1.25.3\n",
@@ -1004,8 +1108,7 @@ func TestCompilePackagesEmitsSideEffectImportsForInterfaceRegistry(t *testing.T)
1004
1108
  for _, want := range []string{
1005
1109
  "import * as dep from \"@goscript/example.test/interface-registry/dep/index.js\"",
1006
1110
  "import \"@goscript/example.test/interface-registry/dep/index.js\"",
1007
- "import * as __goscript_local from \"./local.gs.ts\"",
1008
- "import \"./local.gs.ts\"",
1111
+ "import type * as __goscript_local from \"./local.gs.ts\"",
1009
1112
  "case $.typeAssert<Exclude<__goscript_local.Local, null>>(__goscriptTypeSwitchValue, \"main.Local\").ok",
1010
1113
  "$.typeAssertTuple<dep.Remote | null>(v, \"dep.Remote\")",
1011
1114
  } {
@@ -1138,6 +1241,152 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
1138
1241
  }
1139
1242
  }
1140
1243
 
1244
+ func TestCompilePackagesEscapesReservedTypeNames(t *testing.T) {
1245
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1246
+ "go.mod": "module example.test/reservedtypes\n\ngo 1.25.3\n",
1247
+ "main.go": strings.Join([]string{
1248
+ "package main",
1249
+ "type static struct {",
1250
+ " Value int",
1251
+ "}",
1252
+ "func newStatic() *static {",
1253
+ " return &static{Value: 7}",
1254
+ "}",
1255
+ "func (s *static) Read() int {",
1256
+ " return s.Value",
1257
+ "}",
1258
+ "func main() {",
1259
+ " println(newStatic().Read())",
1260
+ "}",
1261
+ "",
1262
+ }, "\n"),
1263
+ })
1264
+ outputDir := filepath.Join(t.TempDir(), "output")
1265
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1266
+ if err != nil {
1267
+ t.Fatal(err.Error())
1268
+ }
1269
+
1270
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1271
+ t.Fatal(err.Error())
1272
+ }
1273
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "reservedtypes", "main.gs.ts")
1274
+ content, err := os.ReadFile(outputFile)
1275
+ if err != nil {
1276
+ t.Fatal(err.Error())
1277
+ }
1278
+ text := string(content)
1279
+ for _, want := range []string{
1280
+ "export class _static",
1281
+ "new _static({Value: 7})",
1282
+ "public Read(): number",
1283
+ "_static.prototype.Read.call(newStatic())",
1284
+ "\"main.static\"",
1285
+ } {
1286
+ if !strings.Contains(text, want) {
1287
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1288
+ }
1289
+ }
1290
+ if strings.Contains(text, "class static") {
1291
+ t.Fatalf("reserved type name was not escaped:\n%s", text)
1292
+ }
1293
+ }
1294
+
1295
+ func TestCompilePackagesAvoidsPointerMethodTypeNameShadow(t *testing.T) {
1296
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1297
+ "go.mod": "module example.test/methodshadow\n\ngo 1.25.3\n",
1298
+ "main.go": strings.Join([]string{
1299
+ "package main",
1300
+ "type item struct {",
1301
+ " Value int",
1302
+ "}",
1303
+ "func (i *item) read() int {",
1304
+ " return i.Value",
1305
+ "}",
1306
+ "func total(items []*item) int {",
1307
+ " sum := 0",
1308
+ " for _, item := range items {",
1309
+ " sum += item.read()",
1310
+ " }",
1311
+ " return sum",
1312
+ "}",
1313
+ "",
1314
+ }, "\n"),
1315
+ })
1316
+ outputDir := filepath.Join(t.TempDir(), "output")
1317
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1318
+ if err != nil {
1319
+ t.Fatal(err.Error())
1320
+ }
1321
+
1322
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1323
+ t.Fatal(err.Error())
1324
+ }
1325
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "methodshadow", "main.gs.ts")
1326
+ content, err := os.ReadFile(outputFile)
1327
+ if err != nil {
1328
+ t.Fatal(err.Error())
1329
+ }
1330
+ text := string(content)
1331
+ if !strings.Contains(text, "$.pointerValue<item>(item).read()") {
1332
+ t.Fatalf("missing direct pointer method call for shadowed type name:\n%s", text)
1333
+ }
1334
+ if strings.Contains(text, "item.prototype.read.call(item") {
1335
+ t.Fatalf("shadowed local variable used as method class:\n%s", text)
1336
+ }
1337
+ }
1338
+
1339
+ func TestCompilePackagesErasesUnimportedTransitiveInterfaceField(t *testing.T) {
1340
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1341
+ "go.mod": "module example.test/transitivefield\n\ngo 1.25.3\n",
1342
+ "hash/hash.go": strings.Join([]string{
1343
+ "package hash",
1344
+ "type Hash interface {",
1345
+ " Write([]byte) (int, error)",
1346
+ "}",
1347
+ "",
1348
+ }, "\n"),
1349
+ "holder/holder.go": strings.Join([]string{
1350
+ "package holder",
1351
+ "import \"example.test/transitivefield/hash\"",
1352
+ "type Hasher struct {",
1353
+ " hash.Hash",
1354
+ "}",
1355
+ "",
1356
+ }, "\n"),
1357
+ "main.go": strings.Join([]string{
1358
+ "package main",
1359
+ "import \"example.test/transitivefield/holder\"",
1360
+ "func write(h holder.Hasher, p []byte) error {",
1361
+ " _, err := h.Hash.Write(p)",
1362
+ " return err",
1363
+ "}",
1364
+ "",
1365
+ }, "\n"),
1366
+ })
1367
+ outputDir := filepath.Join(t.TempDir(), "output")
1368
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1369
+ if err != nil {
1370
+ t.Fatal(err.Error())
1371
+ }
1372
+
1373
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1374
+ t.Fatal(err.Error())
1375
+ }
1376
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "transitivefield", "main.gs.ts")
1377
+ content, err := os.ReadFile(outputFile)
1378
+ if err != nil {
1379
+ t.Fatal(err.Error())
1380
+ }
1381
+ text := string(content)
1382
+ if !strings.Contains(text, "$.pointerValue<any>(h.Hash).Write(p)") {
1383
+ t.Fatalf("missing erased transitive interface field type:\n%s", text)
1384
+ }
1385
+ if strings.Contains(text, "Exclude<Hash") {
1386
+ t.Fatalf("unimported transitive interface type leaked into output:\n%s", text)
1387
+ }
1388
+ }
1389
+
1141
1390
  func TestCompilePackagesClonesNestedStructFieldsWithCloneMethodCollision(t *testing.T) {
1142
1391
  moduleDir := writePackageGraphFixture(t, map[string]string{
1143
1392
  "go.mod": "module example.test/nestedclone\n\ngo 1.25.3\n",
@@ -1341,6 +1590,47 @@ func TestCompilePackagesWrapsAddressedMapRangeValue(t *testing.T) {
1341
1590
  }
1342
1591
  }
1343
1592
 
1593
+ func TestCompilePackagesLowersTupleAssignmentToMapIndexSet(t *testing.T) {
1594
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1595
+ "go.mod": "module example.test/maptupleassign\n\ngo 1.25.3\n",
1596
+ "main.go": strings.Join([]string{
1597
+ "package main",
1598
+ "func resolve(k string) (int, error) { return 7, nil }",
1599
+ "func Apply(keys []string) (map[string]int, error) {",
1600
+ " out := make(map[string]int)",
1601
+ " var err error",
1602
+ " for _, k := range keys {",
1603
+ " out[k], err = resolve(k)",
1604
+ " if err != nil { return nil, err }",
1605
+ " }",
1606
+ " return out, nil",
1607
+ "}",
1608
+ "",
1609
+ }, "\n"),
1610
+ })
1611
+ outputDir := filepath.Join(t.TempDir(), "output")
1612
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1613
+ if err != nil {
1614
+ t.Fatal(err.Error())
1615
+ }
1616
+
1617
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1618
+ t.Fatal(err.Error())
1619
+ }
1620
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "maptupleassign", "main.gs.ts")
1621
+ content, err := os.ReadFile(outputFile)
1622
+ if err != nil {
1623
+ t.Fatal(err.Error())
1624
+ }
1625
+ text := string(content)
1626
+ if !strings.Contains(text, "$.mapSet(out, k, __goscriptTuple") {
1627
+ t.Fatalf("tuple assignment to map index did not write through mapSet:\n%s", text)
1628
+ }
1629
+ if strings.Contains(text, "$.mapGet(out, k, 0)[0] =") {
1630
+ t.Fatalf("tuple assignment still mutates mapGet tuple instead of map:\n%s", text)
1631
+ }
1632
+ }
1633
+
1344
1634
  func TestCompilePackagesLowersPromotedNamedPrimitiveMethod(t *testing.T) {
1345
1635
  moduleDir := writePackageGraphFixture(t, map[string]string{
1346
1636
  "go.mod": "module example.test/promotedprimitive\n\ngo 1.25.3\n",
@@ -1887,6 +2177,48 @@ func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
1887
2177
  }
1888
2178
  }
1889
2179
 
2180
+ func TestCompilePackagesBoxesAliasPointerInterfacesWithTargetRuntimeType(t *testing.T) {
2181
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2182
+ "go.mod": "module example.test/alias-interface-box\n\ngo 1.25.3\n",
2183
+ "dep/dep.go": strings.Join([]string{
2184
+ "package dep",
2185
+ "type CloudError struct{}",
2186
+ "func (*CloudError) Error() string { return \"cloud\" }",
2187
+ "",
2188
+ }, "\n"),
2189
+ "main.go": strings.Join([]string{
2190
+ "package main",
2191
+ "import \"example.test/alias-interface-box/dep\"",
2192
+ "type cloudError = dep.CloudError",
2193
+ "func Build() error {",
2194
+ " return &cloudError{}",
2195
+ "}",
2196
+ "",
2197
+ }, "\n"),
2198
+ })
2199
+ outputDir := filepath.Join(t.TempDir(), "output")
2200
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2201
+ if err != nil {
2202
+ t.Fatal(err.Error())
2203
+ }
2204
+
2205
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2206
+ t.Fatal(err.Error())
2207
+ }
2208
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "alias-interface-box", "main.gs.ts")
2209
+ content, err := os.ReadFile(outputFile)
2210
+ if err != nil {
2211
+ t.Fatal(err.Error())
2212
+ }
2213
+ text := string(content)
2214
+ if !strings.Contains(text, "$.interfaceValue<$.GoError>(new dep.CloudError(), \"*dep.CloudError\")") {
2215
+ t.Fatalf("alias pointer was not boxed with target runtime type:\n%s", text)
2216
+ }
2217
+ if strings.Contains(text, "*main.cloudError") {
2218
+ t.Fatalf("alias pointer leaked alias runtime type:\n%s", text)
2219
+ }
2220
+ }
2221
+
1890
2222
  func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T) {
1891
2223
  moduleDir := writePackageGraphFixture(t, map[string]string{
1892
2224
  "go.mod": "module example.test/generics\n\ngo 1.25.3\n",
@@ -1951,9 +2283,9 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
1951
2283
  "$.mapSet(seen, 1, {})",
1952
2284
  "$.genericZero(__typeArgs, \"T\", null)",
1953
2285
  "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
1954
- "ZeroValue({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.pointerValue(receiver), ...args)} }})",
1955
- "CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.pointerValue(receiver), ...args)} }}, zero)",
1956
- "Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.pointerValue(receiver), ...args)} }}, null)",
2286
+ "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)} }})",
2287
+ "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)",
2288
+ "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)",
1957
2289
  } {
1958
2290
  if !strings.Contains(text, want) {
1959
2291
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -2602,6 +2934,91 @@ func TestCompilePackagesMarksSelectReturningIfElseCasesUnreachable(t *testing.T)
2602
2934
  }
2603
2935
  }
2604
2936
 
2937
+ func TestCompilePackagesAddsUnreachableReturnFallback(t *testing.T) {
2938
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2939
+ "go.mod": "module example.test/select-named\n\ngo 1.25.3\n",
2940
+ "main.go": strings.Join([]string{
2941
+ "package main",
2942
+ "import \"context\"",
2943
+ "func wait(ctx context.Context, ch <-chan error) (rerr error) {",
2944
+ " select {",
2945
+ " case <-ctx.Done():",
2946
+ " return context.Canceled",
2947
+ " case err := <-ch:",
2948
+ " return err",
2949
+ " }",
2950
+ "}",
2951
+ "",
2952
+ }, "\n"),
2953
+ })
2954
+ outputDir := filepath.Join(t.TempDir(), "output")
2955
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2956
+ if err != nil {
2957
+ t.Fatal(err.Error())
2958
+ }
2959
+
2960
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2961
+ t.Fatal(err.Error())
2962
+ }
2963
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "select-named", "main.gs.ts")
2964
+ content, err := os.ReadFile(outputFile)
2965
+ if err != nil {
2966
+ t.Fatal(err.Error())
2967
+ }
2968
+ text := string(content)
2969
+ for _, want := range []string{
2970
+ "export async function wait(ctx: context.Context | null, ch: $.Channel<$.GoError> | null): globalThis.Promise<$.GoError>",
2971
+ "let rerr: $.GoError = null as $.GoError",
2972
+ "throw new globalThis.Error(\"goscript: unreachable return\")",
2973
+ } {
2974
+ if !strings.Contains(text, want) {
2975
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2976
+ }
2977
+ }
2978
+ }
2979
+
2980
+ func TestCompilePackagesAnnotatesShortDeclInterfaceValues(t *testing.T) {
2981
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2982
+ "go.mod": "module example.test/interface-short-decl\n\ngo 1.25.3\n",
2983
+ "main.go": strings.Join([]string{
2984
+ "package main",
2985
+ "type Reader interface { Read() int }",
2986
+ "type impl struct{ value int }",
2987
+ "func (i *impl) Read() int { return i.value }",
2988
+ "func replacement() Reader { return &impl{value: 2} }",
2989
+ "func use(r Reader, swap bool) int {",
2990
+ " if r == nil {",
2991
+ " return 0",
2992
+ " }",
2993
+ " current := r",
2994
+ " if swap {",
2995
+ " current = replacement()",
2996
+ " }",
2997
+ " return current.Read()",
2998
+ "}",
2999
+ "",
3000
+ }, "\n"),
3001
+ })
3002
+ outputDir := filepath.Join(t.TempDir(), "output")
3003
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3004
+ if err != nil {
3005
+ t.Fatal(err.Error())
3006
+ }
3007
+
3008
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3009
+ t.Fatal(err.Error())
3010
+ }
3011
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interface-short-decl", "main.gs.ts")
3012
+ content, err := os.ReadFile(outputFile)
3013
+ if err != nil {
3014
+ t.Fatal(err.Error())
3015
+ }
3016
+ text := string(content)
3017
+ if !strings.Contains(text, "let current: Reader | null = r") {
3018
+ t.Fatalf("missing interface short declaration annotation:\n%s", text)
3019
+ }
3020
+ }
3021
+
2605
3022
  func TestCompilePackagesPropagatesImmediateFuncLitAsync(t *testing.T) {
2606
3023
  moduleDir := writePackageGraphFixture(t, map[string]string{
2607
3024
  "go.mod": "module example.test/immediate-func-lit-async\n\ngo 1.25.3\n",
@@ -3098,6 +3515,41 @@ func TestCompilePackagesUnwrapsImportedVarRefValueMethodReceiver(t *testing.T) {
3098
3515
  }
3099
3516
  }
3100
3517
 
3518
+ func TestCompilePackagesUnwrapsOverridePointerMethodReceiver(t *testing.T) {
3519
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3520
+ "go.mod": "module example.test/override-pointer-receiver\n\ngo 1.25.3\n",
3521
+ "main.go": strings.Join([]string{
3522
+ "package main",
3523
+ "import \"sync/atomic\"",
3524
+ "func Read(active *atomic.Int32) int32 {",
3525
+ " return active.Load()",
3526
+ "}",
3527
+ "",
3528
+ }, "\n"),
3529
+ })
3530
+ outputDir := filepath.Join(t.TempDir(), "output")
3531
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3532
+ if err != nil {
3533
+ t.Fatal(err.Error())
3534
+ }
3535
+
3536
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3537
+ t.Fatal(err.Error())
3538
+ }
3539
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "override-pointer-receiver", "main.gs.ts")
3540
+ content, err := os.ReadFile(outputFile)
3541
+ if err != nil {
3542
+ t.Fatal(err.Error())
3543
+ }
3544
+ text := string(content)
3545
+ if !strings.Contains(text, "atomic.Int32.prototype.Load.call($.pointerValue<atomic.Int32>(active))") {
3546
+ t.Fatalf("override pointer receiver was not unwrapped:\n%s", text)
3547
+ }
3548
+ if strings.Contains(text, "atomic.Int32.prototype.Load.call(active)") {
3549
+ t.Fatalf("override pointer receiver stayed wrapped:\n%s", text)
3550
+ }
3551
+ }
3552
+
3101
3553
  func TestCompilePackagesUnwrapsImportedArrayPackageVarReads(t *testing.T) {
3102
3554
  moduleDir := writePackageGraphFixture(t, map[string]string{
3103
3555
  "go.mod": "module example.test/imported-array-var\n\ngo 1.25.3\n",
@@ -126,7 +126,11 @@ func (o *TypeScriptEmitOwner) renderLoweredFile(pkg *loweredPackage, file *lower
126
126
  if idx != 0 {
127
127
  b.WriteString("\n")
128
128
  }
129
- b.WriteString("import * as ")
129
+ b.WriteString("import ")
130
+ if imp.typeOnly {
131
+ b.WriteString("type ")
132
+ }
133
+ b.WriteString("* as ")
130
134
  b.WriteString(imp.alias)
131
135
  b.WriteString(" from \"")
132
136
  b.WriteString(imp.source)
@@ -134,7 +138,7 @@ func (o *TypeScriptEmitOwner) renderLoweredFile(pkg *loweredPackage, file *lower
134
138
  }
135
139
  sideEffectImports := make(map[string]bool)
136
140
  for _, imp := range file.imports {
137
- if !imp.sideEffect || sideEffectImports[imp.source] {
141
+ if imp.typeOnly || !imp.sideEffect || sideEffectImports[imp.source] {
138
142
  continue
139
143
  }
140
144
  sideEffectImports[imp.source] = true
@@ -484,6 +488,7 @@ func renderFunction(b *strings.Builder, fn *loweredFunction) {
484
488
  renderNamedResults(b, fn.namedResults, 1)
485
489
  renderDeferStack(b, fn.deferState, 1)
486
490
  renderStmts(b, fn.body, 1)
491
+ renderUnreachableReturn(b, fn, 1)
487
492
  b.WriteString("}\n")
488
493
  }
489
494
 
@@ -527,10 +532,31 @@ func renderMethod(b *strings.Builder, fn *loweredFunction) {
527
532
  renderNamedResults(b, fn.namedResults, 2)
528
533
  renderDeferStack(b, fn.deferState, 2)
529
534
  renderStmts(b, fn.body, 2)
535
+ renderUnreachableReturn(b, fn, 2)
530
536
  writeIndent(b, 1)
531
537
  b.WriteString("}\n")
532
538
  }
533
539
 
540
+ func renderUnreachableReturn(b *strings.Builder, fn *loweredFunction, indent int) {
541
+ if fn.result == "void" || fn.result == "globalThis.Promise<void>" {
542
+ return
543
+ }
544
+ if loweredStmtsEndWithTerminal(fn.body) {
545
+ return
546
+ }
547
+ writeIndent(b, indent)
548
+ b.WriteString("throw new globalThis.Error(\"goscript: unreachable return\")\n")
549
+ }
550
+
551
+ func loweredStmtsEndWithTerminal(stmts []loweredStmt) bool {
552
+ if len(stmts) == 0 {
553
+ return false
554
+ }
555
+ last := stmts[len(stmts)-1]
556
+ text := strings.TrimSpace(last.text)
557
+ return strings.HasPrefix(text, "return") || strings.HasPrefix(text, "throw ")
558
+ }
559
+
534
560
  func renderFunctionTypeParams(b *strings.Builder, fn *loweredFunction) {
535
561
  if len(fn.typeParams) == 0 {
536
562
  return