goscript 0.1.3 → 0.1.4

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 (117) hide show
  1. package/cmd/goscript/cmd_compile.go +28 -8
  2. package/cmd/goscript/cmd_compile_test.go +105 -6
  3. package/compiler/build-flags.go +9 -10
  4. package/compiler/gotest/runner_test.go +127 -0
  5. package/compiler/lowering.go +596 -136
  6. package/compiler/lowering_bench_test.go +350 -0
  7. package/compiler/package-graph.go +61 -4
  8. package/compiler/package-graph_test.go +30 -0
  9. package/compiler/semantic-model-types.go +8 -0
  10. package/compiler/semantic-model.go +447 -22
  11. package/compiler/semantic-model_test.go +138 -0
  12. package/compiler/skeleton_test.go +948 -14
  13. package/compiler/typescript-emitter.go +19 -2
  14. package/dist/gs/builtin/builtin.d.ts +2 -2
  15. package/dist/gs/builtin/builtin.js +20 -0
  16. package/dist/gs/builtin/builtin.js.map +1 -1
  17. package/dist/gs/builtin/slice.js +5 -0
  18. package/dist/gs/builtin/slice.js.map +1 -1
  19. package/dist/gs/builtin/type.d.ts +1 -1
  20. package/dist/gs/builtin/type.js +72 -5
  21. package/dist/gs/builtin/type.js.map +1 -1
  22. package/dist/gs/compress/zlib/index.d.ts +3 -3
  23. package/dist/gs/compress/zlib/index.js +88 -26
  24. package/dist/gs/compress/zlib/index.js.map +1 -1
  25. package/dist/gs/crypto/sha1/index.js +2 -5
  26. package/dist/gs/crypto/sha1/index.js.map +1 -1
  27. package/dist/gs/crypto/sha256/index.js +2 -5
  28. package/dist/gs/crypto/sha256/index.js.map +1 -1
  29. package/dist/gs/crypto/sha512/index.js +2 -5
  30. package/dist/gs/crypto/sha512/index.js.map +1 -1
  31. package/dist/gs/embed/index.d.ts +6 -0
  32. package/dist/gs/embed/index.js +210 -5
  33. package/dist/gs/embed/index.js.map +1 -1
  34. package/dist/gs/fmt/fmt.d.ts +3 -3
  35. package/dist/gs/fmt/fmt.js +29 -16
  36. package/dist/gs/fmt/fmt.js.map +1 -1
  37. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +118 -6
  38. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -1
  39. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
  40. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
  41. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
  42. package/dist/gs/io/fs/readdir.js +5 -3
  43. package/dist/gs/io/fs/readdir.js.map +1 -1
  44. package/dist/gs/io/io.d.ts +10 -6
  45. package/dist/gs/io/io.js +87 -42
  46. package/dist/gs/io/io.js.map +1 -1
  47. package/dist/gs/math/bits/index.d.ts +26 -5
  48. package/dist/gs/math/bits/index.js +13 -24
  49. package/dist/gs/math/bits/index.js.map +1 -1
  50. package/dist/gs/net/http/index.d.ts +3 -1
  51. package/dist/gs/net/http/index.js +18 -1
  52. package/dist/gs/net/http/index.js.map +1 -1
  53. package/dist/gs/os/types_js.gs.d.ts +6 -2
  54. package/dist/gs/os/types_js.gs.js +169 -8
  55. package/dist/gs/os/types_js.gs.js.map +1 -1
  56. package/dist/gs/reflect/type.d.ts +1 -0
  57. package/dist/gs/reflect/type.js +80 -51
  58. package/dist/gs/reflect/type.js.map +1 -1
  59. package/dist/gs/strings/reader.d.ts +1 -1
  60. package/dist/gs/strings/reader.js +2 -2
  61. package/dist/gs/strings/reader.js.map +1 -1
  62. package/dist/gs/sync/sync.d.ts +2 -1
  63. package/dist/gs/sync/sync.js +37 -16
  64. package/dist/gs/sync/sync.js.map +1 -1
  65. package/dist/gs/syscall/js/index.js +9 -0
  66. package/dist/gs/syscall/js/index.js.map +1 -1
  67. package/dist/gs/testing/testing.js +8 -6
  68. package/dist/gs/testing/testing.js.map +1 -1
  69. package/gs/builtin/builtin.ts +25 -2
  70. package/gs/builtin/runtime-contract.test.ts +45 -0
  71. package/gs/builtin/slice.ts +7 -0
  72. package/gs/builtin/type.ts +85 -5
  73. package/gs/compress/zlib/index.test.ts +97 -0
  74. package/gs/compress/zlib/index.ts +117 -27
  75. package/gs/compress/zlib/meta.json +4 -1
  76. package/gs/crypto/sha1/index.test.ts +19 -2
  77. package/gs/crypto/sha1/index.ts +3 -6
  78. package/gs/crypto/sha256/index.test.ts +14 -2
  79. package/gs/crypto/sha256/index.ts +3 -6
  80. package/gs/crypto/sha512/index.test.ts +17 -2
  81. package/gs/crypto/sha512/index.ts +3 -6
  82. package/gs/embed/index.test.ts +87 -0
  83. package/gs/embed/index.ts +229 -5
  84. package/gs/fmt/fmt.test.ts +41 -3
  85. package/gs/fmt/fmt.ts +40 -17
  86. package/gs/fmt/meta.json +6 -1
  87. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +8 -1
  88. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +139 -11
  89. package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
  90. package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
  91. package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
  92. package/gs/io/fs/readdir.test.ts +38 -0
  93. package/gs/io/fs/readdir.ts +7 -3
  94. package/gs/io/io.test.ts +77 -6
  95. package/gs/io/io.ts +114 -52
  96. package/gs/io/meta.json +7 -1
  97. package/gs/math/bits/index.ts +52 -28
  98. package/gs/net/http/index.test.ts +16 -0
  99. package/gs/net/http/index.ts +19 -2
  100. package/gs/os/file_unix_js.test.ts +52 -0
  101. package/gs/os/meta.json +4 -0
  102. package/gs/os/readdir.test.ts +56 -0
  103. package/gs/os/types_js.gs.ts +169 -8
  104. package/gs/reflect/deepequal.test.ts +10 -1
  105. package/gs/reflect/type.ts +91 -56
  106. package/gs/reflect/typefor.test.ts +31 -1
  107. package/gs/strings/meta.json +5 -2
  108. package/gs/strings/reader.test.ts +2 -2
  109. package/gs/strings/reader.ts +2 -2
  110. package/gs/sync/meta.json +1 -0
  111. package/gs/sync/sync.test.ts +41 -1
  112. package/gs/sync/sync.ts +41 -16
  113. package/gs/syscall/js/index.test.ts +18 -0
  114. package/gs/syscall/js/index.ts +12 -0
  115. package/gs/testing/testing.test.ts +32 -3
  116. package/gs/testing/testing.ts +13 -10
  117. package/package.json +1 -1
@@ -0,0 +1,56 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { afterEach, describe, expect, it } from 'vitest'
5
+
6
+ import * as io from '@goscript/io/index.js'
7
+
8
+ import { Open } from './file_js.gs.js'
9
+
10
+ const tempRoots: string[] = []
11
+
12
+ afterEach(() => {
13
+ for (const root of tempRoots.splice(0)) {
14
+ rmSync(root, { force: true, recursive: true })
15
+ }
16
+ })
17
+
18
+ function makeTempRoot(): string {
19
+ const root = mkdtempSync(join(tmpdir(), 'goscript-os-readdir-'))
20
+ tempRoots.push(root)
21
+ return root
22
+ }
23
+
24
+ describe('os file directory reads', () => {
25
+ it('reads host directory entries through os.File', () => {
26
+ const root = makeTempRoot()
27
+ writeFileSync(join(root, 'a.txt'), 'a')
28
+ writeFileSync(join(root, 'b.txt'), 'b')
29
+
30
+ const [dir, openErr] = Open(root)
31
+ expect(openErr).toBeNull()
32
+ expect(dir).not.toBeNull()
33
+ const [entries, readErr] = dir!.ReadDir(-1)
34
+ expect(readErr).toBeNull()
35
+ expect(entries?.map((entry) => entry!.Name()).sort()).toEqual([
36
+ 'a.txt',
37
+ 'b.txt',
38
+ ])
39
+ expect(dir!.Close()).toBeNull()
40
+ })
41
+
42
+ it('returns EOF after positive-count directory reads are exhausted', () => {
43
+ const root = makeTempRoot()
44
+ writeFileSync(join(root, 'a.txt'), 'a')
45
+
46
+ const [dir] = Open(root)
47
+ const [first, firstErr] = dir!.Readdirnames(1)
48
+ expect(firstErr).toBeNull()
49
+ expect(first).toHaveLength(1)
50
+
51
+ const [second, secondErr] = dir!.Readdirnames(1)
52
+ expect(second).toBeNull()
53
+ expect(secondErr).toBe(io.EOF)
54
+ expect(dir!.Close()).toBeNull()
55
+ })
56
+ })
@@ -2,6 +2,7 @@ import * as $ from "@goscript/builtin/index.js";
2
2
  import { ErrClosed, ErrInvalid, ErrUnimplemented } from "./error.gs.js";
3
3
 
4
4
  import * as fs from "@goscript/io/fs/index.js"
5
+ import { FileInfoToDirEntry } from "@goscript/io/fs/readdir.js"
5
6
  import * as io from "@goscript/io/index.js"
6
7
  import * as time from "@goscript/time/index.js"
7
8
  import * as syscall from "@goscript/syscall/index.js"
@@ -219,6 +220,70 @@ export function createFileInfo(name: string, stat: HostStatLike): fs.FileInfo {
219
220
  })
220
221
  }
221
222
 
223
+ function joinFilePath(dir: string, name: string): string {
224
+ if (dir === "" || dir === ".") {
225
+ return name
226
+ }
227
+ return dir.replace(/\/+$/, "") + "/" + name
228
+ }
229
+
230
+ function readDirectoryInfos(name: string): [fs.FileInfo[] | null, $.GoError] {
231
+ const denoObj = getDeno()
232
+ if (denoObj?.readDirSync) {
233
+ try {
234
+ const infos: fs.FileInfo[] = []
235
+ for (const entry of denoObj.readDirSync(name)) {
236
+ infos.push(createFileInfo(entry.name, {
237
+ isDirectory: () => entry.isDirectory,
238
+ isSymbolicLink: () => entry.isSymlink,
239
+ mode: 0,
240
+ size: 0,
241
+ }))
242
+ }
243
+ return [infos, null]
244
+ } catch (err) {
245
+ return [null, newHostError(err)]
246
+ }
247
+ }
248
+ const nodeFS = getNodeFS()
249
+ if (nodeFS?.readdirSync) {
250
+ try {
251
+ const infos = nodeFS.readdirSync(name, { withFileTypes: true }).map((entry: any) => {
252
+ const childName = String(entry.name)
253
+ const childPath = joinFilePath(name, childName)
254
+ if (nodeFS.lstatSync) {
255
+ try {
256
+ return createFileInfo(childName, nodeFS.lstatSync(childPath))
257
+ } catch {
258
+ // Fall back to Dirent metadata when the child cannot be statted.
259
+ }
260
+ }
261
+ return createFileInfo(childName, {
262
+ isDirectory: () => typeof entry.isDirectory === "function" ? entry.isDirectory() : false,
263
+ isSymbolicLink: () => typeof entry.isSymbolicLink === "function" ? entry.isSymbolicLink() : false,
264
+ mode: 0,
265
+ size: 0,
266
+ })
267
+ })
268
+ return [infos, null]
269
+ } catch (err) {
270
+ return [null, newHostError(err)]
271
+ }
272
+ }
273
+ return [null, ErrUnimplemented]
274
+ }
275
+
276
+ function consumeDirectoryEntries<T>(entries: T[], offset: number, n: number): [$.Slice<T>, number, $.GoError] {
277
+ if (n <= 0) {
278
+ return [$.arrayToSlice(entries.slice(offset)), entries.length, null]
279
+ }
280
+ if (offset >= entries.length) {
281
+ return [null, offset, io.EOF]
282
+ }
283
+ const next = Math.min(entries.length, offset + n)
284
+ return [$.arrayToSlice(entries.slice(offset, next)), next, null]
285
+ }
286
+
222
287
  export function createHostFile(name: string, fd: number = -1, handle: DenoFileLike | null = null): File {
223
288
  return new File({
224
289
  fd,
@@ -260,6 +325,10 @@ export class File {
260
325
  public name: string
261
326
  public closed: boolean
262
327
  public fd: number
328
+ public dirEntryOffset: number
329
+ public dirInfoOffset: number
330
+ public cachedDirEntries: fs.DirEntry[] | null
331
+ public cachedDirInfos: fs.FileInfo[] | null
263
332
 
264
333
  public _fields: {
265
334
  file: $.VarRef<file | null>;
@@ -269,6 +338,10 @@ export class File {
269
338
  this.name = init?.name ?? ""
270
339
  this.closed = init?.closed ?? false
271
340
  this.fd = init?.fd ?? -1
341
+ this.dirEntryOffset = 0
342
+ this.dirInfoOffset = 0
343
+ this.cachedDirEntries = null
344
+ this.cachedDirInfos = null
272
345
  this._fields = {
273
346
  file: $.varRef(init?.file ?? null)
274
347
  }
@@ -284,19 +357,58 @@ export class File {
284
357
  }
285
358
 
286
359
  public Readdir(n: number): [$.Slice<fs.FileInfo>, $.GoError] {
287
- return [null, ErrUnimplemented]
360
+ if (this.closed) {
361
+ return [null, ErrClosed]
362
+ }
363
+ if (this.cachedDirInfos === null) {
364
+ const [infos, err] = readDirectoryInfos(this.name)
365
+ if (err !== null) {
366
+ return [null, err]
367
+ }
368
+ this.cachedDirInfos = infos ?? []
369
+ this.dirInfoOffset = 0
370
+ }
371
+ const [out, next, err] = consumeDirectoryEntries(this.cachedDirInfos, this.dirInfoOffset, n)
372
+ this.dirInfoOffset = next
373
+ return [out, err]
288
374
  }
289
375
 
290
376
  public Readdirnames(n: number): [$.Slice<string>, $.GoError] {
291
- return [null, ErrUnimplemented]
377
+ const [infos, err] = this.Readdir(n)
378
+ if (err !== null) {
379
+ return [null, err]
380
+ }
381
+ return [$.arrayToSlice((infos ?? []).map((info) => info!.Name())), null]
292
382
  }
293
383
 
294
384
  public ReadDir(n: number): [$.Slice<fs.DirEntry>, $.GoError] {
295
- return [null, ErrUnimplemented]
385
+ if (this.closed) {
386
+ return [null, ErrClosed]
387
+ }
388
+ if (this.cachedDirEntries === null) {
389
+ const [infos, err] = readDirectoryInfos(this.name)
390
+ if (err !== null) {
391
+ return [null, err]
392
+ }
393
+ this.cachedDirEntries = (infos ?? []).map((info) => FileInfoToDirEntry(info)).filter((entry): entry is fs.DirEntry => entry !== null)
394
+ this.dirEntryOffset = 0
395
+ }
396
+ const [out, next, err] = consumeDirectoryEntries(this.cachedDirEntries, this.dirEntryOffset, n)
397
+ this.dirEntryOffset = next
398
+ return [out, err]
296
399
  }
297
400
 
298
401
  public readdir(n: number, mode: readdirMode): [$.Slice<string>, $.Slice<fs.DirEntry>, $.Slice<fs.FileInfo>, $.GoError] {
299
- return [null, null, null, ErrUnimplemented]
402
+ if (mode === readdirName) {
403
+ const [names, err] = this.Readdirnames(n)
404
+ return [names, null, null, err]
405
+ }
406
+ if (mode === readdirDirEntry) {
407
+ const [entries, err] = this.ReadDir(n)
408
+ return [null, entries, null, err]
409
+ }
410
+ const [infos, err] = this.Readdir(n)
411
+ return [null, null, infos, err]
300
412
  }
301
413
 
302
414
  public Name(): string {
@@ -366,11 +478,34 @@ export class File {
366
478
  return [0, ErrUnimplemented]
367
479
  }
368
480
 
369
- public ReadFrom(r: io.Reader): [number, $.GoError] {
481
+ public async ReadFrom(r: io.Reader): Promise<[number, $.GoError]> {
370
482
  if (this.closed) {
371
483
  return [0, ErrClosed]
372
484
  }
373
- return io.Copy(this, r) as any
485
+ const buf = $.makeSlice<number>(32 * 1024, undefined, "byte")
486
+ let written = 0
487
+ while (true) {
488
+ const [nr, er] = await (r.Read(buf) as any)
489
+ if (nr > 0) {
490
+ const [nw, ew] = this.Write($.goSlice(buf, 0, nr))
491
+ if (nw < 0 || nr < nw) {
492
+ return [written, ew ?? io.ErrShortWrite]
493
+ }
494
+ written += nw
495
+ if (ew !== null) {
496
+ return [written, ew]
497
+ }
498
+ if (nr !== nw) {
499
+ return [written, io.ErrShortWrite]
500
+ }
501
+ }
502
+ if (er !== null) {
503
+ if (er === io.EOF) {
504
+ return [written, null]
505
+ }
506
+ return [written, er]
507
+ }
508
+ }
374
509
  }
375
510
 
376
511
  public Write(b: $.Bytes): [number, $.GoError] {
@@ -424,11 +559,34 @@ export class File {
424
559
  return [0, ErrUnimplemented]
425
560
  }
426
561
 
427
- public WriteTo(w: io.Writer): [number, $.GoError] {
562
+ public async WriteTo(w: io.Writer): Promise<[number, $.GoError]> {
428
563
  if (this.closed) {
429
564
  return [0, ErrClosed]
430
565
  }
431
- return io.Copy(w, this) as any
566
+ const buf = $.makeSlice<number>(32 * 1024, undefined, "byte")
567
+ let written = 0
568
+ while (true) {
569
+ const [nr, er] = this.Read(buf)
570
+ if (nr > 0) {
571
+ const [nw, ew] = await (w.Write($.goSlice(buf, 0, nr)) as any)
572
+ if (nw < 0 || nr < nw) {
573
+ return [written, ew ?? io.ErrShortWrite]
574
+ }
575
+ written += nw
576
+ if (ew !== null) {
577
+ return [written, ew]
578
+ }
579
+ if (nr !== nw) {
580
+ return [written, io.ErrShortWrite]
581
+ }
582
+ }
583
+ if (er !== null) {
584
+ if (er === io.EOF) {
585
+ return [written, null]
586
+ }
587
+ return [written, er]
588
+ }
589
+ }
432
590
  }
433
591
 
434
592
  public Seek(offset: number, whence: number): [number, $.GoError] {
@@ -681,6 +839,9 @@ class file {
681
839
 
682
840
  // readdirMode mirrors the Go runtime helper enum.
683
841
  type readdirMode = number
842
+ const readdirName: readdirMode = 0
843
+ const readdirDirEntry: readdirMode = 1
844
+ const readdirFileInfo: readdirMode = 2
684
845
 
685
846
  // File mode constants
686
847
  export let ModeDir: fs.FileMode = fs.ModeDir
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest'
2
2
  import { DeepEqual } from './deepequal.js'
3
- import { Value, BasicType, Int, String as StringType } from './type.js'
3
+ import { Value, BasicType, Int, String as StringType, Uint32 } from './type.js'
4
4
 
5
5
  describe('DeepEqual', () => {
6
6
  it('should compare primitive values correctly', () => {
@@ -38,4 +38,13 @@ describe('DeepEqual', () => {
38
38
  expect(DeepEqual(v1, v2)).toBe(true)
39
39
  expect(DeepEqual(v1, v3)).toBe(false)
40
40
  })
41
+
42
+ it('reads numeric boxed values with basic numeric kinds', () => {
43
+ const v = new Value(
44
+ { sentinel: true, valueOf: () => 7 } as any,
45
+ new BasicType(Uint32, 'uint32'),
46
+ )
47
+
48
+ expect(v.Uint()).toBe(7)
49
+ })
41
50
  })
@@ -401,8 +401,9 @@ export class Value {
401
401
 
402
402
  // Methods required by godoc.txt and used throughout the codebase
403
403
  public Int(): number {
404
- if (typeof this._value === 'number' && Number.isInteger(this._value)) {
405
- return this._value
404
+ const value = this.numericValue()
405
+ if (value !== null && Number.isInteger(value)) {
406
+ return value
406
407
  }
407
408
  throw new Error(
408
409
  'reflect: call of reflect.Value.Int on ' +
@@ -412,8 +413,9 @@ export class Value {
412
413
  }
413
414
 
414
415
  public Uint(): number {
415
- if (typeof this._value === 'number' && this._value >= 0) {
416
- return this._value
416
+ const value = this.numericValue()
417
+ if (value !== null && value >= 0) {
418
+ return value
417
419
  }
418
420
  throw new Error(
419
421
  'reflect: call of reflect.Value.Uint on ' +
@@ -423,8 +425,9 @@ export class Value {
423
425
  }
424
426
 
425
427
  public Float(): number {
426
- if (typeof this._value === 'number') {
427
- return this._value
428
+ const value = this.numericValue()
429
+ if (value !== null) {
430
+ return value
428
431
  }
429
432
  throw new Error(
430
433
  'reflect: call of reflect.Value.Float on ' +
@@ -444,6 +447,23 @@ export class Value {
444
447
  )
445
448
  }
446
449
 
450
+ private numericValue(): number | null {
451
+ if (typeof this._value === 'number') {
452
+ return this._value
453
+ }
454
+ if (
455
+ this._value !== null &&
456
+ typeof this._value === 'object' &&
457
+ typeof (this._value as { valueOf?: unknown }).valueOf === 'function'
458
+ ) {
459
+ const value = (this._value as { valueOf(): unknown }).valueOf()
460
+ if (typeof value === 'number') {
461
+ return value
462
+ }
463
+ }
464
+ return null
465
+ }
466
+
447
467
  public String(): string {
448
468
  if (typeof this._value === 'string') {
449
469
  return this._value
@@ -576,6 +596,17 @@ export class Value {
576
596
  const elemType = this._type.Elem()
577
597
  return new Value(varRef.value, elemType, varRef)
578
598
  }
599
+ if (
600
+ this._type.Kind() === Ptr &&
601
+ this._value &&
602
+ typeof this._value === 'object' &&
603
+ '__goValue' in this._value &&
604
+ $.isVarRef((this._value as { __goValue: unknown }).__goValue)
605
+ ) {
606
+ const varRef = (this._value as { __goValue: $.VarRef<ReflectValue> })
607
+ .__goValue
608
+ return new Value(varRef.value, this._type.Elem(), varRef)
609
+ }
579
610
  // For interfaces, return the underlying value
580
611
  return new Value(this._value, this._type, this._parentVarRef)
581
612
  }
@@ -2172,62 +2203,14 @@ class StructType implements Type {
2172
2203
 
2173
2204
  static createTypeFromFieldInfo(ti: any): Type {
2174
2205
  if (typeof ti === 'string') {
2175
- switch (ti) {
2176
- case 'string':
2177
- return new BasicType(String, ti, 16)
2178
- case 'int':
2179
- case 'int32':
2180
- case 'int64':
2181
- case 'number':
2182
- return new BasicType(Int, ti === 'number' ? 'int' : ti, 8)
2183
- case 'bool':
2184
- case 'boolean':
2185
- return new BasicType(Bool, 'bool', 1)
2186
- case 'float64':
2187
- return new BasicType(Float64, ti, 8)
2188
- case 'complex64':
2189
- return new BasicType(Complex64, ti, 8)
2190
- case 'complex128':
2191
- return new BasicType(Complex128, ti, 16)
2192
- case 'uint':
2193
- case 'uint32':
2194
- case 'uint64':
2195
- return new BasicType(Uint, ti, 8)
2196
- default:
2197
- return new BasicType(Invalid, ti, 8)
2198
- }
2206
+ return basicTypeFromName(ti === 'number' ? 'int' : ti)
2199
2207
  } else if (ti && ti.kind) {
2200
2208
  // Handle TypeInfo objects from the builtin type system
2201
2209
  const name = ti.name || 'unknown'
2202
2210
  const typeName = ti.typeName || ''
2203
2211
  switch (ti.kind) {
2204
2212
  case 'basic':
2205
- // Map TypeScript type names to Go type names
2206
- switch (name) {
2207
- case 'string':
2208
- return new BasicType(String, 'string', 16, typeName)
2209
- case 'number':
2210
- case 'int':
2211
- case 'int32':
2212
- case 'int64':
2213
- return new BasicType(
2214
- Int,
2215
- name === 'number' ? 'int' : name,
2216
- 8,
2217
- typeName,
2218
- )
2219
- case 'boolean':
2220
- case 'bool':
2221
- return new BasicType(Bool, 'bool', 1, typeName)
2222
- case 'float64':
2223
- return new BasicType(Float64, 'float64', 8, typeName)
2224
- case 'complex64':
2225
- return new BasicType(Complex64, 'complex64', 8, typeName)
2226
- case 'complex128':
2227
- return new BasicType(Complex128, 'complex128', 16, typeName)
2228
- default:
2229
- return new BasicType(Invalid, name, 8, typeName)
2230
- }
2213
+ return basicTypeFromName(name === 'number' ? 'int' : name, typeName)
2231
2214
  case 'slice':
2232
2215
  if (ti.elemType) {
2233
2216
  return new SliceType(
@@ -2254,6 +2237,49 @@ class StructType implements Type {
2254
2237
  }
2255
2238
  }
2256
2239
 
2240
+ function basicTypeFromName(name: string, typeName = ''): BasicType {
2241
+ switch (name) {
2242
+ case 'string':
2243
+ return new BasicType(String, 'string', 16, typeName)
2244
+ case 'bool':
2245
+ case 'boolean':
2246
+ return new BasicType(Bool, 'bool', 1, typeName)
2247
+ case 'int':
2248
+ return new BasicType(Int, 'int', 8, typeName)
2249
+ case 'int8':
2250
+ return new BasicType(Int8, 'int8', 1, typeName)
2251
+ case 'int16':
2252
+ return new BasicType(Int16, 'int16', 2, typeName)
2253
+ case 'int32':
2254
+ return new BasicType(Int32, 'int32', 4, typeName)
2255
+ case 'int64':
2256
+ return new BasicType(Int64, 'int64', 8, typeName)
2257
+ case 'uint':
2258
+ return new BasicType(Uint, 'uint', 8, typeName)
2259
+ case 'uint8':
2260
+ case 'byte':
2261
+ return new BasicType(Uint8, name, 1, typeName)
2262
+ case 'uint16':
2263
+ return new BasicType(Uint16, 'uint16', 2, typeName)
2264
+ case 'uint32':
2265
+ return new BasicType(Uint32, 'uint32', 4, typeName)
2266
+ case 'uint64':
2267
+ return new BasicType(Uint64, 'uint64', 8, typeName)
2268
+ case 'uintptr':
2269
+ return new BasicType(Uintptr, 'uintptr', 8, typeName)
2270
+ case 'float32':
2271
+ return new BasicType(Float32, 'float32', 4, typeName)
2272
+ case 'float64':
2273
+ return new BasicType(Float64, 'float64', 8, typeName)
2274
+ case 'complex64':
2275
+ return new BasicType(Complex64, 'complex64', 8, typeName)
2276
+ case 'complex128':
2277
+ return new BasicType(Complex128, 'complex128', 16, typeName)
2278
+ default:
2279
+ return new BasicType(Invalid, name, 8, typeName)
2280
+ }
2281
+ }
2282
+
2257
2283
  function structFieldsFromTypeInfo(
2258
2284
  ti: $.StructTypeInfo,
2259
2285
  ): Array<{ name: string; type: Type; tag?: string }> {
@@ -2614,6 +2640,15 @@ function getTypeOf(value: ReflectValue): Type {
2614
2640
  return new PointerType(elemType)
2615
2641
  }
2616
2642
 
2643
+ if (
2644
+ '__goTypeInfo' in value &&
2645
+ (value as { __goTypeInfo?: $.TypeInfo | string }).__goTypeInfo
2646
+ ) {
2647
+ return typeFromTypeInfo(
2648
+ (value as { __goTypeInfo: $.TypeInfo | string }).__goTypeInfo,
2649
+ )
2650
+ }
2651
+
2617
2652
  if (
2618
2653
  'real' in value &&
2619
2654
  'imag' in value &&
@@ -3,12 +3,14 @@ import {
3
3
  makeMap,
4
4
  mapGet,
5
5
  mapSet,
6
+ namedValueInterfaceValue,
6
7
  TypeKind,
7
8
  registerInterfaceType,
8
9
  registerStructType,
10
+ varRef,
9
11
  } from '../builtin/index.js'
10
12
  import { StructField } from './types.js'
11
- import { Int, Struct, TypeFor } from './type.js'
13
+ import { Int, Ptr, Struct, TypeFor, TypeOf, Uint64, ValueOf } from './type.js'
12
14
 
13
15
  describe('TypeFor', () => {
14
16
  it('exposes StructField PkgPath and exported semantics', () => {
@@ -48,6 +50,34 @@ describe('TypeFor', () => {
48
50
  expect(namedIntType.Kind()).toBe(Int)
49
51
  })
50
52
 
53
+ it('preserves named pointer type metadata on interface boxes', () => {
54
+ const target = varRef(0)
55
+ const boxed = namedValueInterfaceValue(
56
+ target,
57
+ '*chunker.Pol',
58
+ {},
59
+ {
60
+ kind: TypeKind.Pointer,
61
+ elemType: {
62
+ kind: TypeKind.Basic,
63
+ name: 'uint64',
64
+ typeName: 'chunker.Pol',
65
+ },
66
+ },
67
+ )
68
+
69
+ const typ = TypeOf(boxed)
70
+ expect(typ.String()).toBe('*chunker.Pol')
71
+ expect(typ.Kind()).toBe(Ptr)
72
+ expect(typ.Elem().Kind()).toBe(Uint64)
73
+ expect(typ.Elem().Name()).toBe('Pol')
74
+
75
+ const elem = ValueOf(boxed).Elem()
76
+ expect(elem.Kind()).toBe(Uint64)
77
+ elem.SetUint(15)
78
+ expect(target.value).toBe(15)
79
+ })
80
+
51
81
  it('formats literal interface methods from type metadata', () => {
52
82
  const ifaceType = TypeFor({
53
83
  T: {
@@ -5,5 +5,8 @@
5
5
  "iter",
6
6
  "unicode",
7
7
  "unicode/utf8"
8
- ]
9
- }
8
+ ],
9
+ "asyncMethods": {
10
+ "Reader.WriteTo": true
11
+ }
12
+ }
@@ -165,7 +165,7 @@ describe('strings/Reader', () => {
165
165
  expect(err).not.toBeNull()
166
166
  })
167
167
 
168
- it('should write to writer', () => {
168
+ it('should write to writer', async () => {
169
169
  const r = new Reader({ s: 'hello world' })
170
170
  r.ReadByte() // advance position
171
171
 
@@ -177,7 +177,7 @@ describe('strings/Reader', () => {
177
177
  },
178
178
  }
179
179
 
180
- const [n, err] = r.WriteTo(writer)
180
+ const [n, err] = await r.WriteTo(writer)
181
181
  expect(n).toBe(10) // remaining bytes
182
182
  expect(err).toBeNull()
183
183
  expect(r.Len()).toBe(0)
@@ -188,7 +188,7 @@ export class Reader {
188
188
  }
189
189
 
190
190
  // WriteTo implements the [io.WriterTo] interface.
191
- public WriteTo(w: io.Writer): [number, $.GoError] {
191
+ public async WriteTo(w: io.Writer): Promise<[number, $.GoError]> {
192
192
  const r = this
193
193
  r!.prevRune = -1
194
194
  if (r!.i >= ($.len(r!.s) as number)) {
@@ -197,7 +197,7 @@ export class Reader {
197
197
  let s = $.sliceString(r!.s, r!.i, undefined)
198
198
  let m: number
199
199
  let err: $.GoError
200
- ;[m, err] = io.WriteString(w, s)
200
+ ;[m, err] = await io.WriteString(w, s)
201
201
  if (m > $.len(s)) {
202
202
  $.panic('strings.Reader.WriteTo: invalid WriteString count')
203
203
  }
package/gs/sync/meta.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "WaitGroup.Wait": true,
11
11
  "Once.Do": true,
12
12
  "Cond.Wait": true,
13
+ "Pool.Get": true,
13
14
  "Map.CompareAndDelete": true,
14
15
  "Map.Delete": true,
15
16
  "Map.Load": true,
@@ -1,6 +1,8 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
 
3
- import { Cond, Map, Mutex, RWMutex, WaitGroup } from './sync.js'
3
+ import * as $ from '@goscript/builtin/index.js'
4
+
5
+ import { Cond, Map, Mutex, Pool, RWMutex, WaitGroup } from './sync.js'
4
6
 
5
7
  describe('sync.WaitGroup', () => {
6
8
  it('Go tracks scheduled work and unblocks Wait after completion', async () => {
@@ -89,4 +91,42 @@ describe('sync.Map', () => {
89
91
 
90
92
  expect(visited).toEqual(['a:1'])
91
93
  })
94
+
95
+ it('matches boxed comparable interface keys', async () => {
96
+ const m = new Map()
97
+ const first = $.namedValueInterfaceValue(8, 'uint16', {}, {
98
+ kind: $.TypeKind.Basic,
99
+ name: 'uint16',
100
+ })
101
+ const second = $.namedValueInterfaceValue(8, 'uint16', {}, {
102
+ kind: $.TypeKind.Basic,
103
+ name: 'uint16',
104
+ })
105
+
106
+ await m.Store(first, 'compressor')
107
+ expect(await m.Load(second)).toEqual(['compressor', true])
108
+ expect(await m.LoadOrStore(second, 'duplicate')).toEqual([
109
+ 'compressor',
110
+ true,
111
+ ])
112
+ expect(await m.CompareAndSwap(second, 'compressor', 'updated')).toBe(true)
113
+ expect(await m.Load(first)).toEqual(['updated', true])
114
+ expect(await m.CompareAndDelete(first, 'updated')).toBe(true)
115
+ expect(await m.Load(second)).toEqual([undefined, false])
116
+ })
117
+ })
118
+
119
+ describe('sync.Pool', () => {
120
+ it('awaits async New functions before returning a pooled value', async () => {
121
+ const pool = new Pool({
122
+ New: async () => {
123
+ await Promise.resolve()
124
+ return 'created'
125
+ },
126
+ })
127
+
128
+ expect(await pool.Get()).toBe('created')
129
+ pool.Put('reused')
130
+ expect(await pool.Get()).toBe('reused')
131
+ })
92
132
  })