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.
- package/cmd/goscript/cmd_compile.go +28 -8
- package/cmd/goscript/cmd_compile_test.go +105 -6
- package/compiler/build-flags.go +9 -10
- package/compiler/gotest/runner_test.go +127 -0
- package/compiler/lowering.go +596 -136
- package/compiler/lowering_bench_test.go +350 -0
- package/compiler/package-graph.go +61 -4
- package/compiler/package-graph_test.go +30 -0
- package/compiler/semantic-model-types.go +8 -0
- package/compiler/semantic-model.go +447 -22
- package/compiler/semantic-model_test.go +138 -0
- package/compiler/skeleton_test.go +948 -14
- package/compiler/typescript-emitter.go +19 -2
- package/dist/gs/builtin/builtin.d.ts +2 -2
- package/dist/gs/builtin/builtin.js +20 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.js +5 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +1 -1
- package/dist/gs/builtin/type.js +72 -5
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/compress/zlib/index.d.ts +3 -3
- package/dist/gs/compress/zlib/index.js +88 -26
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/crypto/sha1/index.js +2 -5
- package/dist/gs/crypto/sha1/index.js.map +1 -1
- package/dist/gs/crypto/sha256/index.js +2 -5
- package/dist/gs/crypto/sha256/index.js.map +1 -1
- package/dist/gs/crypto/sha512/index.js +2 -5
- package/dist/gs/crypto/sha512/index.js.map +1 -1
- package/dist/gs/embed/index.d.ts +6 -0
- package/dist/gs/embed/index.js +210 -5
- package/dist/gs/embed/index.js.map +1 -1
- package/dist/gs/fmt/fmt.d.ts +3 -3
- package/dist/gs/fmt/fmt.js +29 -16
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +118 -6
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -1
- package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
- package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
- package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
- package/dist/gs/io/fs/readdir.js +5 -3
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/io.d.ts +10 -6
- package/dist/gs/io/io.js +87 -42
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.d.ts +26 -5
- package/dist/gs/math/bits/index.js +13 -24
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +3 -1
- package/dist/gs/net/http/index.js +18 -1
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +6 -2
- package/dist/gs/os/types_js.gs.js +169 -8
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +1 -0
- package/dist/gs/reflect/type.js +80 -51
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/strings/reader.d.ts +1 -1
- package/dist/gs/strings/reader.js +2 -2
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +2 -1
- package/dist/gs/sync/sync.js +37 -16
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/syscall/js/index.js +9 -0
- package/dist/gs/syscall/js/index.js.map +1 -1
- package/dist/gs/testing/testing.js +8 -6
- package/dist/gs/testing/testing.js.map +1 -1
- package/gs/builtin/builtin.ts +25 -2
- package/gs/builtin/runtime-contract.test.ts +45 -0
- package/gs/builtin/slice.ts +7 -0
- package/gs/builtin/type.ts +85 -5
- package/gs/compress/zlib/index.test.ts +97 -0
- package/gs/compress/zlib/index.ts +117 -27
- package/gs/compress/zlib/meta.json +4 -1
- package/gs/crypto/sha1/index.test.ts +19 -2
- package/gs/crypto/sha1/index.ts +3 -6
- package/gs/crypto/sha256/index.test.ts +14 -2
- package/gs/crypto/sha256/index.ts +3 -6
- package/gs/crypto/sha512/index.test.ts +17 -2
- package/gs/crypto/sha512/index.ts +3 -6
- package/gs/embed/index.test.ts +87 -0
- package/gs/embed/index.ts +229 -5
- package/gs/fmt/fmt.test.ts +41 -3
- package/gs/fmt/fmt.ts +40 -17
- package/gs/fmt/meta.json +6 -1
- package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +8 -1
- package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +139 -11
- package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
- package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
- package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
- package/gs/io/fs/readdir.test.ts +38 -0
- package/gs/io/fs/readdir.ts +7 -3
- package/gs/io/io.test.ts +77 -6
- package/gs/io/io.ts +114 -52
- package/gs/io/meta.json +7 -1
- package/gs/math/bits/index.ts +52 -28
- package/gs/net/http/index.test.ts +16 -0
- package/gs/net/http/index.ts +19 -2
- package/gs/os/file_unix_js.test.ts +52 -0
- package/gs/os/meta.json +4 -0
- package/gs/os/readdir.test.ts +56 -0
- package/gs/os/types_js.gs.ts +169 -8
- package/gs/reflect/deepequal.test.ts +10 -1
- package/gs/reflect/type.ts +91 -56
- package/gs/reflect/typefor.test.ts +31 -1
- package/gs/strings/meta.json +5 -2
- package/gs/strings/reader.test.ts +2 -2
- package/gs/strings/reader.ts +2 -2
- package/gs/sync/meta.json +1 -0
- package/gs/sync/sync.test.ts +41 -1
- package/gs/sync/sync.ts +41 -16
- package/gs/syscall/js/index.test.ts +18 -0
- package/gs/syscall/js/index.ts +12 -0
- package/gs/testing/testing.test.ts +32 -3
- package/gs/testing/testing.ts +13 -10
- 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
|
+
})
|
package/gs/os/types_js.gs.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
})
|
package/gs/reflect/type.ts
CHANGED
|
@@ -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
|
-
|
|
405
|
-
|
|
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
|
-
|
|
416
|
-
|
|
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
|
-
|
|
427
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
package/gs/strings/meta.json
CHANGED
|
@@ -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)
|
package/gs/strings/reader.ts
CHANGED
|
@@ -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
package/gs/sync/sync.test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
-
import
|
|
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
|
})
|