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.
- package/compiler/lowered-program.go +1 -0
- package/compiler/lowering.go +715 -44
- package/compiler/override-registry_test.go +43 -0
- package/compiler/skeleton_test.go +464 -12
- package/compiler/typescript-emitter.go +28 -2
- package/dist/gs/builtin/channel.js +36 -9
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -3
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.d.ts +7 -5
- package/dist/gs/bytes/bytes.gs.js +10 -4
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/crypto/sha1/index.d.ts +5 -0
- package/dist/gs/crypto/sha1/index.js +106 -0
- package/dist/gs/crypto/sha1/index.js.map +1 -0
- package/dist/gs/fmt/fmt.d.ts +1 -1
- package/dist/gs/fmt/fmt.js +64 -3
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/io/io.d.ts +8 -5
- package/dist/gs/io/io.js +20 -2
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/net/http/httptest/index.js +7 -5
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +8 -0
- package/dist/gs/net/http/index.js +139 -10
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
- package/gs/builtin/channel.ts +47 -9
- package/gs/builtin/runtime-contract.test.ts +33 -0
- package/gs/builtin/type.ts +12 -3
- package/gs/bytes/bytes.gs.ts +19 -10
- package/gs/bytes/bytes.test.ts +17 -0
- package/gs/context/context.test.ts +5 -1
- package/gs/crypto/sha1/index.test.ts +28 -0
- package/gs/crypto/sha1/index.ts +130 -0
- package/gs/crypto/sha1/meta.json +8 -0
- package/gs/fmt/fmt.test.ts +20 -0
- package/gs/fmt/fmt.ts +75 -5
- package/gs/github.com/aperturerobotics/util/conc/index.test.ts +1 -1
- package/gs/io/io.test.ts +64 -0
- package/gs/io/io.ts +30 -12
- package/gs/net/http/httptest/index.test.ts +34 -2
- package/gs/net/http/httptest/index.ts +23 -8
- package/gs/net/http/index.test.ts +30 -0
- package/gs/net/http/index.ts +159 -10
- package/gs/os/zero_copy_posix.gs.ts +1 -2
- package/gs/sync/meta.json +1 -0
- 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
|
|
218
|
+
"export var two: holder = undefined as unknown as holder",
|
|
219
219
|
"export function __goscript_get_two(): holder",
|
|
220
|
-
"export
|
|
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.
|
|
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
|
|
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
|
|
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, "
|
|
396
|
-
t.Fatalf("missing
|
|
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)($.
|
|
1955
|
-
"CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.
|
|
1956
|
-
"Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.
|
|
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
|
|
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
|