-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Fix JSON tests. * Add workflow to run JSON tests. * Fix linter. * Update readme.
- Loading branch information
Showing
11 changed files
with
383 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { RELEVANT_ARGS } from "./arguments"; | ||
import { INSTRUCTIONS } from "./instructions"; | ||
import { Interpreter, Status } from "./interpreter"; | ||
import { Memory, MemoryBuilder } from "./memory"; | ||
import { Access, PAGE_SIZE } from "./memory-page"; | ||
import { Program, decodeArguments, decodeProgram } from "./program"; | ||
import { NO_OF_REGISTERS, Registers } from "./registers"; | ||
|
||
export class InitialPage { | ||
address: u32 = 0; | ||
length: u32 = 0; | ||
access: Access = Access.None; | ||
} | ||
export class InitialChunk { | ||
address: u32 = 0; | ||
data: u8[] = []; | ||
} | ||
|
||
export class VmInput { | ||
registers: u32[] = new Array<u32>(NO_OF_REGISTERS).fill(0); | ||
pc: u32 = 0; | ||
gas: i64 = 0; | ||
program: u8[] = []; | ||
pageMap: InitialPage[] = []; | ||
memory: InitialChunk[] = []; | ||
} | ||
|
||
export class VmOutput { | ||
status: Status = Status.OK; | ||
registers: u32[] = []; | ||
pc: u32 = 0; | ||
memory: InitialChunk[] = []; | ||
gas: i64 = 0; | ||
} | ||
|
||
export function getAssembly(p: Program): string { | ||
let v = ""; | ||
const len = p.code.length; | ||
for (let i = 0; i < len; i++) { | ||
if (!p.mask.isInstruction(i)) { | ||
throw new Error("We should iterate only over instructions!"); | ||
} | ||
const instruction = p.code[i]; | ||
const iData = INSTRUCTIONS[instruction]; | ||
v += "\n"; | ||
v += changetype<string>(iData.namePtr); | ||
|
||
const argsLen = p.mask.argsLen(i); | ||
const end = i + 1 + argsLen; | ||
if (end > len) { | ||
const name = changetype<string>(iData.namePtr); | ||
const intro = "Invalid program - code is not long enough"; | ||
throw new Error(`${intro} Expected: ${argsLen} for ${name} at ${i} (${end} > ${len})`); | ||
} | ||
|
||
const args = decodeArguments(iData.kind, p.code.subarray(i + 1, end)); | ||
const argsArray = [args.a, args.b, args.c, args.d]; | ||
const relevantArgs = <i32>RELEVANT_ARGS[iData.kind]; | ||
for (let i = 0; i < relevantArgs; i++) { | ||
v += ` ${argsArray[i]}, `; | ||
} | ||
i += argsLen; | ||
} | ||
return v; | ||
} | ||
|
||
export function runVm(input: VmInput, logs: boolean = false): VmOutput { | ||
const p = decodeProgram(input.program); | ||
|
||
const registers: Registers = new StaticArray(NO_OF_REGISTERS); | ||
for (let r = 0; r < registers.length; r++) { | ||
registers[r] = input.registers[r]; | ||
} | ||
const memory = buildMemory(input.pageMap, input.memory); | ||
|
||
const int = new Interpreter(p, registers, memory); | ||
int.nextPc = -1; | ||
int.gas.set(input.gas); | ||
|
||
let isOk = true; | ||
for (;;) { | ||
if (!isOk) { | ||
if (logs) console.log(`Finished with status: ${int.status}`); | ||
break; | ||
} | ||
|
||
if (logs) console.log(`PC = ${int.pc}`); | ||
if (logs) console.log(`STATUS = ${int.status}`); | ||
if (logs) console.log(`REGISTERS = ${registers.join(", ")}`); | ||
if (logs && int.pc < u32(int.program.code.length)) { | ||
const name = changetype<string>(INSTRUCTIONS[int.program.code[int.pc]].namePtr); | ||
console.log(`INSTRUCTION = ${name} (${int.program.code[int.pc]})`); | ||
} | ||
|
||
isOk = int.nextStep(); | ||
} | ||
const output = new VmOutput(); | ||
output.status = int.status; | ||
output.registers = int.registers.slice(0); | ||
output.pc = int.pc; | ||
output.gas = int.gas.get(); | ||
output.memory = getOutputChunks(int.memory); | ||
return output; | ||
} | ||
|
||
export function getOutputChunks(memory: Memory): InitialChunk[] { | ||
const chunks: InitialChunk[] = []; | ||
const pages = memory.pages.keys(); | ||
let currentChunk: InitialChunk | null = null; | ||
for (let i = 0; i < pages.length; i++) { | ||
const pageIdx = pages[i]; | ||
const page = memory.pages.get(pageIdx); | ||
|
||
for (let n = 0; n < page.raw.data.length; n++) { | ||
const v = page.raw.data[n]; | ||
if (v !== 0) { | ||
if (currentChunk !== null) { | ||
currentChunk.data.push(v); | ||
} else { | ||
currentChunk = new InitialChunk(); | ||
currentChunk.address = pageIdx * PAGE_SIZE + n; | ||
currentChunk.data = [v]; | ||
} | ||
} else if (currentChunk !== null) { | ||
chunks.push(currentChunk); | ||
currentChunk = null; | ||
} | ||
} | ||
} | ||
if (currentChunk !== null) { | ||
chunks.push(currentChunk); | ||
} | ||
return chunks; | ||
} | ||
|
||
export function buildMemory(pages: InitialPage[], chunks: InitialChunk[]): Memory { | ||
const builder = new MemoryBuilder(); | ||
for (let i = 0; i < pages.length; i++) { | ||
const initPage = pages[i]; | ||
builder.setData(initPage.access, initPage.address, new Uint8Array(initPage.length)); | ||
} | ||
|
||
for (let i = 0; i < chunks.length; i++) { | ||
const initChunk = chunks[i]; | ||
// access should not matter now, since we created the pages already. | ||
const data = new Uint8Array(initChunk.data.length); | ||
for (let i = 0; i < data.length; i++) { | ||
data[i] = initChunk.data[i]; | ||
} | ||
builder.setData(Access.None, initChunk.address, data); | ||
} | ||
|
||
return builder.build(0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,19 @@ | ||
import { Interpreter } from "./interpreter"; | ||
import { decodeProgram, getAssembly } from "./program"; | ||
import { NO_OF_REGISTERS, Registers } from "./registers"; | ||
import { VmInput, getAssembly, runVm } from "./api-generic"; | ||
import { decodeProgram } from "./program"; | ||
|
||
export * from "./api"; | ||
export { runVm } from "./api-generic"; | ||
|
||
export function exampleGetAssembly(program: u8[]): string { | ||
const p = decodeProgram(program); | ||
console.log(`Got program: ${p.toString()}`); | ||
return getAssembly(p); | ||
} | ||
|
||
export function exampleRun(program: u8[]): void { | ||
const p = decodeProgram(program); | ||
const registers: Registers = new StaticArray(NO_OF_REGISTERS); | ||
registers[7] = 9; | ||
const int = new Interpreter(p, registers); | ||
int.gas.set(10_000); | ||
|
||
let isOk = true; | ||
for (;;) { | ||
if (!isOk) { | ||
console.log(`Finished with status: ${int.status}`); | ||
break; | ||
} | ||
|
||
console.log(`PC = ${int.pc}`); | ||
console.log(`STATUS = ${int.status}`); | ||
console.log(`REGISTERS = ${registers.join(", ")}`); | ||
|
||
isOk = int.nextStep(); | ||
} | ||
const input = new VmInput(); | ||
input.registers[7] = 9; | ||
input.gas = 10_000; | ||
input.program = program; | ||
const output = runVm(input, true); | ||
console.log(`Finished with status: ${output.status}`); | ||
} |
Oops, something went wrong.