goscript 0.2.0 → 0.2.1
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-wasm/main.go +38 -6
- package/compiler/diagnostic.go +104 -12
- package/compiler/diagnostic_test.go +106 -0
- package/compiler/gotest/runner.go +1 -17
- package/compiler/gotest/runner_test.go +20 -0
- package/compiler/index.test.ts +23 -0
- package/compiler/lowered-program.go +9 -7
- package/compiler/lowering.go +359 -72
- package/compiler/lowering_bench_test.go +1 -0
- package/compiler/lowering_internal_test.go +18 -0
- package/compiler/protobuf-ts-binding.go +65 -12
- package/compiler/protobuf-ts-binding_test.go +230 -0
- package/compiler/runtime-contract.go +4 -0
- package/compiler/runtime-contract_test.go +2 -0
- package/compiler/service.go +1 -0
- package/compiler/skeleton_test.go +56 -2
- package/compiler/wasm/compile_test.go +37 -4
- package/compiler/wasm-api.go +57 -7
- package/dist/gs/builtin/hostio.js +5 -0
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +11 -1
- package/dist/gs/builtin/slice.js +158 -2
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +30 -5
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/reflect/type.js +57 -0
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/gs/builtin/hostio.test.ts +16 -0
- package/gs/builtin/hostio.ts +7 -0
- package/gs/builtin/runtime-contract.test.ts +28 -0
- package/gs/builtin/slice.ts +225 -20
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +162 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +41 -5
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
- package/gs/internal/byteorder/index.test.ts +2 -2
- package/gs/internal/byteorder/index.ts +2 -2
- package/gs/reflect/type.ts +64 -0
- package/gs/reflect/typefor.test.ts +21 -1
- package/gs/sync/atomic/doc_64.gs.ts +6 -7
- package/gs/sync/atomic/doc_64.test.ts +43 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doc_64.gs.js","sourceRoot":"","sources":["../../../../gs/sync/atomic/doc_64.gs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doc_64.gs.js","sourceRoot":"","sources":["../../../../gs/sync/atomic/doc_64.gs.ts"],"names":[],"mappings":"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"}
|
|
@@ -195,6 +195,22 @@ describe('hostio text writes', () => {
|
|
|
195
195
|
expect(getHostRuntime().nodeFS).toBeNull()
|
|
196
196
|
expect(consoleLog).toHaveBeenCalledWith('browser')
|
|
197
197
|
})
|
|
198
|
+
|
|
199
|
+
it('writes stdout and stderr file descriptors to console in browser-like hosts', () => {
|
|
200
|
+
const consoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
201
|
+
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
202
|
+
|
|
203
|
+
delete (globalThis as any).Deno
|
|
204
|
+
delete (globalThis as any).process
|
|
205
|
+
resetHostRuntimeForTests()
|
|
206
|
+
|
|
207
|
+
const runtime = getHostRuntime()
|
|
208
|
+
expect(runtime.writeFD(1, new Uint8Array([111, 107, 10]))).toBe(3)
|
|
209
|
+
expect(runtime.writeFD(2, new Uint8Array([101, 114, 114, 10]))).toBe(4)
|
|
210
|
+
|
|
211
|
+
expect(consoleLog).toHaveBeenCalledWith('ok')
|
|
212
|
+
expect(consoleError).toHaveBeenCalledWith('err')
|
|
213
|
+
})
|
|
198
214
|
})
|
|
199
215
|
|
|
200
216
|
describe('hostio isMainScript', () => {
|
package/gs/builtin/hostio.ts
CHANGED
|
@@ -104,6 +104,7 @@ export type MainScriptMeta = {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const encoder = new TextEncoder()
|
|
107
|
+
const decoder = new TextDecoder()
|
|
107
108
|
|
|
108
109
|
function getDynamicRequire(): ((specifier: string) => unknown) | null {
|
|
109
110
|
try {
|
|
@@ -342,6 +343,12 @@ function detectHostRuntime(): HostRuntime {
|
|
|
342
343
|
buffer,
|
|
343
344
|
)
|
|
344
345
|
}
|
|
346
|
+
if (fd === 1 || fd === 2) {
|
|
347
|
+
fallbackConsoleWriter(fd === 2 ? 'error' : 'log')(
|
|
348
|
+
decoder.decode(buffer),
|
|
349
|
+
)
|
|
350
|
+
return buffer.length
|
|
351
|
+
}
|
|
345
352
|
throw new HostUnsupportedError()
|
|
346
353
|
}
|
|
347
354
|
const writeStdoutText: HostTextWrite = (data: string) => {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
int,
|
|
18
18
|
arrayPointerFromIndexRef,
|
|
19
19
|
indexAddress,
|
|
20
|
+
indexByteAddress,
|
|
20
21
|
indexRef,
|
|
21
22
|
interfaceValue,
|
|
22
23
|
len,
|
|
@@ -62,6 +63,7 @@ import {
|
|
|
62
63
|
uintShr,
|
|
63
64
|
unref,
|
|
64
65
|
unsupportedPointerRef,
|
|
66
|
+
unsafePointerRef,
|
|
65
67
|
varRef,
|
|
66
68
|
} from './index.js'
|
|
67
69
|
|
|
@@ -423,6 +425,16 @@ describe('builtin runtime contract helpers', () => {
|
|
|
423
425
|
arrayView[2] = 19
|
|
424
426
|
expect((byteBacking as Uint8Array)[3]).toBe(19)
|
|
425
427
|
|
|
428
|
+
const words = [0x11223344, 0]
|
|
429
|
+
const wordBytes = arrayPointerFromIndexRef(indexRef(words, 0), 8, 4, 1)
|
|
430
|
+
const byteView = pointerValue(wordBytes) as number[]
|
|
431
|
+
expect(byteView[0]).toBe(0x44)
|
|
432
|
+
expect(byteView[3]).toBe(0x11)
|
|
433
|
+
byteView[4] = 0xaa
|
|
434
|
+
expect(words[1]).toBe(0xaa)
|
|
435
|
+
wordBytes.value = [1, 2, 3, 4]
|
|
436
|
+
expect(words[0]).toBe(0x04030201)
|
|
437
|
+
|
|
426
438
|
shortBytes![0] = 14
|
|
427
439
|
expect(bytesToUint8Array(shortBytes)).toEqual(new Uint8Array([14, 0]))
|
|
428
440
|
|
|
@@ -473,6 +485,22 @@ describe('builtin runtime contract helpers', () => {
|
|
|
473
485
|
expect(indexAddress(other, 0)).not.toBe(indexAddress(left, 0))
|
|
474
486
|
})
|
|
475
487
|
|
|
488
|
+
it('resolves unsafe byte addresses within numeric slice elements', () => {
|
|
489
|
+
const words = makeSlice<number>(2, undefined, 'number')
|
|
490
|
+
const first = indexByteAddress(words, 0, 8)
|
|
491
|
+
const second = indexByteAddress(words, 1, 8)
|
|
492
|
+
|
|
493
|
+
expect(second - first).toBe(8)
|
|
494
|
+
unsafePointerRef<number>(first + 1).value = 0x12
|
|
495
|
+
unsafePointerRef<number>(first + 7).value = 0x80
|
|
496
|
+
expect(unsafePointerRef<number>(first + 1).value).toBe(0x12)
|
|
497
|
+
expect(unsafePointerRef<number>(first + 7).value).toBe(0x80)
|
|
498
|
+
expect(words![0]).toBe(0x8000000000001200n as unknown as number)
|
|
499
|
+
|
|
500
|
+
unsafePointerRef<number>(second).value = 0x34
|
|
501
|
+
expect(words![1]).toBe(0x34)
|
|
502
|
+
})
|
|
503
|
+
|
|
476
504
|
it('exposes owned pointer handles for addressable collection elements', () => {
|
|
477
505
|
const values = [1, 2, 3, 4]
|
|
478
506
|
const second = indexRef(values, 1)
|
package/gs/builtin/slice.ts
CHANGED
|
@@ -76,6 +76,14 @@ interface GoSliceObject<T> {
|
|
|
76
76
|
const addressStride = 0x100000000
|
|
77
77
|
let nextAddressBase = 1
|
|
78
78
|
const addressBases = new WeakMap<object, number>()
|
|
79
|
+
const byteAddressBases = new WeakMap<object, number>()
|
|
80
|
+
const byteAddressSources = new globalThis.Map<number, ByteAddressSource>()
|
|
81
|
+
|
|
82
|
+
interface ByteAddressSource {
|
|
83
|
+
byteLength: number
|
|
84
|
+
getByte(offset: number): number
|
|
85
|
+
setByte(offset: number, value: number): void
|
|
86
|
+
}
|
|
79
87
|
|
|
80
88
|
/**
|
|
81
89
|
* SliceProxy is a proxy object for complex slices
|
|
@@ -129,9 +137,7 @@ function wrapSliceProxy<T>(proxy: SliceProxy<T>): SliceProxy<T> {
|
|
|
129
137
|
if (index < meta.length) {
|
|
130
138
|
return meta.backing[meta.offset + index]
|
|
131
139
|
}
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Slice index out of range: ${index} >= ${meta.length}`,
|
|
134
|
-
)
|
|
140
|
+
throw new Error(`Slice index out of range: ${index} >= ${meta.length}`)
|
|
135
141
|
}
|
|
136
142
|
|
|
137
143
|
if (prop === 'length') {
|
|
@@ -153,9 +159,7 @@ function wrapSliceProxy<T>(proxy: SliceProxy<T>): SliceProxy<T> {
|
|
|
153
159
|
target[index] = value // Also update the proxy target for consistency
|
|
154
160
|
return true
|
|
155
161
|
}
|
|
156
|
-
throw new Error(
|
|
157
|
-
`Slice index out of range: ${index} >= ${meta.length}`,
|
|
158
|
-
)
|
|
162
|
+
throw new Error(`Slice index out of range: ${index} >= ${meta.length}`)
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
if (prop === 'length' || prop === '__meta__') {
|
|
@@ -329,7 +333,12 @@ export const makeSlice = <T>(
|
|
|
329
333
|
return new Uint8Array(length) as Slice<T>
|
|
330
334
|
}
|
|
331
335
|
|
|
332
|
-
return byteSliceView(
|
|
336
|
+
return byteSliceView(
|
|
337
|
+
new Uint8Array(actualCapacity),
|
|
338
|
+
0,
|
|
339
|
+
length,
|
|
340
|
+
actualCapacity,
|
|
341
|
+
) as Slice<T>
|
|
333
342
|
}
|
|
334
343
|
|
|
335
344
|
const actualCapacity = capacity === undefined ? length : capacity
|
|
@@ -535,8 +544,7 @@ export function goSlice<T>( // T can be number for Uint8Array case
|
|
|
535
544
|
if (s instanceof Uint8Array) {
|
|
536
545
|
const meta = byteSliceMeta(s)
|
|
537
546
|
const metaBacking = meta?.backing as unknown
|
|
538
|
-
const backing =
|
|
539
|
-
metaBacking instanceof Uint8Array ? metaBacking : s
|
|
547
|
+
const backing = metaBacking instanceof Uint8Array ? metaBacking : s
|
|
540
548
|
const baseOffset = meta?.offset ?? 0
|
|
541
549
|
const baseCapacity = meta?.capacity ?? s.length
|
|
542
550
|
const actualLow = low ?? 0
|
|
@@ -1003,8 +1011,7 @@ export function append<T>(
|
|
|
1003
1011
|
function appendByteSlice(slice: Uint8Array, elements: any[]): Uint8Array {
|
|
1004
1012
|
const meta = byteSliceMeta(slice)
|
|
1005
1013
|
const metaBacking = meta?.backing as unknown
|
|
1006
|
-
const backing =
|
|
1007
|
-
metaBacking instanceof Uint8Array ? metaBacking : slice
|
|
1014
|
+
const backing = metaBacking instanceof Uint8Array ? metaBacking : slice
|
|
1008
1015
|
const offset = meta?.offset ?? 0
|
|
1009
1016
|
const oldLength = slice.length
|
|
1010
1017
|
const oldCapacity = meta?.capacity ?? oldLength
|
|
@@ -1032,12 +1039,18 @@ function byteElementLength(item: any): number {
|
|
|
1032
1039
|
return len(item as Slice<any>)
|
|
1033
1040
|
}
|
|
1034
1041
|
if (typeof item !== 'number') {
|
|
1035
|
-
throw new Error(
|
|
1042
|
+
throw new Error(
|
|
1043
|
+
'Cannot produce Uint8Array: appended elements contain non-numbers.',
|
|
1044
|
+
)
|
|
1036
1045
|
}
|
|
1037
1046
|
return 1
|
|
1038
1047
|
}
|
|
1039
1048
|
|
|
1040
|
-
function writeByteElements(
|
|
1049
|
+
function writeByteElements(
|
|
1050
|
+
dst: Uint8Array,
|
|
1051
|
+
offset: number,
|
|
1052
|
+
elements: any[],
|
|
1053
|
+
): void {
|
|
1041
1054
|
let cursor = offset
|
|
1042
1055
|
for (const item of elements) {
|
|
1043
1056
|
if (item instanceof Uint8Array) {
|
|
@@ -1060,7 +1073,9 @@ function writeByteElements(dst: Uint8Array, offset: number, elements: any[]): vo
|
|
|
1060
1073
|
continue
|
|
1061
1074
|
}
|
|
1062
1075
|
if (typeof item !== 'number') {
|
|
1063
|
-
throw new Error(
|
|
1076
|
+
throw new Error(
|
|
1077
|
+
'Cannot produce Uint8Array: appended elements contain non-numbers.',
|
|
1078
|
+
)
|
|
1064
1079
|
}
|
|
1065
1080
|
dst[cursor] = item
|
|
1066
1081
|
cursor++
|
|
@@ -1394,6 +1409,8 @@ export function sliceFromOwnedPointer<T>(
|
|
|
1394
1409
|
export function arrayPointerFromIndexRef<T>(
|
|
1395
1410
|
ref: VarRef<T>,
|
|
1396
1411
|
length: number,
|
|
1412
|
+
sourceElementByteSize = 1,
|
|
1413
|
+
targetElementByteSize = sourceElementByteSize,
|
|
1397
1414
|
): VarRef<Slice<T> | T[] | Uint8Array> {
|
|
1398
1415
|
const collection = ref.__goCollection as
|
|
1399
1416
|
| Slice<T>
|
|
@@ -1406,12 +1423,25 @@ export function arrayPointerFromIndexRef<T>(
|
|
|
1406
1423
|
)
|
|
1407
1424
|
}
|
|
1408
1425
|
const index = ref.__goIndex ?? 0
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1426
|
+
const view =
|
|
1427
|
+
targetElementByteSize === 1 && sourceElementByteSize > 1 ?
|
|
1428
|
+
byteArrayFromAddress(
|
|
1429
|
+
indexByteAddress(collection, index, sourceElementByteSize),
|
|
1430
|
+
length,
|
|
1431
|
+
)
|
|
1432
|
+
: (goSlice(collection as any, index, index + length) as
|
|
1433
|
+
| Slice<T>
|
|
1434
|
+
| T[]
|
|
1435
|
+
| Uint8Array)
|
|
1436
|
+
return {
|
|
1437
|
+
get value() {
|
|
1438
|
+
return view as Slice<T> | T[] | Uint8Array
|
|
1439
|
+
},
|
|
1440
|
+
set value(value: Slice<T> | T[] | Uint8Array) {
|
|
1441
|
+
copy(view as any, value as any)
|
|
1442
|
+
},
|
|
1443
|
+
__isVarRef: true,
|
|
1444
|
+
}
|
|
1415
1445
|
}
|
|
1416
1446
|
|
|
1417
1447
|
/**
|
|
@@ -1460,6 +1490,181 @@ export function indexAddress<T>(
|
|
|
1460
1490
|
return base + backingIndex
|
|
1461
1491
|
}
|
|
1462
1492
|
|
|
1493
|
+
function uintElementValue(value: unknown, byteSize: number): bigint {
|
|
1494
|
+
const bits = BigInt(byteSize * 8)
|
|
1495
|
+
if (typeof value === 'bigint') {
|
|
1496
|
+
return BigInt.asUintN(Number(bits), value)
|
|
1497
|
+
}
|
|
1498
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1499
|
+
return BigInt.asUintN(Number(bits), BigInt(Math.trunc(value)))
|
|
1500
|
+
}
|
|
1501
|
+
return 0n
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
function uintElementResult(value: bigint, byteSize: number): number {
|
|
1505
|
+
const normalized = BigInt.asUintN(byteSize * 8, value)
|
|
1506
|
+
if (normalized <= BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
1507
|
+
return Number(normalized)
|
|
1508
|
+
}
|
|
1509
|
+
return normalized as unknown as number
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function byteAddressBase(backing: object, source: ByteAddressSource): number {
|
|
1513
|
+
let base = byteAddressBases.get(backing)
|
|
1514
|
+
if (base === undefined) {
|
|
1515
|
+
base = nextAddressBase * addressStride
|
|
1516
|
+
nextAddressBase++
|
|
1517
|
+
byteAddressBases.set(backing, base)
|
|
1518
|
+
}
|
|
1519
|
+
byteAddressSources.set(base, source)
|
|
1520
|
+
return base
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function numericByteSource(
|
|
1524
|
+
backing: number[],
|
|
1525
|
+
elementByteSize: number,
|
|
1526
|
+
): ByteAddressSource {
|
|
1527
|
+
const byteSize = Math.max(1, Math.trunc(elementByteSize))
|
|
1528
|
+
return {
|
|
1529
|
+
byteLength: backing.length * byteSize,
|
|
1530
|
+
getByte(offset: number): number {
|
|
1531
|
+
const elementIndex = Math.trunc(offset / byteSize)
|
|
1532
|
+
const byteOffset = offset % byteSize
|
|
1533
|
+
const value = uintElementValue(backing[elementIndex], byteSize)
|
|
1534
|
+
return Number((value >> BigInt(byteOffset * 8)) & 0xffn)
|
|
1535
|
+
},
|
|
1536
|
+
setByte(offset: number, value: number): void {
|
|
1537
|
+
const elementIndex = Math.trunc(offset / byteSize)
|
|
1538
|
+
const byteOffset = offset % byteSize
|
|
1539
|
+
const shift = BigInt(byteOffset * 8)
|
|
1540
|
+
const mask = 0xffn << shift
|
|
1541
|
+
const current = uintElementValue(backing[elementIndex], byteSize)
|
|
1542
|
+
const next = (current & ~mask) | ((BigInt(value) & 0xffn) << shift)
|
|
1543
|
+
backing[elementIndex] = uintElementResult(next, byteSize)
|
|
1544
|
+
},
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* indexByteAddress returns a byte-addressed synthetic address for unsafe
|
|
1550
|
+
* uintptr arithmetic rooted at a slice or array element.
|
|
1551
|
+
*/
|
|
1552
|
+
export function indexByteAddress<T>(
|
|
1553
|
+
collection: Slice<T> | T[] | Uint8Array,
|
|
1554
|
+
index: number,
|
|
1555
|
+
elementByteSize: number,
|
|
1556
|
+
): number {
|
|
1557
|
+
if (collection === null || collection === undefined) {
|
|
1558
|
+
throw new Error('runtime error: index on nil or undefined collection')
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
if (collection instanceof Uint8Array) {
|
|
1562
|
+
if (index < 0 || index >= collection.length) {
|
|
1563
|
+
throw new Error(
|
|
1564
|
+
`runtime error: index out of range [${index}] with length ${collection.length}`,
|
|
1565
|
+
)
|
|
1566
|
+
}
|
|
1567
|
+
const view = new Uint8Array(collection.buffer)
|
|
1568
|
+
const base = byteAddressBase(collection.buffer, {
|
|
1569
|
+
byteLength: view.length,
|
|
1570
|
+
getByte(offset: number): number {
|
|
1571
|
+
return view[offset]
|
|
1572
|
+
},
|
|
1573
|
+
setByte(offset: number, value: number): void {
|
|
1574
|
+
view[offset] = value
|
|
1575
|
+
},
|
|
1576
|
+
})
|
|
1577
|
+
return base + collection.byteOffset + index
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
if (isComplexSlice(collection)) {
|
|
1581
|
+
if (index < 0 || index >= collection.__meta__.length) {
|
|
1582
|
+
throw new Error(
|
|
1583
|
+
`runtime error: index out of range [${index}] with length ${collection.__meta__.length}`,
|
|
1584
|
+
)
|
|
1585
|
+
}
|
|
1586
|
+
const backing = collection.__meta__.backing as unknown as number[]
|
|
1587
|
+
const byteSize = Math.max(1, Math.trunc(elementByteSize))
|
|
1588
|
+
const base = byteAddressBase(backing, numericByteSource(backing, byteSize))
|
|
1589
|
+
return base + (collection.__meta__.offset + index) * byteSize
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
if (Array.isArray(collection)) {
|
|
1593
|
+
if (index < 0 || index >= collection.length) {
|
|
1594
|
+
throw new Error(
|
|
1595
|
+
`runtime error: index out of range [${index}] with length ${collection.length}`,
|
|
1596
|
+
)
|
|
1597
|
+
}
|
|
1598
|
+
const byteSize = Math.max(1, Math.trunc(elementByteSize))
|
|
1599
|
+
const base = byteAddressBase(
|
|
1600
|
+
collection,
|
|
1601
|
+
numericByteSource(collection as unknown as number[], byteSize),
|
|
1602
|
+
)
|
|
1603
|
+
return base + index * byteSize
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
throw new Error('runtime error: index on unsupported type')
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/**
|
|
1610
|
+
* unsafePointerRef resolves a byte-addressed synthetic unsafe pointer created
|
|
1611
|
+
* by indexByteAddress back to an addressable byte reference.
|
|
1612
|
+
*/
|
|
1613
|
+
export function unsafePointerRef<T>(address: number | bigint): VarRef<T> {
|
|
1614
|
+
const numericAddress = Number(address)
|
|
1615
|
+
const base = Math.floor(numericAddress / addressStride) * addressStride
|
|
1616
|
+
const source = byteAddressSources.get(base)
|
|
1617
|
+
if (source === undefined) {
|
|
1618
|
+
throw new Error(
|
|
1619
|
+
'unsafe pointer dereference is not supported in JavaScript/TypeScript',
|
|
1620
|
+
)
|
|
1621
|
+
}
|
|
1622
|
+
const offset = numericAddress - base
|
|
1623
|
+
if (offset < 0 || offset >= source.byteLength) {
|
|
1624
|
+
throw new Error('runtime error: unsafe pointer address out of range')
|
|
1625
|
+
}
|
|
1626
|
+
return {
|
|
1627
|
+
get value(): T {
|
|
1628
|
+
return source.getByte(offset) as T
|
|
1629
|
+
},
|
|
1630
|
+
set value(value: T) {
|
|
1631
|
+
source.setByte(offset, value as number)
|
|
1632
|
+
},
|
|
1633
|
+
__isVarRef: true,
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
function byteArrayFromAddress(
|
|
1638
|
+
address: number | bigint,
|
|
1639
|
+
length: number,
|
|
1640
|
+
): number[] {
|
|
1641
|
+
const start = Number(address)
|
|
1642
|
+
const target = new Array<number>(length)
|
|
1643
|
+
return new Proxy(target, {
|
|
1644
|
+
get(arrayTarget, prop, receiver) {
|
|
1645
|
+
const index = sliceIndexProperty(prop)
|
|
1646
|
+
if (index >= 0) {
|
|
1647
|
+
if (index >= length) {
|
|
1648
|
+
throw new Error(`Slice index out of range: ${index} >= ${length}`)
|
|
1649
|
+
}
|
|
1650
|
+
return unsafePointerRef<number>(start + index).value
|
|
1651
|
+
}
|
|
1652
|
+
return Reflect.get(arrayTarget, prop, receiver)
|
|
1653
|
+
},
|
|
1654
|
+
set(arrayTarget, prop, value, receiver) {
|
|
1655
|
+
const index = sliceIndexProperty(prop)
|
|
1656
|
+
if (index >= 0) {
|
|
1657
|
+
if (index >= length) {
|
|
1658
|
+
throw new Error(`Slice index out of range: ${index} >= ${length}`)
|
|
1659
|
+
}
|
|
1660
|
+
unsafePointerRef<number>(start + index).value = value
|
|
1661
|
+
return true
|
|
1662
|
+
}
|
|
1663
|
+
return Reflect.set(arrayTarget, prop, value, receiver)
|
|
1664
|
+
},
|
|
1665
|
+
})
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1463
1668
|
/**
|
|
1464
1669
|
* Converts a string to an array of Unicode code points (runes).
|
|
1465
1670
|
* @param str The input string.
|
|
@@ -38,6 +38,8 @@ import {
|
|
|
38
38
|
EqualVTSliceImplicit,
|
|
39
39
|
type EqualVT,
|
|
40
40
|
IsEqualVTSlice,
|
|
41
|
+
MarshalBoundMessageVT,
|
|
42
|
+
MarshalBoundMessageToSizedBufferVT,
|
|
41
43
|
SizeBoolNonZero,
|
|
42
44
|
SizeBoolPacked,
|
|
43
45
|
SizeBoolPtr,
|
|
@@ -76,6 +78,7 @@ import {
|
|
|
76
78
|
SizeZigzagSlice,
|
|
77
79
|
SizeZigzagValue,
|
|
78
80
|
Skip,
|
|
81
|
+
UnmarshalBoundMessageVT,
|
|
79
82
|
} from './index.js'
|
|
80
83
|
|
|
81
84
|
class TestValue {
|
|
@@ -146,6 +149,115 @@ class TestCloneMessage implements CloneMessage {
|
|
|
146
149
|
}
|
|
147
150
|
}
|
|
148
151
|
|
|
152
|
+
class BrokenBoundMessage {}
|
|
153
|
+
|
|
154
|
+
;(BrokenBoundMessage as any).__protobufTypeScriptMessage = {
|
|
155
|
+
typeName: 'test.BrokenBoundMessage',
|
|
156
|
+
fields: { list: () => [] },
|
|
157
|
+
fromBinary: () => ({}),
|
|
158
|
+
toBinary: () => {
|
|
159
|
+
throw new Error('invalid uint 32: undefined')
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
class BytesBoundMessage {
|
|
164
|
+
Config: $.Slice<number>
|
|
165
|
+
|
|
166
|
+
constructor(config: $.Slice<number>) {
|
|
167
|
+
this.Config = config
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
;(BytesBoundMessage as any).__protobufTypeScriptMessage = {
|
|
172
|
+
typeName: 'test.BytesBoundMessage',
|
|
173
|
+
fields: {
|
|
174
|
+
list: () => [{ localName: 'config', kind: 'scalar', T: 12 }],
|
|
175
|
+
},
|
|
176
|
+
fromBinary: () => ({}),
|
|
177
|
+
toBinary: (value: { config?: Uint8Array }) => {
|
|
178
|
+
expect(value.config).toBeInstanceOf(Uint8Array)
|
|
179
|
+
expect(Array.from(value.config ?? [])).toEqual([1, 2, 3])
|
|
180
|
+
return new Uint8Array([9])
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
class TimestampBoundMessage {
|
|
185
|
+
public get Seconds(): number {
|
|
186
|
+
return this._fields.Seconds.value
|
|
187
|
+
}
|
|
188
|
+
public set Seconds(value: number) {
|
|
189
|
+
this._fields.Seconds.value = value
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public get Nanos(): number {
|
|
193
|
+
return this._fields.Nanos.value
|
|
194
|
+
}
|
|
195
|
+
public set Nanos(value: number) {
|
|
196
|
+
this._fields.Nanos.value = value
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public _fields: {
|
|
200
|
+
Seconds: $.VarRef<number>
|
|
201
|
+
Nanos: $.VarRef<number>
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
constructor(init?: Partial<{ Seconds?: number; Nanos?: number }>) {
|
|
205
|
+
this._fields = {
|
|
206
|
+
Seconds: $.varRef(init?.Seconds ?? 0),
|
|
207
|
+
Nanos: $.varRef(init?.Nanos ?? 0),
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const timestampMessageType = {
|
|
213
|
+
typeName: 'google.protobuf.Timestamp',
|
|
214
|
+
fields: {
|
|
215
|
+
list: () => [
|
|
216
|
+
{ localName: 'seconds', kind: 'scalar' },
|
|
217
|
+
{ localName: 'nanos', kind: 'scalar' },
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
class TimestampParentBoundMessage {
|
|
223
|
+
public get Timestamp(): TimestampBoundMessage | null {
|
|
224
|
+
return this._fields.Timestamp.value
|
|
225
|
+
}
|
|
226
|
+
public set Timestamp(value: TimestampBoundMessage | null) {
|
|
227
|
+
this._fields.Timestamp.value = value
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public _fields: {
|
|
231
|
+
Timestamp: $.VarRef<TimestampBoundMessage | null>
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
constructor(init?: Partial<{ Timestamp?: TimestampBoundMessage | null }>) {
|
|
235
|
+
this._fields = {
|
|
236
|
+
Timestamp: $.varRef(init?.Timestamp ?? null),
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
;(TimestampParentBoundMessage as any).__protobufTypeScriptMessage = {
|
|
242
|
+
typeName: 'test.TimestampParentBoundMessage',
|
|
243
|
+
fields: {
|
|
244
|
+
list: () => [
|
|
245
|
+
{
|
|
246
|
+
localName: 'timestamp',
|
|
247
|
+
kind: 'message',
|
|
248
|
+
T: timestampMessageType,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
fromBinary: () => ({
|
|
253
|
+
timestamp: new Date(Date.UTC(2026, 4, 31, 12, 34, 56, 789)),
|
|
254
|
+
}),
|
|
255
|
+
toBinary: () => new Uint8Array(),
|
|
256
|
+
}
|
|
257
|
+
;(TimestampParentBoundMessage as any).__protobufTypeScriptFields = {
|
|
258
|
+
timestamp: TimestampBoundMessage,
|
|
259
|
+
}
|
|
260
|
+
|
|
149
261
|
describe('protobuf-go-lite EqualVT helpers', () => {
|
|
150
262
|
it('accepts compiler-emitted runtime type arguments', () => {
|
|
151
263
|
const equal = CompareEqualVT<TestValue>({
|
|
@@ -245,6 +357,56 @@ describe('protobuf-go-lite runtime interfaces', () => {
|
|
|
245
357
|
})
|
|
246
358
|
})
|
|
247
359
|
|
|
360
|
+
describe('protobuf-go-lite TypeScript binding helpers', () => {
|
|
361
|
+
it('adds message type context to binary marshal errors', () => {
|
|
362
|
+
const [, err] = MarshalBoundMessageVT(
|
|
363
|
+
BrokenBoundMessage as any,
|
|
364
|
+
new BrokenBoundMessage(),
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
expect(err?.Error()).toBe(
|
|
368
|
+
'marshal test.BrokenBoundMessage: invalid uint 32: undefined',
|
|
369
|
+
)
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
it('normalizes Go byte slices before binary marshal', () => {
|
|
373
|
+
const [bytes, err] = MarshalBoundMessageVT(
|
|
374
|
+
BytesBoundMessage as any,
|
|
375
|
+
new BytesBoundMessage([1, 2, 3]),
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
expect(err).toBeNull()
|
|
379
|
+
expect(Array.from(bytes ?? [])).toEqual([9])
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
it('returns bytes written after marshaling into a sized buffer', () => {
|
|
383
|
+
const target = new Uint8Array([0, 0, 0])
|
|
384
|
+
const [n, err] = MarshalBoundMessageToSizedBufferVT(
|
|
385
|
+
BytesBoundMessage as any,
|
|
386
|
+
new BytesBoundMessage([1, 2, 3]),
|
|
387
|
+
target,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
expect(err).toBeNull()
|
|
391
|
+
expect(n).toBe(1)
|
|
392
|
+
expect(Array.from(target)).toEqual([0, 0, 9])
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('hydrates protobuf-es-lite timestamp Date fields into Go timestamp structs', () => {
|
|
396
|
+
const target = new TimestampParentBoundMessage()
|
|
397
|
+
|
|
398
|
+
const err = UnmarshalBoundMessageVT(
|
|
399
|
+
TimestampParentBoundMessage as any,
|
|
400
|
+
target,
|
|
401
|
+
new Uint8Array(),
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
expect(err).toBeNull()
|
|
405
|
+
expect(target.Timestamp?.Seconds).toBe(1780230896)
|
|
406
|
+
expect(target.Timestamp?.Nanos).toBe(789000000)
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
|
|
248
410
|
describe('protobuf-go-lite wire helpers', () => {
|
|
249
411
|
it('encodes and decodes varints', () => {
|
|
250
412
|
const buf = new Uint8Array(4)
|