Last active
February 20, 2023 15:11
-
-
Save neopunisher/24d22bd0145b06218d2e6d4b16ae2ba4 to your computer and use it in GitHub Desktop.
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
const buf = new ArrayBuffer(maxBufferLen); | |
const dv = new DataView(buf); | |
class MagicWindow { | |
constructor(name, getter, setter) { | |
Object.defineProperty (window, name, { | |
get: getter, | |
set: setter | |
}); | |
} | |
} | |
class Test extends MagicWindow { | |
constructor(dataView, offset, name) { | |
super(name, dataView.getInt8.bind(dataView, offset),dataView.setInt8.bind(dataView, offset)); | |
} | |
} | |
['BigInt64', 'BigUint64', 'Int32', 'Uint32', 'Int16', 'Uint16', 'Int8', 'Uint8', 'Float32', 'Float64'].forEach(function(dataType){ | |
Object.defineProperty (window, dataType, { | |
get: function(){ | |
const getFuncName = `get${dataType}` | |
const setFuncName = `set${dataType}` | |
return ( | |
class extends MagicWindow { | |
constructor(dataView, offset, name) { | |
super(name, dataView[getFuncName].bind(dataView, offset),dataView[setFuncName].bind(dataView, offset)); | |
} | |
} | |
) | |
} | |
}); | |
}) |
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
const importJs = async (url, module = { exports: {} }) => (Function('module', 'exports', await (await fetch(url)).text()).call(module, module, module.exports), module).exports | |
const importWasm = async (url, imports = {}) => { | |
const { instance } = await WebAssembly.instantiateStreaming(await fetch(url), imports); | |
return instance.exports; | |
} | |
const { parseScript } = await importJs('https://cdn.jsdelivr.net/npm/esprima/dist/esprima.js') | |
// String stuff | |
const encodeStr = (str) => new TextEncoder().encode(str) | |
const strLen = (str) => encodeStr(str).length | |
const stoa = (str) => Uint8Array.from(str, x => x.charCodeAt(0)) | |
const atos = (arr) => String.fromCharCode.apply(null, arr) | |
// Base 2 stuff | |
const getBaseLog = (x, y) => (Math.log(y) / Math.log(x)) | |
const baseTwoLog = (y) => getBaseLog(2, y) | |
const powTwo = (x) => Math.pow(2, x) | |
// byte stuff | |
const byteLen = powTwo(3); | |
const maxBufferLen = powTwo(31) - powTwo(21); | |
const maxIntOfBytes = (i) => (powTwo(i * 8) - 1) | |
const maxSignedIntOfBytes = (i) => (powTwo((i * 8) - 1) - 1) | |
//bit stuff | |
const toBits = (idx) => (idx >>> 0).toString(2).padStart(8, '0') | |
const maxIntOfBits = (i) => (powTwo(i) - 1) | |
const byteToBin = (idx) => ((idx >>> 0).toString(2).padStart(8, '0')) | |
// sizes | |
const pageSize = powTwo(12); | |
const onekb = powTwo(10); | |
const onemb = powTwo(20); | |
const onegb = powTwo(30); | |
// hex | |
const hexDigit = (b) => b.toString(16).padStart(2, '0') | |
const bufToHex = (buf) => Array.from(new Uint8Array(buf)).map(hexDigit).join('') | |
const mergeInt = (left, right, bits, littleEndian = false) => { | |
const scale = powTwo(bits * 8); | |
return littleEndian ? left + scale * right : scale * left + right; | |
} | |
const littleEndian = (() => { | |
const buffer = new ArrayBuffer(2); | |
new DataView(buffer).setInt16(0, 256, true /* littleEndian */); | |
return new Int16Array(buffer)[0] === 256; | |
})(); | |
const FloatType = Symbol("Float") | |
const IntegerType = Symbol("Float") | |
const ArrayType = Symbol("Array") | |
const IntType = Symbol("Int") | |
const UintType = Symbol("Uint") | |
const signed = Symbol("signed") | |
const unsigned = Symbol("unsigned") | |
const _8bit = Symbol("8") | |
const _16bit = Symbol("16") | |
const _32bit = Symbol("32") | |
const _64bit = Symbol("64") | |
const Float64 = Symbol("Float64"); | |
const Float32 = Symbol("Float32"); | |
const Str = Symbol("String"); | |
const Bool = Symbol("Boolean"); | |
const Uint8 = Symbol("Uint8"); | |
const Int8 = Symbol("Uint8"); | |
const Uint16 = Symbol("Uint16"); | |
const Int16 = Symbol("Int16"); | |
const Uint32 = Symbol("Uint32"); | |
const Int32 = Symbol("Int32"); | |
const Uint64 = Symbol("Uint64"); | |
const Int64 = Symbol("Int64"); | |
const getter = Symbol("get"); | |
const setter = Symbol("set"); | |
const heap = Symbol("heap"); | |
const stack = Symbol("stack"); | |
const archs = new Set([_8bit, _16bit, _32bit, _64bit]) | |
const floats = new Set([Float32, Float64]); | |
const signedInts = new Set([Int64, Int32, Int16, Int8]); | |
const unsignedInts = new Set([Uint64, Uint32, Uint16, Uint8]); | |
const dataTypes = new Set([Int64, Uint64, Int32, Uint32, Int16, Uint16, Int8, Uint8, Float32, Float64]); | |
const bigDataTypes = new Set([Uint64, Int64]); | |
const literals = { | |
[FloatType]: { | |
[_32bit]: Float32, | |
[_64bit]: Float64 | |
}, | |
[IntegerType]: { | |
[signed]: { | |
[_8bit]: Int8, | |
[_16bit]: Int16, | |
[_32bit]: Int32, | |
[_64bit]: Int64, | |
}, | |
[unsigned]: { | |
[_8bit]: Uint8, | |
[_16bit]: Uint16, | |
[_32bit]: Uint32, | |
[_64bit]: Uint64, | |
} | |
} | |
} | |
const byteArrays = { | |
[FloatType]: { | |
[_32bit]: Float32Array, | |
[_64bit]: Float64Array | |
}, | |
[IntegerType]: { | |
[signed]: { | |
[_8bit]: Int8Array, | |
[_16bit]: Int16Array, | |
[_32bit]: Int32Array, | |
[_64bit]: BigInt64Array, | |
}, | |
[unsigned]: { | |
[_8bit]: Uint8Array, | |
[_16bit]: Uint16Array, | |
[_32bit]: Uint32Array, | |
[_64bit]: BigUint64Array, | |
} | |
} | |
} | |
const byteLengths = { | |
[Int8]: _8bit, | |
[Uint8]: _8bit, | |
[Int16]: _16bit, | |
[Uint16]: _16bit, | |
[Int32]: _32bit, | |
[Uint32]: _32bit, | |
[Int64]: _64bit, | |
[Uint64]: _64bit, | |
[Float32]: _32bit, | |
[Float64]: _64bit, | |
} | |
const strToDataType = { | |
"Int8": Int8, | |
"Uint8": Uint8, | |
"Int16": Int16, | |
"Uint16": Uint16, | |
"Int32": Int32, | |
"Uint32": Uint32, | |
"Int64": Int64, | |
"Uint64": Uint64, | |
"Float32": Float32, | |
"Float64": Float64, | |
} | |
const archToByteLength = { | |
[_8bit]: 1, | |
[_16bit]: 2, | |
[_32bit]: 3, | |
[_64bit]: 4 | |
} | |
const archUint = { | |
[_8bit]: literals[IntegerType][unsigned][_8bit], | |
[_16bit]: literals[IntegerType][unsigned][_16bit], | |
[_32bit]: literals[IntegerType][unsigned][_32bit], | |
[_64bit]: literals[IntegerType][unsigned][_64bit] | |
} | |
const archUintArray = { | |
[_8bit]: byteArrays[IntegerType][unsigned][_8bit], | |
[_16bit]: byteArrays[IntegerType][unsigned][_16bit], | |
[_32bit]: byteArrays[IntegerType][unsigned][_32bit], | |
[_64bit]: byteArrays[IntegerType][unsigned][_64bit] | |
} | |
const strToArch = { | |
"8": _8bit, | |
"16": _16bit, | |
"32": _32bit, | |
"64": _64bit | |
} | |
const bindViewFuncs = (dataView, offset, dataType) => { | |
dataType = (bigDataTypes.has(dataType) ? 'Big' : '') + dataType.description; | |
// console.log('bindView', offset, dataType) | |
const setFunc = dataView[`set${dataType}`].bind(dataView, offset) | |
return [ | |
dataView[`get${dataType}`].bind(dataView, offset, littleEndian), | |
(val) => setFunc(val, littleEndian) | |
]; | |
} | |
class Stack { | |
#arr; | |
constructor (sm) { | |
this.#arr = sm.av; | |
const [stackGetter, stackSetter] = sm.new(archUint[sm.arch])('esp', this.elementByteLength); // stack pointer | |
Object.defineProperty(this, 'sp', { | |
get: stackGetter, | |
set: stackSetter | |
}); | |
this.sp = maxIntOfBytes(this.elementByteLength); | |
sm.push = this.push.bind(this); | |
sm.pop = this.pop.bind(this); | |
} | |
get elementByteLength () { | |
return this.#arr.BYTES_PER_ELEMENT | |
} | |
push (obj) { | |
this.#arr[this.sp--] = obj; | |
} | |
pop () { | |
return this.#arr[++this.sp]; | |
} | |
} | |
class Heap { | |
#arr; | |
#start; | |
constructor (sm, offset) { | |
this.#start = offset; | |
const [heapGetter, heapSetter] = sm.new(archUint[sm.arch])('ehp', 0); // heap pointer | |
Object.defineProperty(this, 'hp', { | |
get: heapGetter, | |
set: heapSetter | |
}); | |
this.#arr = sm.av; | |
sm.jalloc = this.jalloc.bind(this); | |
sm.strAlloc = this.strAlloc.bind(this); | |
} | |
get elementByteLength () { | |
return this.#arr.BYTES_PER_ELEMENT | |
} | |
get start () { | |
return this.#start; | |
} | |
jalloc (size) { | |
const orig = this.hp; | |
this.hp += size; | |
return orig; | |
} | |
jfree (loc, size) { | |
this.hp -= size; | |
} | |
strAlloc (str) { | |
const loc = this.jalloc(strLen(str)); | |
this.#arr.set(encodeStr(str), loc) | |
return loc; | |
} | |
} | |
class Register { | |
#dataType | |
#slot | |
constructor (sm, name, dataType) { | |
this.#dataType = dataType; | |
const slot = this.sm.jalloc(this.byteLength); | |
this.#slot = slot; | |
const [getterFunc, setterFunc] = bindViewFuncs(sm.dv, slot * this.byteLength + sm.heap.start, dataType); | |
Object.defineProperty(sm, name, { | |
get: getterFunc, | |
set: setterFunc | |
}); | |
} | |
get byteLength () { | |
return archToByteLength[byteLengths[this.#dataType]] | |
} | |
get slot () { | |
return this.#slot; | |
} | |
} | |
class StackMachine { | |
#buf; | |
#dv; | |
#av; | |
#arch; | |
#stack; | |
#heap; | |
#inst; | |
#peek; | |
constructor (arch = _16bit, byteLength = maxBufferLen) { | |
this.#arch = arch; | |
const buf = new ArrayBuffer(byteLength); | |
const arrayView = new (archUintArray[arch])(buf, this.elementByteLength * 3); | |
const dataView = new DataView(buf); | |
this.#buf = buf; | |
this.#dv = dataView; | |
this.#av = arrayView; | |
this.#inst = this.getCurrentInstruction(); | |
this.#peek = this.doPeek(); | |
const that = this; | |
const strToFunc = {} | |
dataTypes.forEach(function (dataType) { | |
const func = (name, offset, target = that) => { | |
const [getterFunc, setterFunc] = bindViewFuncs(that.#dv, offset, dataType); | |
Object.defineProperty(target, name, { | |
get: getterFunc, | |
set: setterFunc | |
}); | |
return [getterFunc, setterFunc]; | |
} | |
that[`new_${dataType.description}`] = func; | |
strToFunc[dataType.description] = func; | |
}) | |
this.new = (dataType) => { | |
if (typeof dataType === 'symbol') { | |
dataType = dataType.description | |
} | |
return strToFunc[dataType] | |
} | |
this.new(archUint[arch])('eip', this.elementByteLength * 2) // instruction pointer | |
this.#stack = new Stack(this); | |
this.#heap = new Heap(this, this.elementByteLength * 3); | |
} | |
get elementByteLength () { | |
return archToByteLength[this.#arch] | |
} | |
get buf () { | |
return this.#buf; | |
} | |
get dv () { | |
return this.#dv; | |
} | |
get av () { | |
return this.#av; | |
} | |
get stack () { | |
return this.#stack; | |
} | |
get heap () { | |
return this.#heap; | |
} | |
get arch () { | |
return this.#arch; | |
} | |
get byteLength () { | |
return this.#buf.byteLength | |
} | |
get dataView () { | |
return new Uint16Array(this.#buf, 0, powTwo(this.elementByteLength * 8) + 3) | |
} | |
* getCurrentInstruction () { | |
while (this.eip < this.byteLength) { | |
yield this.#av[this.eip++]; | |
} | |
} | |
* doPeek () { | |
while (this.eip < this.byteLength) { | |
yield this.#av[this.eip + 1]; | |
} | |
} | |
get currentInstruction () { | |
return this.#inst.next().value; | |
} | |
get currentArgument () { | |
return this.#peek.next().value; | |
} | |
step () { | |
switch (this.currentInstruction) { | |
case 0x00: | |
console.log('nop') | |
this.eip++; | |
break; | |
case 0x01: | |
console.log('add') | |
this.r1 = this.r1 + this.r2; | |
this.eip++; | |
break; | |
case 0x02: | |
console.log('sub') | |
this.r1 = this.r1 - this.r2; | |
this.eip++; | |
break; | |
case 0x03: | |
console.log('call') | |
this.push(this.eip + 2); | |
this.eip = this.currentArgument; | |
break; | |
case 0x04: | |
console.log('ret') | |
this.eip = this.pop(); | |
this.eip++; | |
break; | |
case 0x05: | |
console.log('push') | |
this.push(this.r1); | |
this.eip += 2; | |
break; | |
case 0x06: | |
console.log('pop') | |
this.r1 = this.pop(); | |
this.eip++; | |
break; | |
case 0x07: | |
console.log('store') | |
this.r1 = this.currentArgument; | |
this.eip += 2; | |
break; | |
case 0x08: | |
console.log('jmp') | |
this.eip = this.currentArgument; | |
break; | |
case 0x09: | |
console.log('jz') | |
if (this.r1 === 0) { | |
this.eip = this.currentArgument; | |
} else { | |
this.eip += 2; | |
} | |
break; | |
case 0x0A: | |
console.log('jnz') | |
if (this.r1 !== 0) { | |
this.eip = this.currentArgument; | |
} else { | |
this.eip += 2; | |
} | |
break; | |
case 0x0B: | |
console.log('je') | |
if (this.r1 === this.r2) { | |
this.eip = this.currentArgument; | |
} else { | |
this.eip += 2; | |
} | |
break; | |
case 0x0C: | |
console.log('jne') | |
if (this.r1 !== this.r2) { | |
this.eip = this.currentArgument; | |
} else { | |
this.eip += 2; | |
} | |
break; | |
case 0x0D: | |
console.log('load') | |
this.#av[this.currentArgument] = this.r1; | |
this.eip += 2; | |
break; | |
case 0x0E: | |
console.log('mov') | |
this.r1 = this.currentArgument; | |
this.eip += 2; | |
break; | |
case 0x0F: | |
console.log('jalloc') | |
this.r1 = this.jalloc(this.currentArgument); | |
this.eip += 2; | |
break; | |
case 0x10: | |
console.log('jfree') | |
this.jfree(this.currentArgument); | |
this.eip += 2; | |
break; | |
default: | |
console.log() | |
} | |
} | |
} | |
const sm = new StackMachine(); | |
sm.push(420) | |
sm.push(69) | |
slot = sm.jalloc(1) | |
sm.av[slot] = 666; | |
let prog2 = parseScript(` | |
function main(){ | |
var a = 420; | |
var b = 69; | |
var c = a + b; | |
return 69;}`); | |
const TARGET_BYTE_LENGTH = 2; | |
function mergeFunc(funcs, func){ | |
if (func.name in funcs) { | |
if (func.body !== funcs[func.name].body) { | |
throw new Error('Function body mismatch') | |
} | |
if (func.args.length !== funcs[func.name].args.length) { | |
throw new Error('Function argument mismatch') | |
} | |
funcs[func.name].uses = func.uses.concat(func.uses) | |
} else { | |
if (!func.name) { | |
console.error({func}) | |
throw new Error('Function nas no name') | |
} | |
funcs[func.name] = func; | |
} | |
} | |
function mergeFunctions (...mergeFuncs) { | |
return mergeFuncs.reduce((funcs, func) => { | |
if (!func){ | |
return funcs; | |
} else if (!funcs && func && (typeof func === 'object')) { | |
return func; | |
} else { | |
mergeFunc(funcs, func) | |
} | |
return funcs; | |
}, {}) | |
} | |
function mergeVars (...vars) { | |
return vars.reduce((vars, var_) => { | |
if (!var_){ | |
return vars; | |
} else if (!vars && var_ && (typeof var_ === 'object')) { | |
return var_; | |
} else if (var_.name in vars) { | |
if (var_.type !== vars[var_.name].type) { | |
throw new Error('Variable type mismatch') | |
} | |
vars[var_.name].uses = var_.uses.concat(var_.uses) | |
} else { | |
vars[var_.name] = var_; | |
} | |
return vars; | |
}, {}) | |
} | |
function perdictNumericType (val) { | |
if (parseInt(val) !== val) { | |
// test if the float fits into a 32bit float | |
if (val) { | |
return Float32; | |
} else { | |
return Float64; | |
} | |
} else if (val < 0 && val >= maxUnsignedIntOfBytes(1)) { | |
return Int8; | |
} else if (val < 0 && val >= -maxUnsignedIntOfBytes(2)) { | |
return Int16; | |
} else if (val < 0 && val >= -maxUnsignedIntOfBytes(3)) { | |
return Int32; | |
} else if (val < 0 && val >= -maxUnsignedIntOfBytes(4)) { | |
return Int64; | |
} else if (val <= maxIntOfBytes(1)) { | |
return Uint8; | |
} else if (val <= maxIntOfBytes(2)) { | |
return Uint16; | |
} else if (val <= maxIntOfBytes(3)) { | |
return Uint32; | |
} else if (val <= maxIntOfBytes(4)) { | |
return Uint64; | |
} | |
} | |
function perdictType (node) { | |
const nodeType = typeof node; | |
switch (nodeType) { | |
case 'number': | |
return perdictNumericType(node.value); | |
case 'string': | |
return Str; | |
case 'boolean': | |
return Bool; | |
default: | |
console.error({unknown: node}); | |
throw new Error(`Unknown type! ${nodeType}`) | |
} | |
} | |
class Var { | |
#name; | |
#type; | |
#uses; | |
#addr; | |
#arg = -1; | |
#defaultValue; | |
constructor (name, type, uses = [], arg = false, defaultValue = null) { | |
this.#name = name; | |
this.#type = type; | |
this.#uses = uses; | |
this.#defaultValue = defaultValue; | |
this.#arg = arg; | |
} | |
get name () { | |
return this.#name; | |
} | |
get type () { | |
return this.#type; | |
} | |
get uses () { | |
return this.#uses; | |
} | |
set uses (uses) { | |
this.#uses = uses; | |
} | |
get addr () { | |
return this.#addr; | |
} | |
set addr (addr) { | |
if (this.#addr) { | |
throw new Error('Variable address already set') | |
} | |
this.#addr = addr; | |
} | |
get isArg () { | |
return this.#arg !== -1; | |
} | |
get argIndex () { | |
return this.#arg; | |
} | |
get defaultValue () { | |
return this.#defaultValue; | |
} | |
} | |
class Func { | |
#name; | |
#args = {}; | |
#body; | |
#uses = []; | |
#vars; | |
#loc; | |
constructor (name, args, body = [], vars = {}) { | |
this.#name = name; | |
if (typeof args === 'array') { | |
this.#args = mergeVars(...args.map((arg, i) => new Var(arg.name, perdictType(arg.type), [0], i, node.init))); | |
} else if (typeof args === 'object'){ | |
this.#args = args; | |
} | |
this.#body = body; | |
this.#vars = vars; | |
} | |
toBin () { | |
return new Bin(this.#body, {[this.#name]: this}, mergeVars(this.#vars)); | |
} | |
set loc (loc) { | |
if (this.#loc) { | |
throw new Error('Function location already set') | |
} | |
this.#loc = loc; | |
} | |
get loc () { | |
return this.#loc; | |
} | |
get name () { | |
return this.#name; | |
} | |
get args () { | |
return this.#args; | |
} | |
get body () { | |
return this.#body; | |
} | |
get vars () { | |
return this.#vars; | |
} | |
get uses () { | |
return this.#uses; | |
} | |
set uses (uses) { | |
this.#uses = uses; | |
} | |
} | |
function mergeBin (bin, ...bins) { | |
bins.map(bin.mergeBin) | |
return bin | |
} | |
class Bin { | |
#bin = []; | |
#funcs; | |
#vars; | |
constructor (bin = [], funcs = {}, vars = {}) { | |
this.concat(bin); | |
this.#funcs = funcs; | |
this.#vars = vars; | |
this.mergeNode = this.mergeNode.bind(this); | |
this.mergeBin = this.mergeBin.bind(this); | |
} | |
push (val) { | |
if (typeof val === 'Bin') { | |
this.mergeBin(val); | |
} else { | |
this.#bin.push(val); | |
} | |
} | |
concat (arr) { | |
if (typeof arr === 'Bin') { | |
this.mergeBin(arr); | |
} else { | |
this.#bin = this.#bin.concat(arr); | |
} | |
} | |
mergeBin (bin) { | |
this.#bin = this.#bin.concat(bin.raw); | |
this.#funcs = mergeFunctions(this.#funcs, ...Object.values(bin.funcs)); | |
this.#vars = mergeVars(this.#vars, ...Object.values(bin.vars)); | |
} | |
mergeNode (node) { | |
this.mergeBin(astToBin(node)); | |
} | |
get byteLength () { | |
return this.#bin.length; | |
} | |
get elementByteLength () { | |
return TARGET_BYTE_LENGTH; | |
} | |
get raw () { | |
return this.#bin; | |
} | |
get funcs () { | |
return this.#funcs; | |
} | |
get vars () { | |
return this.#vars; | |
} | |
} | |
function astToBin (node) { | |
if (typeof node === 'undefined') debugger; | |
const bin = new Bin(); | |
if (Array.isArray(node)){ | |
return node.map(bin.mergeNode); | |
} | |
switch (node.type) { | |
case 'FunctionDeclaration': | |
console.log('define func:', node.id.name) | |
bin.funcs[node.id.name] = new Func(node.id.name, node.arguments, astToBin(node.body).raw); | |
break; | |
case 'ReturnStatement': | |
if (node.argument) { | |
bin.concat(astToBin(node.argument)) | |
} | |
bin.push(0x04) | |
break; | |
case 'ExpressionStatement': | |
exprs[node.expression] = astToBin(node.expression); | |
break; | |
case 'CallExpression': | |
bin.push(0x03) | |
bin.funcs[node.callee.name].uses.push(bin.byteLength); | |
bin.push(0x00) | |
break; | |
case 'Identifier': | |
bin.push(0x02) | |
bin.push(0x00) | |
break; | |
case 'Literal': | |
bin.push(0x07) | |
bin.push(node.value) | |
switch (typeof node.value) { | |
case 'number': | |
if (node.value > maxIntOfBytes(TARGET_BYTE_LENGTH)) { | |
throw new Error('Number too large') | |
} | |
bin.push(node.value); | |
break; | |
default: | |
throw new Error('Unhandled type'); | |
} | |
break; | |
case 'EmptyStatement': | |
bin.push(0x00) | |
break; | |
case 'BlockStatement': | |
bin.concat(astToBin(node.body)); | |
break; | |
case 'VariableDeclaration': | |
node.declarations.forEach((decl) => { | |
bin.vars[decl.id.name] = new Var(decl.id.name, perdictType(decl.type), [bin.byteLength], -1, decl.init); | |
}) | |
break; | |
case 'Program': | |
bin.concat(astToBin(node.body)) | |
break; | |
default: | |
console.error('Unhandled', node.type, node) | |
} | |
if (bin.funcs.length > 0) { | |
Array.from(funcs.keys()).forEach(function (funcName) { | |
const currIdx = bin.byteLength; | |
bin.funcs[funcName].loc = currIdx; | |
bin = bin.concat(funcs[funcName].raw); | |
}) | |
} | |
return bin; | |
} | |
const bin = astToBin(prog2); // keikaku |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment