From 0cbdb3527041d016097aa3384af9c5908af2cce6 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 17 Mar 2017 07:56:18 +0100 Subject: lua_dump, string.dump --- README.md | 3 +-- src/lapi.js | 23 ++++++++++++++++++++ src/ldump.js | 64 +++++++++++++++++++++++++++++++++++--------------------- src/lstrlib.js | 18 ++++++++++++++++ src/lundump.js | 2 +- tests/lstrlib.js | 46 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e8257cc..6de8cab 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ - [x] ... - [ ] lua_arith - [ ] lua_close - - [ ] lua_dump - [ ] lua_gethook - [ ] lua_gethookcount - [ ] lua_gethookmask @@ -99,6 +98,7 @@ - [ ] String - [x] string.byte - [x] string.char + - [x] string.dump - [x] string.format - [x] string.len - [x] string.lower @@ -106,7 +106,6 @@ - [x] string.reverse - [x] string.sub - [x] string.upper - - [ ] string.dump - [ ] string.find - [ ] string.gmatch - [ ] string.gsub diff --git a/src/lapi.js b/src/lapi.js index 96fe207..a51796b 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -4,6 +4,7 @@ const assert = require('assert'); const ldebug = require('./ldebug.js'); const ldo = require('./ldo.js'); +const ldump = require('./ldump.js'); const lfunc = require('./lfunc.js'); const llex = require('./llex.js'); const lobject = require('./lobject.js'); @@ -239,6 +240,18 @@ const lua_pushstring = function (L, s) { return s; }; +// Push string without 8-bit conversion +const lua_pushrawstring = function(L, s) { + if (typeof s !== "string") + L.stack[L.top] = ldo.nil; + else { + L.stack[L.top] = new TValue(CT.LUA_TLNGSTR, s.split('').map(e => e.charCodeAt(0))); + } + L.top++; + assert(L.top <= L.ci.top, "stack overflow"); + return s; +}; + const lua_pushliteral = lua_pushstring; const lua_pushcclosure = function(L, fn, n) { @@ -693,6 +706,14 @@ const lua_load = function(L, reader, data, chunckname, mode) { return status; }; +const lua_dump = function(L, writer, data, strip) { + assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack"); + let o = L.stack[L.top -1]; + if (o.ttisLclosure()) + return ldump.luaU_dump(L, o.p, writer, data, strip); + return 1; +}; + const lua_status = function(L) { return L.status; }; @@ -835,6 +856,7 @@ module.exports.lua_compare_ = lua_compare_; module.exports.lua_concat = lua_concat; module.exports.lua_copy = lua_copy; module.exports.lua_createtable = lua_createtable; +module.exports.lua_dump = lua_dump; module.exports.lua_error = lua_error; module.exports.lua_gc = lua_gc; module.exports.lua_getallocf = lua_getallocf; @@ -871,6 +893,7 @@ module.exports.lua_pushliteral = lua_pushliteral; module.exports.lua_pushlstring = lua_pushlstring; module.exports.lua_pushnil = lua_pushnil; module.exports.lua_pushnumber = lua_pushnumber; +module.exports.lua_pushrawstring = lua_pushrawstring; module.exports.lua_pushstring = lua_pushstring; module.exports.lua_pushthread = lua_pushthread; module.exports.lua_pushtvalue = lua_pushtvalue; diff --git a/src/ldump.js b/src/ldump.js index 795bd19..dd8eb29 100644 --- a/src/ldump.js +++ b/src/ldump.js @@ -10,7 +10,7 @@ const CT = lua.constant_types; const LUAC_DATA = "\x19\x93\r\n\x1a\n"; const LUAC_INT = 0x5678; const LUAC_NUM = 370.5; -const LUAC_VERSION = lua.LUA_VERSION_MAJOR.charCodeAt(0) * 16 + lua.LUA_VERSION_MINOR.charCodeAt(0); +const LUAC_VERSION = Number.parseInt(lua.LUA_VERSION_MAJOR) * 16 + Number.parseInt(lua.LUA_VERSION_MINOR); const LUAC_FORMAT = 0; /* this is the official format */ class DumpState { @@ -28,51 +28,66 @@ const DumpBlock = function(b, size, D) { D.status = D.writer(D.L, b, size, D.data); }; -const DumpVector = function(v, n, D) { - DumpBlock(v, n, D); -}; - const DumpLiteral = function(s, D) { + s = lua.to_luastring(s); DumpBlock(s, s.length, D); }; -const DumpVar = function (x,D) { - DumpVector(x, 1, D); -}; - const DumpByte = function(y, D) { - DumpVar(y, D); + DumpBlock([y], 1, D); }; const DumpInt = function(x, D) { - DumpVar(x, D); + let dv = new DataView(new ArrayBuffer(4)); + dv.setInt32(0, x, true); + let t = []; + for (let i = 0; i < 4; i++) + t.push(dv.getUint8(i, true)); + + DumpBlock(t, 4, D); }; -const DumpInteger = DumpInt; +const DumpInteger = function(x, D) { + let dv = new DataView(new ArrayBuffer(8)); + dv.setInt32(0, x, true); + let t = []; + for (let i = 0; i < 8; i++) + t.push(dv.getUint8(i, true)); + DumpBlock(t, 8, D); +}; const DumpNumber = function(x, D) { - DumpVar(x, D); + let dv = new DataView(new ArrayBuffer(8)); + dv.setFloat64(0, x, true); + let t = []; + for (let i = 0; i < 8; i++) + t.push(dv.getUint8(i, true)); + + DumpBlock(t, 8, D); }; const DumpString = function(s, D) { if (s === null) DumpByte(0, D); else { - let size = s.value.length + 1; - let str = s.value; + let size = s.length + 1; + let str = s; if (size < 0xFF) DumpByte(size, D); else { DumpByte(0xFF, D); - DumpVar(size, D); + DumpInt(size, D); } - DumpVector(str, size - 1, D); /* no need to save '\0' */ + DumpBlock(str, size - 1, D); /* no need to save '\0' */ } }; const DumpCode = function(f, D) { - DumpInt(f.code.length, D); - DumpVector(f.code, f.code.length, D); + let s = f.code.map(e => e.code); + DumpInt(s.length, D); + + for (let i = 0; i < s.length; i++) + DumpInt(s[i], D); }; const DumpConstants = function(f, D) { @@ -120,25 +135,25 @@ const DumpUpvalues = function(f, D) { const DumpDebug = function(f, D) { let n = D.strip ? 0 : f.lineinfo.length; DumpInt(n, D); - DumpVector(f.lineinfo, n, D); + DumpBlock(f.lineinfo, n, D); n = D.strip ? 0 : f.locvars.length; DumpInt(n, D); for (let i = 0; i < n; i++) { - DumpString(f.locvars[i].varname, D); + DumpString(f.locvars[i].varname.value, D); DumpInt(f.locvars[i].startpc, D); DumpInt(f.locvars[i].endpc, D); } n = D.strip ? 0 : f.upvalues.length; DumpInt(n, D); for (let i = 0; i < n; i++) - DumpString(f.upvalues[i].name, D); + DumpString(f.upvalues[i].name.value, D); }; const DumpFunction = function(f, psource, D) { if (D.strip || f.source === psource) DumpString(null, D); /* no debug info or same source as its parent */ else - DumpString(f.source, D); + DumpString(f.source.value, D); DumpInt(f.linedefined, D); DumpInt(f.lastlinedefined, D); DumpByte(f.numparams, D); @@ -155,7 +170,8 @@ const DumpHeader = function(D) { DumpLiteral(lua.LUA_SIGNATURE, D); DumpByte(LUAC_VERSION, D); DumpByte(LUAC_FORMAT, D); - DumpLiteral(LUAC_DATA, D); + let cdata = LUAC_DATA.split('').map(e => e.charCodeAt(0)); + DumpBlock(cdata, cdata.length, D); DumpByte(4, D); // intSize DumpByte(8, D); // size_tSize DumpByte(4, D); // instructionSize diff --git a/src/lstrlib.js b/src/lstrlib.js index b47138f..83e82b0 100644 --- a/src/lstrlib.js +++ b/src/lstrlib.js @@ -48,6 +48,23 @@ const str_char = function(L) { return 1; }; +const writer = function(L, b, size, B) { + assert(Array.isArray(b)); + B.push(...b.slice(0, size)); + return 0; +}; + +const str_dump = function(L) { + let b = []; + let strip = lapi.lua_toboolean(L, 2); + lauxlib.luaL_checktype(L, 1, CT.LUA_TFUNCTION); + lapi.lua_settop(L, 1); + if (lapi.lua_dump(L, writer, b, strip) !== 0) + return lauxlib.luaL_error(L, "unable to dump given function"); + lapi.lua_pushrawstring(L, b.map(e => String.fromCharCode(e)).join('')); + return 1; +}; + const SIZELENMOD = luaconf.LUA_NUMBER_FRMLEN.length; const L_NBFD = 16; // TODO: arbitrary @@ -379,6 +396,7 @@ const str_byte = function(L) { const strlib = { "byte": str_byte, "char": str_char, + "dump": str_dump, "format": str_format, "len": str_len, "lower": str_lower, diff --git a/src/lundump.js b/src/lundump.js index c44c847..70adbec 100644 --- a/src/lundump.js +++ b/src/lundump.js @@ -38,7 +38,7 @@ class BytecodeParser { } peekInteger() { - return this.dataView.getInt32(this.offset, true); + return this.dataView.getInt32(this.offset, true); // TODO: 64b ? } readInteger() { diff --git a/tests/lstrlib.js b/tests/lstrlib.js index 4df4283..aec8e2a 100644 --- a/tests/lstrlib.js +++ b/tests/lstrlib.js @@ -414,4 +414,50 @@ test('string.sub', function (t) { "456", "Correct element(s) on the stack" ); +}); + + +test('string.dump', function (t) { + let luaCodeToDump = ` + local todump = function(p1, p2, p3) + local s = "hello" + local i = 12 + local f = 12.5 + local b = true + + return p1 + p2 + p3 + end`, + luaCode = ` + ${luaCodeToDump} + + return string.dump(todump) + `, L, bytes = []; + + t.plan(3); + + t.doesNotThrow(function () { + + let bc = toByteCode(luaCodeToDump).dataView; + for (let i = 0; i < bc.byteLength; i++) + bytes.push(bc.getUint8(i, true)); + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + lauxlib.luaL_loadstring(L, luaCode); + + }, "Lua program loaded without error"); + + t.doesNotThrow(function () { + + lapi.lua_call(L, 0, -1); + + }, "Lua program ran without error"); + + t.deepEqual( + L.stack[L.top -1].value, + bytes, + "Correct element(s) on the stack" + ); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2