diff --git a/.gitignore b/.gitignore index e21e37c..dd5d55d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules +assembly/generated-pvm.ts tests/* diff --git a/assembly/api-generic.ts b/assembly/api-generic.ts index 53eb762..44a8f01 100644 --- a/assembly/api-generic.ts +++ b/assembly/api-generic.ts @@ -1,10 +1,11 @@ import { RELEVANT_ARGS } from "./arguments"; +import * as compiler from "./compile/compiler"; import { INSTRUCTIONS, MISSING_INSTRUCTION } from "./instructions"; import { Interpreter, Status } from "./interpreter"; import { Memory, MemoryBuilder } from "./memory"; import { Access, PAGE_SIZE } from "./memory-page"; import { Program, decodeArguments, decodeProgram, liftBytes, resolveArguments } from "./program"; -import { NO_OF_REGISTERS, Registers } from "./registers"; +import { NO_OF_REGISTERS, newRegisters } from "./registers"; export class InitialPage { address: u32 = 0; @@ -66,17 +67,28 @@ export function getAssembly(p: Program): string { return v; } +export function compile(input: VmInput, _useSbrkGas: boolean = false): string { + const program = decodeProgram(liftBytes(input.program)); + const registers = newRegisters(); + for (let r = 0; r < registers.length; r++) { + registers[r] = input.registers[r]; + } + + const result = compiler.compile(program, input.pc, input.gas, registers); + return result; +} + export function runVm(input: VmInput, logs: boolean = false, useSbrkGas: boolean = false): VmOutput { - const p = decodeProgram(liftBytes(input.program)); + const program = decodeProgram(liftBytes(input.program)); - const registers: Registers = new StaticArray(NO_OF_REGISTERS); + const registers = newRegisters(); for (let r = 0; r < registers.length; r++) { registers[r] = input.registers[r]; } const builder = new MemoryBuilder(); const memory = buildMemory(builder, input.pageMap, input.memory); - const int = new Interpreter(p, registers, memory); + const int = new Interpreter(program, registers, memory); int.useSbrkGas = useSbrkGas; int.nextPc = input.pc; int.gas.set(input.gas); @@ -97,7 +109,7 @@ export function runVm(input: VmInput, logs: boolean = false, useSbrkGas: boolean if (logs) { const instruction = int.pc < u32(int.program.code.length) ? int.program.code[int.pc] : 0; const iData = instruction >= INSTRUCTIONS.length ? MISSING_INSTRUCTION : INSTRUCTIONS[instruction]; - const skipBytes = p.mask.bytesToNextInstruction(int.pc); + const skipBytes = program.mask.bytesToNextInstruction(int.pc); const name = changetype(iData.namePtr); console.log(`INSTRUCTION = ${name} (${instruction})`); const args = resolveArguments(iData.kind, int.program.code.subarray(int.pc + 1), skipBytes, int.registers); diff --git a/assembly/compile-gen.js b/assembly/compile-gen.js new file mode 100755 index 0000000..0685f6a --- /dev/null +++ b/assembly/compile-gen.js @@ -0,0 +1,70 @@ +#!/usr/bin/env node + +// generate files that produce a compiled version +import { readFileSync, readdirSync, writeFileSync } from "node:fs"; + +main(); + +function main() { + // instructions-compile + const exe = read("./instructions-exe.ts").replace("RUN", "COMPILE"); + write("./compile/instructions.ts", exe); + // instruction files + const files = readdirSync("./instructions").filter((x) => !x.endsWith(".test.ts")); + + for (const f of files) { + let c = read(`./instructions/${f}`); + if (f === "outcome.ts" || f === "utils.ts") { + c = c.replace(/\(args: Args.*/, "(context: CompileContext, args: Args) => void"); + c = c.replace(/"\.\.\//g, '"../../'); + c = `import {CompileContext} from "../context";\n\n ${c}`; + } else { + // make sure we get the context parameter + c = c.replace(/\(args[,)].*/g, "(ctx, args) => {"); + c = c.replace(/\(\) =>/g, "(ctx) =>"); + // now replace all content + const regex = /({\n)(.*?)(\n};)/gs; + c = c.replace(regex, (match, start, content, end) => { + return replaceWithCompiled(match, start, content, end); + }); + } + write(`./compile/instructions/${f}`, c); + } +} + +function replaceWithCompiled(_match, start, content, end) { + const contentArray = content.split("\n"); + const newContent = contentArray + .map((v) => { + let x = v; + x = x.replace(/registers/g, "regs"); + x = x.replace(/regs\[/g, "regs[${"); + x = x.replace(/\]/g, "}]"); + x = x.replace(/u32SignExtend\((args.[^>]?)\)/g, "${u32SignExtend($1)}"); + if (x.indexOf("return") !== -1) { + if (x.indexOf("ok()") !== -1) { + return ""; + } + + // returns processed in context + x = `ctx.${x.replace(/^\s*return /g, "")}`; + // args to okOrFault should be stringified + x = x.replace(/okOrFault\((.*?)\)/, 'okOrFault("$1")'); + x = x.replace(/dJump\((.*?)\)/, 'dJump("$1")'); + return x; + } + return ` ctx.addBlockLine(\`${x}\`, args);`; + }) + .filter((x) => x.length > 0) + .join("\n"); + + return `${start}${newContent}\n${end}`; +} + +function read(path) { + return readFileSync(path, "utf8"); +} +function write(path, content) { + const v = "// This file is auto-generated, take a look at compile-gen.js\n\n"; + return writeFileSync(path, v + content); +} diff --git a/assembly/compile/.gitignore b/assembly/compile/.gitignore new file mode 100644 index 0000000..6da0a35 --- /dev/null +++ b/assembly/compile/.gitignore @@ -0,0 +1 @@ +instructions.ts diff --git a/assembly/compile/compiler.ts b/assembly/compile/compiler.ts new file mode 100644 index 0000000..48880fa --- /dev/null +++ b/assembly/compile/compiler.ts @@ -0,0 +1,71 @@ +import { INSTRUCTIONS, MISSING_INSTRUCTION } from "../instructions"; +import { Program, decodeArguments } from "../program"; +import { Registers } from "../registers"; +import { CompileContext } from "./context"; +import { COMPILE } from "./instructions"; + +export function compile(program: Program, initialPc: u32, gas: i64, registers: Registers): string { + const ctx = new CompileContext(); + // externalities + ctx.push("declare function trap(): void;"); + ctx.push("declare function outOfGas(): void;"); + ctx.push("declare function fault(address: u32): void;"); + ctx.push("declare function dumpReg(idx: u32, data: i64): void;"); + + for (let i = 0; i < registers.length; i++) { + ctx.push(`let regs${i}: i64 = 0x${registers[i].toString(16)};`); + } + ctx.push(`\nlet gas = 0x${gas.toString(16)};\n`); + + // initial entry point + ctx.push(`\nblock${initialPc}();\n`); + + // program + ctx.pc = 0; + let blockGas: i64 = 0; + while (ctx.pc < program.code.length) { + if (!program.mask.isInstruction(ctx.pc)) { + throw new Error("not an instruction?"); + } + + if (program.basicBlocks.isStart(ctx.pc)) { + if (ctx.pc > 0) { + ctx.endBlock(blockGas); + } + ctx.startBlock(ctx.pc); + blockGas = 0; + } + + const instruction = program.code[ctx.pc]; + const iData = instruction < INSTRUCTIONS.length ? INSTRUCTIONS[instruction] : MISSING_INSTRUCTION; + + blockGas += iData.gas; + const skipBytes = program.mask.bytesToNextInstruction(ctx.pc); + const args = decodeArguments(iData.kind, program.code.subarray(ctx.pc + 1), skipBytes); + // TODO gas stuff? + const exe = COMPILE[instruction]; + ctx.addBlockLine(`{ // ${changetype(iData.namePtr)}`); + // handle jumps and other results? + exe(ctx, args); + ctx.addBlockLine("};"); + // move to next instruction + ctx.pc += 1 + skipBytes; + } + + ctx.endBlock(blockGas); + + // utility functions + ctx.push("// utils"); + ctx.push("@inline function u32SignExtend(v: u32): i64 { return i64(i32(v)); }"); + ctx.push("@inline function u16SignExtend(v: u16): i64 { return i64(i32(i16(v))); }"); + ctx.push("@inline function u8SignExtend(v: u8): i64 { return i64(i32(i16(i8(v)))); }"); + + // print out registers at the end + ctx.push("\n// registers"); + for (let i = 0; i < registers.length; i++) { + ctx.push(`dumpReg(${i}, regs${i});`); + } + + // TODO [ToDr] print out what we have here? + return ctx.data.join("\n"); +} diff --git a/assembly/compile/context.ts b/assembly/compile/context.ts new file mode 100644 index 0000000..f7d1b3d --- /dev/null +++ b/assembly/compile/context.ts @@ -0,0 +1,68 @@ +import { Args } from "../arguments"; +import { NO_OF_REGISTERS } from "../registers"; + +export class CompileContext { + public readonly data: string[] = []; + public readonly bufferedBlock: string[] = []; + public pc: u32 = 0; + + flushBlockBuffer(): void { + for (let i = 0; i < this.bufferedBlock.length; i++) { + this.data.push(this.bufferedBlock[i]); + } + this.bufferedBlock.length = 0; + } + + push(v: string): void { + this.data.push(v); + } + + addBlockLine(x: string, args: Args = new Args()): void { + let v = x; + // some replacing happens here + v = v + .replace("args.a", `${args.a}`) + .replace("args.b", `${args.b}`) + .replace("args.c", `${args.c}`) + .replace("args.d", `${args.d}`); + for (let i = 0; i < NO_OF_REGISTERS; i++) { + for (let j = 0; j < 3; j++) { + v = v.replace(`regs[${i}]`, `regs${i}`); + } + } + + this.bufferedBlock.push(` ${v}`); + } + + staticJump(arg: u32): void { + this.addBlockLine(` return block${this.pc + arg}(); // ${this.pc} + ${arg}`); + } + + panic(): void { + this.addBlockLine(" trap(); abort();"); + } + + hostCall(num: u32): void { + this.addBlockLine(` host(${num})`); + } + + okOrFault(v: string): void { + this.addBlockLine(` if (${v}.isFault) { fault(0); abort(); }`); + } + + dJump(v: string): void { + // TODO ToDr this must be using the jump table + this.addBlockLine(` // dynamic jump to ${v}`); + } + + startBlock(pc: u32): void { + this.push(`function block${pc}(): void {`); + } + + endBlock(blockGas: i64): void { + this.push(` gas -= ${blockGas};`); + this.push(" if (gas < 0) { outOfGas(); }"); + this.flushBlockBuffer(); + this.push("}"); + } +} diff --git a/assembly/compile/instructions/.gitignore b/assembly/compile/instructions/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/assembly/compile/instructions/.gitignore @@ -0,0 +1 @@ +* diff --git a/assembly/index.ts b/assembly/index.ts index 949ba01..276766a 100644 --- a/assembly/index.ts +++ b/assembly/index.ts @@ -2,7 +2,7 @@ import { VmInput, VmOutput, getAssembly, runVm } from "./api-generic"; import { decodeProgram, decodeSpi, liftBytes } from "./program"; export * from "./api"; -export { runVm, getAssembly } from "./api-generic"; +export { runVm, compile, getAssembly } from "./api-generic"; export { wrapAsProgram } from "./program-build"; export enum InputKind { diff --git a/assembly/instructions/jump.ts b/assembly/instructions/jump.ts index d9526e6..b0f4289 100644 --- a/assembly/instructions/jump.ts +++ b/assembly/instructions/jump.ts @@ -2,7 +2,9 @@ import { InstructionRun, dJump, staticJump } from "./outcome"; import { reg, u32SignExtend } from "./utils"; // JUMP -export const jump: InstructionRun = (args) => staticJump(args.a); +export const jump: InstructionRun = (args) => { + return staticJump(args.a); +}; // JUMP_IND export const jump_ind: InstructionRun = (args, registers) => { diff --git a/assembly/instructions/misc.ts b/assembly/instructions/misc.ts index fa3af42..7de7cf9 100644 --- a/assembly/instructions/misc.ts +++ b/assembly/instructions/misc.ts @@ -2,16 +2,24 @@ import { InstructionRun, hostCall, ok, okOrFault, panic } from "./outcome"; import { reg } from "./utils"; // INVALID -export const INVALID: InstructionRun = () => panic(); +export const INVALID: InstructionRun = () => { + return panic(); +}; // TRAP -export const trap: InstructionRun = () => panic(); +export const trap: InstructionRun = () => { + return panic(); +}; // FALLTHROUGH -export const fallthrough: InstructionRun = () => ok(); +export const fallthrough: InstructionRun = () => { + return ok(); +}; // ECALLI -export const ecalli: InstructionRun = (args) => hostCall(args.a); +export const ecalli: InstructionRun = (args) => { + return hostCall(args.a); +}; // SBRK export const sbrk: InstructionRun = (args, registers, memory) => { diff --git a/assembly/math.ts b/assembly/math.ts deleted file mode 100644 index e69de29..0000000 diff --git a/bin/disassemble.js b/bin/disassemble.js index 3dc4e33..66a8190 100755 --- a/bin/disassemble.js +++ b/bin/disassemble.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import fs from 'node:fs'; +import fs from "node:fs"; import { InputKind, disassemble } from "../build/release.js"; main(); @@ -9,7 +9,7 @@ function main() { const args = process.argv.slice(2); let kind = InputKind.Generic; - if (args.length > 0 && args[0] === '--spi') { + if (args.length > 0 && args[0] === "--spi") { args.shift(); kind = InputKind.SPI; } @@ -20,11 +20,10 @@ function main() { process.exit(1); } - - args.forEach(arg => { + for (const arg of args) { const f = fs.readFileSync(arg); - const name = kind === InputKind.Generic ? 'generic PVM' : 'JAM SPI'; + const name = kind === InputKind.Generic ? "generic PVM" : "JAM SPI"; console.log(`🤖 Assembly of ${arg} (as ${name})`); console.log(disassemble(Array.from(f), kind)); - }); + } } diff --git a/bin/fuzz.js b/bin/fuzz.js index 16d597c..b03d97e 100755 --- a/bin/fuzz.js +++ b/bin/fuzz.js @@ -1,11 +1,11 @@ #!/usr/bin/env node import "json-bigint-patch"; -import fs from 'node:fs'; +import fs from "node:fs"; import { Pvm } from "@typeberry/pvm-debugger-adapter"; -import { wrapAsProgram, runVm, disassemble, InputKind } from "../build/release.js"; +import { InputKind, disassemble, runVm, wrapAsProgram } from "../build/release.js"; -let runNumber = 0; +const runNumber = 0; export function fuzz(data) { const gas = 200n; @@ -17,29 +17,31 @@ export function fuzz(data) { } try { - pvm.reset( - program, - pc, - gas, - ); - while(pvm.nSteps(100)) {} + pvm.reset(program, pc, gas); + while (pvm.nSteps(100)) {} const printDebugInfo = false; - const registers = Array(13).join(',').split(',').map(() => BigInt(0)); - const output = runVm({ - registers, - pc, - pageMap: [], - memory: [], - gas, - program, - }, printDebugInfo); - + const registers = Array(13) + .join(",") + .split(",") + .map(() => BigInt(0)); + const output = runVm( + { + registers, + pc, + pageMap: [], + memory: [], + gas, + program, + }, + printDebugInfo, + ); + collectErrors((assert) => { - assert(pvm.getStatus(), normalizeStatus(output.status), 'status'); - assert(pvm.getGasLeft(), output.gas, 'gas'); - assert(Array.from(pvm.getRegisters()), output.registers, 'registers'); - assert(pvm.getProgramCounter(), output.pc, 'pc'); + assert(pvm.getStatus(), normalizeStatus(output.status), "status"); + assert(pvm.getGasLeft(), output.gas, "gas"); + assert(Array.from(pvm.getRegisters()), output.registers, "registers"); + assert(pvm.getProgramCounter(), output.pc, "pc"); }); try { @@ -55,12 +57,12 @@ export function fuzz(data) { status: pvm.getStatus(), gasLeft: pvm.getGasLeft(), pc: pvm.getProgramCounter(), - registers: pvm.getRegisters() + registers: pvm.getRegisters(), }, ); } } catch (e) { - console.warn('Unable to write file', e); + console.warn("Unable to write file", e); } } catch (e) { const hex = programHex(program); @@ -72,7 +74,9 @@ export function fuzz(data) { } function programHex(program) { - return Array.from(program).map(x => x.toString(16).padStart(2, '0')).join(''); + return Array.from(program) + .map((x) => x.toString(16).padStart(2, "0")) + .join(""); } function linkTo(programHex) { return `https://pvm-debugger.netlify.app/#/load?program=0x${programHex}`; @@ -85,8 +89,8 @@ function normalizeStatus(status) { return status; } -function assert(tb, an, comment = '') { - let condition = tb !== an; +function assert(tb, an, comment = "") { + let condition = tb !== an; if (Array.isArray(tb) && Array.isArray(an)) { condition = tb.toString() !== an.toString(); } @@ -94,10 +98,10 @@ function assert(tb, an, comment = '') { if (condition) { const alsoAsHex = (f) => { if (Array.isArray(f)) { - return `${f.map(alsoAsHex).join(', ')}`; + return `${f.map(alsoAsHex).join(", ")}`; } - if (typeof f === 'number' || typeof f === 'bigint') { + if (typeof f === "number" || typeof f === "bigint") { if (BigInt(f) !== 0n) { return `${f} | 0x${f.toString(16)}`; } @@ -123,32 +127,35 @@ function collectErrors(cb) { }); if (errors.length > 0) { - throw new Error(errors.join('\n')); + throw new Error(errors.join("\n")); } } function writeTestCase(program, initial, expected) { const hex = programHex(program); fs.mkdirSync(`../tests/length_${hex.length}`, { recursive: true }); - fs.writeFileSync(`../tests/length_${hex.length}/${hex}.json`, JSON.stringify({ - name: linkTo(hex), - "initial-regs": initial.registers, - "initial-pc": initial.pc, - "initial-page-map": [], - "initial-memory": [], - "initial-gas": initial.gas, - program: Array.from(program), - "expected-status": statusToStr(expected.status), - "expected-regs": Array.from(expected.registers), - "expected-pc": expected.pc, - "expected-gas": expected.gasLeft, - "expected-memory": [], - })); + fs.writeFileSync( + `../tests/length_${hex.length}/${hex}.json`, + JSON.stringify({ + name: linkTo(hex), + "initial-regs": initial.registers, + "initial-pc": initial.pc, + "initial-page-map": [], + "initial-memory": [], + "initial-gas": initial.gas, + program: Array.from(program), + "expected-status": statusToStr(expected.status), + "expected-regs": Array.from(expected.registers), + "expected-pc": expected.pc, + "expected-gas": expected.gasLeft, + "expected-memory": [], + }), + ); } function statusToStr(status) { if (status === 0) { - return 'halt'; + return "halt"; } if (status === 1) { return "trap"; diff --git a/bin/index.js b/bin/index.js index 105178a..69fa20f 100755 --- a/bin/index.js +++ b/bin/index.js @@ -1,14 +1,14 @@ #!/usr/bin/env node import "json-bigint-patch"; -import {readFileSync, readdirSync} from 'node:fs'; -import {resolve, join} from 'node:path'; -import * as assert from 'node:assert'; +import * as assert from "node:assert"; +import { readFileSync, readdirSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; -import { runVm, InputKind, disassemble } from "../build/release.js"; +import { InputKind, compile, disassemble, runVm } from "../build/release.js"; -const OK = '🟢'; -const ERR = '🔴'; +const OK = "🟢"; +const ERR = "🔴"; // Run the CLI application main(); @@ -17,22 +17,26 @@ main(); function main() { const options = { isDebug: false, + compile: false, useSbrkGas: false, }; // Get the JSON file arguments from the command line - let args = process.argv.slice(2); + const args = process.argv.slice(2); for (;;) { if (args.length === 0) { break; } - if (args[0] === '--debug') { + if (args[0] === "--debug") { args.shift(); options.isDebug = true; - } else if (args[0] === '--sbrk-gas') { + } else if (args[0] === "--sbrk-gas") { args.shift(); options.useSbrkGas = true; + } else if (args[0] === "--compile") { + args.shift(); + options.compile = true; } else { break; } @@ -45,7 +49,7 @@ function main() { process.exit(1); } - if (args[0] === '-') { + if (args[0] === "-") { readFromStdin(options); return; } @@ -57,63 +61,64 @@ function main() { }; // Process each file - args.forEach((filePath) => { + for (const filePath of args) { // try whole directory let dir = null; try { dir = readdirSync(filePath); - } catch (e) { - } + } catch (_e) {} if (dir !== null) { status.all += dir.length; - dir.forEach((file) => processFile(options, status, join(filePath, file))); + for (const file of dir) { + processFile(options, status, join(filePath, file)); + } } else { status.all += 1; // or just process file processFile(options, status, filePath); // TODO print results to stdout } - }); + } const icon = status.ok.length === status.all ? OK : ERR; console.log(`${icon} Tests status: ${status.ok.length}/${status.all}`); if (status.fail.length) { - console.error('Failures:'); + console.error("Failures:"); for (const e of status.fail) { console.error(`❗ ${e.filePath} (${e.name})`); } - process.exit(-1) + process.exit(-1); } } function readFromStdin(options) { - process.stdin.setEncoding('utf8'); - process.stderr.write('awaiting input\n'); + process.stdin.setEncoding("utf8"); + process.stderr.write("awaiting input\n"); // Read from stdin - let buffer = ''; - process.stdin.on('data', (data) => { + let buffer = ""; + process.stdin.on("data", (data) => { buffer += data; if (buffer.endsWith("\n\n")) { const json = JSON.parse(buffer); const input = { - registers: read(json, 'initial-regs').map(x => BigInt(x)), - pc: read(json, 'initial-pc'), - pageMap: asPageMap(read(json, 'initial-page-map')), - memory: asChunks(read(json, 'initial-memory')), - gas: BigInt(read(json, 'initial-gas')), - program: read(json, 'program'), + registers: read(json, "initial-regs").map((x) => BigInt(x)), + pc: read(json, "initial-pc"), + pageMap: asPageMap(read(json, "initial-page-map")), + memory: asChunks(read(json, "initial-memory")), + gas: BigInt(read(json, "initial-gas")), + program: read(json, "program"), }; const result = runVm(input, options.isDebug, options.useSbrkGas); - json['expected-pc'] = result.pc; - json['expected-gas'] = result.gas; - json['expected-status'] = statusAsString(result.status); - json['expected-regs'] = result.registers; - json['expected-page-fault-address'] = result.exitCode; + json["expected-pc"] = result.pc; + json["expected-gas"] = result.gas; + json["expected-status"] = statusAsString(result.status); + json["expected-regs"] = result.registers; + json["expected-page-fault-address"] = result.exitCode; // clear previous buffer - buffer = ''; + buffer = ""; console.log(JSON.stringify(json)); console.log(); @@ -137,28 +142,41 @@ function processJson(data, options) { } // input const input = { - registers: read(data, 'initial-regs').map(x => BigInt(x)), - pc: read(data, 'initial-pc'), - pageMap: asPageMap(read(data, 'initial-page-map')), - memory: asChunks(read(data, 'initial-memory')), - gas: BigInt(read(data, 'initial-gas')), - program: read(data, 'program'), + registers: read(data, "initial-regs").map((x) => BigInt(x)), + pc: read(data, "initial-pc"), + pageMap: asPageMap(read(data, "initial-page-map")), + memory: asChunks(read(data, "initial-memory")), + gas: BigInt(read(data, "initial-gas")), + program: read(data, "program"), }; // expected const expected = { - status: read(data, 'expected-status'), - registers: read(data, 'expected-regs').map(x => BigInt(x)), - pc: read(data, 'expected-pc'), - memory: asChunks(read(data, 'expected-memory')), - gas: BigInt(read(data, 'expected-gas')), - exitCode: read(data, 'expected-page-fault-address', 0), + status: read(data, "expected-status"), + registers: read(data, "expected-regs").map((x) => BigInt(x)), + pc: read(data, "expected-pc"), + memory: asChunks(read(data, "expected-memory")), + gas: BigInt(read(data, "expected-gas")), + exitCode: read(data, "expected-page-fault-address", 0), }; if (options.isDebug) { const assembly = disassemble(input.program, InputKind.Generic); - console.info('==========='); + console.info("==========="); console.info(assembly); - console.info('\n^^^^^^^^^^^\n'); + console.info("\n^^^^^^^^^^^\n"); + } + + if (options.compile) { + const program = compile(input, options.useSbrkGas); + + console.info("\nCompile: AssemblyScript source"); + console.info("==========="); + console.info(program); + const loc = "./assembly/generated-pvm.ts"; + writeFileSync(loc, program); + console.info(`Written down to ${loc}`); + console.info(`Compile using 'npm run compile-pvm'`); + console.info("\n^^^^^^^^^^^\n"); } const result = runVm(input, options.isDebug, options.useSbrkGas); @@ -174,28 +192,29 @@ function processJson(data, options) { } function asChunks(chunks) { - return chunks.map(chunk => { - chunk.data = read(chunk, 'contents'); + return chunks.map((chunk) => { + chunk.data = read(chunk, "contents"); + // biome-ignore lint/performance/noDelete: we don't want to have that key delete chunk.contents; return chunk; }); } function asPageMap(pages) { - return pages.map(page => { - page.access = read(page, 'is-writable') ? 2 : 1; + return pages.map((page) => { + page.access = read(page, "is-writable") ? 2 : 1; return page; }); } function statusAsString(status) { const map = { - 255: 'ok', - 0: 'halt', - 1: 'panic', // panic - 2: 'page-fault', // page fault - 3: 'host', - 4: 'oog' + 255: "ok", + 0: "halt", + 1: "panic", // panic + 2: "page-fault", // page fault + 3: "host", + 4: "oog", }; return map[status] || `unknown(${status})`; @@ -208,12 +227,12 @@ function processFile(options, status, filePath) { const absolutePath = resolve(filePath); // Read the file synchronously - const fileContent = readFileSync(absolutePath, 'utf-8'); + const fileContent = readFileSync(absolutePath, "utf-8"); // Parse the JSON content jsonData = JSON.parse(fileContent); } catch (error) { - status.fail.push({ filePath, name: '' }); + status.fail.push({ filePath, name: "" }); console.error(`Error reading file: ${filePath}`); console.error(error.message); return; @@ -229,4 +248,3 @@ function processFile(options, status, filePath) { console.error(error.message); } } - diff --git a/bin/wrap.js b/bin/wrap.js index 0e48f94..6333ff9 100755 --- a/bin/wrap.js +++ b/bin/wrap.js @@ -2,10 +2,10 @@ // wrap input as program -import {fuzz} from './fuzz.js' - +import { fuzz } from "./fuzz.js"; + const program = fuzz([ - 20,8,0, 0, 0, 0, 255, 255, 255, 255,20,7,0, 0, 0, 0, 1, 0, 0, 0,193,135,9,194,135,10,195,135,11 + 20, 8, 0, 0, 0, 0, 255, 255, 255, 255, 20, 7, 0, 0, 0, 0, 1, 0, 0, 0, 193, 135, 9, 194, 135, 10, 195, 135, 11, ]); console.log(program); diff --git a/biome.jsonc b/biome.jsonc index ea51cea..dd6fccd 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -47,6 +47,7 @@ } }, "files": { - "include": ["assembly/*"] + "include": ["assembly/*", "bin/*"], + "ignore": ["assembly/compile/instructions/*", "assembly/compile/instructions.ts"] } } diff --git a/package.json b/package.json index 570c052..cbdd376 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "asbuild:release": "asc assembly/index.ts --target release", "asbuild:test": "asc assembly/test-run.ts --target test", "cp-build": "rm -rf ./web/build; cp -r ./build ./web/", + "prebuild": "cd ./assembly && ./compile-gen.js", "build": "npm run asbuild && npm run cp-build", + "compile-pvm": "cd assembly && npm exec asc -Osize --runtime=stub --uncheckedBehavior=always --shrinkLevel=z --optimizeLevel=3 ./generated-pvm.ts", "update-version": "node ./web/bump-version.js $GITHUB_SHA", "format": "biome format --write", "lint": "biome lint --write; biome check --write",