Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ jobs:
env:
SARIF_ESLINT_IGNORE_SUPPRESSED: "true"
run: npx eslint src/**/*
--config .eslint.config.mjs
--config eslint.config.mjs
--format @microsoft/eslint-formatter-sarif
--output-file eslint-results.sarif
continue-on-error: true

- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: eslint-results.sarif
wait-for-processing: true
Expand Down
2 changes: 1 addition & 1 deletion .eslint.config.mjs → eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default [
{ plugins: {
'@stylistic/js': stylisticJs
}, rules: {
'@stylistic/js/indent': ['error', 4],
'@stylistic/js/indent': ['error', 4, { "SwitchCase": 1 }],
'@typescript-eslint/no-wrapper-object-types': 'off'
}
},
Expand Down
5 changes: 3 additions & 2 deletions src/debug/WARDuino.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export namespace WARDuino {
import Frame = WASM.Frame;
import Table = WASM.Table;
import Memory = WASM.Memory;
import Type = WASM.Type;

export interface CallbackMapping {
callbackid: string;
Expand Down Expand Up @@ -63,9 +64,9 @@ export namespace WARDuino {
pc_error?: number;
exception_msg?: string;
breakpoints?: number[];
stack?: Value[];
stack?: Value<Type>[];
callstack?: Frame[];
globals?: Value[];
globals?: Value<Type>[];
table?: Table;
memory?: Memory;
br_table?: BRTable;
Expand Down
2 changes: 1 addition & 1 deletion src/framework/Testee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function timeout<T>(label: string, time: number, promise: Promise<T>): Pr
* @param field dot string describing the field of the value (or path)
*/
export function getValue(object: any, field: string): any {
if (object?.type == WASM.Type.nothing) {
if (object?.type == WASM.Special.nothing) {
return undefined;
}

Expand Down
8 changes: 4 additions & 4 deletions src/framework/scenario/Invoker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class Invoker implements Step {
readonly expected?: Expectation[];
readonly target?: Target;

constructor(func: string, args: Value[], result: Value | undefined, target?: Target) {
constructor(func: string, args: Value<Type>[], result: Value<Type> | undefined, target?: Target) {
let prefix = '';
this.instruction = invoke(func, args);
this.expected = (result == undefined) ? returns(nothing) : returns(result);
Expand All @@ -24,12 +24,12 @@ export class Invoker implements Step {
}
}

export function invoke(func: string, args: Value[]): Instruction {
export function invoke(func: string, args: Value<Type>[]): Instruction {
return {kind: Kind.Request, value: Message.invoke(func, args)};
}

export function returns(n: Value): Expectation[] {
if (n.type == Type.nothing) {
export function returns(n: Value<Type>): Expectation[] {
if (n.type === WASM.Special.nothing) {
return [{'value': {kind: 'primitive', value: undefined} as Expected<undefined>}]
}
return [{'value': {kind: 'primitive', value: n.value} as Expected<number>}]
Expand Down
37 changes: 25 additions & 12 deletions src/messaging/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {WARDuino} from '../debug/WARDuino';
import {ackParser, breakpointParser, invokeParser, stateParser} from './Parsers';
import {Breakpoint} from '../debug/Breakpoint';
import {WASM} from '../sourcemap/Wasm';
import {write} from 'ieee754';
import ieee754 from 'ieee754';
import {SourceMap} from '../sourcemap/SourceMap';
import {readFileSync} from 'fs';
import {CompileOutput, CompilerFactory} from '../manage/Compiler';
Expand All @@ -11,6 +11,7 @@ import Interrupt = WARDuino.Interrupt;
import State = WARDuino.State;
import Value = WASM.Value;
import Type = WASM.Type;
import Float = WASM.Float;

// An acknowledgement returned by the debugger
export interface Ack {
Expand All @@ -28,6 +29,7 @@ export interface Request<R> {
}

export namespace Message {
import Float = WASM.Float;
export const run: Request<Ack> = {
type: Interrupt.run,
parser: (line: string) => {
Expand Down Expand Up @@ -135,7 +137,7 @@ export namespace Message {
export function updateModule(wasm: string): Request<Ack> {
function payload(binary: Buffer): string {
const w = new Uint8Array(binary);
const sizeHex: string = WASM.leb128(w.length);
const sizeHex: string = WASM.leb128(BigInt(w.length));
const sizeBuffer = Buffer.allocUnsafe(4);
sizeBuffer.writeUint32BE(w.length);
const wasmHex = Buffer.from(w).toString('hex');
Expand All @@ -161,7 +163,7 @@ export namespace Message {
}
}

export function invoke(func: string, args: Value[]): Request<WASM.Value | Exception> {
export function invoke(func: string, args: Value<Type>[]): Request<WASM.Value<Type> | Exception> {
function fidx(map: SourceMap.Mapping, func: string): number {
const fidx: number | void = map.functions.find((closure: SourceMap.Closure) => closure.name === func)?.index;
if (fidx === undefined) {
Expand All @@ -170,23 +172,28 @@ export namespace Message {
return fidx!;
}

function convert(args: Value[]) {
function convert(args: Value<Type>[]) {
let payload: string = '';
args.forEach((arg: Value) => {
if (arg.type === Type.i32 || arg.type === Type.i64) {
payload += WASM.leb128(arg.value);
} else {
const buff = Buffer.alloc(arg.type === Type.f32 ? 4 : 8);
write(buff, arg.value, 0, true, arg.type === Type.f32 ? 23 : 52, buff.length);
payload += buff.toString('hex');
args.forEach((arg: Value<Type>) => {
switch (arg.type) {
case WASM.Float.f32:
case WASM.Float.f64:
payload += ieeefloat(<Value<Float>>arg)
break;
case WASM.Integer.i32:
case WASM.Integer.i64:
payload += WASM.leb128(<bigint>arg.value);
break;
default:
break;
}
});
return payload;
}

return {
type: Interrupt.invoke,
payload: (map: SourceMap.Mapping) => `${WASM.leb128(fidx(map, func))}${convert(args)}`,
payload: (map: SourceMap.Mapping) => `${WASM.leb128(BigInt(fidx(map, func)))}${convert(args)}`,
parser: invokeParser
}
}
Expand Down Expand Up @@ -218,3 +225,9 @@ export namespace Message {
}
};
}

function ieeefloat(arg: Value<Float>): String {
const buff = Buffer.alloc(arg.type === Float.f32 ? 4 : 8);
ieee754.write(buff, <number>arg.value, 0, true, arg.type === Float.f32 ? 23 : 52, buff.length); // TODO write BigInt without loss of precision (don't use ieee754.write)
return buff.toString('hex');
}
15 changes: 8 additions & 7 deletions src/messaging/Parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Breakpoint} from '../debug/Breakpoint';
import {WARDuino} from '../debug/WARDuino';
import State = WARDuino.State;
import nothing = WASM.nothing;
import Type = WASM.Type;

export function identityParser(text: string) {
return stripEnd(text);
Expand All @@ -14,7 +15,7 @@ export function stateParser(text: string): State {
return JSON.parse(text);
}

export function invokeParser(text: string): WASM.Value | Exception {
export function invokeParser(text: string): WASM.Value<Type> | Exception {
if (exception(text)) {
return {text: text};
}
Expand Down Expand Up @@ -58,21 +59,21 @@ export function breakpointHitParser(text: string): Breakpoint {
throw new Error('Could not messaging BREAKPOINT address in ack.');
}

function stacking(objects: {value: any, type: any}[]): WASM.Value[] {
const stacked: WASM.Value[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function stacking(objects: {value: any, type: any}[]): WASM.Value<Type>[] {
const stacked: WASM.Value<Type>[] = [];
for (const object of objects) {
let value: number = object.value;
const type: WASM.Type = WASM.typing.get(object.type.toLowerCase()) ?? WASM.Type.unknown;
if (type === WASM.Type.f32 || type === WASM.Type.f64) {
const type: WASM.Type = WASM.typing.get(object.type.toLowerCase()) ?? WASM.Special.unknown;
if (type === WASM.Float.f32 || type === WASM.Float.f64) {
const buff = Buffer.from(object.value, 'hex');
value = ieee754.read(buff, 0, false, type === WASM.Type.f32 ? 23 : 52, buff.length);
value = ieee754.read(buff, 0, false, type === WASM.Float.f32 ? 23 : 52, buff.length);
}
stacked.push({value: value, type: type});
}
return stacked;
}


// Strips all trailing newlines
function stripEnd(text: string): string {
return text.replace(/\s+$/g, '');
Expand Down
66 changes: 31 additions & 35 deletions src/sourcemap/Wasm.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
import * as leb from "@thi.ng/leb128";

export namespace WASM {
export enum Type {

export enum Float {
f32,
f64,
}

export enum Integer {
i32,
i64,
}

export enum Special {
nothing,
unknown
}

export type Type = Float | Integer | Special;


export const typing = new Map<string, Type>([
['f32', Type.f32],
['f64', Type.f64],
['i32', Type.i32],
['i64', Type.i64]
['f32', Float.f32],
['f64', Float.f64],
['i32', Integer.i32],
['i64', Integer.i64]
]);

export interface Value {
type: Type;
value: number;
export interface Value<T extends Type> {
type: T;
value: T extends Float ? number : bigint;
}

export interface Nothing extends Value {}
export type Nothing = Value<Type>

export const nothing: Nothing = {
type: Type.nothing, value: 0
type: Special.nothing, value: 0
}

export function i32(n: number): WASM.Value {
return {value: n, type: Type.i32};
export function i32(n: bigint): WASM.Value<Integer> {
return {value: n, type: Integer.i32};
}

export function f32(n: number): WASM.Value {
return {value: n, type: Type.f32};
export function f32(n: number): WASM.Value<Float> {
return {value: n, type: Float.f32};
}

export function f64(n: number): WASM.Value {
return {value: n, type: Type.f64};
export function f64(n: number): WASM.Value<Float> {
return {value: n, type: Float.f64};
}

export function i64(n: number): WASM.Value {
return {value: n, type: Type.i64};
export function i64(n: bigint): WASM.Value<Integer> {
return {value: n, type: Integer.i64};
}

export interface Frame {
Expand All @@ -65,21 +77,5 @@ export namespace WASM {
bytes: Uint8Array;
}

export function leb128(a: number): string { // TODO can only handle 32 bit
a |= 0;
const result = [];
while (true) {
const byte_ = a & 0x7f;
a >>= 7;
if (
(a === 0 && (byte_ & 0x40) === 0) ||
(a === -1 && (byte_ & 0x40) !== 0)
) {
result.push(byte_.toString(16).padStart(2, '0'));
return result.join('').toUpperCase();
}
result.push((byte_ | 0x80).toString(16).padStart(2, '0'));
}
}

export const leb128 = (v: number | bigint) => Buffer.from(leb.encodeSLEB128(v)).toString('hex').toUpperCase().padStart(2, '0')
}
4 changes: 2 additions & 2 deletions tests/examples/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ spec.testee('emulator[:8100]', new EmulatorSpecification(8100));
const steps: Step[] = [];

// ✔ ((invoke "8u_good1" (i32.const 0)) (i32.const 97))
steps.push(new Invoker('8u_good1', [WASM.i32(0)], WASM.i32(97)));
steps.push(new Invoker('8u_good1', [WASM.i32(BigInt(0))], WASM.i32(BigInt(97))));

// ✔ ((invoke "8u_good3" (i32.const 0)) (i32.const 98))
steps.push(new Invoker('8u_good3', [WASM.i32(0)], WASM.i32(98)));
steps.push(new Invoker('8u_good3', [WASM.i32(BigInt(0))], WASM.i32(BigInt(98))));

// ✔ ((invoke "func-unwind-by-br"))
steps.push(new Invoker('func-unwind-by-br', [], undefined));
Expand Down
31 changes: 23 additions & 8 deletions tests/unit/sourcemap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,34 @@ import {SourceMap} from '../../src/sourcemap/SourceMap';
import {WABT} from '../../src/util/env';
import {copyFileSync, mkdtempSync, readFileSync, rmSync} from 'fs';

import * as leb from '@thi.ng/leb128';

const artifacts = `${__dirname}/../../../tests/artifacts`;

/**
* Check LEB 128 encoding
*/
test('[leb128] : test encoding', t => {
t.is(WASM.leb128(0), '00');
t.is(WASM.leb128(1), '01');
t.is(WASM.leb128(8), '08');
t.is(WASM.leb128(32), '20');
t.is(WASM.leb128(64), 'C000');
t.is(WASM.leb128(128), '8001');
t.is(WASM.leb128(1202), 'B209');
test('[leb128] : test encode positive numbers', t => {
t.is(WASM.leb128(BigInt(0)), '00');
t.is(WASM.leb128(BigInt(1)), '01');
t.is(WASM.leb128(BigInt(8)), '08');
t.is(WASM.leb128(BigInt(32)), '20');
t.is(WASM.leb128(BigInt(64)), 'C000');
t.is(WASM.leb128(BigInt(127)), 'FF00');
t.is(WASM.leb128(BigInt(128)), '8001');
t.is(WASM.leb128(BigInt(1202)), 'B209');
t.is(WASM.leb128(BigInt(2147483647)), 'FFFFFFFF07');
t.is(WASM.leb128(BigInt(4294967295)), 'FFFFFFFF0F');
t.is(WASM.leb128(Number.MAX_SAFE_INTEGER), 'FFFFFFFFFFFFFF0F');
});

test('[leb128] : test encode negative numbers', t => {
t.is(WASM.leb128(BigInt(-1)), '7F');
t.is(WASM.leb128(BigInt(-8)), '78');
t.is(WASM.leb128(BigInt(-32)), '60');
t.is(WASM.leb128(BigInt(-64)), '40');
t.is(WASM.leb128(BigInt(-127)), '817F');
t.is(WASM.leb128(BigInt(-128)), '807F');
});

test('[extractLineInfo] : test against artifacts (1)', async t => {
Expand Down
Loading