Browse Source

Add support for bytecode loading/saving.

Mike Pall 14 years ago
parent
commit
4994fcc32c
16 changed files with 1019 additions and 48 deletions
  1. 0 5
      doc/status.html
  2. 1 1
      src/Makefile
  3. 21 14
      src/Makefile.dep
  4. 17 2
      src/lib_string.c
  5. 10 5
      src/lj_api.c
  6. 66 0
      src/lj_bcdump.h
  7. 466 0
      src/lj_bcread.c
  8. 388 0
      src/lj_bcwrite.c
  9. 5 2
      src/lj_errmsg.h
  10. 32 5
      src/lj_func.c
  11. 1 1
      src/lj_func.h
  12. 1 1
      src/lj_gc.c
  13. 3 6
      src/lj_lex.c
  14. 1 1
      src/lj_lex.h
  15. 5 5
      src/lj_obj.h
  16. 2 0
      src/ljamalg.c

+ 0 - 5
doc/status.html

@@ -114,11 +114,6 @@ hooks for non-Lua functions) and shows slightly different behavior
 (no per-coroutine hooks, no tail call counting).
 (no per-coroutine hooks, no tail call counting).
 </li>
 </li>
 <li>
 <li>
-<b>Bytecode</b> currently cannot be loaded or dumped. Note that
-the bytecode format differs from Lua&nbsp;5.1 &mdash; loading foreign
-bytecode is not supported at all.
-</li>
-<li>
 Some of the <b>configuration options</b> of Lua&nbsp;5.1 are not supported:
 Some of the <b>configuration options</b> of Lua&nbsp;5.1 are not supported:
 <ul>
 <ul>
 <li>The <b>number type</b> cannot be changed (it's always a <tt>double</tt>).</li>
 <li>The <b>number type</b> cannot be changed (it's always a <tt>double</tt>).</li>

+ 1 - 1
src/Makefile

@@ -360,7 +360,7 @@ LJLIB_C= $(LJLIB_O:.o=.c)
 LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \
 LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \
 	  lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
 	  lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
 	  lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_api.o \
 	  lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_api.o \
-	  lj_lex.o lj_parse.o \
+	  lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o \
 	  lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
 	  lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
 	  lj_opt_dce.o lj_opt_loop.o lj_opt_split.o \
 	  lj_opt_dce.o lj_opt_loop.o lj_opt_split.o \
 	  lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
 	  lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \

+ 21 - 14
src/Makefile.dep

@@ -42,7 +42,8 @@ lib_package.o: lib_package.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
  lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h
  lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h
 lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
 lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
  lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h \
  lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h \
- lj_state.h lj_ff.h lj_ffdef.h lj_char.h lj_lib.h lj_libdef.h
+ lj_state.h lj_ff.h lj_ffdef.h lj_bcdump.h lj_lex.h lj_char.h lj_lib.h \
+ lj_libdef.h
 lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
 lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
  lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_lib.h \
  lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_lib.h \
  lj_libdef.h
  lj_libdef.h
@@ -50,7 +51,7 @@ lj_alloc.o: lj_alloc.c lj_def.h lua.h luaconf.h lj_arch.h lj_alloc.h
 lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
 lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
  lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \
  lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \
  lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
  lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
- lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_parse.h
+ lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h
 lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
 lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
  lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \
  lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \
  lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \
  lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \
@@ -58,6 +59,12 @@ lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
  lj_asm_*.h
  lj_asm_*.h
 lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
 lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
  lj_bcdef.h
  lj_bcdef.h
+lj_bcread.o: lj_bcread.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
+ lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_bc.h lj_ctype.h \
+ lj_cdata.h lj_lex.h lj_bcdump.h lj_state.h
+lj_bcwrite.o: lj_bcwrite.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
+ lj_gc.h lj_str.h lj_bc.h lj_ctype.h lj_dispatch.h lj_jit.h lj_ir.h \
+ lj_bcdump.h lj_lex.h lj_err.h lj_errmsg.h lj_vm.h
 lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
 lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
  lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ctype.h lj_cconv.h \
  lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ctype.h lj_cconv.h \
  lj_cdata.h lj_carith.h
  lj_cdata.h lj_carith.h
@@ -180,16 +187,16 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
  lj_debug.h lj_ff.h lj_ffdef.h lj_char.c lj_char.h lj_bc.c lj_bcdef.h \
  lj_debug.h lj_ff.h lj_ffdef.h lj_char.c lj_char.h lj_bc.c lj_bcdef.h \
  lj_obj.c lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_debug.c \
  lj_obj.c lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_debug.c \
  lj_state.c lj_lex.h lj_alloc.h lj_dispatch.c luajit.h lj_vmevent.c \
  lj_state.c lj_lex.h lj_alloc.h lj_dispatch.c luajit.h lj_vmevent.c \
- lj_vmevent.h lj_vmmath.c lj_api.c lj_parse.h lj_lex.c lualib.h \
- lj_parse.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c \
- lj_ccall.h lj_carith.c lj_carith.h lj_clib.c lj_clib.h lj_cparse.c \
- lj_cparse.h lj_lib.c lj_lib.h lj_ir.c lj_ircall.h lj_iropt.h \
- lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c \
- lj_opt_loop.c lj_snap.h lj_opt_split.c lj_mcode.c lj_mcode.h lj_snap.c \
- lj_target.h lj_target_*.h lj_record.c lj_record.h lj_ffrecord.h \
- lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \
- lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \
- lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c \
- lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \
- lib_ffi.c lib_init.c
+ lj_vmevent.h lj_vmmath.c lj_api.c lj_bcdump.h lj_parse.h lj_lex.c \
+ lualib.h lj_parse.c lj_bcread.c lj_bcwrite.c lj_ctype.c lj_cdata.c \
+ lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h lj_carith.c lj_carith.h \
+ lj_clib.c lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_lib.h lj_ir.c \
+ lj_ircall.h lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h \
+ lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c \
+ lj_mcode.c lj_mcode.h lj_snap.c lj_target.h lj_target_*.h lj_record.c \
+ lj_record.h lj_ffrecord.h lj_crecord.c lj_crecord.h lj_ffrecord.c \
+ lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h lj_asm_*.h lj_trace.c \
+ lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c lib_base.c lj_libdef.h \
+ lib_math.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c \
+ lib_debug.c lib_bit.c lib_jit.c lib_ffi.c lib_init.c
 luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h
 luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h

+ 17 - 2
src/lib_string.c

@@ -22,6 +22,7 @@
 #include "lj_tab.h"
 #include "lj_tab.h"
 #include "lj_state.h"
 #include "lj_state.h"
 #include "lj_ff.h"
 #include "lj_ff.h"
+#include "lj_bcdump.h"
 #include "lj_char.h"
 #include "lj_char.h"
 #include "lj_lib.h"
 #include "lj_lib.h"
 
 
@@ -114,10 +115,24 @@ LJLIB_ASM_(string_upper)
 
 
 /* ------------------------------------------------------------------------ */
 /* ------------------------------------------------------------------------ */
 
 
+static int writer_buf(lua_State *L, const void *p, size_t size, void *b)
+{
+  luaL_addlstring((luaL_Buffer *)b, (const char *)p, size);
+  UNUSED(L);
+  return 0;
+}
+
 LJLIB_CF(string_dump)
 LJLIB_CF(string_dump)
 {
 {
-  lj_err_caller(L, LJ_ERR_STRDUMP);
-  return 0;  /* unreachable */
+  GCfunc *fn = lj_lib_checkfunc(L, 1);
+  int strip = L->base+1 < L->top && tvistruecond(L->base+1);
+  luaL_Buffer b;
+  L->top = L->base+1;
+  luaL_buffinit(L, &b);
+  if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, &b, strip))
+    lj_err_caller(L, LJ_ERR_STRDUMP);
+  luaL_pushresult(&b);
+  return 1;
 }
 }
 
 
 /* ------------------------------------------------------------------------ */
 /* ------------------------------------------------------------------------ */

+ 10 - 5
src/lj_api.c

@@ -24,6 +24,7 @@
 #include "lj_trace.h"
 #include "lj_trace.h"
 #include "lj_vm.h"
 #include "lj_vm.h"
 #include "lj_lex.h"
 #include "lj_lex.h"
+#include "lj_bcdump.h"
 #include "lj_parse.h"
 #include "lj_parse.h"
 
 
 /* -- Common helper functions --------------------------------------------- */
 /* -- Common helper functions --------------------------------------------- */
@@ -1115,12 +1116,13 @@ LUA_API int lua_resume(lua_State *L, int nargs)
 static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud)
 static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud)
 {
 {
   LexState *ls = (LexState *)ud;
   LexState *ls = (LexState *)ud;
+  GCproto *pt;
   GCfunc *fn;
   GCfunc *fn;
   UNUSED(dummy);
   UNUSED(dummy);
   cframe_errfunc(L->cframe) = -1;  /* Inherit error function. */
   cframe_errfunc(L->cframe) = -1;  /* Inherit error function. */
-  lj_lex_setup(L, ls);
-  fn = lj_func_newL(L, lj_parse(ls), tabref(L->env));
-  /* Parser may realloc stack. Don't combine above/below into one statement. */
+  pt = lj_lex_setup(L, ls) ? lj_bcread(ls) : lj_parse(ls);
+  fn = lj_func_newL_empty(L, pt, tabref(L->env));
+  /* Don't combine above/below into one statement. */
   setfuncV(L, L->top++, fn);
   setfuncV(L, L->top++, fn);
   return NULL;
   return NULL;
 }
 }
@@ -1142,9 +1144,12 @@ LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data,
 
 
 LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data)
 LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data)
 {
 {
+  cTValue *o = L->top-1;
   api_checknelems(L, 1);
   api_checknelems(L, 1);
-  UNUSED(L); UNUSED(writer); UNUSED(data);
-  return 1;  /* Error, not supported. */
+  if (tvisfunc(o) && isluafunc(funcV(o)))
+    return lj_bcwrite(L, funcproto(funcV(o)), writer, data, 0);
+  else
+    return 1;
 }
 }
 
 
 /* -- GC and memory management -------------------------------------------- */
 /* -- GC and memory management -------------------------------------------- */

+ 66 - 0
src/lj_bcdump.h

@@ -0,0 +1,66 @@
+/*
+** Bytecode dump definitions.
+** Copyright (C) 2005-2011 Mike Pall. See Copyright Notice in luajit.h
+*/
+
+#ifndef _LJ_BCDUMP_H
+#define _LJ_BCDUMP_H
+
+#include "lj_obj.h"
+#include "lj_lex.h"
+
+/* -- Bytecode dump format ------------------------------------------------ */
+
+/*
+** dump   = header proto+ 0U
+** header = ESC 'L' 'J' versionB flagsU [namelenU nameB*]
+** proto  = lengthU pdata
+** pdata  = phead bcinsW* uvdataH* kgc* knum* [debugB*]
+** phead  = flagsB numparamsB framesizeB numuvB numkgcU numknU numbcU
+**          [debuglenU [firstlineU numlineU]]
+** kgc    = kgctypeU { ktab | (loU hiU) | (rloU rhiU iloU ihiU) | strB* }
+** knum   = intU0 | (loU1 hiU)
+** ktab   = narrayU nhashU karray* khash*
+** karray = ktabk
+** khash  = ktabk ktabk
+** ktabk  = ktabtypeU { intU | (loU hiU) | strB* }
+**
+** B = 8 bit, H = 16 bit, W = 32 bit, U = ULEB128 of W, U0/U1 = ULEB128 of W+1
+*/
+
+/* Bytecode dump header. */
+#define BCDUMP_HEAD1		0x1b
+#define BCDUMP_HEAD2		0x4c
+#define BCDUMP_HEAD3		0x4a
+
+/* If you perform *any* kind of private modifications to the bytecode itself
+** or to the dump format, you *must* set BCDUMP_VERSION to 0x80 or higher.
+*/
+#define BCDUMP_VERSION		1
+
+/* Compatibility flags. */
+#define BCDUMP_F_BE		0x01
+#define BCDUMP_F_STRIP		0x02
+#define BCDUMP_F_FFI		0x04
+
+#define BCDUMP_F_KNOWN		(BCDUMP_F_FFI*2-1)
+
+/* Type codes for the GC constants of a prototype. Plus length for strings. */
+enum {
+  BCDUMP_KGC_CHILD, BCDUMP_KGC_TAB, BCDUMP_KGC_I64, BCDUMP_KGC_U64,
+  BCDUMP_KGC_COMPLEX, BCDUMP_KGC_STR
+};
+
+/* Type codes for the keys/values of a constant table. */
+enum {
+  BCDUMP_KTAB_NIL, BCDUMP_KTAB_FALSE, BCDUMP_KTAB_TRUE,
+  BCDUMP_KTAB_INT, BCDUMP_KTAB_NUM, BCDUMP_KTAB_STR
+};
+
+/* -- Bytecode reader/writer ---------------------------------------------- */
+
+LJ_FUNC int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer,
+		       void *data, int strip);
+LJ_FUNC GCproto *lj_bcread(LexState *ls);
+
+#endif

+ 466 - 0
src/lj_bcread.c

@@ -0,0 +1,466 @@
+/*
+** Bytecode reader.
+** Copyright (C) 2005-2011 Mike Pall. See Copyright Notice in luajit.h
+*/
+
+#define lj_bcread_c
+#define LUA_CORE
+
+#include "lj_obj.h"
+#include "lj_gc.h"
+#include "lj_err.h"
+#include "lj_str.h"
+#include "lj_tab.h"
+#include "lj_bc.h"
+#if LJ_HASFFI
+#include "lj_ctype.h"
+#include "lj_cdata.h"
+#endif
+#include "lj_lex.h"
+#include "lj_bcdump.h"
+#include "lj_state.h"
+
+/* Reuse some lexer fields for our own purposes. */
+#define bcread_flags(ls)	ls->level
+#define bcread_swap(ls) \
+  ((bcread_flags(ls) & BCDUMP_F_BE) != LJ_BE*BCDUMP_F_BE)
+#define bcread_oldtop(L, ls)	restorestack(L, ls->lastline)
+#define bcread_savetop(L, ls, top) \
+  ls->lastline = (BCLine)savestack(L, (top))
+
+/* -- Input buffer handling ----------------------------------------------- */
+
+/* Throw reader error. */
+static LJ_NOINLINE void bcread_error(LexState *ls, ErrMsg em)
+{
+  lua_State *L = ls->L;
+  const char *name = ls->chunkarg;
+  if (*name == BCDUMP_HEAD1) name = "(binary)";
+  else if (*name == '@' || *name == '=') name++;
+  lj_str_pushf(L, "%s: %s", name, err2msg(em));
+  lj_err_throw(L, LUA_ERRSYNTAX);
+}
+
+/* Resize input buffer. */
+static void bcread_resize(LexState *ls, MSize len)
+{
+  if (ls->sb.sz < len) {
+    MSize sz = ls->sb.sz * 2;
+    while (len > sz) sz = sz * 2;
+    lj_str_resizebuf(ls->L, &ls->sb, sz);
+    /* Caveat: this may change ls->sb.buf which may affect ls->p. */
+  }
+}
+
+/* Refill buffer if needed. */
+static LJ_NOINLINE void bcread_fill(LexState *ls, MSize len, int need)
+{
+  lua_assert(len != 0);
+  if (len > LJ_MAX_MEM || ls->current < 0)
+    bcread_error(ls, LJ_ERR_BCBAD);
+  do {
+    const char *buf;
+    size_t size;
+    if (ls->n) {  /* Copy remainder to buffer. */
+      if (ls->sb.n) {  /* Move down in buffer. */
+	lua_assert(ls->p + ls->n == ls->sb.buf + ls->sb.n);
+	if (ls->n != ls->sb.n)
+	  memmove(ls->sb.buf, ls->p, ls->n);
+      } else {  /* Copy from buffer provided by reader. */
+	bcread_resize(ls, len);
+	memcpy(ls->sb.buf, ls->p, ls->n);
+      }
+      ls->p = ls->sb.buf;
+    }
+    ls->sb.n = ls->n;
+    buf = ls->rfunc(ls->L, ls->rdata, &size);  /* Get more data from reader. */
+    if (buf == NULL || size == 0) {  /* EOF? */
+      if (need) bcread_error(ls, LJ_ERR_BCBAD);
+      ls->current = -1;  /* Only bad if we get called again. */
+      break;
+    }
+    if (ls->sb.n) {  /* Append to buffer. */
+      MSize n = ls->sb.n + (MSize)size;
+      bcread_resize(ls, n < len ? len : n);
+      memcpy(ls->sb.buf + ls->sb.n, buf, size);
+      ls->n = ls->sb.n = n;
+      ls->p = ls->sb.buf;
+    } else {  /* Return buffer provided by reader. */
+      ls->n = (MSize)size;
+      ls->p = buf;
+    }
+  } while (ls->n < len);
+}
+
+/* Need a certain number of bytes. */
+static LJ_AINLINE void bcread_need(LexState *ls, MSize len)
+{
+  if (LJ_UNLIKELY(ls->n < len))
+    bcread_fill(ls, len, 1);
+}
+
+/* Want to read up to a certain number of bytes, but may need less. */
+static LJ_AINLINE void bcread_want(LexState *ls, MSize len)
+{
+  if (LJ_UNLIKELY(ls->n < len))
+    bcread_fill(ls, len, 0);
+}
+
+#define bcread_dec(ls)		check_exp(ls->n > 0, ls->n--)
+#define bcread_consume(ls, len)	check_exp(ls->n >= (len), ls->n -= (len))
+
+/* Return memory block from buffer. */
+static uint8_t *bcread_mem(LexState *ls, MSize len)
+{
+  uint8_t *p = (uint8_t *)ls->p;
+  bcread_consume(ls, len);
+  ls->p = (char *)p + len;
+  return p;
+}
+
+/* Copy memory block from buffer. */
+static void bcread_block(LexState *ls, void *q, MSize len)
+{
+  memcpy(q, bcread_mem(ls, len), len);
+}
+
+/* Read byte from buffer. */
+static LJ_AINLINE uint32_t bcread_byte(LexState *ls)
+{
+  bcread_dec(ls);
+  return (uint32_t)(uint8_t)*ls->p++;
+}
+
+/* Read ULEB128 value from buffer. */
+static uint32_t bcread_uleb128(LexState *ls)
+{
+  const uint8_t *p = (const uint8_t *)ls->p;
+  uint32_t v = *p++;
+  if (LJ_UNLIKELY(v >= 0x80)) {
+    int sh = 0;
+    v &= 0x7f;
+    do {
+     v |= ((*p & 0x7f) << (sh += 7));
+     bcread_dec(ls);
+   } while (*p++ >= 0x80);
+  }
+  bcread_dec(ls);
+  ls->p = (char *)p;
+  return v;
+}
+
+/* Read top 32 bits of 33 bit ULEB128 value from buffer. */
+static uint32_t bcread_uleb128_33(LexState *ls)
+{
+  const uint8_t *p = (const uint8_t *)ls->p;
+  uint32_t v = (*p++ >> 1);
+  if (LJ_UNLIKELY(v >= 0x40)) {
+    int sh = -1;
+    v &= 0x3f;
+    do {
+     v |= ((*p & 0x7f) << (sh += 7));
+     bcread_dec(ls);
+   } while (*p++ >= 0x80);
+  }
+  bcread_dec(ls);
+  ls->p = (char *)p;
+  return v;
+}
+
+/* -- Bytecode reader ----------------------------------------------------- */
+
+/* Read debug info of a prototype. */
+static void bcread_dbg(LexState *ls, GCproto *pt, MSize sizedbg)
+{
+  void *lineinfo = (void *)proto_lineinfo(pt);
+  bcread_block(ls, lineinfo, sizedbg);
+  /* Swap lineinfo if the endianess differs. */
+  if (bcread_swap(ls) && pt->numline >= 256) {
+    MSize i, n = pt->sizebc-1;
+    if (pt->numline < 65536) {
+      uint16_t *p = (uint16_t *)lineinfo;
+      for (i = 0; i < n; i++) p[i] = (uint16_t)((p[i] >> 8)|(p[i] << 8));
+    } else {
+      uint32_t *p = (uint32_t *)lineinfo;
+      for (i = 0; i < n; i++) p[i] = lj_bswap(p[i]);
+    }
+  }
+}
+
+/* Find pointer to varinfo. */
+static const void *bcread_varinfo(GCproto *pt)
+{
+  const uint8_t *p = proto_uvinfo(pt);
+  MSize n = pt->sizeuv;
+  if (n) while (*p++ || --n) ;
+  return p;
+}
+
+/* Read a single constant key/value of a template table. */
+static void bcread_ktabk(LexState *ls, TValue *o)
+{
+  MSize tp = bcread_uleb128(ls);
+  if (tp >= BCDUMP_KTAB_STR) {
+    MSize len = tp - BCDUMP_KTAB_STR;
+    const char *p = (const char *)bcread_mem(ls, len);
+    setstrV(ls->L, o, lj_str_new(ls->L, p, len));
+  } else if (tp == BCDUMP_KTAB_INT) {
+    setintV(o, (int32_t)bcread_uleb128(ls));
+  } else if (tp == BCDUMP_KTAB_NUM) {
+    o->u32.lo = bcread_uleb128(ls);
+    o->u32.hi = bcread_uleb128(ls);
+  } else {
+    lua_assert(tp <= BCDUMP_KTAB_TRUE);
+    setitype(o, ~tp);
+  }
+}
+
+/* Read a template table. */
+static GCtab *bcread_ktab(LexState *ls)
+{
+  MSize narray = bcread_uleb128(ls);
+  MSize nhash = bcread_uleb128(ls);
+  GCtab *t = lj_tab_new(ls->L, narray, hsize2hbits(nhash));
+  if (narray) {  /* Read array entries. */
+    MSize i;
+    TValue *o = tvref(t->array);
+    for (i = 0; i < narray; i++, o++)
+      bcread_ktabk(ls, o);
+  }
+  if (nhash) {  /* Read hash entries. */
+    MSize i;
+    for (i = 0; i < nhash; i++) {
+      TValue key;
+      bcread_ktabk(ls, &key);
+      lua_assert(!tvisnil(&key));
+      bcread_ktabk(ls, lj_tab_set(ls->L, t, &key));
+    }
+  }
+  return t;
+}
+
+/* Read GC constants of a prototype. */
+static void bcread_kgc(LexState *ls, GCproto *pt, MSize sizekgc)
+{
+  MSize i;
+  GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc;
+  for (i = 0; i < sizekgc; i++, kr++) {
+    MSize tp = bcread_uleb128(ls);
+    if (tp >= BCDUMP_KGC_STR) {
+      MSize len = tp - BCDUMP_KGC_STR;
+      const char *p = (const char *)bcread_mem(ls, len);
+      setgcref(*kr, obj2gco(lj_str_new(ls->L, p, len)));
+    } else if (tp == BCDUMP_KGC_TAB) {
+      setgcref(*kr, obj2gco(bcread_ktab(ls)));
+#if LJ_HASFFI
+    } else if (tp != BCDUMP_KGC_CHILD) {
+      CTypeID id = tp == BCDUMP_KGC_COMPLEX ? CTID_COMPLEX_DOUBLE :
+		   tp == BCDUMP_KGC_I64 ? CTID_INT64 : CTID_UINT64;
+      CTSize sz = tp == BCDUMP_KGC_COMPLEX ? 16 : 8;
+      GCcdata *cd = lj_cdata_new_(ls->L, id, sz);
+      TValue *p = (TValue *)cdataptr(cd);
+      setgcref(*kr, obj2gco(cd));
+      p[0].u32.lo = bcread_uleb128(ls);
+      p[0].u32.hi = bcread_uleb128(ls);
+      if (tp == BCDUMP_KGC_COMPLEX) {
+	p[1].u32.lo = bcread_uleb128(ls);
+	p[1].u32.hi = bcread_uleb128(ls);
+      }
+#endif
+    } else {
+      lua_State *L = ls->L;
+      lua_assert(tp == BCDUMP_KGC_CHILD);
+      if (L->top <= bcread_oldtop(L, ls))  /* Stack underflow? */
+	bcread_error(ls, LJ_ERR_BCBAD);
+      L->top--;
+      setgcref(*kr, obj2gco(protoV(L->top)));
+    }
+  }
+}
+
+/* Read number constants of a prototype. */
+static void bcread_knum(LexState *ls, GCproto *pt, MSize sizekn)
+{
+  MSize i;
+  TValue *o = mref(pt->k, TValue);
+  for (i = 0; i < sizekn; i++, o++) {
+    int isnum = (ls->p[0] & 1);
+    uint32_t lo = bcread_uleb128_33(ls);
+    if (isnum) {
+      o->u32.lo = lo;
+      o->u32.hi = bcread_uleb128(ls);
+    } else {
+      setintV(o, lo);
+    }
+  }
+}
+
+/* Read bytecode instructions. */
+static void bcread_bytecode(LexState *ls, GCproto *pt, MSize sizebc)
+{
+  BCIns *bc = proto_bc(pt);
+  bc[0] = BCINS_AD((pt->flags & PROTO_VARARG) ? BC_FUNCV : BC_FUNCF,
+		   pt->framesize, 0);
+  bcread_block(ls, bc+1, (sizebc-1)*(MSize)sizeof(BCIns));
+  /* Swap bytecode instructions if the endianess differs. */
+  if (bcread_swap(ls)) {
+    MSize i;
+    for (i = 1; i < sizebc; i++) bc[i] = lj_bswap(bc[i]);
+  }
+}
+
+/* Read upvalue refs. */
+static void bcread_uv(LexState *ls, GCproto *pt, MSize sizeuv)
+{
+  if (sizeuv) {
+    uint16_t *uv = proto_uv(pt);
+    bcread_block(ls, uv, sizeuv*2);
+    /* Swap upvalue refs if the endianess differs. */
+    if (bcread_swap(ls)) {
+      MSize i;
+      for (i = 0; i < sizeuv; i++)
+	uv[i] = (uint16_t)((uv[i] >> 8)|(uv[i] << 8));
+    }
+  }
+}
+
+/* Read a prototype. */
+static GCproto *bcread_proto(LexState *ls)
+{
+  GCproto *pt;
+  MSize framesize, numparams, flags, sizeuv, sizekgc, sizekn, sizebc, sizept;
+  MSize ofsk, ofsuv, ofsdbg;
+  MSize sizedbg = 0;
+  BCLine firstline = 0, numline = 0;
+  MSize len, startn;
+
+  /* Read length. */
+  if (ls->n > 0 && ls->p[0] == 0) {  /* Shortcut EOF. */
+    ls->n--; ls->p++;
+    return NULL;
+  }
+  bcread_want(ls, 5);
+  len = bcread_uleb128(ls);
+  if (!len) return NULL;  /* EOF */
+  bcread_need(ls, len);
+  startn = ls->n;
+
+  /* Read prototype header. */
+  flags = bcread_byte(ls);
+  numparams = bcread_byte(ls);
+  framesize = bcread_byte(ls);
+  sizeuv = bcread_byte(ls);
+  sizekgc = bcread_uleb128(ls);
+  sizekn = bcread_uleb128(ls);
+  sizebc = bcread_uleb128(ls) + 1;
+  if (!(bcread_flags(ls) & BCDUMP_F_STRIP)) {
+    sizedbg = bcread_uleb128(ls);
+    if (sizedbg) {
+      firstline = bcread_uleb128(ls);
+      numline = bcread_uleb128(ls);
+    }
+  }
+
+  /* Calculate total size of prototype including all colocated arrays. */
+  sizept = (MSize)sizeof(GCproto) +
+	   sizebc*(MSize)sizeof(BCIns) +
+	   sizekgc*(MSize)sizeof(GCRef);
+  sizept = (sizept + (MSize)sizeof(TValue)-1) & ~((MSize)sizeof(TValue)-1);
+  ofsk = sizept; sizept += sizekn*(MSize)sizeof(TValue);
+  ofsuv = sizept; sizept += ((sizeuv+1)&~1)*2;
+  ofsdbg = sizept; sizept += sizedbg;
+
+  /* Allocate prototype object and initialize its fields. */
+  pt = (GCproto *)lj_mem_newgco(ls->L, (MSize)sizept);
+  pt->gct = ~LJ_TPROTO;
+  pt->numparams = (uint8_t)numparams;
+  pt->framesize = (uint8_t)framesize;
+  pt->sizebc = sizebc;
+  setmref(pt->k, (char *)pt + ofsk);
+  setmref(pt->uv, (char *)pt + ofsuv);
+  pt->sizekgc = 0;  /* Set to zero until fully initialized. */
+  pt->sizekn = sizekn;
+  pt->sizept = sizept;
+  pt->sizeuv = (uint8_t)sizeuv;
+  pt->flags = (uint8_t)flags;
+  pt->trace = 0;
+  setgcref(pt->chunkname, obj2gco(ls->chunkname));
+
+  /* Close potentially uninitialized gap between bc and kgc. */
+  *(uint32_t *)((char *)pt + ofsk - sizeof(GCRef)*(sizekgc+1)) = 0;
+
+  /* Read bytecode instructions and upvalue refs. */
+  bcread_bytecode(ls, pt, sizebc);
+  bcread_uv(ls, pt, sizeuv);
+
+  /* Read constants. */
+  bcread_kgc(ls, pt, sizekgc);
+  pt->sizekgc = sizekgc;
+  bcread_knum(ls, pt, sizekn);
+
+  /* Read and initialize debug info. */
+  pt->firstline = firstline;
+  pt->numline = numline;
+  if (sizedbg) {
+    MSize sizeli = (sizebc-1) << (numline < 256 ? 0 : numline < 65536 ? 1 : 2);
+    setmref(pt->lineinfo, (char *)pt + ofsdbg);
+    setmref(pt->uvinfo, (char *)pt + ofsdbg + sizeli);
+    bcread_dbg(ls, pt, sizedbg);
+    setmref(pt->varinfo, bcread_varinfo(pt));
+  } else {
+    setmref(pt->lineinfo, NULL);
+    setmref(pt->uvinfo, NULL);
+    setmref(pt->varinfo, NULL);
+  }
+
+  if (len != startn - ls->n)
+    bcread_error(ls, LJ_ERR_BCBAD);
+  return pt;
+}
+
+/* Read and check header of bytecode dump. */
+static int bcread_header(LexState *ls)
+{
+  uint32_t flags;
+  bcread_want(ls, 3+5+5);
+  if (bcread_byte(ls) != BCDUMP_HEAD2 ||
+      bcread_byte(ls) != BCDUMP_HEAD3 ||
+      bcread_byte(ls) != BCDUMP_VERSION) return 0;
+  bcread_flags(ls) = flags = bcread_uleb128(ls);
+  if ((flags & ~(BCDUMP_F_KNOWN)) != 0) return 0;
+#if !LJ_HASFFI
+  if ((flags & BCDUMP_F_FFI)) return 0;
+#endif
+  if ((flags & BCDUMP_F_STRIP)) {
+    ls->chunkname = lj_str_newz(ls->L, ls->chunkarg);
+  } else {
+    MSize len = bcread_uleb128(ls);
+    bcread_need(ls, len);
+    ls->chunkname = lj_str_new(ls->L, (const char *)bcread_mem(ls, len), len);
+  }
+  return 1;  /* Ok. */
+}
+
+/* Read a bytecode dump. */
+GCproto *lj_bcread(LexState *ls)
+{
+  lua_State *L = ls->L;
+  lua_assert(ls->current == BCDUMP_HEAD1);
+  bcread_savetop(L, ls, L->top);
+  lj_str_resetbuf(&ls->sb);
+  /* Check for a valid bytecode dump header. */
+  if (!bcread_header(ls))
+    bcread_error(ls, LJ_ERR_BCFMT);
+  for (;;) {  /* Process all prototypes in the bytecode dump. */
+    GCproto *pt = bcread_proto(ls);
+    if (!pt) break;
+    setprotoV(L, L->top, pt);
+    incr_top(L);
+  }
+  if (ls->n != 0 || L->top-1 != bcread_oldtop(L, ls))
+    bcread_error(ls, LJ_ERR_BCBAD);
+  /* Pop off last prototype. */
+  L->top--;
+  return protoV(L->top);
+}
+

+ 388 - 0
src/lj_bcwrite.c

@@ -0,0 +1,388 @@
+/*
+** Bytecode writer.
+** Copyright (C) 2005-2011 Mike Pall. See Copyright Notice in luajit.h
+*/
+
+#define lj_bcwrite_c
+#define LUA_CORE
+
+#include "lj_obj.h"
+#include "lj_gc.h"
+#include "lj_str.h"
+#include "lj_bc.h"
+#if LJ_HASFFI
+#include "lj_ctype.h"
+#endif
+#if LJ_HASJIT
+#include "lj_dispatch.h"
+#include "lj_jit.h"
+#endif
+#include "lj_bcdump.h"
+#include "lj_vm.h"
+
+/* Context for bytecode writer. */
+typedef struct BCWriteCtx {
+  SBuf sb;			/* Output buffer. */
+  lua_State *L;			/* Lua state. */
+  GCproto *pt;			/* Root prototype. */
+  lua_Writer wfunc;		/* Writer callback. */
+  void *wdata;			/* Writer callback data. */
+  int strip;			/* Strip debug info. */
+  int status;			/* Status from writer callback. */
+} BCWriteCtx;
+
+/* -- Output buffer handling ---------------------------------------------- */
+
+/* Resize buffer if needed. */
+static LJ_NOINLINE void bcwrite_resize(BCWriteCtx *ctx, MSize len)
+{
+  MSize sz = ctx->sb.sz * 2;
+  while (ctx->sb.n + len > sz) sz = sz * 2;
+  lj_str_resizebuf(ctx->L, &ctx->sb, sz);
+}
+
+/* Need a certain amount of buffer space. */
+static LJ_AINLINE void bcwrite_need(BCWriteCtx *ctx, MSize len)
+{
+  if (LJ_UNLIKELY(ctx->sb.n + len > ctx->sb.sz))
+    bcwrite_resize(ctx, len);
+}
+
+/* Add memory block to buffer. */
+static void bcwrite_block(BCWriteCtx *ctx, const void *p, MSize len)
+{
+  uint8_t *q = (uint8_t *)(ctx->sb.buf + ctx->sb.n);
+  MSize i;
+  ctx->sb.n += len;
+  for (i = 0; i < len; i++) q[i] = ((uint8_t *)p)[i];
+}
+
+/* Add byte to buffer. */
+static LJ_AINLINE void bcwrite_byte(BCWriteCtx *ctx, uint8_t b)
+{
+  ctx->sb.buf[ctx->sb.n++] = b;
+}
+
+/* Add ULEB128 value to buffer. */
+static void bcwrite_uleb128(BCWriteCtx *ctx, uint32_t v)
+{
+  MSize n = ctx->sb.n;
+  uint8_t *p = (uint8_t *)ctx->sb.buf;
+  for (; v >= 0x80; v >>= 7)
+    p[n++] = (uint8_t)((v & 0x7f) | 0x80);
+  p[n++] = (uint8_t)v;
+  ctx->sb.n = n;
+}
+
+/* -- Bytecode writer ----------------------------------------------------- */
+
+/* Write a single constant key/value of a template table. */
+static void bcwrite_ktabk(BCWriteCtx *ctx, cTValue *o, int narrow)
+{
+  bcwrite_need(ctx, 1+10);
+  if (tvisstr(o)) {
+    const GCstr *str = strV(o);
+    MSize len = str->len;
+    bcwrite_need(ctx, 5+len);
+    bcwrite_uleb128(ctx, BCDUMP_KTAB_STR+len);
+    bcwrite_block(ctx, strdata(str), len);
+  } else if (tvisint(o)) {
+    bcwrite_byte(ctx, BCDUMP_KTAB_INT);
+    bcwrite_uleb128(ctx, intV(o));
+  } else if (tvisnum(o)) {
+    if (!LJ_DUALNUM && narrow) {  /* Narrow number constants to integers. */
+      lua_Number num = numV(o);
+      int32_t k = lj_num2int(num);
+      if (num == (lua_Number)k) {  /* -0 is never a constant. */
+	bcwrite_byte(ctx, BCDUMP_KTAB_INT);
+	bcwrite_uleb128(ctx, k);
+	return;
+      }
+    }
+    bcwrite_byte(ctx, BCDUMP_KTAB_NUM);
+    bcwrite_uleb128(ctx, o->u32.lo);
+    bcwrite_uleb128(ctx, o->u32.hi);
+  } else {
+    lua_assert(tvispri(o));
+    bcwrite_byte(ctx, BCDUMP_KTAB_NIL+~itype(o));
+  }
+}
+
+/* Write a template table. */
+static void bcwrite_ktab(BCWriteCtx *ctx, const GCtab *t)
+{
+  MSize narray = 0, nhash = 0;
+  if (t->asize > 0) {  /* Determine max. length of array part. */
+    ptrdiff_t i;
+    TValue *array = tvref(t->array);
+    for (i = (ptrdiff_t)t->asize-1; i >= 0; i--)
+      if (!tvisnil(&array[i]))
+	break;
+    narray = (MSize)(i+1);
+  }
+  if (t->hmask > 0) {  /* Count number of used hash slots. */
+    MSize i, hmask = t->hmask;
+    Node *node = noderef(t->node);
+    for (i = 0; i <= hmask; i++)
+      nhash += !tvisnil(&node[i].val);
+  }
+  /* Write number of array slots and hash slots. */
+  bcwrite_uleb128(ctx, narray);
+  bcwrite_uleb128(ctx, nhash);
+  if (narray) {  /* Write array entries (may contain nil). */
+    MSize i;
+    TValue *o = tvref(t->array);
+    for (i = 0; i < narray; i++, o++)
+      bcwrite_ktabk(ctx, o, 1);
+  }
+  if (nhash) {  /* Write hash entries. */
+    MSize i = nhash;
+    Node *node = noderef(t->node) + t->hmask;
+    for (;; node--)
+      if (!tvisnil(&node->val)) {
+	bcwrite_ktabk(ctx, &node->key, 0);
+	bcwrite_ktabk(ctx, &node->val, 1);
+	if (--i == 0) break;
+      }
+  }
+}
+
+/* Write GC constants of a prototype. */
+static void bcwrite_kgc(BCWriteCtx *ctx, GCproto *pt)
+{
+  MSize i, sizekgc = pt->sizekgc;
+  GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc;
+  for (i = 0; i < sizekgc; i++, kr++) {
+    GCobj *o = gcref(*kr);
+    MSize tp, need = 1;
+    /* Determine constant type and needed size. */
+    if (o->gch.gct == ~LJ_TSTR) {
+      tp = BCDUMP_KGC_STR + gco2str(o)->len;
+      need = 5+gco2str(o)->len;
+    } else if (o->gch.gct == ~LJ_TPROTO) {
+      lua_assert((pt->flags & PROTO_CHILD));
+      tp = BCDUMP_KGC_CHILD;
+#if LJ_HASFFI
+    } else if (o->gch.gct == ~LJ_TCDATA) {
+      CTypeID id = gco2cd(o)->typeid;
+      need = 1+4*5;
+      if (id == CTID_INT64) {
+	tp = BCDUMP_KGC_I64;
+      } else if (id == CTID_UINT64) {
+	tp = BCDUMP_KGC_U64;
+      } else {
+	lua_assert(id == CTID_COMPLEX_DOUBLE);
+	tp = BCDUMP_KGC_COMPLEX;
+      }
+#endif
+    } else {
+      lua_assert(o->gch.gct == ~LJ_TTAB);
+      tp = BCDUMP_KGC_TAB;
+    }
+    /* Write constant type. */
+    bcwrite_need(ctx, need);
+    bcwrite_uleb128(ctx, tp);
+    /* Write constant data (if any). */
+    if (tp >= BCDUMP_KGC_STR) {
+      bcwrite_block(ctx, strdata(gco2str(o)), gco2str(o)->len);
+    } else if (tp == BCDUMP_KGC_TAB) {
+      bcwrite_ktab(ctx, gco2tab(o));
+#if LJ_HASFFI
+    } else if (tp != BCDUMP_KGC_CHILD) {
+      cTValue *p = (TValue *)cdataptr(gco2cd(o));
+      bcwrite_uleb128(ctx, p[0].u32.lo);
+      bcwrite_uleb128(ctx, p[0].u32.hi);
+      if (tp == BCDUMP_KGC_COMPLEX) {
+	bcwrite_uleb128(ctx, p[1].u32.lo);
+	bcwrite_uleb128(ctx, p[1].u32.hi);
+      }
+#endif
+    }
+  }
+}
+
+/* Write number constants of a prototype. */
+static void bcwrite_knum(BCWriteCtx *ctx, GCproto *pt)
+{
+  MSize i, sizekn = pt->sizekn;
+  cTValue *o = mref(pt->k, TValue);
+  bcwrite_need(ctx, 10*sizekn);
+  for (i = 0; i < sizekn; i++, o++) {
+    int32_t k;
+    if (tvisint(o)) {
+      k = intV(o);
+      goto save_int;
+    } else {
+      /* Write a 33 bit ULEB128 for the int (lsb=0) or loword (lsb=1). */
+      if (!LJ_DUALNUM) {  /* Narrow number constants to integers. */
+	lua_Number num = numV(o);
+	k = lj_num2int(num);
+	if (num == (lua_Number)k) {  /* -0 is never a constant. */
+	save_int:
+	  bcwrite_uleb128(ctx, 2*(uint32_t)k);
+	  if (k < 0) ctx->sb.buf[ctx->sb.n-1] |= 0x10;
+	  continue;
+	}
+      }
+      bcwrite_uleb128(ctx, 1+2*o->u32.lo);
+      if (o->u32.lo >= 0x80000000u) ctx->sb.buf[ctx->sb.n-1] |= 0x10;
+      bcwrite_uleb128(ctx, o->u32.hi);
+    }
+  }
+}
+
+/* Write bytecode instructions. */
+static void bcwrite_bytecode(BCWriteCtx *ctx, GCproto *pt)
+{
+  MSize nbc = pt->sizebc-1;  /* Omit the [JI]FUNC* header. */
+#if LJ_HASJIT
+  uint8_t *p = (uint8_t *)&ctx->sb.buf[ctx->sb.n];
+#endif
+  bcwrite_block(ctx, proto_bc(pt)+1, nbc*(MSize)sizeof(BCIns));
+#if LJ_HASJIT
+  /* Unpatch modified bytecode containing ILOOP/JLOOP etc. */
+  if ((pt->flags & PROTO_ILOOP) || pt->trace) {
+    jit_State *J = L2J(ctx->L);
+    MSize i;
+    for (i = 0; i < nbc; i++, p += sizeof(BCIns)) {
+      BCOp op = (BCOp)p[LJ_ENDIAN_SELECT(0, 3)];
+      if (op == BC_IFORL || op == BC_IITERL || op == BC_ILOOP ||
+	  op == BC_JFORI) {
+	p[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_IFORL+BC_FORL);
+      } else if (op == BC_JFORL || op == BC_JITERL || op == BC_JLOOP) {
+	BCReg rd = p[LJ_ENDIAN_SELECT(2, 1)] + (p[LJ_ENDIAN_SELECT(3, 0)] << 8);
+	BCIns ins = traceref(J, rd)->startins;
+	p[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_JFORL+BC_FORL);
+	p[LJ_ENDIAN_SELECT(2, 1)] = bc_c(ins);
+	p[LJ_ENDIAN_SELECT(3, 0)] = bc_b(ins);
+      }
+    }
+  }
+#endif
+}
+
+/* Write prototype. */
+static void bcwrite_proto(BCWriteCtx *ctx, GCproto *pt)
+{
+  MSize sizedbg = 0;
+
+  /* Recursively write children of prototype. */
+  if ((pt->flags & PROTO_CHILD)) {
+    ptrdiff_t i, n = pt->sizekgc;
+    GCRef *kr = mref(pt->k, GCRef) - 1;
+    for (i = 0; i < n; i++, kr--) {
+      GCobj *o = gcref(*kr);
+      if (o->gch.gct == ~LJ_TPROTO)
+	bcwrite_proto(ctx, gco2pt(o));
+    }
+  }
+
+  /* Start writing the prototype info to a buffer. */
+  lj_str_resetbuf(&ctx->sb);
+  ctx->sb.n = 5;  /* Leave room for final size. */
+  bcwrite_need(ctx, 4+6*5+(pt->sizebc-1)*(MSize)sizeof(BCIns)+pt->sizeuv*2);
+
+  /* Write prototype header. */
+  bcwrite_byte(ctx, (pt->flags & (PROTO_CHILD|PROTO_VARARG|PROTO_FFI)));
+  bcwrite_byte(ctx, pt->numparams);
+  bcwrite_byte(ctx, pt->framesize);
+  bcwrite_byte(ctx, pt->sizeuv);
+  bcwrite_uleb128(ctx, pt->sizekgc);
+  bcwrite_uleb128(ctx, pt->sizekn);
+  bcwrite_uleb128(ctx, pt->sizebc-1);
+  if (!ctx->strip) {
+    sizedbg = pt->sizept - (MSize)((char *)proto_lineinfo(pt) - (char *)pt);
+    bcwrite_uleb128(ctx, sizedbg);
+    if (sizedbg) {
+      bcwrite_uleb128(ctx, pt->firstline);
+      bcwrite_uleb128(ctx, pt->numline);
+    }
+  }
+
+  /* Write bytecode instructions and upvalue refs. */
+  bcwrite_bytecode(ctx, pt);
+  bcwrite_block(ctx, proto_uv(pt), pt->sizeuv*2);
+
+  /* Write constants. */
+  bcwrite_kgc(ctx, pt);
+  bcwrite_knum(ctx, pt);
+
+  /* Write debug info, if not stripped. */
+  if (sizedbg) {
+    bcwrite_need(ctx, sizedbg);
+    bcwrite_block(ctx, proto_lineinfo(pt), sizedbg);
+  }
+
+  /* Pass buffer to writer function. */
+  if (ctx->status == 0) {
+    MSize n = ctx->sb.n - 5;
+    MSize nn = 1 + lj_fls(n)/7;
+    ctx->sb.n = 5 - nn;
+    bcwrite_uleb128(ctx, n);  /* Fill in final size. */
+    lua_assert(ctx->sb.n == 5);
+    ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf+5-nn, nn+n, ctx->wdata);
+  }
+}
+
+/* Write header of bytecode dump. */
+static void bcwrite_header(BCWriteCtx *ctx)
+{
+  GCstr *chunkname = proto_chunkname(ctx->pt);
+  const char *name = strdata(chunkname);
+  MSize len = chunkname->len;
+  lj_str_resetbuf(&ctx->sb);
+  bcwrite_need(ctx, 5+5+len);
+  bcwrite_byte(ctx, BCDUMP_HEAD1);
+  bcwrite_byte(ctx, BCDUMP_HEAD2);
+  bcwrite_byte(ctx, BCDUMP_HEAD3);
+  bcwrite_byte(ctx, BCDUMP_VERSION);
+  bcwrite_byte(ctx, (ctx->strip ? BCDUMP_F_STRIP : 0) +
+		   (LJ_BE ? BCDUMP_F_BE : 0) +
+		   ((ctx->pt->flags & PROTO_FFI) ? BCDUMP_F_FFI : 0));
+  if (!ctx->strip) {
+    bcwrite_uleb128(ctx, len);
+    bcwrite_block(ctx, name, len);
+  }
+  ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf, ctx->sb.n, ctx->wdata);
+}
+
+/* Write footer of bytecode dump. */
+static void bcwrite_footer(BCWriteCtx *ctx)
+{
+  if (ctx->status == 0) {
+    uint8_t zero = 0;
+    ctx->status = ctx->wfunc(ctx->L, &zero, 1, ctx->wdata);
+  }
+}
+
+/* Protected callback for bytecode writer. */
+static TValue *cpwriter(lua_State *L, lua_CFunction dummy, void *ud)
+{
+  BCWriteCtx *ctx = (BCWriteCtx *)ud;
+  UNUSED(dummy);
+  lj_str_resizebuf(L, &ctx->sb, 1024);  /* Avoids resize for most prototypes. */
+  bcwrite_header(ctx);
+  bcwrite_proto(ctx, ctx->pt);
+  bcwrite_footer(ctx);
+  return NULL;
+}
+
+/* Write bytecode for a prototype. */
+int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, void *data,
+	      int strip)
+{
+  BCWriteCtx ctx;
+  int status;
+  ctx.L = L;
+  ctx.pt = pt;
+  ctx.wfunc = writer;
+  ctx.wdata = data;
+  ctx.strip = strip;
+  ctx.status = 0;
+  lj_str_initbuf(&ctx.sb);
+  status = lj_vm_cpcall(L, NULL, &ctx, cpwriter);
+  if (status == 0) status = ctx.status;
+  lj_str_freebuf(G(ctx.L), &ctx.sb);
+  return status;
+}
+

+ 5 - 2
src/lj_errmsg.h

@@ -84,7 +84,7 @@ ERRDEF(IOCLFL,	"attempt to use a closed file")
 ERRDEF(IOSTDCL,	"standard file is closed")
 ERRDEF(IOSTDCL,	"standard file is closed")
 ERRDEF(OSUNIQF,	"unable to generate a unique filename")
 ERRDEF(OSUNIQF,	"unable to generate a unique filename")
 ERRDEF(OSDATEF,	"field " LUA_QS " missing in date table")
 ERRDEF(OSDATEF,	"field " LUA_QS " missing in date table")
-ERRDEF(STRDUMP,	"cannot dump functions")
+ERRDEF(STRDUMP,	"unable to dump given function")
 ERRDEF(STRSLC,	"string slice too long")
 ERRDEF(STRSLC,	"string slice too long")
 ERRDEF(STRPATB,	"missing " LUA_QL("[") " after " LUA_QL("%f") " in pattern")
 ERRDEF(STRPATB,	"missing " LUA_QL("[") " after " LUA_QL("%f") " in pattern")
 ERRDEF(STRPATC,	"invalid pattern capture")
 ERRDEF(STRPATC,	"invalid pattern capture")
@@ -119,7 +119,6 @@ ERRDEF(XLCOM,	"unfinished long comment")
 ERRDEF(XSTR,	"unfinished string")
 ERRDEF(XSTR,	"unfinished string")
 ERRDEF(XESC,	"invalid escape sequence")
 ERRDEF(XESC,	"invalid escape sequence")
 ERRDEF(XLDELIM,	"invalid long string delimiter")
 ERRDEF(XLDELIM,	"invalid long string delimiter")
-ERRDEF(XBCLOAD,	"cannot load Lua bytecode")
 ERRDEF(XTOKEN,	LUA_QS " expected")
 ERRDEF(XTOKEN,	LUA_QS " expected")
 ERRDEF(XJUMP,	"control structure too long")
 ERRDEF(XJUMP,	"control structure too long")
 ERRDEF(XSLOTS,	"function or expression too complex")
 ERRDEF(XSLOTS,	"function or expression too complex")
@@ -137,6 +136,10 @@ ERRDEF(XSYNTAX,	"syntax error")
 ERRDEF(XBREAK,	"no loop to break")
 ERRDEF(XBREAK,	"no loop to break")
 ERRDEF(XFOR,	LUA_QL("=") " or " LUA_QL("in") " expected")
 ERRDEF(XFOR,	LUA_QL("=") " or " LUA_QL("in") " expected")
 
 
+/* Bytecode reader errors. */
+ERRDEF(BCFMT,	"cannot load incompatible bytecode")
+ERRDEF(BCBAD,	"cannot load malformed bytecode")
+
 #if LJ_HASFFI
 #if LJ_HASFFI
 /* FFI errors. */
 /* FFI errors. */
 ERRDEF(FFI_INVTYPE,	"invalid C type")
 ERRDEF(FFI_INVTYPE,	"invalid C type")

+ 32 - 5
src/lj_func.c

@@ -65,6 +65,17 @@ static GCupval *func_finduv(lua_State *L, TValue *slot)
   return uv;
   return uv;
 }
 }
 
 
+/* Create an empty and closed upvalue. */
+static GCupval *func_emptyuv(lua_State *L)
+{
+  GCupval *uv = (GCupval *)lj_mem_newgco(L, sizeof(GCupval));
+  uv->gct = ~LJ_TUPVAL;
+  uv->closed = 1;
+  setnilV(&uv->tv);
+  setmref(uv->v, &uv->tv);
+  return uv;
+}
+
 /* Close all open upvalues pointing to some stack level or above. */
 /* Close all open upvalues pointing to some stack level or above. */
 void LJ_FASTCALL lj_func_closeuv(lua_State *L, TValue *level)
 void LJ_FASTCALL lj_func_closeuv(lua_State *L, TValue *level)
 {
 {
@@ -105,30 +116,45 @@ GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env)
   return fn;
   return fn;
 }
 }
 
 
-GCfunc *lj_func_newL(lua_State *L, GCproto *pt, GCtab *env)
+static GCfunc *func_newL(lua_State *L, GCproto *pt, GCtab *env)
 {
 {
   GCfunc *fn = (GCfunc *)lj_mem_newgco(L, sizeLfunc((MSize)pt->sizeuv));
   GCfunc *fn = (GCfunc *)lj_mem_newgco(L, sizeLfunc((MSize)pt->sizeuv));
   fn->l.gct = ~LJ_TFUNC;
   fn->l.gct = ~LJ_TFUNC;
   fn->l.ffid = FF_LUA;
   fn->l.ffid = FF_LUA;
-  fn->l.nupvalues = (uint8_t)pt->sizeuv;
+  fn->l.nupvalues = 0;  /* Set to zero until upvalues are initialized. */
   /* NOBARRIER: Really a setgcref. But the GCfunc is new (marked white). */
   /* NOBARRIER: Really a setgcref. But the GCfunc is new (marked white). */
   setmref(fn->l.pc, proto_bc(pt));
   setmref(fn->l.pc, proto_bc(pt));
   setgcref(fn->l.env, obj2gco(env));
   setgcref(fn->l.env, obj2gco(env));
   return fn;
   return fn;
 }
 }
 
 
+/* Create a new Lua function with empty upvalues. */
+GCfunc *lj_func_newL_empty(lua_State *L, GCproto *pt, GCtab *env)
+{
+  GCfunc *fn = func_newL(L, pt, env);
+  MSize i, nuv = pt->sizeuv;
+  /* NOBARRIER: The GCfunc is new (marked white). */
+  for (i = 0; i < nuv; i++) {
+    GCupval *uv = func_emptyuv(L);
+    uv->dhash = (uint32_t)(uintptr_t)pt ^ ((uint32_t)proto_uv(pt)[i] << 24);
+    setgcref(fn->l.uvptr[i], obj2gco(uv));
+  }
+  fn->l.nupvalues = (uint8_t)nuv;
+  return fn;
+}
+
 /* Do a GC check and create a new Lua function with inherited upvalues. */
 /* Do a GC check and create a new Lua function with inherited upvalues. */
 GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent)
 GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent)
 {
 {
   GCfunc *fn;
   GCfunc *fn;
   GCRef *puv;
   GCRef *puv;
-  uint32_t i, nuv;
+  MSize i, nuv;
   TValue *base;
   TValue *base;
   lj_gc_check_fixtop(L);
   lj_gc_check_fixtop(L);
-  fn = lj_func_newL(L, pt, tabref(parent->env));
+  fn = func_newL(L, pt, tabref(parent->env));
   /* NOBARRIER: The GCfunc is new (marked white). */
   /* NOBARRIER: The GCfunc is new (marked white). */
   puv = parent->uvptr;
   puv = parent->uvptr;
-  nuv = fn->l.nupvalues;
+  nuv = pt->sizeuv;
   base = L->base;
   base = L->base;
   for (i = 0; i < nuv; i++) {
   for (i = 0; i < nuv; i++) {
     uint32_t v = proto_uv(pt)[i];
     uint32_t v = proto_uv(pt)[i];
@@ -141,6 +167,7 @@ GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent)
     }
     }
     setgcref(fn->l.uvptr[i], obj2gco(uv));
     setgcref(fn->l.uvptr[i], obj2gco(uv));
   }
   }
+  fn->l.nupvalues = (uint8_t)nuv;
   return fn;
   return fn;
 }
 }
 
 

+ 1 - 1
src/lj_func.h

@@ -17,7 +17,7 @@ LJ_FUNC void LJ_FASTCALL lj_func_freeuv(global_State *g, GCupval *uv);
 
 
 /* Functions (closures). */
 /* Functions (closures). */
 LJ_FUNC GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env);
 LJ_FUNC GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env);
-LJ_FUNC GCfunc *lj_func_newL(lua_State *L, GCproto *pt, GCtab *env);
+LJ_FUNC GCfunc *lj_func_newL_empty(lua_State *L, GCproto *pt, GCtab *env);
 LJ_FUNCA GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent);
 LJ_FUNCA GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent);
 LJ_FUNC void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *c);
 LJ_FUNC void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *c);
 
 

+ 1 - 1
src/lj_gc.c

@@ -204,7 +204,7 @@ static void gc_traverse_func(global_State *g, GCfunc *fn)
   gc_markobj(g, tabref(fn->c.env));
   gc_markobj(g, tabref(fn->c.env));
   if (isluafunc(fn)) {
   if (isluafunc(fn)) {
     uint32_t i;
     uint32_t i;
-    lua_assert(fn->l.nupvalues == funcproto(fn)->sizeuv);
+    lua_assert(fn->l.nupvalues <= funcproto(fn)->sizeuv);
     gc_markobj(g, funcproto(fn));
     gc_markobj(g, funcproto(fn));
     for (i = 0; i < fn->l.nupvalues; i++)  /* Mark Lua function upvalues. */
     for (i = 0; i < fn->l.nupvalues; i++)  /* Mark Lua function upvalues. */
       gc_markobj(g, &gcref(fn->l.uvptr[i])->uv);
       gc_markobj(g, &gcref(fn->l.uvptr[i])->uv);

+ 3 - 6
src/lj_lex.c

@@ -408,7 +408,7 @@ static int llex(LexState *ls, TValue *tv)
 /* -- Lexer API ----------------------------------------------------------- */
 /* -- Lexer API ----------------------------------------------------------- */
 
 
 /* Setup lexer state. */
 /* Setup lexer state. */
-void lj_lex_setup(lua_State *L, LexState *ls)
+int lj_lex_setup(lua_State *L, LexState *ls)
 {
 {
   ls->L = L;
   ls->L = L;
   ls->fs = NULL;
   ls->fs = NULL;
@@ -433,14 +433,11 @@ void lj_lex_setup(lua_State *L, LexState *ls)
   if (ls->current == '#') {  /* Skip POSIX #! header line. */
   if (ls->current == '#') {  /* Skip POSIX #! header line. */
     do {
     do {
       next(ls);
       next(ls);
-      if (ls->current == END_OF_STREAM) return;
+      if (ls->current == END_OF_STREAM) return 0;
     } while (!currIsNewline(ls));
     } while (!currIsNewline(ls));
     inclinenumber(ls);
     inclinenumber(ls);
   }
   }
-  if (ls->current == LUA_SIGNATURE[0]) {
-    setstrV(L, L->top++, lj_err_str(L, LJ_ERR_XBCLOAD));
-    lj_err_throw(L, LUA_ERRSYNTAX);
-  }
+  return (ls->current == LUA_SIGNATURE[0]);  /* Bytecode dump? */
 }
 }
 
 
 /* Cleanup lexer state. */
 /* Cleanup lexer state. */

+ 1 - 1
src/lj_lex.h

@@ -71,7 +71,7 @@ typedef struct LexState {
   uint32_t level;	/* Syntactical nesting level. */
   uint32_t level;	/* Syntactical nesting level. */
 } LexState;
 } LexState;
 
 
-LJ_FUNC void lj_lex_setup(lua_State *L, LexState *ls);
+LJ_FUNC int lj_lex_setup(lua_State *L, LexState *ls);
 LJ_FUNC void lj_lex_cleanup(lua_State *L, LexState *ls);
 LJ_FUNC void lj_lex_cleanup(lua_State *L, LexState *ls);
 LJ_FUNC void lj_lex_next(LexState *ls);
 LJ_FUNC void lj_lex_next(LexState *ls);
 LJ_FUNC LexToken lj_lex_lookahead(LexState *ls);
 LJ_FUNC LexToken lj_lex_lookahead(LexState *ls);

+ 5 - 5
src/lj_obj.h

@@ -310,11 +310,11 @@ typedef struct GCproto {
 } GCproto;
 } GCproto;
 
 
 /* Flags for prototype. */
 /* Flags for prototype. */
-#define PROTO_VARARG		0x01	/* Vararg function. */
-#define PROTO_CHILD		0x02	/* Has child prototypes. */
-#define PROTO_NOJIT		0x04	/* JIT disabled for this function. */
-#define PROTO_ILOOP		0x08	/* Patched bytecode with ILOOP etc. */
-#define PROTO_FFI		0x10	/* Uses BC_KCDATA for FFI datatypes. */
+#define PROTO_CHILD		0x01	/* Has child prototypes. */
+#define PROTO_VARARG		0x02	/* Vararg function. */
+#define PROTO_FFI		0x04	/* Uses BC_KCDATA for FFI datatypes. */
+#define PROTO_NOJIT		0x08	/* JIT disabled for this function. */
+#define PROTO_ILOOP		0x10	/* Patched bytecode with ILOOP etc. */
 /* Only used during parsing. */
 /* Only used during parsing. */
 #define PROTO_HAS_RETURN	0x20	/* Already emitted a return. */
 #define PROTO_HAS_RETURN	0x20	/* Already emitted a return. */
 #define PROTO_FIXUP_RETURN	0x40	/* Need to fixup emitted returns. */
 #define PROTO_FIXUP_RETURN	0x40	/* Need to fixup emitted returns. */

+ 2 - 0
src/ljamalg.c

@@ -46,6 +46,8 @@
 #include "lj_api.c"
 #include "lj_api.c"
 #include "lj_lex.c"
 #include "lj_lex.c"
 #include "lj_parse.c"
 #include "lj_parse.c"
+#include "lj_bcread.c"
+#include "lj_bcwrite.c"
 #include "lj_ctype.c"
 #include "lj_ctype.c"
 #include "lj_cdata.c"
 #include "lj_cdata.c"
 #include "lj_cconv.c"
 #include "lj_cconv.c"