goscript 0.1.2 → 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/lowered-program.go +1 -0
- package/compiler/lowering.go +1325 -194
- package/compiler/lowering_bench_test.go +350 -0
- package/compiler/override-registry_test.go +43 -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 +1436 -50
- package/compiler/typescript-emitter.go +47 -4
- 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/channel.js +36 -9
- package/dist/gs/builtin/channel.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 +80 -8
- 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/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.d.ts +5 -0
- package/dist/gs/crypto/sha1/index.js +103 -0
- package/dist/gs/crypto/sha1/index.js.map +1 -0
- 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 +4 -4
- package/dist/gs/fmt/fmt.js +93 -19
- 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 +18 -11
- package/dist/gs/io/io.js +107 -44
- 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/httptest/index.js +7 -5
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +11 -1
- package/dist/gs/net/http/index.js +157 -11
- 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/os/zero_copy_posix.gs.js +1 -1
- package/dist/gs/os/zero_copy_posix.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/channel.ts +47 -9
- package/gs/builtin/runtime-contract.test.ts +78 -0
- package/gs/builtin/slice.ts +7 -0
- package/gs/builtin/type.ts +97 -8
- package/gs/bytes/bytes.gs.ts +19 -10
- package/gs/bytes/bytes.test.ts +17 -0
- 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/context/context.test.ts +5 -1
- package/gs/crypto/sha1/index.test.ts +45 -0
- package/gs/crypto/sha1/index.ts +127 -0
- package/gs/crypto/sha1/meta.json +8 -0
- 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 +61 -3
- package/gs/fmt/fmt.ts +115 -22
- 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/aperturerobotics/util/conc/index.test.ts +1 -1
- 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 +135 -0
- package/gs/io/io.ts +143 -63
- package/gs/io/meta.json +7 -1
- package/gs/math/bits/index.ts +52 -28
- 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 +46 -0
- package/gs/net/http/index.ts +178 -12
- 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/os/zero_copy_posix.gs.ts +1 -2
- 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 +2 -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
package/gs/bytes/bytes.gs.ts
CHANGED
|
@@ -8,6 +8,9 @@ import * as utf8 from "@goscript/unicode/utf8/index.js"
|
|
|
8
8
|
// for linkname
|
|
9
9
|
import * as _ from "@goscript/unsafe/index.js"
|
|
10
10
|
|
|
11
|
+
type SyncCallbackResult<T> = T | globalThis.Promise<T>
|
|
12
|
+
type PredicateCallback = ((r: number) => SyncCallbackResult<boolean>) | null
|
|
13
|
+
|
|
11
14
|
// Equal reports whether a and b
|
|
12
15
|
// are the same length and contain the same bytes.
|
|
13
16
|
// A nil argument is equivalent to an empty slice.
|
|
@@ -90,7 +93,7 @@ export function ContainsRune(b: $.Bytes, r: number): boolean {
|
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
// ContainsFunc reports whether any of the UTF-8-encoded code points r within b satisfy f(r).
|
|
93
|
-
export function ContainsFunc(b: $.Bytes, f:
|
|
96
|
+
export function ContainsFunc(b: $.Bytes, f: PredicateCallback): boolean {
|
|
94
97
|
return IndexFunc(b, f) >= 0
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -794,21 +797,21 @@ export function TrimSuffix(s: $.Bytes, suffix: $.Bytes): $.Bytes {
|
|
|
794
797
|
// IndexFunc interprets s as a sequence of UTF-8-encoded code points.
|
|
795
798
|
// It returns the byte index in s of the first Unicode
|
|
796
799
|
// code point satisfying f(c), or -1 if none do.
|
|
797
|
-
export function IndexFunc(s: $.Bytes, f:
|
|
800
|
+
export function IndexFunc(s: $.Bytes, f: PredicateCallback): number {
|
|
798
801
|
return indexFunc(s, f, true)
|
|
799
802
|
}
|
|
800
803
|
|
|
801
804
|
// LastIndexFunc interprets s as a sequence of UTF-8-encoded code points.
|
|
802
805
|
// It returns the byte index in s of the last Unicode
|
|
803
806
|
// code point satisfying f(c), or -1 if none do.
|
|
804
|
-
export function LastIndexFunc(s: $.Bytes, f:
|
|
807
|
+
export function LastIndexFunc(s: $.Bytes, f: PredicateCallback): number {
|
|
805
808
|
return lastIndexFunc(s, f, true)
|
|
806
809
|
}
|
|
807
810
|
|
|
808
811
|
// indexFunc is the same as IndexFunc except that if
|
|
809
812
|
// truth==false, the sense of the predicate function is
|
|
810
813
|
// inverted.
|
|
811
|
-
export function indexFunc(s: $.Bytes, f:
|
|
814
|
+
export function indexFunc(s: $.Bytes, f: PredicateCallback, truth: boolean): number {
|
|
812
815
|
if (s === null || f === null) {
|
|
813
816
|
return -1
|
|
814
817
|
}
|
|
@@ -817,12 +820,12 @@ export function indexFunc(s: $.Bytes, f: ((r: number) => boolean) | null, truth:
|
|
|
817
820
|
const [r, size] = utf8.DecodeRune($.goSlice(s, i, undefined))
|
|
818
821
|
if (size <= 0) {
|
|
819
822
|
// Invalid UTF-8
|
|
820
|
-
if (f(utf8.RuneError) === truth) {
|
|
823
|
+
if (syncBoolean(f(utf8.RuneError)) === truth) {
|
|
821
824
|
return i
|
|
822
825
|
}
|
|
823
826
|
i++
|
|
824
827
|
} else {
|
|
825
|
-
if (f(r) === truth) {
|
|
828
|
+
if (syncBoolean(f(r)) === truth) {
|
|
826
829
|
return i
|
|
827
830
|
}
|
|
828
831
|
i += size
|
|
@@ -835,7 +838,7 @@ export function indexFunc(s: $.Bytes, f: ((r: number) => boolean) | null, truth:
|
|
|
835
838
|
// lastIndexFunc is the same as LastIndexFunc except that if
|
|
836
839
|
// truth==false, the sense of the predicate function is
|
|
837
840
|
// inverted.
|
|
838
|
-
export function lastIndexFunc(s: $.Bytes, f:
|
|
841
|
+
export function lastIndexFunc(s: $.Bytes, f: PredicateCallback, truth: boolean): number {
|
|
839
842
|
if (s === null || f === null) {
|
|
840
843
|
return -1
|
|
841
844
|
}
|
|
@@ -846,12 +849,12 @@ export function lastIndexFunc(s: $.Bytes, f: ((r: number) => boolean) | null, tr
|
|
|
846
849
|
const [r, size] = utf8.DecodeRune($.goSlice(s, i, undefined))
|
|
847
850
|
if (size <= 0) {
|
|
848
851
|
// Invalid UTF-8
|
|
849
|
-
if (f(utf8.RuneError) === truth) {
|
|
852
|
+
if (syncBoolean(f(utf8.RuneError)) === truth) {
|
|
850
853
|
lastIndex = i
|
|
851
854
|
}
|
|
852
855
|
i++
|
|
853
856
|
} else {
|
|
854
|
-
if (f(r) === truth) {
|
|
857
|
+
if (syncBoolean(f(r)) === truth) {
|
|
855
858
|
lastIndex = i
|
|
856
859
|
}
|
|
857
860
|
i += size
|
|
@@ -861,6 +864,13 @@ export function lastIndexFunc(s: $.Bytes, f: ((r: number) => boolean) | null, tr
|
|
|
861
864
|
return lastIndex
|
|
862
865
|
}
|
|
863
866
|
|
|
867
|
+
function syncBoolean(value: SyncCallbackResult<boolean>): boolean {
|
|
868
|
+
if (value instanceof Promise) {
|
|
869
|
+
throw new Error("bytes: asynchronous callback result is not supported")
|
|
870
|
+
}
|
|
871
|
+
return value
|
|
872
|
+
}
|
|
873
|
+
|
|
864
874
|
class asciiSet {
|
|
865
875
|
constructor(public _value: number[]) {}
|
|
866
876
|
|
|
@@ -1285,4 +1295,3 @@ export function CutSuffix(s: $.Bytes, suffix: $.Bytes): [$.Bytes, boolean] {
|
|
|
1285
1295
|
}
|
|
1286
1296
|
return [$.goSlice(s, undefined, $.len(s) - $.len(suffix)), true]
|
|
1287
1297
|
}
|
|
1288
|
-
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as $ from "@goscript/builtin/index.js"
|
|
2
|
+
import { IndexFunc } from "./index.js"
|
|
3
|
+
import { describe, expect, test } from "vitest"
|
|
4
|
+
|
|
5
|
+
describe("bytes", () => {
|
|
6
|
+
test("IndexFunc accepts generated async-shaped callbacks", () => {
|
|
7
|
+
const predicate: (r: number) => boolean | Promise<boolean> = (r) => r === 0x62
|
|
8
|
+
|
|
9
|
+
expect(IndexFunc($.stringToBytes("abc"), predicate)).toBe(1)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test("IndexFunc rejects actual async callback results", () => {
|
|
13
|
+
expect(() => IndexFunc($.stringToBytes("abc"), async (r) => r === 0x62)).toThrow(
|
|
14
|
+
"bytes: asynchronous callback result is not supported",
|
|
15
|
+
)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -25,4 +25,101 @@ describe('compress/zlib override', () => {
|
|
|
25
25
|
expect(readErr).toBeNull()
|
|
26
26
|
expect($.bytesToString(out)).toBe('hello compressed world')
|
|
27
27
|
})
|
|
28
|
+
|
|
29
|
+
test('reader implements resettable zlib reader contract', async () => {
|
|
30
|
+
const first = $.markAsStructValue(new bytes.Buffer())
|
|
31
|
+
const firstWriter = NewWriter(first)
|
|
32
|
+
expect(firstWriter.Write($.stringToBytes('first stream'))[1]).toBeNull()
|
|
33
|
+
expect(await firstWriter.Close()).toBeNull()
|
|
34
|
+
|
|
35
|
+
const second = $.markAsStructValue(new bytes.Buffer())
|
|
36
|
+
const secondWriter = NewWriter(second)
|
|
37
|
+
expect(secondWriter.Write($.stringToBytes('second stream'))[1]).toBeNull()
|
|
38
|
+
expect(await secondWriter.Close()).toBeNull()
|
|
39
|
+
|
|
40
|
+
const readerInterface = $.registerInterfaceType(
|
|
41
|
+
'compress/zlib.testReader',
|
|
42
|
+
null,
|
|
43
|
+
[
|
|
44
|
+
{ name: 'Close', args: [], returns: [{ type: 'error' }] },
|
|
45
|
+
{
|
|
46
|
+
name: 'Read',
|
|
47
|
+
args: [
|
|
48
|
+
{
|
|
49
|
+
name: 'p',
|
|
50
|
+
type: {
|
|
51
|
+
kind: $.TypeKind.Slice,
|
|
52
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
returns: [{ type: 'int' }, { type: 'error' }],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Reset',
|
|
60
|
+
args: [{ type: 'io.Reader' }, { type: '[]byte' }],
|
|
61
|
+
returns: [{ type: 'error' }],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const [reader, readerErr] = NewReader(bytes.NewReader(first.Bytes()))
|
|
67
|
+
expect(readerErr).toBeNull()
|
|
68
|
+
const [zlibReader, ok] = $.typeAssertTuple<
|
|
69
|
+
io.ReadCloser & {
|
|
70
|
+
Reset(r: io.Reader | null, dict: $.Bytes | null): $.GoError
|
|
71
|
+
}
|
|
72
|
+
>(reader, readerInterface)
|
|
73
|
+
expect(ok).toBe(true)
|
|
74
|
+
|
|
75
|
+
const [firstOut, firstReadErr] = await io.ReadAll(zlibReader)
|
|
76
|
+
expect(firstReadErr).toBeNull()
|
|
77
|
+
expect($.bytesToString(firstOut)).toBe('first stream')
|
|
78
|
+
|
|
79
|
+
expect(zlibReader.Reset(bytes.NewReader(second.Bytes()), null)).toBeNull()
|
|
80
|
+
const [secondOut, secondReadErr] = await io.ReadAll(zlibReader)
|
|
81
|
+
expect(secondReadErr).toBeNull()
|
|
82
|
+
expect($.bytesToString(secondOut)).toBe('second stream')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('reader reset accepts async generated readers', async () => {
|
|
86
|
+
const compressed = $.markAsStructValue(new bytes.Buffer())
|
|
87
|
+
const writer = NewWriter(compressed)
|
|
88
|
+
expect(writer.Write($.stringToBytes('async source stream'))[1]).toBeNull()
|
|
89
|
+
expect(await writer.Close()).toBeNull()
|
|
90
|
+
|
|
91
|
+
const source = bytes.NewReader(compressed.Bytes())
|
|
92
|
+
const asyncReader = {
|
|
93
|
+
async Read(p: $.Bytes): Promise<[number, $.GoError]> {
|
|
94
|
+
await Promise.resolve()
|
|
95
|
+
return source.Read(p)
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const [reader, readerErr] = NewReader(asyncReader as io.Reader)
|
|
100
|
+
expect(readerErr).toBeNull()
|
|
101
|
+
const [out, readErr] = await io.ReadAll(reader!)
|
|
102
|
+
expect(readErr).toBeNull()
|
|
103
|
+
expect($.bytesToString(out)).toBe('async source stream')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('Close awaits pointer-wrapped generated writers', async () => {
|
|
107
|
+
const chunks: number[] = []
|
|
108
|
+
const sink = {
|
|
109
|
+
async Write(p: $.Bytes): Promise<[number, $.GoError]> {
|
|
110
|
+
await Promise.resolve()
|
|
111
|
+
chunks.push(...Array.from(p ?? []))
|
|
112
|
+
return [$.len(p), null]
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
const writer = NewWriter(
|
|
116
|
+
$.interfaceValue($.varRef(sink), '*zlib.asyncWriter'),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
const [written, writeErr] = writer.Write($.stringToBytes('async zlib sink'))
|
|
120
|
+
expect(writeErr).toBeNull()
|
|
121
|
+
expect(written).toBe('async zlib sink'.length)
|
|
122
|
+
expect(await writer.Close()).toBeNull()
|
|
123
|
+
expect(chunks.length).toBeGreaterThan(0)
|
|
124
|
+
})
|
|
28
125
|
})
|
|
@@ -3,7 +3,11 @@ import * as errors from '@goscript/errors/index.js'
|
|
|
3
3
|
import * as io from '@goscript/io/index.js'
|
|
4
4
|
|
|
5
5
|
export type Resetter = {
|
|
6
|
-
Reset(r: io.Reader | null, dict: $.Bytes | null):
|
|
6
|
+
Reset(r: io.Reader | null, dict: $.Bytes | null): $.GoError
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type maybeAsyncWriter = {
|
|
10
|
+
Write(p: $.Bytes): [number, $.GoError] | Promise<[number, $.GoError]>
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export let ErrChecksum = errors.New('zlib: invalid checksum')
|
|
@@ -22,12 +26,30 @@ export function __goscript_set_ErrHeader(value: $.GoError): void {
|
|
|
22
26
|
ErrHeader = value
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
class zlibReader implements
|
|
29
|
+
class zlibReader implements Resetter {
|
|
30
|
+
private data: Uint8Array = new Uint8Array(0)
|
|
26
31
|
private offset = 0
|
|
32
|
+
private pending:
|
|
33
|
+
| Promise<{ data: Uint8Array | null; err: $.GoError }>
|
|
34
|
+
| null = null
|
|
27
35
|
|
|
28
|
-
constructor(
|
|
36
|
+
constructor(data?: Uint8Array) {
|
|
37
|
+
if (data != null) {
|
|
38
|
+
this.data = data
|
|
39
|
+
}
|
|
40
|
+
}
|
|
29
41
|
|
|
30
|
-
Read(p: $.Bytes): [number, $.GoError] {
|
|
42
|
+
async Read(p: $.Bytes): Promise<[number, $.GoError]> {
|
|
43
|
+
const pending = this.pending
|
|
44
|
+
if (pending != null) {
|
|
45
|
+
this.pending = null
|
|
46
|
+
const result = await pending
|
|
47
|
+
if (result.err != null) {
|
|
48
|
+
return [0, result.err]
|
|
49
|
+
}
|
|
50
|
+
this.data = result.data ?? new Uint8Array(0)
|
|
51
|
+
this.offset = 0
|
|
52
|
+
}
|
|
31
53
|
if (this.offset >= this.data.length) {
|
|
32
54
|
return [0, io.EOF]
|
|
33
55
|
}
|
|
@@ -41,6 +63,26 @@ class zlibReader implements io.ReadCloser {
|
|
|
41
63
|
Close(): $.GoError {
|
|
42
64
|
return null
|
|
43
65
|
}
|
|
66
|
+
|
|
67
|
+
Reset(r: io.Reader | null, dict: $.Bytes | null): $.GoError {
|
|
68
|
+
if (r == null) {
|
|
69
|
+
return errors.New('zlib: nil reader')
|
|
70
|
+
}
|
|
71
|
+
const result = readInflated(r, dict)
|
|
72
|
+
if (result instanceof Promise) {
|
|
73
|
+
this.data = new Uint8Array(0)
|
|
74
|
+
this.offset = 0
|
|
75
|
+
this.pending = result
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
if (result.err != null) {
|
|
79
|
+
return result.err
|
|
80
|
+
}
|
|
81
|
+
this.data = result.data ?? new Uint8Array(0)
|
|
82
|
+
this.offset = 0
|
|
83
|
+
this.pending = null
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
44
86
|
}
|
|
45
87
|
|
|
46
88
|
export class Writer {
|
|
@@ -58,7 +100,7 @@ export class Writer {
|
|
|
58
100
|
return [data.length, null]
|
|
59
101
|
}
|
|
60
102
|
|
|
61
|
-
Close():
|
|
103
|
+
async Close(): Promise<$.GoError> {
|
|
62
104
|
if (this.closed) {
|
|
63
105
|
return null
|
|
64
106
|
}
|
|
@@ -67,7 +109,8 @@ export class Writer {
|
|
|
67
109
|
return errors.New('zlib: nil writer')
|
|
68
110
|
}
|
|
69
111
|
const compressed = deflate(concat(this.chunks))
|
|
70
|
-
const
|
|
112
|
+
const writer = $.pointerValue<maybeAsyncWriter>(this.w)
|
|
113
|
+
const [, err] = await writer.Write(compressed)
|
|
71
114
|
return err
|
|
72
115
|
}
|
|
73
116
|
|
|
@@ -109,21 +152,14 @@ export function NewReader(
|
|
|
109
152
|
|
|
110
153
|
export function NewReaderDict(
|
|
111
154
|
r: io.Reader | null,
|
|
112
|
-
|
|
155
|
+
dict: $.Bytes | null,
|
|
113
156
|
): [io.ReadCloser | null, $.GoError] {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (readErr != null) {
|
|
119
|
-
return [null, readErr]
|
|
120
|
-
}
|
|
121
|
-
try {
|
|
122
|
-
const out = inflate($.bytesToUint8Array(data))
|
|
123
|
-
return [new zlibReader(out), null]
|
|
124
|
-
} catch {
|
|
125
|
-
return [null, ErrHeader]
|
|
157
|
+
const reader = new zlibReader()
|
|
158
|
+
const err = reader.Reset(r, dict)
|
|
159
|
+
if (err != null) {
|
|
160
|
+
return [null, err]
|
|
126
161
|
}
|
|
162
|
+
return [reader as any, null]
|
|
127
163
|
}
|
|
128
164
|
|
|
129
165
|
function deflate(data: Uint8Array): Uint8Array {
|
|
@@ -168,20 +204,74 @@ function nodeZlib(): any {
|
|
|
168
204
|
throw new Error('compress/zlib: node zlib module unavailable')
|
|
169
205
|
}
|
|
170
206
|
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
|
|
207
|
+
function readInflated(
|
|
208
|
+
r: io.Reader,
|
|
209
|
+
dict: $.Bytes | null,
|
|
210
|
+
):
|
|
211
|
+
| { data: Uint8Array | null; err: $.GoError }
|
|
212
|
+
| Promise<{ data: Uint8Array | null; err: $.GoError }> {
|
|
213
|
+
const chunks: number[] = []
|
|
214
|
+
const buf = $.makeSlice<number>(1, undefined, 'byte')
|
|
215
|
+
while (true) {
|
|
216
|
+
const read = r.Read(buf)
|
|
217
|
+
if (read instanceof Promise) {
|
|
218
|
+
return readInflatedAsync(read, r, buf, chunks, dict)
|
|
219
|
+
}
|
|
220
|
+
const [n, err] = read
|
|
221
|
+
const result = recordCompressedBytes(chunks, buf, n, dict)
|
|
222
|
+
if (result.err == null && result.data != null) {
|
|
223
|
+
return result
|
|
224
|
+
}
|
|
225
|
+
if (err != null) {
|
|
226
|
+
if (err === io.EOF) {
|
|
227
|
+
return result
|
|
228
|
+
}
|
|
229
|
+
return { data: null, err }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function readInflatedAsync(
|
|
235
|
+
first: Promise<[number, $.GoError]>,
|
|
236
|
+
r: io.Reader,
|
|
237
|
+
buf: $.Bytes,
|
|
238
|
+
chunks: number[],
|
|
239
|
+
dict: $.Bytes | null,
|
|
240
|
+
): Promise<{ data: Uint8Array | null; err: $.GoError }> {
|
|
241
|
+
let read = await first
|
|
174
242
|
while (true) {
|
|
175
|
-
const [n, err] =
|
|
176
|
-
|
|
177
|
-
|
|
243
|
+
const [n, err] = read
|
|
244
|
+
const result = recordCompressedBytes(chunks, buf, n, dict)
|
|
245
|
+
if (result.err == null && result.data != null) {
|
|
246
|
+
return result
|
|
178
247
|
}
|
|
179
248
|
if (err != null) {
|
|
180
249
|
if (err === io.EOF) {
|
|
181
|
-
return
|
|
250
|
+
return result
|
|
182
251
|
}
|
|
183
|
-
return
|
|
252
|
+
return { data: null, err }
|
|
253
|
+
}
|
|
254
|
+
read = await r.Read(buf)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function recordCompressedBytes(
|
|
259
|
+
chunks: number[],
|
|
260
|
+
buf: $.Bytes,
|
|
261
|
+
n: number,
|
|
262
|
+
dict: $.Bytes | null,
|
|
263
|
+
): { data: Uint8Array | null; err: $.GoError } {
|
|
264
|
+
if (n > 0) {
|
|
265
|
+
chunks.push(...$.bytesToUint8Array($.goSlice(buf, 0, n)))
|
|
266
|
+
}
|
|
267
|
+
const compressed = new Uint8Array(chunks)
|
|
268
|
+
try {
|
|
269
|
+
return { data: inflate(compressed), err: null }
|
|
270
|
+
} catch {
|
|
271
|
+
if (dict != null && $.len(dict) > 0) {
|
|
272
|
+
return { data: null, err: ErrDictionary }
|
|
184
273
|
}
|
|
274
|
+
return { data: null, err: ErrHeader }
|
|
185
275
|
}
|
|
186
276
|
}
|
|
187
277
|
|
|
@@ -8,6 +8,10 @@ async function nextMicrotask(): Promise<void> {
|
|
|
8
8
|
await new Promise<void>((resolve) => queueMicrotask(resolve))
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
async function nextTask(): Promise<void> {
|
|
12
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 0))
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
describe('context override', () => {
|
|
12
16
|
it('matches generated struct keys by Go comparable value', () => {
|
|
13
17
|
class Key {
|
|
@@ -39,7 +43,7 @@ describe('context override', () => {
|
|
|
39
43
|
|
|
40
44
|
cancel?.()
|
|
41
45
|
await nextMicrotask()
|
|
42
|
-
await
|
|
46
|
+
await nextTask()
|
|
43
47
|
|
|
44
48
|
expect(called).toBe(true)
|
|
45
49
|
expect(stop()).toBe(false)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
|
|
5
|
+
import { New, Size, Sum } from './index.js'
|
|
6
|
+
|
|
7
|
+
describe('crypto/sha1 override', () => {
|
|
8
|
+
it('matches Node digest', async () => {
|
|
9
|
+
const data = new TextEncoder().encode('goscript sha1')
|
|
10
|
+
|
|
11
|
+
expect(toHex(await Sum(data))).toBe(nodeHash(data))
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('supports incremental hash.Hash use', async () => {
|
|
15
|
+
const h = New()
|
|
16
|
+
h.Write(new TextEncoder().encode('go'))
|
|
17
|
+
h.Write(new TextEncoder().encode('script'))
|
|
18
|
+
|
|
19
|
+
expect(toHex(await h.Sum(null))).toBe(
|
|
20
|
+
nodeHash(new TextEncoder().encode('goscript')),
|
|
21
|
+
)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('appends into spare byte-slice backing', async () => {
|
|
25
|
+
const h = New()
|
|
26
|
+
h.Write(new TextEncoder().encode('abc'))
|
|
27
|
+
|
|
28
|
+
const backing = $.makeSlice<number>(Size, undefined, 'byte')
|
|
29
|
+
const out = await h.Sum($.goSlice(backing, 0, 0))
|
|
30
|
+
expect(out.length).toBe(Size)
|
|
31
|
+
expect(backing[0]).toBe(out[0])
|
|
32
|
+
expect(backing[Size - 1]).toBe(out[Size - 1])
|
|
33
|
+
expect(toHex($.bytesToUint8Array(backing))).toBe(
|
|
34
|
+
nodeHash(new TextEncoder().encode('abc')),
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
function nodeHash(data: Uint8Array): string {
|
|
40
|
+
return createHash('sha1').update(data).digest('hex')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function toHex(value: Uint8Array): string {
|
|
44
|
+
return Buffer.from(value).toString('hex')
|
|
45
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
+
import {
|
|
3
|
+
getHostRuntime,
|
|
4
|
+
type NodeCryptoHash,
|
|
5
|
+
} from '@goscript/builtin/hostio.js'
|
|
6
|
+
|
|
7
|
+
export const Size = 20
|
|
8
|
+
export const BlockSize = 64
|
|
9
|
+
|
|
10
|
+
class Sha1Error {
|
|
11
|
+
constructor(private readonly message: string) {}
|
|
12
|
+
|
|
13
|
+
Error(): string {
|
|
14
|
+
return this.message
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class Digest {
|
|
19
|
+
private chunks: Uint8Array[] = []
|
|
20
|
+
private dataLength = 0
|
|
21
|
+
private hash: NodeCryptoHash | null
|
|
22
|
+
private canCopyHash: boolean
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
this.hash = createNodeHash()
|
|
26
|
+
this.canCopyHash = typeof this.hash?.copy === 'function'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Write(p: $.Bytes): [number, $.GoError] {
|
|
30
|
+
const bytes = $.bytesToUint8Array(p)
|
|
31
|
+
this.hash?.update(bytes)
|
|
32
|
+
if (!this.canCopyHash) {
|
|
33
|
+
this.chunks.push(bytes.slice())
|
|
34
|
+
this.dataLength += bytes.length
|
|
35
|
+
}
|
|
36
|
+
return [bytes.length, null]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async Sum(b: $.Bytes): Promise<$.Bytes> {
|
|
40
|
+
const digest =
|
|
41
|
+
this.canCopyHash ?
|
|
42
|
+
new Uint8Array(this.hash!.copy!().digest())
|
|
43
|
+
: await sum(this.snapshotBytes())
|
|
44
|
+
return appendDigest(b, digest)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Reset(): void {
|
|
48
|
+
this.chunks = []
|
|
49
|
+
this.dataLength = 0
|
|
50
|
+
this.hash = createNodeHash()
|
|
51
|
+
this.canCopyHash = typeof this.hash?.copy === 'function'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Size(): number {
|
|
55
|
+
return Size
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
BlockSize(): number {
|
|
59
|
+
return BlockSize
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private snapshotBytes(): Uint8Array {
|
|
63
|
+
return concatChunks(this.chunks, this.dataLength)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function New(): any {
|
|
68
|
+
return new Digest()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function Sum(data: $.Bytes): Promise<Uint8Array> {
|
|
72
|
+
return sum(data)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function sum(data: $.Bytes): Promise<Uint8Array> {
|
|
76
|
+
const hash = createNodeHash()
|
|
77
|
+
if (hash != null) {
|
|
78
|
+
return new Uint8Array(hash.update($.bytesToUint8Array(data)).digest())
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const subtle = subtleCrypto()
|
|
82
|
+
if (subtle == null) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
new Sha1Error('crypto/sha1: WebCrypto digest is unavailable').Error(),
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const digest = await subtle.digest(
|
|
89
|
+
'SHA-1',
|
|
90
|
+
$.bytesToUint8Array(data) as unknown as BufferSource,
|
|
91
|
+
)
|
|
92
|
+
return new Uint8Array(digest)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function appendDigest(prefix: $.Bytes, digest: Uint8Array): $.Bytes {
|
|
96
|
+
return $.append(prefix as any, ...digest) as $.Bytes
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createNodeHash(): NodeCryptoHash | null {
|
|
100
|
+
const nodeCrypto = getHostRuntime().nodeCrypto
|
|
101
|
+
if (!nodeCrypto?.createHash) {
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return nodeCrypto.createHash('sha1')
|
|
106
|
+
} catch {
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function concatChunks(chunks: Uint8Array[], length: number): Uint8Array {
|
|
112
|
+
const out = new Uint8Array(length)
|
|
113
|
+
let offset = 0
|
|
114
|
+
for (const chunk of chunks) {
|
|
115
|
+
out.set(chunk, offset)
|
|
116
|
+
offset += chunk.length
|
|
117
|
+
}
|
|
118
|
+
return out
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function subtleCrypto(): SubtleCrypto | null {
|
|
122
|
+
const crypto = globalThis.crypto
|
|
123
|
+
if (crypto?.subtle && typeof crypto.subtle.digest === 'function') {
|
|
124
|
+
return crypto.subtle
|
|
125
|
+
}
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
@@ -25,11 +25,23 @@ describe('crypto/sha256 override', () => {
|
|
|
25
25
|
)
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
+
test('streaming digest appends into spare byte-slice backing', async () => {
|
|
29
|
+
const digest = New()
|
|
30
|
+
expect(digest.Write($.stringToBytes('abc'))).toEqual([3, null])
|
|
31
|
+
|
|
32
|
+
const backing = $.makeSlice<number>(Size, undefined, 'byte')
|
|
33
|
+
const out = await digest.Sum($.goSlice(backing, 0, 0))
|
|
34
|
+
expect(out.length).toBe(Size)
|
|
35
|
+
expect(Array.from($.bytesToUint8Array(backing))).toEqual(
|
|
36
|
+
Array.from(await Sum256($.stringToBytes('abc'))),
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
|
|
28
40
|
test('sums SHA-224 with host crypto', async () => {
|
|
29
41
|
const sum = await Sum224($.stringToBytes('abc'))
|
|
30
42
|
expect(Array.from(sum)).toEqual([
|
|
31
|
-
35, 9, 125, 34, 52, 5, 216, 34, 134, 66, 164, 119, 189, 162, 85, 179,
|
|
32
|
-
|
|
43
|
+
35, 9, 125, 34, 52, 5, 216, 34, 134, 66, 164, 119, 189, 162, 85, 179, 42,
|
|
44
|
+
173, 188, 228, 189, 160, 179, 247, 227, 108, 157, 167,
|
|
33
45
|
])
|
|
34
46
|
})
|
|
35
47
|
|
|
@@ -44,7 +44,7 @@ class Digest {
|
|
|
44
44
|
this.canCopyHash ?
|
|
45
45
|
new Uint8Array(this.hash!.copy!().digest())
|
|
46
46
|
: await sum(this.algorithm, this.snapshotBytes())
|
|
47
|
-
return appendDigest(
|
|
47
|
+
return appendDigest(b, digest)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
Reset(): void {
|
|
@@ -112,11 +112,8 @@ async function sum(
|
|
|
112
112
|
return new Uint8Array(digest)
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
function appendDigest(prefix:
|
|
116
|
-
|
|
117
|
-
out.set(prefix)
|
|
118
|
-
out.set(digest, prefix.length)
|
|
119
|
-
return out
|
|
115
|
+
function appendDigest(prefix: $.Bytes, digest: Uint8Array): $.Bytes {
|
|
116
|
+
return $.append(prefix as any, ...digest) as $.Bytes
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
function createNodeHash(algorithm: ShaAlgorithm): NodeCryptoHash | null {
|