diff options
author | Benoit Giannangeli <giann008@gmail.com> | 2017-02-02 22:38:35 +0100 |
---|---|---|
committer | Benoit Giannangeli <benoit.giannangeli@boursorama.fr> | 2017-02-03 07:07:35 +0100 |
commit | 7b844c5caf81ac843a210477ef47fd16e8af2f2c (patch) | |
tree | 5464f83feb24355ebe67f8d0f4b41a1a76227ad0 | |
parent | 9f222971e304d4ec03a3af9015744530f2c8793d (diff) | |
download | fengari-7b844c5caf81ac843a210477ef47fd16e8af2f2c.tar.gz fengari-7b844c5caf81ac843a210477ef47fd16e8af2f2c.tar.bz2 fengari-7b844c5caf81ac843a210477ef47fd16e8af2f2c.zip |
Lua VM
-rw-r--r-- | luac.out | bin | 192 -> 0 bytes | |||
-rw-r--r-- | sandbox/hello.bc.txt | 17 | ||||
-rw-r--r-- | sandbox/hello.js | 3 | ||||
-rw-r--r-- | src/lobject.js | 2 | ||||
-rw-r--r-- | src/lopcodes.js | 109 | ||||
-rw-r--r-- | src/lstate.js | 28 | ||||
-rw-r--r-- | src/lundump.js | 63 | ||||
-rw-r--r-- | src/lvm.js | 24 |
8 files changed, 194 insertions, 52 deletions
diff --git a/luac.out b/luac.out Binary files differdeleted file mode 100644 index 25258bb..0000000 --- a/luac.out +++ /dev/null diff --git a/sandbox/hello.bc.txt b/sandbox/hello.bc.txt new file mode 100644 index 0000000..b41f555 --- /dev/null +++ b/sandbox/hello.bc.txt @@ -0,0 +1,17 @@ +main <sandbox/hello2.lua:0,0> (9 instructions at 0x7ff931403170) +0+ params, 5 slots, 1 upvalue, 2 locals, 4 constants, 1 function + 1 [1] LOADK 0 -1 ; 1 + 2 [2] LOADK 1 -2 ; 2 + 3 [3] GETTABUP 2 0 -3 ; _ENV "print" + 4 [3] MOVE 3 0 + 5 [3] MOVE 4 1 + 6 [3] CALL 2 3 1 + 7 [7] CLOSURE 2 0 ; 0x7ff9314032e0 + 8 [5] SETTABUP 0 -4 2 ; _ENV "c" + 9 [7] RETURN 0 1 + +function <sandbox/hello2.lua:5,7> (3 instructions at 0x7ff9314032e0) +2 params, 3 slots, 0 upvalues, 2 locals, 0 constants, 0 functions + 1 [6] ADD 2 0 1 + 2 [6] RETURN 2 2 + 3 [7] RETURN 0 1
\ No newline at end of file diff --git a/sandbox/hello.js b/sandbox/hello.js index f8f6e15..df32dd8 100644 --- a/sandbox/hello.js +++ b/sandbox/hello.js @@ -2,7 +2,6 @@ const DataView = require('buffer-dataview'); const fs = require('fs'); const BytecodeParser = require("../src/lundump.js"); -const lua_State = require('../src/lstate.js').lua_State; -let p = new BytecodeParser(new lua_State(), new DataView(fs.readFileSync("./sandbox/hello.bc"))) +let p = new BytecodeParser(new DataView(fs.readFileSync("./sandbox/hello.bc"))) p.luaU_undump();
\ No newline at end of file diff --git a/src/lobject.js b/src/lobject.js index 1858fd3..262b38d 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -3,7 +3,7 @@ class LClosure { - constructor(L, n) { + constructor(n) { this.p = null; this.nupvalues = n; } diff --git a/src/lopcodes.js b/src/lopcodes.js new file mode 100644 index 0000000..b7dfb0f --- /dev/null +++ b/src/lopcodes.js @@ -0,0 +1,109 @@ +/*jshint esversion: 6 */ +"use strict"; + +const OpCodes = [ + "OP_MOVE", /* A B R(A) := R(B) */ + "OP_LOADK", /* A Bx R(A) := Kst(Bx) */ + "OP_LOADKX", /* A R(A) := Kst(extra arg) */ + "OP_LOADBOOL", /* A B C R(A) := (Bool)B; if (C) pc++ */ + "OP_LOADNIL", /* A B R(A), R(A+1), ..., R(A+B) := nil */ + "OP_GETUPVAL", /* A B R(A) := UpValue[B] */ + + "OP_GETTABUP", /* A B C R(A) := UpValue[B][RK(C)] */ + "OP_GETTABLE", /* A B C R(A) := R(B)[RK(C)] */ + + "OP_SETTABUP", /* A B C UpValue[A][RK(B)] := RK(C) */ + "OP_SETUPVAL", /* A B UpValue[B] := R(A) */ + "OP_SETTABLE", /* A B C R(A)[RK(B)] := RK(C) */ + + "OP_NEWTABLE", /* A B C R(A) := {} (size = B,C) */ + + "OP_SELF", /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ + + "OP_ADD", /* A B C R(A) := RK(B) + RK(C) */ + "OP_SUB", /* A B C R(A) := RK(B) - RK(C) */ + "OP_MUL", /* A B C R(A) := RK(B) * RK(C) */ + "OP_MOD", /* A B C R(A) := RK(B) % RK(C) */ + "OP_POW", /* A B C R(A) := RK(B) ^ RK(C) */ + "OP_DIV", /* A B C R(A) := RK(B) / RK(C) */ + "OP_IDIV", /* A B C R(A) := RK(B) // RK(C) */ + "OP_BAND", /* A B C R(A) := RK(B) & RK(C) */ + "OP_BOR", /* A B C R(A) := RK(B) | RK(C) */ + "OP_BXOR", /* A B C R(A) := RK(B) ~ RK(C) */ + "OP_SHL", /* A B C R(A) := RK(B) << RK(C) */ + "OP_SHR", /* A B C R(A) := RK(B) >> RK(C) */ + "OP_UNM", /* A B R(A) := -R(B) */ + "OP_BNOT", /* A B R(A) := ~R(B) */ + "OP_NOT", /* A B R(A) := not R(B) */ + "OP_LEN", /* A B R(A) := length of R(B) */ + + "OP_CONCAT", /* A B C R(A) := R(B).. ... ..R(C) */ + + "OP_JMP", /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ + "OP_EQ", /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + "OP_LT", /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + "OP_LE", /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + + "OP_TEST", /* A C if not (R(A) <=> C) then pc++ */ + "OP_TESTSET", /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + + "OP_CALL", /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + "OP_TAILCALL", /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + "OP_RETURN", /* A B return R(A), ... ,R(A+B-2) (see note) */ + + "OP_FORLOOP", /* A sBx R(A)+=R(A+2); + if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/ + "OP_FORPREP", /* A sBx R(A)-=R(A+2); pc+=sBx */ + + "OP_TFORCALL", /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ + "OP_TFORLOOP", /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/ + + "OP_SETLIST", /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ + + "OP_CLOSURE", /* A Bx R(A) := closure(KPROTO[Bx]) */ + + "OP_VARARG", /* A B R(A), R(A+1), ..., R(A+B-2) = vararg */ + + "OP_EXTRAARG" /* Ax extra (larger) argument for previous opcode */ +]; + +const SIZE_C = 9; +const SIZE_B = 9; +const SIZE_Bx = (SIZE_C + SIZE_B); +const SIZE_A = 8; +const SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A); +const SIZE_OP = 6; +const POS_OP = 0; +const POS_A = (POS_OP + SIZE_OP); +const POS_C = (POS_A + SIZE_A); +const POS_B = (POS_C + SIZE_C); +const POS_Bx = POS_C; +const POS_Ax = POS_A; +const MAXARG_Bx = ((1 << SIZE_Bx) - 1); +const MAXARG_sBx = (MAXARG_Bx >> 1); /* 'sBx' is signed */ +const MAXARG_Ax = ((1<<SIZE_Ax)-1); +const MAXARG_A = ((1 << SIZE_A) - 1); +const MAXARG_B = ((1 << SIZE_B) - 1); +const MAXARG_C = ((1 << SIZE_C) - 1); + +module.exports = { + OpCodes: OpCodes, + SIZE_C: SIZE_C, + SIZE_B: SIZE_B, + SIZE_Bx: SIZE_Bx, + SIZE_A: SIZE_A, + SIZE_Ax: SIZE_Ax, + SIZE_OP: SIZE_OP, + POS_OP: POS_OP, + POS_A: POS_A, + POS_C: POS_C, + POS_B: POS_B, + POS_Bx: POS_Bx, + POS_Ax: POS_Ax, + MAXARG_Bx: MAXARG_Bx, + MAXARG_sBx: MAXARG_sBx, + MAXARG_Ax: MAXARG_Ax, + MAXARG_A: MAXARG_A, + MAXARG_B: MAXARG_B, + MAXARG_C: MAXARG_C +};
\ No newline at end of file diff --git a/src/lstate.js b/src/lstate.js deleted file mode 100644 index 979b89c..0000000 --- a/src/lstate.js +++ /dev/null @@ -1,28 +0,0 @@ -/*jshint esversion: 6 */ -"use strict"; - -const thread_status = require('./lua.js').thread_status; - -class lua_State { - - constructor() { - this.next = null; - this.stack = null; - this.ci = null; - this.nci = 0; - this.stacksize = 0; - this.twups = [this]; - this.errorJmp = null; - this.hook = null; - this.allowhook = true; - this.openupval = null; - this.nny = 1; - this.status = thread_status.LUA_OK; - this.errfunc = 0; - } - -} - -module.exports = { - lua_State: lua_State -};
\ No newline at end of file diff --git a/src/lundump.js b/src/lundump.js index 46a9d70..07d86df 100644 --- a/src/lundump.js +++ b/src/lundump.js @@ -1,14 +1,14 @@ /*jshint esversion: 6 */ "use strict"; -const DataView = require('buffer-dataview'); -const fs = require('fs'); -const assert = require('assert'); +const DataView = require('buffer-dataview'); +const fs = require('fs'); +const assert = require('assert'); -const lua_State = require('./lstate.js').lua_State; -const LClosure = require('./lobject.js').LClosure; -const Proto = require('./lfunc.js').Proto; +const LClosure = require('./lobject.js').LClosure; +const Proto = require('./lfunc.js').Proto; const constant_types = require('./lua.js').constant_types; +const OpCodes = require('./lopcodes.js'); const LUAI_MAXSHORTLEN = 40; @@ -24,14 +24,13 @@ class BytecodeParser { * @param {lua_State} Lua state object * @param {DataView} dataView Contains the binary data */ - constructor(L, dataView) { + constructor(dataView) { this.intSize = 4; this.size_tSize = 8; this.instructionSize = 4; this.integerSize = 8; this.numberSize = 8; - this.L = L; this.dataView = dataView; this.offset = 0; } @@ -97,21 +96,44 @@ class BytecodeParser { return string; } + /* creates a mask with 'n' 1 bits at position 'p' */ + static MASK1(n, p) { + return ((~((~0)<<(n)))<<(p)); + } + + /* creates a mask with 'n' 0 bits at position 'p' */ + static MASK0(n, p) { + return (~MASK1(n,p)); + } + readInstruction() { let ins = new DataView(new Buffer(this.instructionSize)) for (let i = 0; i < this.instructionSize; i++) ins.setUint8(i, this.readByte()); - console.log(ins); - - return ins; + return ins.getUint32(0, true); } readCode(f) { let n = this.readInt(); + let o = OpCodes; + let p = BytecodeParser; - for (let i = 0; i < n; i++) - f.code.push(this.readInstruction()); + for (let i = 0; i < n; i++) { + let ins = this.readInstruction(); + f.code[i] = { + code: ins, + opcode: (ins >> o.POS_OP) & p.MASK1(o.SIZE_OP, 0), + A: (ins >> o.POS_A) & p.MASK1(o.SIZE_A, 0), + B: (ins >> o.POS_B) & p.MASK1(o.SIZE_B, 0), + C: (ins >> o.POS_C) & p.MASK1(o.SIZE_C, 0), + Bx: (ins >> o.POS_Bx) & p.MASK1(o.SIZE_Bx, 0), + Ax: (ins >> o.POS_Ax) & p.MASK1(o.SIZE_Ax, 0), + sBx: (ins >> o.POS_Bx) & p.MASK1(o.SIZE_Bx, 0) - o.MAXARG_sBx + }; + + console.log(` [${i}] Op: ${o.OpCodes[f.code[i].opcode]} A: ${f.code[i].A} B: ${f.code[i].B} C: ${f.code[i].C} Ax: ${f.code[i].Ax} Bx: ${f.code[i].Bx} sBx: ${f.code[i].sBx}`); + } } readUpvalues(f) { @@ -144,28 +166,28 @@ class BytecodeParser { type: constant_types.LUA_TNIL, value: null }); - console.log(`LUA_TNIL = ${f.k[f.k.length - 1].value}`); + console.log(` LUA_TNIL = ${f.k[f.k.length - 1].value}`); break; case constant_types.LUA_TBOOLEAN: f.k.push({ type: constant_types.LUA_TBOOLEAN, value: this.readByte() }); - console.log(`LUA_TBOOLEAN = ${f.k[f.k.length - 1].value}`); + console.log(` LUA_TBOOLEAN = ${f.k[f.k.length - 1].value}`); break; case constant_types.LUA_TNUMFLT: f.k.push({ type: constant_types.LUA_TNUMFLT, value: this.readNumber() }); - console.log(`LUA_TNUMFLT = ${f.k[f.k.length - 1].value}`); + console.log(` LUA_TNUMFLT = ${f.k[f.k.length - 1].value}`); break; case constant_types.LUA_TNUMINT: f.k.push({ type: constant_types.LUA_TNUMINT, value: this.readInteger() }); - console.log(`LUA_TNUMINT = ${f.k[f.k.length - 1].value}`); + console.log(` LUA_TNUMINT = ${f.k[f.k.length - 1].value}`); break; case constant_types.LUA_TSHRSTR: case constant_types.LUA_TLNGSTR: @@ -173,7 +195,7 @@ class BytecodeParser { type: constant_types.LUA_TLNGSTR, value: this.readString() }); - console.log(`LUA_TLNGSTR = ${f.k[f.k.length - 1].value}`); + console.log(` LUA_TLNGSTR = ${f.k[f.k.length - 1].value}`); break; default: throw new Error(`unrecognized constant '${t}'`); @@ -284,9 +306,8 @@ class BytecodeParser { luaU_undump() { this.checkHeader(); - let cl = new LClosure(this.L, this.readByte()); - this.L.top++; - cl.p = new Proto(this.L); + let cl = new LClosure(this.readByte()); + cl.p = new Proto(); this.readFunction(cl.p); diff --git a/src/lvm.js b/src/lvm.js new file mode 100644 index 0000000..0ea3342 --- /dev/null +++ b/src/lvm.js @@ -0,0 +1,24 @@ +/*jshint esversion: 6 */ +"use strict"; + +const BytecodeParser = require("./lundump.js"); + +class LuaVM { + + constructor(cl) { + this.cl = cl + } + + execute() { + newframe: + for (;;) { + + } + } + +} + +module.exports = { + LuaVM: LuaVM, + OpCodes: OpCodes +};
\ No newline at end of file |