Skip to content

Commit 39e8bb1

Browse files
authored
Hotfix WASM.value parsing (#75)
* Unsigned to signed conversion * Fix parser * Fix precision loss in reading f64
1 parent 89d0500 commit 39e8bb1

11 files changed

Lines changed: 139 additions & 38 deletions

File tree

package-lock.json

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"bin"
1515
],
1616
"scripts": {
17-
"clean": "rm -rf dist",
17+
"clean": "rm -rf dist out",
1818
"build": "tsc --project tsconfig.build.json",
1919
"build:tests": "tsc --outDir out --project tsconfig.tests.json",
2020
"watch": "tsc -watch -p ./",
@@ -29,6 +29,7 @@
2929
"@thi.ng/leb128": "^3.1.75",
3030
"ansi-colors": "^4.1.3",
3131
"ieee754": "^1.2.1",
32+
"json-with-bigint": "^3.5.8",
3233
"ora": "^8.0.1",
3334
"source-map": "^0.7.4",
3435
"ts-node": "^10.5.0",
@@ -58,6 +59,7 @@
5859
"files": [
5960
"out/tests/unit/describers.test.js",
6061
"out/tests/unit/messaging.test.js",
62+
"out/tests/unit/parsing.test.js",
6163
"out/tests/unit/sourcemap.test.js",
6264
"out/tests/unit/util.test.js"
6365
],

src/debug/WARDuino.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ export namespace WARDuino {
7777
pc_error?: number;
7878
exception_msg?: string;
7979
breakpoints?: number[];
80-
stack?: Value[];
80+
stack?: Value<bigint | number>[];
8181
callstack?: Frame[];
82-
globals?: Value[];
82+
globals?: Value<bigint | number>[];
8383
table?: Table;
8484
memory?: Memory;
8585
br_table?: BRTable;

src/framework/Verifier.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {StepOutcome} from '../reporter/Results';
33
import {getValue} from './Testee';
44
import {Outcome} from '../reporter/describers/Describer';
55
import {bold} from 'ansi-colors';
6+
import {JSONStringify} from "json-with-bigint";
67

78
// decorator for Step class
89
export class Verifier {
@@ -32,7 +33,7 @@ export class Verifier {
3233
result = this.expectBehaviour(value, getValue(previous, field), entry.value);
3334
}
3435
} catch {
35-
return this.error(`Failure: ${JSON.stringify(actual)} state does not contain '${field}'.`);
36+
return this.error(`Failure: ${JSONStringify(actual)} state does not contain '${field}'.`);
3637
}
3738

3839
if (result.outcome !== Outcome.succeeded) {
@@ -124,5 +125,5 @@ export class Verifier {
124125

125126
/* eslint @typescript-eslint/no-explicit-any: off */
126127
function deepEqual(a: any, b: any): boolean {
127-
return a === b || (isNaN(a) && isNaN(b));
128+
return a === b || (isNaN(Number(a)) && isNaN(Number(b)));
128129
}

src/framework/scenario/Invoker.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class Invoker implements Step {
1212
readonly expected?: Expectation[];
1313
readonly target?: Target;
1414

15-
constructor(func: string, args: Value[], result: Value | undefined, target?: Target) {
15+
constructor(func: string, args: Value<any>[], result: Value<any> | undefined, target?: Target) {
1616
let prefix = '';
1717
this.instruction = invoke(func, args);
1818
this.expected = (result == undefined) ? returns(nothing) : returns(result);
@@ -24,13 +24,13 @@ export class Invoker implements Step {
2424
}
2525
}
2626

27-
export function invoke(func: string, args: Value[]): Instruction {
27+
export function invoke(func: string, args: Value<any>[]): Instruction {
2828
return {kind: Kind.Request, value: Message.invoke(func, args)};
2929
}
3030

31-
export function returns(n: Value): Expectation[] {
31+
export function returns<T extends bigint | number>(n: Value<T>): Expectation[] {
3232
if (n.type == Type.nothing) {
3333
return [{'value': {kind: 'primitive', value: undefined} as Expected<undefined>}]
3434
}
35-
return [{'value': {kind: 'primitive', value: n.value} as Expected<number>}]
35+
return [{'value': {kind: 'primitive', value: n.value} as Expected<T>}]
3636
}

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export * from './util/env';
66
export * from './framework/scenario/Actions';
77
export * from './framework/Testee';
88
export * from './framework/Framework';
9-
export * from './messaging/Parsers';
109
export * from './messaging/Message';
1110
export * from './framework/Scheduler';
1211
export * from './sourcemap/Wasm';

src/messaging/Message.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export namespace Message {
136136
export function updateModule(wasm: string): Request<Ack> {
137137
function payload(binary: Buffer): string {
138138
const w = new Uint8Array(binary);
139-
const sizeHex: string = WASM.leb128(w.length);
139+
const sizeHex: string = WASM.leb128(BigInt(w.length));
140140
const sizeBuffer = Buffer.allocUnsafe(4);
141141
sizeBuffer.writeUint32BE(w.length);
142142
const wasmHex = Buffer.from(w).toString('hex');
@@ -162,7 +162,7 @@ export namespace Message {
162162
}
163163
}
164164

165-
export function invoke(func: string, args: Value[]): Request<WASM.Value | Exception> {
165+
export function invoke(func: string, args: Value<bigint | number>[]): Request<WASM.Value<bigint | number> | Exception> {
166166
function fidx(map: SourceMap.Mapping, func: string): number {
167167
const fidx: number | void = map.functions.find((closure: SourceMap.Closure) => closure.name === func)?.index;
168168
if (fidx === undefined) {
@@ -171,14 +171,14 @@ export namespace Message {
171171
return fidx!;
172172
}
173173

174-
function convert(args: Value[]) {
174+
function convert(args: Value<bigint | number>[]) {
175175
let payload: string = '';
176-
args.forEach((arg: Value) => {
176+
args.forEach((arg: Value<bigint | number>) => {
177177
if (arg.type === Type.i32 || arg.type === Type.i64) {
178178
payload += WASM.leb128(arg.value);
179179
} else {
180180
const buff = Buffer.alloc(arg.type === Type.f32 ? 4 : 8);
181-
write(buff, arg.value, 0, true, arg.type === Type.f32 ? 23 : 52, buff.length);
181+
write(buff, Number(arg.value), 0, true, arg.type === Type.f32 ? 23 : 52, buff.length); // todo fix precision loss
182182
payload += buff.toString('hex');
183183
}
184184
});
@@ -187,7 +187,7 @@ export namespace Message {
187187

188188
return {
189189
type: Interrupt.invoke,
190-
payload: (map: SourceMap.Mapping) => `${WASM.leb128(fidx(map, func))}${convert(args)}`,
190+
payload: (map: SourceMap.Mapping) => `${WASM.leb128(BigInt(fidx(map, func)))}${convert(args)}`,
191191
parser: invokeParser
192192
}
193193
}

src/messaging/Parsers.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as ieee754 from 'ieee754';
33
import {Ack, Exception} from './Message';
44
import {Breakpoint} from '../debug/Breakpoint';
55
import {WARDuino} from '../debug/WARDuino';
6+
import {JSONParse} from 'json-with-bigint';
67
import State = WARDuino.State;
78
import nothing = WASM.nothing;
89

@@ -11,10 +12,10 @@ export function identityParser(text: string) {
1112
}
1213

1314
export function stateParser(text: string): State {
14-
return JSON.parse(text);
15+
return JSONParse(text);
1516
}
1617

17-
export function invokeParser(text: string): WASM.Value | Exception {
18+
export function invokeParser(text: string): WASM.Value<bigint | number> | Exception {
1819
if (exception(text)) {
1920
return {text: text};
2021
}
@@ -58,15 +59,29 @@ export function breakpointHitParser(text: string): Breakpoint {
5859
throw new Error('Could not messaging BREAKPOINT address in ack.');
5960
}
6061

61-
function stacking(objects: {value: any, type: any}[]): WASM.Value[] {
62-
const stacked: WASM.Value[] = [];
62+
export function signed(value: bigint, bits = 32) {
63+
let x = BigInt(value);
64+
const sign = 1n << BigInt(bits - 1);
65+
const mod = 1n << BigInt(bits);
66+
return x >= sign ? x - mod : x;
67+
68+
}
69+
70+
function stacking(objects: {value: bigint, type: any}[]): WASM.Value<bigint | number>[] {
71+
const stacked: WASM.Value<bigint | number>[] = [];
6372
for (const object of objects) {
6473
const type: WASM.Type = WASM.typing.get(object.type.toLowerCase()) ?? WASM.Type.unknown;
6574
let buff;
6675
switch (type) {
76+
case WASM.Type.u32:
77+
case WASM.Type.u64:
78+
stacked.push({value: object.value, type: type});
79+
break;
6780
case WASM.Type.i32:
81+
stacked.push({value: signed(object.value, 32), type: type});
82+
break;
6883
case WASM.Type.i64:
69-
stacked.push({value: Number(object.value), type: type});
84+
stacked.push({value: signed(object.value, 64), type: type});
7085
break;
7186
case WASM.Type.f32:
7287
buff = Buffer.from(Number(object.value.toString(16)).toString(16), 'hex');

src/sourcemap/Wasm.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ export namespace WASM {
22
export enum Type {
33
f32,
44
f64,
5+
u32,
56
i32,
7+
u64,
68
i64,
79
nothing,
810
unknown
@@ -11,34 +13,44 @@ export namespace WASM {
1113
export const typing = new Map<string, Type>([
1214
['f32', Type.f32],
1315
['f64', Type.f64],
16+
['u32', Type.u32],
1417
['i32', Type.i32],
18+
['u64', Type.u64],
1519
['i64', Type.i64]
1620
]);
1721

18-
export interface Value {
22+
export interface Value<T extends bigint | number> {
1923
type: Type;
20-
value: number;
24+
value: T;
2125
}
2226

23-
export interface Nothing extends Value {}
27+
export interface Nothing extends Value<number> {}
2428

2529
export const nothing: Nothing = {
2630
type: Type.nothing, value: 0
2731
}
2832

29-
export function i32(n: number): WASM.Value {
33+
export function u32(n: bigint): WASM.Value<bigint> {
34+
return {value: n, type: Type.u32};
35+
}
36+
37+
export function i32(n: bigint): WASM.Value<bigint> {
3038
return {value: n, type: Type.i32};
3139
}
3240

33-
export function f32(n: number): WASM.Value {
41+
export function f32(n: number): WASM.Value<number> {
3442
return {value: n, type: Type.f32};
3543
}
3644

37-
export function f64(n: number): WASM.Value {
45+
export function f64(n: number): WASM.Value<number> {
3846
return {value: n, type: Type.f64};
3947
}
4048

41-
export function i64(n: number): WASM.Value {
49+
export function u64(n: bigint): WASM.Value<bigint> {
50+
return {value: n, type: Type.u64};
51+
}
52+
53+
export function i64(n: bigint): WASM.Value<bigint> {
4254
return {value: n, type: Type.i64};
4355
}
4456

@@ -65,7 +77,8 @@ export namespace WASM {
6577
bytes: Uint8Array;
6678
}
6779

68-
export function leb128(a: number): string { // TODO can only handle 32 bit
80+
export function leb128(value: bigint | number): string { // TODO can only handle 32 bit
81+
let a = Number(value);
6982
a |= 0;
7083
const result = [];
7184
while (true) {

0 commit comments

Comments
 (0)