goscript 0.2.3 → 0.2.5
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/README.md +8 -8
- package/cmd/go_js_wasm_exec/main.go +1 -1
- package/cmd/go_js_wasm_exec/main_test.go +1 -1
- package/cmd/goscript/cmd-compile.go +9 -1
- package/cmd/goscript/cmd-test.go +1 -1
- package/cmd/goscript/cmd_compile_test.go +44 -0
- package/cmd/goscript/deps.go +1 -1
- package/cmd/goscript-wasm/main.go +2 -2
- package/compiler/compile-request.go +19 -0
- package/compiler/compile_bench_test.go +121 -0
- package/compiler/compliance_test.go +17 -1
- package/compiler/config.go +2 -0
- package/compiler/gotest/result.go +1 -1
- package/compiler/gotest/runner.go +2 -2
- package/compiler/gotest/runner_test.go +4 -7
- package/compiler/index.test.ts +28 -0
- package/compiler/index.ts +32 -16
- package/compiler/lowering.go +1236 -143
- package/compiler/lowering_bench_test.go +4 -0
- package/compiler/override-facts.go +1 -1
- package/compiler/override-registry_test.go +125 -0
- package/compiler/package-graph.go +92 -0
- package/compiler/package-graph_test.go +113 -0
- package/compiler/runtime-contract.go +1 -1
- package/compiler/semantic-model.go +32 -0
- package/compiler/skeleton_test.go +284 -11
- package/compiler/wasm/compile.go +1 -1
- package/compiler/wasm/compile_test.go +1 -1
- package/dist/compiler/index.d.ts +4 -0
- package/dist/compiler/index.js +26 -15
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/compress/gzip/index.d.ts +41 -0
- package/dist/gs/compress/gzip/index.js +235 -0
- package/dist/gs/compress/gzip/index.js.map +1 -0
- package/dist/gs/database/sql/driver/index.d.ts +165 -0
- package/dist/gs/database/sql/driver/index.js +432 -0
- package/dist/gs/database/sql/driver/index.js.map +1 -0
- package/dist/gs/encoding/binary/index.d.ts +71 -0
- package/dist/gs/encoding/binary/index.js +778 -0
- package/dist/gs/encoding/binary/index.js.map +1 -0
- package/dist/gs/fmt/fmt.js +156 -57
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/klauspost/cpuid/v2/index.d.ts +11 -0
- package/dist/gs/github.com/klauspost/cpuid/v2/index.js +28 -0
- package/dist/gs/github.com/klauspost/cpuid/v2/index.js.map +1 -0
- package/dist/gs/github.com/pkg/errors/errors.d.ts +0 -2
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/index.d.ts +2 -1
- package/dist/gs/github.com/pkg/errors/index.js +1 -1
- package/dist/gs/github.com/pkg/errors/index.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/stack.d.ts +8 -19
- package/dist/gs/github.com/pkg/errors/stack.js +26 -61
- package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/cryptobyte/asn1/index.d.ts +19 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/asn1/index.js +25 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/asn1/index.js.map +1 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.d.ts +104 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js +1107 -0
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js.map +1 -0
- package/dist/gs/golang.org/x/crypto/internal/alias/index.d.ts +3 -0
- package/dist/gs/golang.org/x/crypto/internal/alias/index.js +39 -0
- package/dist/gs/golang.org/x/crypto/internal/alias/index.js.map +1 -0
- package/dist/gs/io/fs/glob.js +1 -1
- package/dist/gs/io/fs/glob.js.map +1 -1
- package/dist/gs/io/fs/readlink.d.ts +1 -1
- package/dist/gs/io/fs/readlink.js +2 -2
- package/dist/gs/io/fs/readlink.js.map +1 -1
- package/dist/gs/io/fs/stat.d.ts +4 -2
- package/dist/gs/io/fs/stat.js +12 -73
- package/dist/gs/io/fs/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.d.ts +2 -2
- package/dist/gs/io/fs/sub.js +7 -7
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/fs/walk.js +1 -1
- package/dist/gs/io/fs/walk.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +18 -14
- package/dist/gs/net/http/index.js +44 -23
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/net/http/pprof/index.d.ts +5 -5
- package/dist/gs/net/http/pprof/index.js +21 -21
- package/dist/gs/net/http/pprof/index.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +6 -1
- package/dist/gs/runtime/runtime.js +15 -8
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.d.ts +8 -5
- package/dist/gs/runtime/trace/index.js +324 -23
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +2 -1
- package/dist/gs/slices/slices.js +9 -3
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/sort/search.gs.d.ts +3 -1
- package/dist/gs/sort/search.gs.js +18 -53
- package/dist/gs/sort/search.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +1 -1
- package/dist/gs/sync/sync.js +3 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/time/time.d.ts +22 -29
- package/dist/gs/time/time.js +111 -32
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unsafe/unsafe.d.ts +3 -2
- package/dist/gs/unsafe/unsafe.js.map +1 -1
- package/go.mod +7 -5
- package/go.sum +12 -26
- package/gs/builtin/runtime-contract.test.ts +25 -0
- package/gs/compress/gzip/index.test.ts +86 -0
- package/gs/compress/gzip/index.ts +297 -0
- package/gs/compress/gzip/meta.json +6 -0
- package/gs/compress/gzip/parity.json +45 -0
- package/gs/database/sql/driver/index.test.ts +88 -0
- package/gs/database/sql/driver/index.ts +675 -0
- package/gs/database/sql/driver/meta.json +3 -0
- package/gs/database/sql/driver/parity.json +144 -0
- package/gs/embed/index.test.ts +1 -1
- package/gs/encoding/binary/index.test.ts +239 -0
- package/gs/encoding/binary/index.ts +999 -0
- package/gs/encoding/binary/meta.json +9 -0
- package/gs/encoding/binary/parity.json +72 -0
- package/gs/fmt/fmt.test.ts +28 -0
- package/gs/fmt/fmt.ts +198 -61
- package/gs/fmt/meta.json +2 -1
- package/gs/github.com/klauspost/cpuid/v2/index.ts +38 -0
- package/gs/github.com/klauspost/cpuid/v2/meta.json +3 -0
- package/gs/github.com/pkg/errors/errors.ts +1 -2
- package/gs/github.com/pkg/errors/index.ts +2 -1
- package/gs/github.com/pkg/errors/stack.ts +34 -62
- package/gs/golang.org/x/crypto/cryptobyte/asn1/index.test.ts +19 -0
- package/gs/golang.org/x/crypto/cryptobyte/asn1/index.ts +29 -0
- package/gs/golang.org/x/crypto/cryptobyte/index.test.ts +255 -0
- package/gs/golang.org/x/crypto/cryptobyte/index.ts +1441 -0
- package/gs/golang.org/x/crypto/cryptobyte/meta.json +3 -0
- package/gs/golang.org/x/crypto/internal/alias/index.test.ts +40 -0
- package/gs/golang.org/x/crypto/internal/alias/index.ts +40 -0
- package/gs/io/fs/glob.ts +1 -1
- package/gs/io/fs/meta.json +3 -0
- package/gs/io/fs/readlink.test.ts +2 -2
- package/gs/io/fs/readlink.ts +5 -2
- package/gs/io/fs/stat.test.ts +79 -0
- package/gs/io/fs/stat.ts +24 -10
- package/gs/io/fs/sub.test.ts +93 -0
- package/gs/io/fs/sub.ts +9 -9
- package/gs/io/fs/walk.ts +1 -1
- package/gs/net/http/index.test.ts +207 -2
- package/gs/net/http/index.ts +68 -37
- package/gs/net/http/meta.json +3 -1
- package/gs/net/http/pprof/index.test.ts +4 -4
- package/gs/net/http/pprof/index.ts +30 -27
- package/gs/runtime/runtime.test.ts +16 -0
- package/gs/runtime/runtime.ts +17 -9
- package/gs/runtime/trace/index.test.ts +113 -14
- package/gs/runtime/trace/index.ts +384 -34
- package/gs/runtime/trace/meta.json +1 -0
- package/gs/slices/slices.test.ts +24 -1
- package/gs/slices/slices.ts +14 -4
- package/gs/sort/meta.json +1 -0
- package/gs/sort/search.gs.ts +20 -5
- package/gs/sync/sync.ts +4 -1
- package/gs/time/time.test.ts +79 -2
- package/gs/time/time.ts +133 -33
- package/gs/unsafe/unsafe.ts +4 -2
- package/package.json +2 -2
package/gs/net/http/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import * as context from '@goscript/context/index.js'
|
|
|
4
4
|
import * as errors from '@goscript/errors/index.js'
|
|
5
5
|
import * as fs from '@goscript/io/fs/fs.js'
|
|
6
6
|
import * as io from '@goscript/io/index.js'
|
|
7
|
+
import * as mime from '@goscript/mime/index.js'
|
|
8
|
+
import * as path from '@goscript/path/index.js'
|
|
7
9
|
import * as strings from '@goscript/strings/index.js'
|
|
8
10
|
import * as time from '@goscript/time/index.js'
|
|
9
11
|
|
|
@@ -248,6 +250,8 @@ export function StatusText(code: number): string {
|
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
export type Header = Map<string, $.Slice<string>>
|
|
253
|
+
type HeaderBox = { __goValue: HeaderValue }
|
|
254
|
+
type HeaderValue = Header | $.VarRef<Header> | HeaderBox
|
|
251
255
|
|
|
252
256
|
export const Header = Map as {
|
|
253
257
|
new (entries?: Iterable<readonly [string, $.Slice<string>]> | null): Header
|
|
@@ -257,48 +261,62 @@ export function CanonicalHeaderKey(s: string): string {
|
|
|
257
261
|
return canonicalMIMEHeaderKey(s)
|
|
258
262
|
}
|
|
259
263
|
|
|
260
|
-
|
|
264
|
+
function headerMap(h: HeaderValue): Header {
|
|
265
|
+
let value: unknown = $.pointerValue(h as Header | $.VarRef<Header>)
|
|
266
|
+
while (
|
|
267
|
+
value !== null &&
|
|
268
|
+
value !== undefined &&
|
|
269
|
+
typeof value === 'object' &&
|
|
270
|
+
'__goValue' in value
|
|
271
|
+
) {
|
|
272
|
+
value = $.pointerValue((value as HeaderBox).__goValue)
|
|
273
|
+
}
|
|
274
|
+
return value as Header
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function Header_Add(h: HeaderValue, key: string, value: string): void {
|
|
278
|
+
const headers = headerMap(h)
|
|
261
279
|
key = canonicalMIMEHeaderKey(key)
|
|
262
|
-
const values = Array.from(
|
|
280
|
+
const values = Array.from(headers.get(key) ?? [])
|
|
263
281
|
values.push(value)
|
|
264
|
-
|
|
282
|
+
headers.set(key, $.arrayToSlice(values))
|
|
265
283
|
}
|
|
266
284
|
|
|
267
|
-
export function Header_Del(h:
|
|
268
|
-
h.delete(canonicalMIMEHeaderKey(key))
|
|
285
|
+
export function Header_Del(h: HeaderValue, key: string): void {
|
|
286
|
+
headerMap(h).delete(canonicalMIMEHeaderKey(key))
|
|
269
287
|
}
|
|
270
288
|
|
|
271
|
-
export function Header_Get(h:
|
|
272
|
-
const values = h.get(canonicalMIMEHeaderKey(key))
|
|
289
|
+
export function Header_Get(h: HeaderValue, key: string): string {
|
|
290
|
+
const values = headerMap(h).get(canonicalMIMEHeaderKey(key))
|
|
273
291
|
return values == null || values.length === 0 ? '' : String(values[0])
|
|
274
292
|
}
|
|
275
293
|
|
|
276
|
-
export function Header_Set(h:
|
|
277
|
-
h.set(canonicalMIMEHeaderKey(key), $.arrayToSlice([value]))
|
|
294
|
+
export function Header_Set(h: HeaderValue, key: string, value: string): void {
|
|
295
|
+
headerMap(h).set(canonicalMIMEHeaderKey(key), $.arrayToSlice([value]))
|
|
278
296
|
}
|
|
279
297
|
|
|
280
|
-
export function Header_Values(h:
|
|
281
|
-
return h.get(canonicalMIMEHeaderKey(key)) ?? null
|
|
298
|
+
export function Header_Values(h: HeaderValue, key: string): $.Slice<string> {
|
|
299
|
+
return headerMap(h).get(canonicalMIMEHeaderKey(key)) ?? null
|
|
282
300
|
}
|
|
283
301
|
|
|
284
|
-
export function Header_Clone(h:
|
|
302
|
+
export function Header_Clone(h: HeaderValue): Header {
|
|
285
303
|
const cloned = new Header()
|
|
286
|
-
for (const [key, values] of h.entries()) {
|
|
304
|
+
for (const [key, values] of headerMap(h).entries()) {
|
|
287
305
|
cloned.set(key, $.arrayToSlice(Array.from(values ?? [])))
|
|
288
306
|
}
|
|
289
307
|
return cloned
|
|
290
308
|
}
|
|
291
309
|
|
|
292
|
-
export function Header_Write(h:
|
|
310
|
+
export function Header_Write(h: HeaderValue, w: io.Writer): $.GoError {
|
|
293
311
|
return Header_WriteSubset(h, w, null)
|
|
294
312
|
}
|
|
295
313
|
|
|
296
314
|
export function Header_WriteSubset(
|
|
297
|
-
h:
|
|
315
|
+
h: HeaderValue,
|
|
298
316
|
w: io.Writer,
|
|
299
317
|
exclude: Map<string, boolean> | null,
|
|
300
318
|
): $.GoError {
|
|
301
|
-
for (const [key, values] of h.entries()) {
|
|
319
|
+
for (const [key, values] of headerMap(h).entries()) {
|
|
302
320
|
if (exclude?.get(key) === true) {
|
|
303
321
|
continue
|
|
304
322
|
}
|
|
@@ -615,15 +633,15 @@ function asciiLower(value: string): [string, boolean] {
|
|
|
615
633
|
return [value.toLowerCase(), true]
|
|
616
634
|
}
|
|
617
635
|
|
|
618
|
-
export function SetCookie(
|
|
636
|
+
export async function SetCookie(
|
|
619
637
|
w: ResponseWriter | null,
|
|
620
638
|
cookie: Cookie | $.VarRef<Cookie> | null,
|
|
621
|
-
): void {
|
|
639
|
+
): Promise<void> {
|
|
622
640
|
const c = $.pointerValue<Cookie | null>(cookie)
|
|
623
641
|
if (w == null || c == null) {
|
|
624
642
|
return
|
|
625
643
|
}
|
|
626
|
-
Header_Add(w.Header(), 'Set-Cookie', c.String())
|
|
644
|
+
Header_Add(await w.Header(), 'Set-Cookie', c.String())
|
|
627
645
|
}
|
|
628
646
|
|
|
629
647
|
class memoryResponseWriter implements ResponseWriter {
|
|
@@ -694,9 +712,9 @@ function inProcessServerRequest(request: Request): Request {
|
|
|
694
712
|
}
|
|
695
713
|
|
|
696
714
|
export interface ResponseWriter {
|
|
697
|
-
Header(): Header
|
|
698
|
-
Write(p: $.Slice<number>): [number, $.GoError]
|
|
699
|
-
WriteHeader(statusCode: number): void
|
|
715
|
+
Header(): Header | Promise<Header>
|
|
716
|
+
Write(p: $.Slice<number>): [number, $.GoError] | Promise<[number, $.GoError]>
|
|
717
|
+
WriteHeader(statusCode: number): void | Promise<void>
|
|
700
718
|
}
|
|
701
719
|
|
|
702
720
|
export class Request {
|
|
@@ -1491,7 +1509,7 @@ async function readFetchBody(
|
|
|
1491
1509
|
type maybePromise<T> = T | Promise<T>
|
|
1492
1510
|
|
|
1493
1511
|
export interface FileSystem {
|
|
1494
|
-
Open(name: string): [File | null, $.GoError]
|
|
1512
|
+
Open(name: string): maybePromise<[File | null, $.GoError]>
|
|
1495
1513
|
}
|
|
1496
1514
|
|
|
1497
1515
|
export interface File extends io.Closer, io.Reader, io.Seeker {
|
|
@@ -1513,9 +1531,9 @@ interface fileServerFile {
|
|
|
1513
1531
|
|
|
1514
1532
|
export function FS(fsys: fs.FS): FileSystem {
|
|
1515
1533
|
return {
|
|
1516
|
-
Open(name: string): [File | null, $.GoError] {
|
|
1534
|
+
async Open(name: string): Promise<[File | null, $.GoError]> {
|
|
1517
1535
|
const cleaned = cleanFileServerPath(name)
|
|
1518
|
-
const [file, err] = fsys?.Open(cleaned) ?? [null, fs.ErrInvalid]
|
|
1536
|
+
const [file, err] = (await fsys?.Open(cleaned)) ?? [null, fs.ErrInvalid]
|
|
1519
1537
|
if (err != null || file == null) {
|
|
1520
1538
|
return [null, err]
|
|
1521
1539
|
}
|
|
@@ -1570,17 +1588,27 @@ export function FileServer(root: fileServerFileSystem | null): Handler {
|
|
|
1570
1588
|
NotFound(w, req)
|
|
1571
1589
|
return
|
|
1572
1590
|
}
|
|
1573
|
-
const
|
|
1574
|
-
if (
|
|
1575
|
-
|
|
1576
|
-
|
|
1591
|
+
const header = await w.Header()
|
|
1592
|
+
if (Header_Get(header, 'Content-Type') === '') {
|
|
1593
|
+
const contentType = mime.TypeByExtension(
|
|
1594
|
+
path.Ext(info?.Name?.() || req.URL?.Path || ''),
|
|
1595
|
+
)
|
|
1596
|
+
if (contentType !== '') {
|
|
1597
|
+
Header_Set(header, 'Content-Type', contentType)
|
|
1598
|
+
}
|
|
1577
1599
|
}
|
|
1578
1600
|
if (info?.Size != null) {
|
|
1579
|
-
Header_Set(
|
|
1601
|
+
Header_Set(header, 'Content-Length', String(info.Size()))
|
|
1580
1602
|
}
|
|
1581
|
-
w.WriteHeader(StatusOK)
|
|
1603
|
+
await w.WriteHeader(StatusOK)
|
|
1582
1604
|
if (req.Method !== MethodHead) {
|
|
1583
|
-
|
|
1605
|
+
const [, copyErr] = await io.Copy(
|
|
1606
|
+
w as unknown as io.Writer,
|
|
1607
|
+
file as io.Reader,
|
|
1608
|
+
)
|
|
1609
|
+
if (copyErr != null) {
|
|
1610
|
+
return
|
|
1611
|
+
}
|
|
1584
1612
|
}
|
|
1585
1613
|
} finally {
|
|
1586
1614
|
await file.Close()
|
|
@@ -2131,7 +2159,7 @@ export function NotFoundHandler(): Handler {
|
|
|
2131
2159
|
export function RedirectHandler(url: string, code: number): Handler {
|
|
2132
2160
|
return {
|
|
2133
2161
|
ServeHTTP(w, r) {
|
|
2134
|
-
Redirect(w, r, url, code)
|
|
2162
|
+
return Redirect(w, r, url, code)
|
|
2135
2163
|
},
|
|
2136
2164
|
}
|
|
2137
2165
|
}
|
|
@@ -2168,17 +2196,20 @@ export function NotFound(
|
|
|
2168
2196
|
Error(w, '404 page not found', StatusNotFound)
|
|
2169
2197
|
}
|
|
2170
2198
|
|
|
2171
|
-
export function Redirect(
|
|
2199
|
+
export async function Redirect(
|
|
2172
2200
|
w: ResponseWriter | null,
|
|
2173
2201
|
_r: Request | $.VarRef<Request> | null,
|
|
2174
2202
|
url: string,
|
|
2175
2203
|
code: number,
|
|
2176
|
-
): void {
|
|
2177
|
-
|
|
2204
|
+
): Promise<void> {
|
|
2205
|
+
if (w == null) {
|
|
2206
|
+
return
|
|
2207
|
+
}
|
|
2208
|
+
const header = await w.Header()
|
|
2178
2209
|
if (header != null) {
|
|
2179
2210
|
Header_Set(header, 'Location', url)
|
|
2180
2211
|
}
|
|
2181
|
-
w
|
|
2212
|
+
await w.WriteHeader(code)
|
|
2182
2213
|
}
|
|
2183
2214
|
|
|
2184
2215
|
export function ParseTime(text: string): [time.Time, $.GoError] {
|
package/gs/net/http/meta.json
CHANGED
|
@@ -27,19 +27,19 @@ class writer implements ResponseWriter {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
describe('net/http/pprof override', () => {
|
|
30
|
-
it('serves the index text expected by debug handlers', () => {
|
|
30
|
+
it('serves the index text expected by debug handlers', async () => {
|
|
31
31
|
const w = new writer()
|
|
32
32
|
|
|
33
|
-
Index(w, null)
|
|
33
|
+
await Index(w, null)
|
|
34
34
|
|
|
35
35
|
expect(Header_Get(w.Header(), 'Content-Type')).toContain('text/html')
|
|
36
36
|
expect(w.String()).toContain('full goroutine stack dump')
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
it('serves named runtime profiles', () => {
|
|
39
|
+
it('serves named runtime profiles', async () => {
|
|
40
40
|
const w = new writer()
|
|
41
41
|
|
|
42
|
-
Handler('goroutine').ServeHTTP(w, null)
|
|
42
|
+
await Handler('goroutine').ServeHTTP(w, null)
|
|
43
43
|
|
|
44
44
|
expect(Header_Get(w.Header(), 'Content-Type')).toContain('text/plain')
|
|
45
45
|
expect(w.String()).toContain('goroutine profile')
|
|
@@ -2,82 +2,85 @@ import * as $ from '@goscript/builtin/index.js'
|
|
|
2
2
|
import * as http from '@goscript/net/http/index.js'
|
|
3
3
|
import * as pprof from '@goscript/runtime/pprof/index.js'
|
|
4
4
|
|
|
5
|
-
function writeString(
|
|
6
|
-
w
|
|
5
|
+
async function writeString(
|
|
6
|
+
w: http.ResponseWriter | null,
|
|
7
|
+
value: string,
|
|
8
|
+
): Promise<void> {
|
|
9
|
+
await w?.Write($.stringToBytes(value))
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
export function Index(
|
|
12
|
+
export async function Index(
|
|
10
13
|
w: http.ResponseWriter | null,
|
|
11
14
|
_r: http.Request | $.VarRef<http.Request> | null,
|
|
12
|
-
): void {
|
|
13
|
-
const header = w?.Header()
|
|
15
|
+
): Promise<void> {
|
|
16
|
+
const header = await w?.Header()
|
|
14
17
|
if (header != null) {
|
|
15
18
|
http.Header_Set(header, 'Content-Type', 'text/html; charset=utf-8')
|
|
16
19
|
}
|
|
17
|
-
writeString(
|
|
20
|
+
await writeString(
|
|
18
21
|
w,
|
|
19
22
|
'<html><body><a href="goroutine?debug=2">full goroutine stack dump</a></body></html>',
|
|
20
23
|
)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
export function Cmdline(
|
|
26
|
+
export async function Cmdline(
|
|
24
27
|
w: http.ResponseWriter | null,
|
|
25
28
|
_r: http.Request | $.VarRef<http.Request> | null,
|
|
26
|
-
): void {
|
|
27
|
-
const header = w?.Header()
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
const header = await w?.Header()
|
|
28
31
|
if (header != null) {
|
|
29
32
|
http.Header_Set(header, 'Content-Type', 'text/plain; charset=utf-8')
|
|
30
33
|
}
|
|
31
|
-
writeString(w, 'goscript')
|
|
34
|
+
await writeString(w, 'goscript')
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
export function Profile(
|
|
37
|
+
export async function Profile(
|
|
35
38
|
w: http.ResponseWriter | null,
|
|
36
39
|
_r: http.Request | $.VarRef<http.Request> | null,
|
|
37
|
-
): void {
|
|
38
|
-
const header = w?.Header()
|
|
40
|
+
): Promise<void> {
|
|
41
|
+
const header = await w?.Header()
|
|
39
42
|
if (header != null) {
|
|
40
43
|
http.Header_Set(header, 'Content-Type', 'application/octet-stream')
|
|
41
44
|
}
|
|
42
|
-
writeString(w, 'cpu profile\n')
|
|
45
|
+
await writeString(w, 'cpu profile\n')
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
export function Symbol(
|
|
48
|
+
export async function Symbol(
|
|
46
49
|
w: http.ResponseWriter | null,
|
|
47
50
|
_r: http.Request | $.VarRef<http.Request> | null,
|
|
48
|
-
): void {
|
|
49
|
-
const header = w?.Header()
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
const header = await w?.Header()
|
|
50
53
|
if (header != null) {
|
|
51
54
|
http.Header_Set(header, 'Content-Type', 'text/plain; charset=utf-8')
|
|
52
55
|
}
|
|
53
|
-
writeString(w, 'num_symbols: 0\n')
|
|
56
|
+
await writeString(w, 'num_symbols: 0\n')
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
export function Trace(
|
|
59
|
+
export async function Trace(
|
|
57
60
|
w: http.ResponseWriter | null,
|
|
58
61
|
_r: http.Request | $.VarRef<http.Request> | null,
|
|
59
|
-
): void {
|
|
60
|
-
const header = w?.Header()
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
const header = await w?.Header()
|
|
61
64
|
if (header != null) {
|
|
62
65
|
http.Header_Set(header, 'Content-Type', 'application/octet-stream')
|
|
63
66
|
}
|
|
64
|
-
writeString(w, 'trace\n')
|
|
67
|
+
await writeString(w, 'trace\n')
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
export function Handler(name: string): http.Handler {
|
|
68
71
|
return {
|
|
69
|
-
ServeHTTP(w) {
|
|
70
|
-
const header = w?.Header()
|
|
72
|
+
async ServeHTTP(w) {
|
|
73
|
+
const header = await w?.Header()
|
|
71
74
|
if (header != null) {
|
|
72
75
|
http.Header_Set(header, 'Content-Type', 'text/plain; charset=utf-8')
|
|
73
76
|
}
|
|
74
77
|
const profile = pprof.Lookup(name)
|
|
75
78
|
if (profile == null) {
|
|
76
|
-
http.NotFound(w, null)
|
|
79
|
+
await http.NotFound(w, null)
|
|
77
80
|
return
|
|
78
81
|
}
|
|
79
|
-
writeString(w, `${name} profile\n`)
|
|
80
|
-
profile.WriteTo(w, 1)
|
|
82
|
+
await writeString(w, `${name} profile\n`)
|
|
83
|
+
profile.WriteTo(w as unknown as any, 1)
|
|
81
84
|
},
|
|
82
85
|
}
|
|
83
86
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
|
|
3
5
|
import {
|
|
4
6
|
Compiler,
|
|
5
7
|
FuncForPC,
|
|
8
|
+
MemStats,
|
|
9
|
+
ReadMemStats,
|
|
6
10
|
ReadTrace,
|
|
7
11
|
SetFinalizer,
|
|
8
12
|
StartTrace,
|
|
@@ -25,4 +29,16 @@ describe('runtime override', () => {
|
|
|
25
29
|
expect(() => SetFinalizer(obj, () => {})).not.toThrow()
|
|
26
30
|
expect(() => SetFinalizer(obj, null)).not.toThrow()
|
|
27
31
|
})
|
|
32
|
+
|
|
33
|
+
it('exposes heap and stack MemStats fields', () => {
|
|
34
|
+
const stats = new MemStats()
|
|
35
|
+
|
|
36
|
+
ReadMemStats($.varRef(stats))
|
|
37
|
+
|
|
38
|
+
expect(stats.HeapAlloc).toBe(stats.Alloc)
|
|
39
|
+
expect(stats.HeapSys).toBe(stats.Sys)
|
|
40
|
+
expect(stats.HeapInuse).toBe(stats.Alloc)
|
|
41
|
+
expect(stats.StackInuse).toBe(0)
|
|
42
|
+
expect(stats.StackSys).toBe(0)
|
|
43
|
+
})
|
|
28
44
|
})
|
package/gs/runtime/runtime.ts
CHANGED
|
@@ -170,6 +170,11 @@ export class MemStats {
|
|
|
170
170
|
public Alloc: number = 0 // bytes allocated and not yet freed
|
|
171
171
|
public TotalAlloc: number = 0 // bytes allocated (even if freed)
|
|
172
172
|
public Sys: number = 0 // bytes obtained from system
|
|
173
|
+
public HeapAlloc: number = 0 // bytes allocated and not yet freed on the heap
|
|
174
|
+
public HeapSys: number = 0 // bytes obtained from system for the heap
|
|
175
|
+
public HeapInuse: number = 0 // bytes in in-use heap spans
|
|
176
|
+
public StackInuse: number = 0 // bytes in stack spans
|
|
177
|
+
public StackSys: number = 0 // bytes obtained from system for stacks
|
|
173
178
|
public Lookups: number = 0 // number of pointer lookups
|
|
174
179
|
public Mallocs: number = 0 // number of mallocs
|
|
175
180
|
public Frees: number = 0 // number of frees
|
|
@@ -181,25 +186,28 @@ export class MemStats {
|
|
|
181
186
|
}
|
|
182
187
|
|
|
183
188
|
private updateMemoryStats(): void {
|
|
184
|
-
|
|
185
|
-
if (typeof performance !== 'undefined' && (performance as any).memory) {
|
|
186
|
-
const mem = (performance as any).memory
|
|
187
|
-
this.Alloc = mem.usedJSHeapSize || 0
|
|
188
|
-
this.Sys = mem.totalJSHeapSize || 0
|
|
189
|
-
this.TotalAlloc = this.Alloc // Simplified
|
|
190
|
-
}
|
|
189
|
+
updateMemoryStats(this)
|
|
191
190
|
}
|
|
192
191
|
}
|
|
193
192
|
|
|
194
193
|
// ReadMemStats populates m with memory allocator statistics
|
|
195
|
-
export function ReadMemStats(m: MemStats): void {
|
|
196
|
-
|
|
194
|
+
export function ReadMemStats(m: MemStats | $.VarRef<MemStats> | null): void {
|
|
195
|
+
m = $.pointerValue<MemStats>(m)
|
|
196
|
+
updateMemoryStats(m)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function updateMemoryStats(m: MemStats): void {
|
|
197
200
|
if (typeof performance !== 'undefined' && (performance as any).memory) {
|
|
198
201
|
const mem = (performance as any).memory
|
|
199
202
|
m.Alloc = mem.usedJSHeapSize || 0
|
|
200
203
|
m.Sys = mem.totalJSHeapSize || 0
|
|
201
204
|
m.TotalAlloc = m.Alloc // Simplified
|
|
202
205
|
}
|
|
206
|
+
m.HeapAlloc = m.Alloc
|
|
207
|
+
m.HeapSys = m.Sys
|
|
208
|
+
m.HeapInuse = m.Alloc
|
|
209
|
+
m.StackInuse = 0
|
|
210
|
+
m.StackSys = 0
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
// Error interface for runtime errors
|
|
@@ -12,8 +12,69 @@ import {
|
|
|
12
12
|
WithRegion,
|
|
13
13
|
} from './index.js'
|
|
14
14
|
|
|
15
|
+
function captureWriter(): { chunks: Uint8Array[]; bytes(): Uint8Array } {
|
|
16
|
+
const chunks: Uint8Array[] = []
|
|
17
|
+
return {
|
|
18
|
+
chunks,
|
|
19
|
+
bytes(): Uint8Array {
|
|
20
|
+
const total = chunks.reduce((n, c) => n + c.length, 0)
|
|
21
|
+
const out = new Uint8Array(total)
|
|
22
|
+
let off = 0
|
|
23
|
+
for (const c of chunks) {
|
|
24
|
+
out.set(c, off)
|
|
25
|
+
off += c.length
|
|
26
|
+
}
|
|
27
|
+
return out
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const writerOf = (chunks: Uint8Array[]) => ({
|
|
33
|
+
Write(p: Uint8Array): [number, null] {
|
|
34
|
+
chunks.push(new Uint8Array(p))
|
|
35
|
+
return [p.length, null]
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// maxBatchSize mirrors the trace v2 reader limit (tracev2.MaxBatchSize, 64<<10).
|
|
40
|
+
// A batch whose declared data length exceeds this is rejected with "invalid
|
|
41
|
+
// batch size", so the encoder must split large captures across batches.
|
|
42
|
+
const maxBatchSize = 64 * 1024
|
|
43
|
+
|
|
44
|
+
// batchSizes walks the trace v2 stream past the 16-byte header and returns the
|
|
45
|
+
// declared data length of every EvEventBatch frame. Each frame is the batch
|
|
46
|
+
// kind byte (1), then uvarints for generation, M id, base timestamp, and data
|
|
47
|
+
// length, then the data block.
|
|
48
|
+
function batchSizes(bytes: Uint8Array): number[] {
|
|
49
|
+
const sizes: number[] = []
|
|
50
|
+
let i = 16 // header: "go 1.22 trace\x00\x00\x00"
|
|
51
|
+
const readUvarint = (): number => {
|
|
52
|
+
let value = 0
|
|
53
|
+
let shift = 0
|
|
54
|
+
for (;;) {
|
|
55
|
+
const b = bytes[i++]
|
|
56
|
+
value += (b & 0x7f) * 2 ** shift
|
|
57
|
+
if ((b & 0x80) === 0) {
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
shift += 7
|
|
61
|
+
}
|
|
62
|
+
return value
|
|
63
|
+
}
|
|
64
|
+
while (i < bytes.length) {
|
|
65
|
+
i++ // EvEventBatch kind byte
|
|
66
|
+
readUvarint() // generation
|
|
67
|
+
readUvarint() // M id
|
|
68
|
+
readUvarint() // base timestamp
|
|
69
|
+
const size = readUvarint()
|
|
70
|
+
sizes.push(size)
|
|
71
|
+
i += size
|
|
72
|
+
}
|
|
73
|
+
return sizes
|
|
74
|
+
}
|
|
75
|
+
|
|
15
76
|
describe('runtime/trace override', () => {
|
|
16
|
-
it('creates no-op tasks
|
|
77
|
+
it('creates no-op tasks when tracing is disabled', () => {
|
|
17
78
|
const [ctx, task] = NewTask(null, 'task')
|
|
18
79
|
|
|
19
80
|
expect(ctx).not.toBeNull()
|
|
@@ -23,7 +84,7 @@ describe('runtime/trace override', () => {
|
|
|
23
84
|
expect(IsEnabled()).toBe(false)
|
|
24
85
|
})
|
|
25
86
|
|
|
26
|
-
it('runs
|
|
87
|
+
it('runs regions', () => {
|
|
27
88
|
const [ctx] = NewTask(context.Background(), 'task')
|
|
28
89
|
const called = { value: false }
|
|
29
90
|
|
|
@@ -35,21 +96,59 @@ describe('runtime/trace override', () => {
|
|
|
35
96
|
expect(called.value).toBe(true)
|
|
36
97
|
})
|
|
37
98
|
|
|
38
|
-
it('
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
99
|
+
it('rejects a nil trace writer', () => {
|
|
100
|
+
expect(Start(null)?.Error()).toBe('runtime/trace: nil trace writer')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('emits a trace v2 stream for user tasks', () => {
|
|
104
|
+
const capture = captureWriter()
|
|
105
|
+
|
|
106
|
+
expect(Start(writerOf(capture.chunks))).toBeNull()
|
|
107
|
+
expect(IsEnabled()).toBe(true)
|
|
108
|
+
|
|
109
|
+
const [ctx, task] = NewTask(context.Background(), 'proof-task')
|
|
110
|
+
WithRegion(ctx, 'proof-region', () => {
|
|
111
|
+
Log(ctx, 'proof-key', 'proof-value')
|
|
112
|
+
})
|
|
113
|
+
task.End()
|
|
114
|
+
|
|
115
|
+
Stop()
|
|
116
|
+
expect(IsEnabled()).toBe(false)
|
|
117
|
+
|
|
118
|
+
const bytes = capture.bytes()
|
|
119
|
+
expect(bytes.length).toBeGreaterThan(0)
|
|
120
|
+
|
|
121
|
+
const header = new TextDecoder().decode(bytes.slice(0, 13))
|
|
122
|
+
expect(header).toBe('go 1.22 trace')
|
|
123
|
+
|
|
124
|
+
// The interned task and region names appear in the string batch.
|
|
125
|
+
const text = new TextDecoder('latin1').decode(bytes)
|
|
126
|
+
expect(text).toContain('proof-task')
|
|
127
|
+
expect(text).toContain('proof-region')
|
|
128
|
+
expect(text).toContain('proof-value')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('splits a large capture into batches under the size limit', () => {
|
|
132
|
+
const capture = captureWriter()
|
|
133
|
+
|
|
134
|
+
expect(Start(writerOf(capture.chunks))).toBeNull()
|
|
135
|
+
|
|
136
|
+
// Record enough distinct tasks and logs to overflow the single-batch ceiling
|
|
137
|
+
// in both the string dictionary and the event stream.
|
|
138
|
+
const eventCount = 4000
|
|
139
|
+
for (let i = 0; i < eventCount; i++) {
|
|
140
|
+
const [ctx, task] = NewTask(context.Background(), `multibatch-task-${i}`)
|
|
141
|
+
Log(ctx, 'multibatch-key', `multibatch-message-${i}`)
|
|
142
|
+
task.End()
|
|
45
143
|
}
|
|
46
144
|
|
|
47
|
-
expect(Start(writer)?.Error()).toBe(
|
|
48
|
-
'runtime/trace: execution tracing is unsupported in GoScript',
|
|
49
|
-
)
|
|
50
|
-
Log(context.Background(), 'category', 'message')
|
|
51
145
|
Stop()
|
|
52
146
|
|
|
53
|
-
|
|
147
|
+
const sizes = batchSizes(capture.bytes())
|
|
148
|
+
// Frequency, plus several string and event batches.
|
|
149
|
+
expect(sizes.length).toBeGreaterThan(3)
|
|
150
|
+
for (const size of sizes) {
|
|
151
|
+
expect(size).toBeLessThanOrEqual(maxBatchSize)
|
|
152
|
+
}
|
|
54
153
|
})
|
|
55
154
|
})
|