Last active
January 27, 2024 04:15
-
-
Save bhelx/c3533d6e1d0ec6aa71da2fae51f09a2b to your computer and use it in GitHub Desktop.
Revisions
-
bhelx revised this gist
Jan 27, 2024 . 1 changed file with 0 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -121,9 +121,6 @@ class Parser { } parseFunctionSection(sectionSize) { this.seek(sectionSize); return { -
bhelx revised this gist
Jan 27, 2024 . 1 changed file with 36 additions and 49 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -9,18 +9,25 @@ class Parser { MAGIC_BYTES = Buffer.from([0x00, 0x61, 0x73, 0x6d]); MOD_VERSION = Buffer.from([0x01, 0x00, 0x00, 0x00]); constructor(data) { this.data = data; this.index = 0; } peekBytes(n) { return this.data.subarray(this.index, this.index + n); } consumeBytes(n) { let result = this.peekBytes(n) this.seek(n) return result } consumeByte() { const result = this.data[this.index]; this.seek(1) return result } seek(n) { @@ -33,23 +40,22 @@ class Parser { return this.index >= this.data.length; } consumeU32() { const bytes = this.peekBytes(MAX_NUMBER_OF_BYTE_U32); const result = decodeUInt32(bytes); this.seek(result.nextIndex) return result.value } parseModule() { const magic = this.consumeBytes(4); if (!magic.equals(this.MAGIC_BYTES)) { throw Error("Magic bytes not found: " + magic); } const version = this.consumeBytes(4); if (!version.equals(this.MOD_VERSION)) { throw Error(`Module version did not magtch : ${version}`); } const sections = []; while (!this.endOfFile()) { const section = this.parseSection(); @@ -63,10 +69,8 @@ class Parser { } parseSection() { const id = this.consumeByte(); let sectionSize = this.consumeU32(); switch (id) { case 0x01: return this.parseTypeSection(sectionSize); @@ -82,33 +86,27 @@ class Parser { } parseTypeSection(sectionSize) { var numTypes = this.consumeU32(); let funcTypes = []; for (var i = 0; i < numTypes; i++) { var headerByte = this.consumeU32(); if (headerByte != 0x60) { throw Error("Expected 0x60 header byte got " + headerByte); } var numFuncElements = this.consumeU32(); const funcSig = { params: [], returns: [], }; // params for (var j = 0; j < numFuncElements; j++) { var valueType = this.consumeU32(); funcSig.params.push(valueType); } var numFuncElements = this.consumeU32(); // returns for (var j = 0; j < numFuncElements; j++) { var valueType = this.consumeU32(); funcSig.returns.push(valueType); } funcTypes.push({ @@ -134,19 +132,14 @@ class Parser { } parseExportSection(sectionSize) { var numExports = this.consumeU32(); const exports = []; for (let i = 0; i < numExports; i++) { var strLen = this.consumeU32(); const bytes = this.consumeBytes(strLen); const name = new TextDecoder().decode(bytes); const type = this.consumeByte(); const idx = this.consumeByte(); exports.push({ name, desc: { @@ -163,30 +156,24 @@ class Parser { } parseCodeSection(sectionSize) { var numCodes = this.consumeU32(); const functions = []; for (let i = 0; i < numCodes; i++) { var codeSize = this.consumeU32(); var funcIndex = this.consumeU32(); const end = this.index + codeSize - 1; const instructions = []; while (this.index < end) { const instr = { opcode: this.consumeByte(), operands: [] }; switch (instr.opcode) { case 0x20: instr.name = "local.get"; instr.operands.push(this.consumeByte()); break; case 0x41: instr.name = "i32.const"; instr.operands.push(this.consumeByte()); break; case 0x6a: instr.name = "i32.add"; -
bhelx revised this gist
Nov 13, 2022 . No changes.There are no files selected for viewing
-
bhelx revised this gist
Nov 13, 2022 . No changes.There are no files selected for viewing
-
bhelx revised this gist
Nov 13, 2022 . No changes.There are no files selected for viewing
-
bhelx created this gist
Nov 13, 2022 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,12 @@ (module (func (export "add41") (param $n i32) (result i32) i32.const 41 local.get $n i32.add ) ) This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,265 @@ const { decodeUInt32, MAX_NUMBER_OF_BYTE_U32, } = require("@webassemblyjs/leb128"); const fs = require("fs"); class Parser { MAGIC_BYTES = Buffer.from([0x00, 0x61, 0x73, 0x6d]); MOD_VERSION = Buffer.from([0x01, 0x00, 0x00, 0x00]); constructor(data) { this.data = data; this.index = 0; } readBytes(n) { return this.data.subarray(this.index, this.index + n); } readByte() { return this.data[this.index]; } seek(n) { if (this.index + n > this.data.length) throw Error("Tring to seek past EOF"); this.index += n; } endOfFile() { return this.index >= this.data.length; } readU32() { const bytes = this.readBytes(MAX_NUMBER_OF_BYTE_U32); const result = decodeUInt32(bytes); return [result.value, result.nextIndex]; } parseModule() { const magic = this.readBytes(4); if (!magic.equals(this.MAGIC_BYTES)) { throw Error("Magic bytes not found: " + magic); } this.seek(4); const version = this.readBytes(4); if (!version.equals(this.MOD_VERSION)) { throw Error(`Module version did not magtch : ${version}`); } this.seek(4); const sections = []; while (!this.endOfFile()) { const section = this.parseSection(); sections.push(section); } return { type: "Module", sections, }; } parseSection() { const id = this.readByte(); this.seek(1); let [sectionSize, nextIndex] = this.readU32(); this.seek(nextIndex); switch (id) { case 0x01: return this.parseTypeSection(sectionSize); case 0x03: return this.parseFunctionSection(sectionSize); case 0x07: return this.parseExportSection(sectionSize); case 0x0A: return this.parseCodeSection(sectionSize); default: throw Error("Unknown section id: " + id); } } parseTypeSection(sectionSize) { var [numTypes, nextIndex] = this.readU32(); this.seek(nextIndex); let funcTypes = []; for (var i = 0; i < numTypes; i++) { var [headerByte, nextIndex] = this.readU32(); this.seek(nextIndex); if (headerByte != 0x60) { throw Error("Expected 0x60 header byte got " + headerByte); } var [numFuncElements, nextIndex] = this.readU32(); this.seek(nextIndex); const funcSig = { params: [], returns: [], }; // params for (var j = 0; j < numFuncElements; j++) { var [valueType, nextIndex] = this.readU32(); this.seek(nextIndex); funcSig.params.push(valueType); } var [numFuncElements, nextIndex] = this.readU32(); this.seek(nextIndex); // returns for (var j = 0; j < numFuncElements; j++) { var [valueType, nextIndex] = this.readU32(); this.seek(nextIndex); funcSig.returns.push(valueType); } funcTypes.push({ funcSig, }); } return { type: "TypeSection", funcTypes, }; } parseFunctionSection(sectionSize) { // var [numTypes, nextIndex] = this.readU32() // this.seek(nextIndex) // console.log({sectionSize, numTypes}) this.seek(sectionSize); return { type: "FunctionSection", }; } parseExportSection(sectionSize) { var [numExports, nextIndex] = this.readU32(); this.seek(nextIndex); const exports = []; for (let i = 0; i < numExports; i++) { var [strLen, nextIndex] = this.readU32(); this.seek(nextIndex); const bytes = this.readBytes(strLen); const name = new TextDecoder().decode(bytes); this.seek(strLen); const type = this.readByte(); this.seek(1); const idx = this.readByte(); this.seek(1); exports.push({ name, desc: { type, idx, }, }); } return { type: "ExportSection", exports, }; } parseCodeSection(sectionSize) { var [numCodes, nextIndex] = this.readU32(); this.seek(nextIndex); const functions = []; for (let i = 0; i < numCodes; i++) { var [codeSize, nextIndex] = this.readU32(); this.seek(nextIndex); var [funcIndex, nextIndex] = this.readU32(); this.seek(nextIndex); const end = this.index + codeSize - 1; const instructions = []; while (this.index < end) { const instr = { opcode: this.readByte(), operands: [] }; this.seek(1); switch (instr.opcode) { case 0x20: instr.name = "local.get"; instr.operands.push(this.readByte()); this.seek(1); break; case 0x41: instr.name = "i32.const"; instr.operands.push(this.readByte()); this.seek(1); break; case 0x6a: instr.name = "i32.add"; break; case 0x0b: instr.name = "end"; break; default: throw Error("Opcode not supported yet: " + instr.opcode); } instructions.push(instr); } functions.push({ funcIndex, instructions, }); } return { type: "CodeSection", functions, }; } } class WamMachine { constructor() { this.stack = []; } run(code, args) { let ret = undefined; code.forEach((c) => { switch (c.opcode) { case 0x20: this.stack.push(args[c.operands[0]]); break; case 0x41: this.stack.push(c.operands[0]); break; case 0x6a: let a = this.stack.pop(); let b = this.stack.pop(); this.stack.push(a + b); break; case 0x0b: ret = this.stack.pop(); break; } }); return ret; } } class WamModule { constructor(file) { const data = fs.readFileSync(file); this.module = new Parser(data).parseModule(); this.machine = new WamMachine(); this.exports = {}; const exportSec = this.module.sections.find( (s) => s.type === "ExportSection" ); const codeSec = this.module.sections.find((s) => s.type === "CodeSection"); //const typeSec = this.module.sections.find(s => s.type === "TypeSection") exportSec.exports.forEach((fe) => { const instructions = codeSec.functions[fe.desc.idx].instructions; //const funcType = typSec.funcTypes[fe.desc.type] this.exports[fe.name] = (...args) => { return this.machine.run(instructions, args); }; }); } } const mod = new WamModule(process.argv[2]); const add41 = mod.exports.add41; console.log(add41(1)); // prints 42