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
|
@@ -345,6 +345,7 @@ export function EqualVTMapImplicit<K, V extends EqualVT<V>>(
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
type BoundMessageType = {
|
|
348
|
+
typeName?: string
|
|
348
349
|
fields?: { list(): readonly BoundFieldInfo[] }
|
|
349
350
|
clone?: (value: any) => any
|
|
350
351
|
equals?: (a: any, b: any) => boolean
|
|
@@ -371,6 +372,8 @@ type BoundMessageCtor<T = any> = {
|
|
|
371
372
|
__protobufTypeScriptMessage?: BoundMessageType
|
|
372
373
|
}
|
|
373
374
|
|
|
375
|
+
const protobufScalarTypeBytes = 12
|
|
376
|
+
|
|
374
377
|
function boundMessageType(ctor: BoundMessageCtor): BoundMessageType {
|
|
375
378
|
const messageType = ctor.__protobufTypeScriptMessage
|
|
376
379
|
if (messageType == null) {
|
|
@@ -401,8 +404,16 @@ function boundFieldValue(source: any, field: BoundFieldInfo): any {
|
|
|
401
404
|
return source[boundFieldGoName(field)]
|
|
402
405
|
}
|
|
403
406
|
|
|
404
|
-
function toTypeScriptScalarValue(value: any): any {
|
|
407
|
+
function toTypeScriptScalarValue(value: any, field?: BoundFieldInfo): any {
|
|
405
408
|
const unwrapped = $.pointerValueOrNil(value)
|
|
409
|
+
if (
|
|
410
|
+
field?.kind === 'scalar' &&
|
|
411
|
+
field.T === protobufScalarTypeBytes &&
|
|
412
|
+
unwrapped != null &&
|
|
413
|
+
!(unwrapped instanceof Uint8Array)
|
|
414
|
+
) {
|
|
415
|
+
return Uint8Array.from($.asArray(unwrapped))
|
|
416
|
+
}
|
|
406
417
|
if (unwrapped instanceof Uint8Array) {
|
|
407
418
|
return unwrapped
|
|
408
419
|
}
|
|
@@ -440,7 +451,7 @@ function toTypeScriptFieldValue(
|
|
|
440
451
|
const messageType = resolveBoundMessageType(field.T)
|
|
441
452
|
return toTypeScriptMessage(value, messageType, ctor)
|
|
442
453
|
}
|
|
443
|
-
return toTypeScriptScalarValue(value)
|
|
454
|
+
return toTypeScriptScalarValue(value, field)
|
|
444
455
|
}
|
|
445
456
|
|
|
446
457
|
function toTypeScriptMessage(
|
|
@@ -520,6 +531,23 @@ function fromTypeScriptFieldValue(
|
|
|
520
531
|
return fromTypeScriptScalarValue(value)
|
|
521
532
|
}
|
|
522
533
|
|
|
534
|
+
function fromTypeScriptWellKnownMessage(
|
|
535
|
+
value: any,
|
|
536
|
+
ctor: BoundMessageCtor,
|
|
537
|
+
messageType?: BoundMessageType,
|
|
538
|
+
): any {
|
|
539
|
+
if (
|
|
540
|
+
messageType?.typeName === 'google.protobuf.Timestamp' &&
|
|
541
|
+
value instanceof Date
|
|
542
|
+
) {
|
|
543
|
+
const ms = value.getTime()
|
|
544
|
+
const seconds = Math.floor(ms / 1000)
|
|
545
|
+
const nanos = Math.trunc((ms - seconds * 1000) * 1000000)
|
|
546
|
+
return new ctor({ Seconds: seconds, Nanos: nanos })
|
|
547
|
+
}
|
|
548
|
+
return undefined
|
|
549
|
+
}
|
|
550
|
+
|
|
523
551
|
function fromTypeScriptMessage(
|
|
524
552
|
value: any,
|
|
525
553
|
ctor: BoundMessageCtor,
|
|
@@ -528,6 +556,10 @@ function fromTypeScriptMessage(
|
|
|
528
556
|
if (value == null) {
|
|
529
557
|
return null
|
|
530
558
|
}
|
|
559
|
+
const wellKnown = fromTypeScriptWellKnownMessage(value, ctor, messageType)
|
|
560
|
+
if (wellKnown !== undefined) {
|
|
561
|
+
return wellKnown
|
|
562
|
+
}
|
|
531
563
|
const out = new ctor()
|
|
532
564
|
const fields = (messageType ?? boundMessageType(ctor)).fields?.list()
|
|
533
565
|
if (fields == null) {
|
|
@@ -579,14 +611,17 @@ export function MarshalBoundMessageVT<T>(
|
|
|
579
611
|
ctor: BoundMessageCtor<T>,
|
|
580
612
|
value: T | $.VarRef<T> | null,
|
|
581
613
|
): [$.Slice<number>, $.GoError] {
|
|
614
|
+
let typeName = ctor.name
|
|
582
615
|
try {
|
|
583
616
|
const messageType = boundMessageType(ctor)
|
|
617
|
+
typeName = messageType.typeName ?? typeName
|
|
584
618
|
return [
|
|
585
619
|
messageType.toBinary(toTypeScriptMessage(value, messageType, ctor)),
|
|
586
620
|
null,
|
|
587
621
|
]
|
|
588
622
|
} catch (err) {
|
|
589
|
-
|
|
623
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
624
|
+
return [null, $.toGoError(new Error(`marshal ${typeName}: ${msg}`))]
|
|
590
625
|
}
|
|
591
626
|
}
|
|
592
627
|
|
|
@@ -599,12 +634,13 @@ export function MarshalBoundMessageToSizedBufferVT<T>(
|
|
|
599
634
|
if (err != null) {
|
|
600
635
|
return [0, err]
|
|
601
636
|
}
|
|
602
|
-
const
|
|
637
|
+
const byteCount = $.len(bytes)
|
|
638
|
+
const offset = $.len(dAtA) - byteCount
|
|
603
639
|
const src = $.asArray(bytes)
|
|
604
640
|
for (let i = 0; i < src.length; i++) {
|
|
605
641
|
;(dAtA as any)[offset + i] = src[i]
|
|
606
642
|
}
|
|
607
|
-
return [
|
|
643
|
+
return [byteCount, null]
|
|
608
644
|
}
|
|
609
645
|
|
|
610
646
|
export function SizeBoundMessageVT<T>(
|
|
@@ -113,6 +113,24 @@ describe('protobuf-go-lite/json override', () => {
|
|
|
113
113
|
])
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
+
it('matches jsoniter value types while reading object fields', () => {
|
|
117
|
+
const state = NewUnmarshalState(
|
|
118
|
+
jsonBytes('{"id":"step","config":{"kind":"inline"}}'),
|
|
119
|
+
DefaultUnmarshalerConfig,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
expect(state?.ReadObjectField()).toBe('id')
|
|
123
|
+
expect(state?.WhatIsNext()).toBe(1)
|
|
124
|
+
expect(state?.ReadString()).toBe('step')
|
|
125
|
+
expect(state?.ReadObjectField()).toBe('config')
|
|
126
|
+
expect(state?.WhatIsNext()).toBe(6)
|
|
127
|
+
expect(
|
|
128
|
+
new TextDecoder().decode(new Uint8Array(state?.SkipAndReturnBytes())),
|
|
129
|
+
).toBe('{"kind":"inline"}')
|
|
130
|
+
expect(state?.ReadObjectField()).toBe('')
|
|
131
|
+
expect(state?.Err()).toBeNull()
|
|
132
|
+
})
|
|
133
|
+
|
|
116
134
|
it('tracks unmarshaled field masks through nested fields', () => {
|
|
117
135
|
const state = NewUnmarshalState(jsonBytes('{}'), DefaultUnmarshalerConfig)
|
|
118
136
|
state?.AddField('top')
|
|
@@ -423,6 +423,7 @@ export class UnmarshalState {
|
|
|
423
423
|
private path: string[]
|
|
424
424
|
private objectEntries: Array<[string, unknown]> | null = null
|
|
425
425
|
private objectIndex = 0
|
|
426
|
+
private objectValue: unknown = undefined
|
|
426
427
|
|
|
427
428
|
constructor(
|
|
428
429
|
init?: Partial<{
|
|
@@ -612,16 +613,21 @@ export class UnmarshalState {
|
|
|
612
613
|
}
|
|
613
614
|
|
|
614
615
|
public ReadObjectField(): string {
|
|
615
|
-
const record = recordValue(this.value)
|
|
616
|
-
if (record == null) {
|
|
617
|
-
this.SetErrorf('expected JSON object')
|
|
618
|
-
return ''
|
|
619
|
-
}
|
|
620
616
|
if (this.objectEntries == null) {
|
|
617
|
+
const record = recordValue(this.value)
|
|
618
|
+
if (record == null) {
|
|
619
|
+
this.SetErrorf('expected JSON object')
|
|
620
|
+
return ''
|
|
621
|
+
}
|
|
621
622
|
this.objectEntries = Object.entries(record)
|
|
622
623
|
this.objectIndex = 0
|
|
624
|
+
this.objectValue = this.value
|
|
623
625
|
}
|
|
624
626
|
if (this.objectIndex >= this.objectEntries.length) {
|
|
627
|
+
this.value = this.objectValue
|
|
628
|
+
this.objectEntries = null
|
|
629
|
+
this.objectIndex = 0
|
|
630
|
+
this.objectValue = undefined
|
|
625
631
|
return ''
|
|
626
632
|
}
|
|
627
633
|
const [key, value] = this.objectEntries[this.objectIndex]
|
|
@@ -747,22 +753,22 @@ export class UnmarshalState {
|
|
|
747
753
|
|
|
748
754
|
public WhatIsNext(): number {
|
|
749
755
|
if (this.value === null) {
|
|
750
|
-
return
|
|
756
|
+
return 3
|
|
751
757
|
}
|
|
752
758
|
if (Array.isArray(this.value)) {
|
|
753
|
-
return
|
|
759
|
+
return 5
|
|
754
760
|
}
|
|
755
761
|
if (typeof this.value === 'object') {
|
|
756
|
-
return
|
|
762
|
+
return 6
|
|
757
763
|
}
|
|
758
764
|
if (typeof this.value === 'string') {
|
|
759
|
-
return
|
|
765
|
+
return 1
|
|
760
766
|
}
|
|
761
767
|
if (typeof this.value === 'number') {
|
|
762
|
-
return
|
|
768
|
+
return 2
|
|
763
769
|
}
|
|
764
770
|
if (typeof this.value === 'boolean') {
|
|
765
|
-
return
|
|
771
|
+
return 4
|
|
766
772
|
}
|
|
767
773
|
return 0
|
|
768
774
|
}
|
|
@@ -16,7 +16,7 @@ describe('internal/byteorder uint64', () => {
|
|
|
16
16
|
BEPutUint64(bytes, 0x0102030405060708n)
|
|
17
17
|
|
|
18
18
|
expect(Array.from(bytes)).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
|
|
19
|
-
expect(BEUint64(bytes)).toBe(
|
|
19
|
+
expect(BEUint64(bytes)).toBe(0x0102030405060708n)
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
test('reads and writes little-endian bigint values', () => {
|
|
@@ -25,7 +25,7 @@ describe('internal/byteorder uint64', () => {
|
|
|
25
25
|
LEPutUint64(bytes, 0x0102030405060708n)
|
|
26
26
|
|
|
27
27
|
expect(Array.from(bytes)).toEqual([8, 7, 6, 5, 4, 3, 2, 1])
|
|
28
|
-
expect(LEUint64(bytes)).toBe(
|
|
28
|
+
expect(LEUint64(bytes)).toBe(0x0102030405060708n)
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
test('appends uint64 values', () => {
|
|
@@ -13,7 +13,7 @@ export function BEUint32(b: $.Bytes): number {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function BEUint64(b: $.Bytes): number {
|
|
16
|
-
return
|
|
16
|
+
return $.uint(
|
|
17
17
|
(BigInt(b![0]) << 56n) |
|
|
18
18
|
(BigInt(b![1]) << 48n) |
|
|
19
19
|
(BigInt(b![2]) << 40n) |
|
|
@@ -34,7 +34,7 @@ export function LEUint32(b: $.Bytes): number {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export function LEUint64(b: $.Bytes): number {
|
|
37
|
-
return
|
|
37
|
+
return $.uint(
|
|
38
38
|
BigInt(b![0]) |
|
|
39
39
|
(BigInt(b![1]) << 8n) |
|
|
40
40
|
(BigInt(b![2]) << 16n) |
|
package/gs/reflect/type.ts
CHANGED
|
@@ -3326,6 +3326,60 @@ function basicTypeFromName(name: string, typeName = ''): BasicType {
|
|
|
3326
3326
|
}
|
|
3327
3327
|
}
|
|
3328
3328
|
|
|
3329
|
+
function typeFromGoTypeName(typeName: string): Type | null {
|
|
3330
|
+
const trimmed = typeName.trim()
|
|
3331
|
+
if (trimmed === '') return null
|
|
3332
|
+
|
|
3333
|
+
if (trimmed.startsWith('<-chan ')) {
|
|
3334
|
+
return new ChannelType(typeFromGoTypeName(trimmed.slice(7)) ?? anyType(), RecvDir)
|
|
3335
|
+
}
|
|
3336
|
+
if (trimmed.startsWith('chan<- ')) {
|
|
3337
|
+
return new ChannelType(typeFromGoTypeName(trimmed.slice(7)) ?? anyType(), SendDir)
|
|
3338
|
+
}
|
|
3339
|
+
if (trimmed.startsWith('chan ')) {
|
|
3340
|
+
return new ChannelType(typeFromGoTypeName(trimmed.slice(5)) ?? anyType(), BothDir)
|
|
3341
|
+
}
|
|
3342
|
+
if (trimmed.startsWith('[]')) {
|
|
3343
|
+
return new SliceType(typeFromGoTypeName(trimmed.slice(2)) ?? anyType())
|
|
3344
|
+
}
|
|
3345
|
+
if (trimmed.startsWith('*')) {
|
|
3346
|
+
return new PointerType(typeFromGoTypeName(trimmed.slice(1)) ?? anyType())
|
|
3347
|
+
}
|
|
3348
|
+
if (trimmed === 'struct{}' || trimmed === 'struct {}') {
|
|
3349
|
+
return new StructType('', [], '', 'struct {}')
|
|
3350
|
+
}
|
|
3351
|
+
if (trimmed === 'interface{}' || trimmed === 'any') {
|
|
3352
|
+
return anyType()
|
|
3353
|
+
}
|
|
3354
|
+
if (trimmed === 'error') {
|
|
3355
|
+
return new InterfaceType('error', 'error')
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
const registered = builtinGetTypeByName(trimmed)
|
|
3359
|
+
if (registered) {
|
|
3360
|
+
return typeFromTypeInfo(registered)
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
const basic = basicTypeFromName(trimmed)
|
|
3364
|
+
if (basic.Kind() !== Invalid) {
|
|
3365
|
+
return basic
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
return null
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
function channelTypeFromGoTypeName(typeName: string): Type | null {
|
|
3372
|
+
const typ = typeFromGoTypeName(typeName)
|
|
3373
|
+
if (typ?.Kind() === Chan) {
|
|
3374
|
+
return typ
|
|
3375
|
+
}
|
|
3376
|
+
return null
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
function anyType(): Type {
|
|
3380
|
+
return new BasicType(Interface, 'interface{}', 16)
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3329
3383
|
function structFieldsFromTypeInfo(
|
|
3330
3384
|
ti: $.StructTypeInfo,
|
|
3331
3385
|
seen = new Set<string>(),
|
|
@@ -3743,6 +3797,16 @@ function getTypeOf(value: ReflectValue): Type {
|
|
|
3743
3797
|
)
|
|
3744
3798
|
}
|
|
3745
3799
|
|
|
3800
|
+
if ('__goType' in value) {
|
|
3801
|
+
const goType = (value as { __goType?: unknown }).__goType
|
|
3802
|
+
if (typeof goType === 'string') {
|
|
3803
|
+
const channelType = channelTypeFromGoTypeName(goType)
|
|
3804
|
+
if (channelType) {
|
|
3805
|
+
return channelType
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3746
3810
|
if (
|
|
3747
3811
|
'real' in value &&
|
|
3748
3812
|
'imag' in value &&
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
2
|
import {
|
|
3
|
+
interfaceValue,
|
|
4
|
+
makeChannel,
|
|
3
5
|
makeMap,
|
|
4
6
|
mapGet,
|
|
5
7
|
mapSet,
|
|
@@ -11,7 +13,9 @@ import {
|
|
|
11
13
|
varRef,
|
|
12
14
|
} from '../builtin/index.js'
|
|
13
15
|
import { StructField } from './types.js'
|
|
16
|
+
import { SelectCase, SelectRecv } from './types.js'
|
|
14
17
|
import {
|
|
18
|
+
Chan,
|
|
15
19
|
Int,
|
|
16
20
|
Ptr,
|
|
17
21
|
Struct,
|
|
@@ -21,7 +25,7 @@ import {
|
|
|
21
25
|
Uint64,
|
|
22
26
|
ValueOf,
|
|
23
27
|
} from './type.js'
|
|
24
|
-
import { Indirect, New, Zero } from './value.js'
|
|
28
|
+
import { Indirect, New, Select, Zero } from './value.js'
|
|
25
29
|
|
|
26
30
|
describe('TypeFor', () => {
|
|
27
31
|
it('exposes StructField PkgPath and exported semantics', () => {
|
|
@@ -184,6 +188,22 @@ describe('TypeFor', () => {
|
|
|
184
188
|
expect(target.value).toBe(15)
|
|
185
189
|
})
|
|
186
190
|
|
|
191
|
+
it('preserves channel type metadata on interface boxes', () => {
|
|
192
|
+
const ch = makeChannel<{}>(0, {}, 'both')
|
|
193
|
+
const chanValue = ValueOf(interfaceValue(ch, '<-chan struct{}'))
|
|
194
|
+
|
|
195
|
+
expect(chanValue.Type().Kind()).toBe(Chan)
|
|
196
|
+
expect(chanValue.Type().String()).toBe('<-chan struct {}')
|
|
197
|
+
expect(chanValue.Type().Elem().Kind()).toBe(Struct)
|
|
198
|
+
|
|
199
|
+
const [chosen, recv, ok] = Select([
|
|
200
|
+
new SelectCase({ Dir: SelectRecv, Chan: chanValue }),
|
|
201
|
+
])
|
|
202
|
+
expect(chosen).toBe(0)
|
|
203
|
+
expect(ok).toBe(true)
|
|
204
|
+
expect(recv.Type().String()).toBe('struct {}')
|
|
205
|
+
})
|
|
206
|
+
|
|
187
207
|
it('formats literal interface methods from type metadata', () => {
|
|
188
208
|
const ifaceType = TypeFor({
|
|
189
209
|
T: {
|
|
@@ -59,7 +59,7 @@ export function CompareAndSwapUint64(addr: $.VarRef<number> | null, old: number,
|
|
|
59
59
|
//go:noescape
|
|
60
60
|
export function AddInt64(addr: $.VarRef<number> | null, delta: number): number {
|
|
61
61
|
if (!addr) return 0;
|
|
62
|
-
addr.value = addr.value
|
|
62
|
+
addr.value = $.int64Add(addr.value, delta);
|
|
63
63
|
return addr.value;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -72,7 +72,7 @@ export function AddInt64(addr: $.VarRef<number> | null, delta: number): number {
|
|
|
72
72
|
//go:noescape
|
|
73
73
|
export function AddUint64(addr: $.VarRef<number> | null, delta: number): number {
|
|
74
74
|
if (!addr) return 0;
|
|
75
|
-
addr.value = addr.value
|
|
75
|
+
addr.value = $.uint64Add(addr.value, delta);
|
|
76
76
|
return addr.value;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -84,7 +84,7 @@ export function AddUint64(addr: $.VarRef<number> | null, delta: number): number
|
|
|
84
84
|
export function AndInt64(addr: $.VarRef<number> | null, mask: number): number {
|
|
85
85
|
if (!addr) return 0;
|
|
86
86
|
let old = addr.value;
|
|
87
|
-
addr.value = addr.value
|
|
87
|
+
addr.value = $.int64And(addr.value, mask);
|
|
88
88
|
return old;
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -96,7 +96,7 @@ export function AndInt64(addr: $.VarRef<number> | null, mask: number): number {
|
|
|
96
96
|
export function AndUint64(addr: $.VarRef<number> | null, mask: number): number {
|
|
97
97
|
if (!addr) return 0;
|
|
98
98
|
let old = addr.value;
|
|
99
|
-
addr.value = addr.value
|
|
99
|
+
addr.value = $.uint64And(addr.value, mask);
|
|
100
100
|
return old;
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -108,7 +108,7 @@ export function AndUint64(addr: $.VarRef<number> | null, mask: number): number {
|
|
|
108
108
|
export function OrInt64(addr: $.VarRef<number> | null, mask: number): number {
|
|
109
109
|
if (!addr) return 0;
|
|
110
110
|
let old = addr.value;
|
|
111
|
-
addr.value = addr.value
|
|
111
|
+
addr.value = $.int64Or(addr.value, mask);
|
|
112
112
|
return old;
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -120,7 +120,7 @@ export function OrInt64(addr: $.VarRef<number> | null, mask: number): number {
|
|
|
120
120
|
export function OrUint64(addr: $.VarRef<number> | null, mask: number): number {
|
|
121
121
|
if (!addr) return 0;
|
|
122
122
|
let old = addr.value;
|
|
123
|
-
addr.value = addr.value
|
|
123
|
+
addr.value = $.uint64Or(addr.value, mask);
|
|
124
124
|
return old;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -165,4 +165,3 @@ export function StoreUint64(addr: $.VarRef<number> | null, val: number): void {
|
|
|
165
165
|
addr.value = val;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import * as $ from '../../builtin/index.js'
|
|
4
|
+
import {
|
|
5
|
+
AddInt64,
|
|
6
|
+
AddUint64,
|
|
7
|
+
AndUint64,
|
|
8
|
+
OrUint64,
|
|
9
|
+
} from './doc_64.gs.js'
|
|
10
|
+
|
|
11
|
+
function asBigInt(value: number): bigint {
|
|
12
|
+
return typeof value === 'bigint' ? value : BigInt(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('sync/atomic 64-bit operations', () => {
|
|
16
|
+
test('adds uint64 values without mixing number and bigint arithmetic', () => {
|
|
17
|
+
const value = $.varRef($.uint('18446744073709551614', 64))
|
|
18
|
+
|
|
19
|
+
expect(AddUint64(value, $.uint(2, 64))).toBe(0)
|
|
20
|
+
expect(value.value).toBe(0)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('preserves high uint64 bits for bitwise operations', () => {
|
|
24
|
+
const value = $.varRef($.uint('9223372036854775808', 64))
|
|
25
|
+
|
|
26
|
+
expect(asBigInt(OrUint64(value, $.uint(1, 64)))).toBe(
|
|
27
|
+
9223372036854775808n,
|
|
28
|
+
)
|
|
29
|
+
expect(asBigInt(value.value)).toBe(9223372036854775809n)
|
|
30
|
+
|
|
31
|
+
expect(asBigInt(AndUint64(value, $.uint('18446744073709551614', 64)))).toBe(
|
|
32
|
+
9223372036854775809n,
|
|
33
|
+
)
|
|
34
|
+
expect(asBigInt(value.value)).toBe(9223372036854775808n)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('adds int64 values without number coercion', () => {
|
|
38
|
+
const value = $.varRef($.int('9223372036854775807', 64))
|
|
39
|
+
|
|
40
|
+
expect(AddInt64(value, 1)).toBe(-9223372036854775808)
|
|
41
|
+
expect(value.value).toBe(-9223372036854775808)
|
|
42
|
+
})
|
|
43
|
+
})
|