From 0cbdb3527041d016097aa3384af9c5908af2cce6 Mon Sep 17 00:00:00 2001
From: Benoit Giannangeli <benoit.giannangeli@boursorama.fr>
Date: Fri, 17 Mar 2017 07:56:18 +0100
Subject: lua_dump, string.dump

---
 src/lapi.js    | 23 +++++++++++++++++++++
 src/ldump.js   | 64 ++++++++++++++++++++++++++++++++++++----------------------
 src/lstrlib.js | 18 +++++++++++++++++
 src/lundump.js |  2 +-
 4 files changed, 82 insertions(+), 25 deletions(-)

(limited to 'src')

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() {
-- 
cgit v1.2.3-70-g09d2