Browse Source

Add -b command line option to save/list bytecode.

Mike Pall 14 years ago
parent
commit
0eee70cd4d
4 changed files with 198 additions and 16 deletions
  1. 1 1
      Makefile
  2. 26 1
      doc/running.html
  3. 125 0
      lib/bcsave.lua
  4. 46 14
      src/luajit.c

+ 1 - 1
Makefile

@@ -76,7 +76,7 @@ FILE_SO= libluajit.so
 FILE_MAN= luajit.1
 FILE_PC= luajit.pc
 FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h
-FILES_JITLIB= bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua vmdef.lua
+FILES_JITLIB= bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua bcsave.lua vmdef.lua
 
 ifeq (,$(findstring Windows,$(OS)))
   ifeq (Darwin,$(shell uname -s))

+ 26 - 1
doc/running.html

@@ -106,9 +106,34 @@ prints a short list of the available options. Please have a look at the
 for details.
 </p>
 <p>
-Two additional options control the behavior of LuaJIT:
+LuaJIT has some additional options:
 </p>
 
+<h3 id="opt_b"><tt>-b[options] input output</tt></h3>
+<p>
+This option saves or lists bytecode. The following additional options
+are accepted:
+</p>
+<ul>
+<li><tt>-l</tt> &mdash; Only list bytecode.</li>
+<li><tt>-s</tt> &mdash; Strip debug info (this is the default).</li>
+<li><tt>-g</tt> &mdash; Keep debug info.</li>
+<li><tt>-e chunk</tt> &mdash; Use chunk string as input.</li>
+<li><tt>-</tt> (a single minus sign) &mdash; Use stdin as input and/or stdout as output.</li>
+</ul>
+<p>
+Typical usage examples:
+</p>
+<pre class="code">
+luajit -b test.lua test.out                     # Save to test.out
+luajit -bg test.lua test.out                    # Keep debug info
+luajit -be "print('hello world') end" test.out  # Save cmdline script
+
+luajit -bl test.lua                             # List to stdout
+luajit -bl test.lua test.txt                    # List to test.txt
+luajit -ble "print('hello world') end"          # List cmdline script
+</pre>
+
 <h3 id="opt_j"><tt>-j cmd[=arg[,arg...]]</tt></h3>
 <p>
 This option performs a LuaJIT control command or activates one of the

+ 125 - 0
lib/bcsave.lua

@@ -0,0 +1,125 @@
+----------------------------------------------------------------------------
+-- LuaJIT module to save/list bytecode.
+--
+-- Copyright (C) 2005-2011 Mike Pall. All rights reserved.
+-- Released under the MIT/X license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module saves or lists the bytecode for an input file.
+-- It's run by the -b command line option.
+--
+------------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
+
+------------------------------------------------------------------------------
+
+local function usage()
+  io.stderr:write[[
+Save LuaJIT bytecode: luajit -b[options] input output
+  -l        Only list bytecode.
+  -s        Strip debug info (default).
+  -g        Keep debug info.
+  -e chunk  Use chunk string as input.
+  --        Stop handling options.
+  -         Use stdin as input and/or stdout as output.
+]]
+  os.exit(1)
+end
+
+local function readfile(input)
+  if type(input) == "function" then return input end
+  if input == "-" then input = nil end
+  local f, err = loadfile(input)
+  if not f then
+    io.stderr:write("luajit: ", err, "\n")
+    os.exit(1)
+  end
+  return f
+end
+
+local function readstring(input)
+  local f, err = loadstring(input)
+  if not f then
+    io.stderr:write("luajit: ", err, "\n")
+    os.exit(1)
+  end
+  return f
+end
+
+local function savefile(name, mode)
+  if name == "-" then return io.stdout end
+  local fp, err = io.open(name, mode)
+  if not fp then
+    io.stderr:write("luajit: cannot write ", err, "\n")
+    os.exit(1)
+  end
+  return fp
+end
+
+------------------------------------------------------------------------------
+
+local function bclist(input, output)
+  local f = readfile(input)
+  require("jit.bc").dump(f, savefile(output, "w"), true)
+end
+
+local function bcsave(input, output, strip)
+  local f = readfile(input)
+  local s = string.dump(f, strip)
+  local fp = savefile(output, "wb")
+  local ok, err = fp:write(s)
+  if ok and output ~= "-" then ok, err = fp:close() end
+  if not ok then
+    io.stderr:write("luajit: cannot write ", arg[2], ": ", err, "\n")
+    os.exit(1)
+  end
+end
+
+local function docmd(...)
+  local arg = {...}
+  local n = 1
+  local list = false
+  local strip = true
+  while n <= #arg do
+    local a = arg[n]
+    if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then
+      if a == "--" then table.remove(arg, n); break end
+      for m=2,#a do
+	local opt = string.sub(a, m, m)
+	if opt == "l" then
+	  list = true
+	elseif opt == "s" then
+	  strip = true
+	elseif opt == "g" then
+	  strip = false
+	elseif opt == "e" then
+	  if n ~= 1 or #arg < 2 or m ~= #a then usage() end
+	  arg[2] = readstring(arg[2])
+	else
+	  usage()
+	end
+      end
+      table.remove(arg, n)
+    else
+      n = n + 1
+    end
+  end
+  if list then
+    if #arg == 0 or #arg > 2 then usage() end
+    bclist(arg[1], arg[2] or "-")
+  else
+    if #arg ~= 2 then usage() end
+    bcsave(arg[1], arg[2], strip)
+  end
+end
+
+------------------------------------------------------------------------------
+
+-- Public module functions.
+module(...)
+
+start = docmd -- Process -b command line option.
+

+ 46 - 14
src/luajit.c

@@ -61,6 +61,7 @@ static void print_usage(void)
   "Available options are:\n"
   "  -e chunk  Execute string " LUA_QL("chunk") ".\n"
   "  -l name   Require library " LUA_QL("name") ".\n"
+  "  -b ...    Save or list bytecode.\n"
   "  -j cmd    Perform LuaJIT control command.\n"
   "  -O[opt]   Control LuaJIT optimizations.\n"
   "  -i        Enter interactive mode after executing " LUA_QL("script") ".\n"
@@ -288,7 +289,7 @@ static int handle_script(lua_State *L, char **argv, int n)
 }
 
 /* Load add-on module. */
-static int loadjitmodule(lua_State *L, const char *notfound)
+static int loadjitmodule(lua_State *L)
 {
   lua_getglobal(L, "require");
   lua_pushliteral(L, "jit.");
@@ -298,7 +299,8 @@ static int loadjitmodule(lua_State *L, const char *notfound)
     const char *msg = lua_tostring(L, -1);
     if (msg && !strncmp(msg, "module ", 7)) {
     err:
-      l_message(progname, notfound);
+      l_message(progname,
+		"unknown luaJIT command or jit.* modules not installed");
       return 1;
     } else {
       return report(L, 1);
@@ -345,7 +347,7 @@ static int dojitcmd(lua_State *L, const char *cmd)
   lua_gettable(L, -2);  /* Lookup library function. */
   if (!lua_isfunction(L, -1)) {
     lua_pop(L, 2);  /* Drop non-function and jit.* table, keep module name. */
-    if (loadjitmodule(L, "unknown luaJIT command"))
+    if (loadjitmodule(L))
       return 1;
   } else {
     lua_remove(L, -2);  /* Drop jit.* table. */
@@ -365,16 +367,38 @@ static int dojitopt(lua_State *L, const char *opt)
   return runcmdopt(L, opt);
 }
 
+/* Save or list bytecode. */
+static int dobytecode(lua_State *L, char **argv)
+{
+  int narg = 0;
+  lua_pushliteral(L, "bcsave");
+  if (loadjitmodule(L))
+    return 1;
+  if (argv[0][2]) {
+    narg++;
+    argv[0][1] = '-';
+    lua_pushstring(L, argv[0]+1);
+  }
+  for (argv++; *argv != NULL; narg++, argv++)
+    lua_pushstring(L, *argv);
+  return report(L, lua_pcall(L, narg, 0, 0));
+}
+
 /* check that argument has no extra characters at the end */
 #define notail(x)	{if ((x)[2] != '\0') return -1;}
 
-static int collectargs(char **argv, int *pi, int *pv, int *pe)
+#define FLAGS_INTERACTIVE	1
+#define FLAGS_VERSION		2
+#define FLAGS_EXEC		4
+#define FLAGS_OPTION		8
+
+static int collectargs(char **argv, int *flags)
 {
   int i;
   for (i = 1; argv[i] != NULL; i++) {
-    if (argv[i][0] != '-')  /* not an option? */
+    if (argv[i][0] != '-')  /* Not an option? */
       return i;
-    switch (argv[i][1]) {  /* option */
+    switch (argv[i][1]) {  /* Check option. */
     case '-':
       notail(argv[i]);
       return (argv[i+1] != NULL ? i+1 : 0);
@@ -382,21 +406,27 @@ static int collectargs(char **argv, int *pi, int *pv, int *pe)
       return i;
     case 'i':
       notail(argv[i]);
-      *pi = 1;  /* go through */
+      *flags |= FLAGS_INTERACTIVE;
+      /* fallthrough */
     case 'v':
       notail(argv[i]);
-      *pv = 1;
+      *flags |= FLAGS_VERSION;
       break;
     case 'e':
-      *pe = 1;  /* go through */
+      *flags |= FLAGS_EXEC;
     case 'j':  /* LuaJIT extension */
     case 'l':
+      *flags |= FLAGS_OPTION;
       if (argv[i][2] == '\0') {
 	i++;
 	if (argv[i] == NULL) return -1;
       }
       break;
     case 'O': break;  /* LuaJIT extension */
+    case 'b':  /* LuaJIT extension */
+      if (*flags) return -1;
+      *flags |= FLAGS_EXEC;
+      return 0;
     default: return -1;  /* invalid option */
     }
   }
@@ -438,6 +468,8 @@ static int runargs(lua_State *L, char **argv, int n)
       if (dojitopt(L, argv[i] + 2))
 	return 1;
       break;
+    case 'b':  /* LuaJIT extension */
+      return dobytecode(L, argv+i);
     default: break;
     }
   }
@@ -466,7 +498,7 @@ static int pmain(lua_State *L)
   struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
   char **argv = s->argv;
   int script;
-  int has_i = 0, has_v = 0, has_e = 0;
+  int flags = 0;
   globalL = L;
   if (argv[0] && argv[0][0]) progname = argv[0];
   LUAJIT_VERSION_SYM();  /* linker-enforced version check */
@@ -475,22 +507,22 @@ static int pmain(lua_State *L)
   lua_gc(L, LUA_GCRESTART, -1);
   s->status = handle_luainit(L);
   if (s->status != 0) return 0;
-  script = collectargs(argv, &has_i, &has_v, &has_e);
+  script = collectargs(argv, &flags);
   if (script < 0) {  /* invalid args? */
     print_usage();
     s->status = 1;
     return 0;
   }
-  if (has_v) print_version();
+  if ((flags & FLAGS_VERSION)) print_version();
   s->status = runargs(L, argv, (script > 0) ? script : s->argc);
   if (s->status != 0) return 0;
   if (script)
     s->status = handle_script(L, argv, script);
   if (s->status != 0) return 0;
-  if (has_i) {
+  if ((flags & FLAGS_INTERACTIVE)) {
     print_jit_status(L);
     dotty(L);
-  } else if (script == 0 && !has_e && !has_v) {
+  } else if (script == 0 && !(flags & (FLAGS_EXEC|FLAGS_VERSION))) {
     if (lua_stdin_is_tty()) {
       print_version();
       print_jit_status(L);