diff --git a/doc/status.html b/doc/status.html
index 1048adc4..c8366488 100644
--- a/doc/status.html
+++ b/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).
-Bytecode currently cannot be loaded or dumped. Note that
-the bytecode format differs from Lua 5.1 — loading foreign
-bytecode is not supported at all.
-
-
Some of the configuration options of Lua 5.1 are not supported:
- The number type cannot be changed (it's always a double).
diff --git a/src/Makefile b/src/Makefile
index 84f9d42f..e670953b 100644
--- a/src/Makefile
+++ b/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 \
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_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_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 \
diff --git a/src/Makefile.dep b/src/Makefile.dep
index 937fd67f..3a5667bc 100644
--- a/src/Makefile.dep
+++ b/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
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_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 \
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_lib.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_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_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_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 \
@@ -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_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_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_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
@@ -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_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_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
diff --git a/src/lib_string.c b/src/lib_string.c
index e54b2d50..5cbe6e4d 100644
--- a/src/lib_string.c
+++ b/src/lib_string.c
@@ -22,6 +22,7 @@
#include "lj_tab.h"
#include "lj_state.h"
#include "lj_ff.h"
+#include "lj_bcdump.h"
#include "lj_char.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)
{
- 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;
}
/* ------------------------------------------------------------------------ */
diff --git a/src/lj_api.c b/src/lj_api.c
index 34c57806..a6fbb1c6 100644
--- a/src/lj_api.c
+++ b/src/lj_api.c
@@ -24,6 +24,7 @@
#include "lj_trace.h"
#include "lj_vm.h"
#include "lj_lex.h"
+#include "lj_bcdump.h"
#include "lj_parse.h"
/* -- 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)
{
LexState *ls = (LexState *)ud;
+ GCproto *pt;
GCfunc *fn;
UNUSED(dummy);
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);
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)
{
+ cTValue *o = L->top-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 -------------------------------------------- */
diff --git a/src/lj_bcdump.h b/src/lj_bcdump.h
new file mode 100644
index 00000000..49b59e85
--- /dev/null
+++ b/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
diff --git a/src/lj_bcread.c b/src/lj_bcread.c
new file mode 100644
index 00000000..c5d4cd9d
--- /dev/null
+++ b/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);
+}
+
diff --git a/src/lj_bcwrite.c b/src/lj_bcwrite.c
new file mode 100644
index 00000000..b90f7850
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/lj_errmsg.h b/src/lj_errmsg.h
index 7b0c15cd..dc015ef2 100644
--- a/src/lj_errmsg.h
+++ b/src/lj_errmsg.h
@@ -84,7 +84,7 @@ ERRDEF(IOCLFL, "attempt to use a closed file")
ERRDEF(IOSTDCL, "standard file is closed")
ERRDEF(OSUNIQF, "unable to generate a unique filename")
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(STRPATB, "missing " LUA_QL("[") " after " LUA_QL("%f") " in pattern")
ERRDEF(STRPATC, "invalid pattern capture")
@@ -119,7 +119,6 @@ ERRDEF(XLCOM, "unfinished long comment")
ERRDEF(XSTR, "unfinished string")
ERRDEF(XESC, "invalid escape sequence")
ERRDEF(XLDELIM, "invalid long string delimiter")
-ERRDEF(XBCLOAD, "cannot load Lua bytecode")
ERRDEF(XTOKEN, LUA_QS " expected")
ERRDEF(XJUMP, "control structure too long")
ERRDEF(XSLOTS, "function or expression too complex")
@@ -137,6 +136,10 @@ ERRDEF(XSYNTAX, "syntax error")
ERRDEF(XBREAK, "no loop to break")
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
/* FFI errors. */
ERRDEF(FFI_INVTYPE, "invalid C type")
diff --git a/src/lj_func.c b/src/lj_func.c
index d7d37802..334ba4c8 100644
--- a/src/lj_func.c
+++ b/src/lj_func.c
@@ -65,6 +65,17 @@ static GCupval *func_finduv(lua_State *L, TValue *slot)
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. */
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;
}
-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));
fn->l.gct = ~LJ_TFUNC;
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). */
setmref(fn->l.pc, proto_bc(pt));
setgcref(fn->l.env, obj2gco(env));
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. */
GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent)
{
GCfunc *fn;
GCRef *puv;
- uint32_t i, nuv;
+ MSize i, nuv;
TValue *base;
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). */
puv = parent->uvptr;
- nuv = fn->l.nupvalues;
+ nuv = pt->sizeuv;
base = L->base;
for (i = 0; i < nuv; 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));
}
+ fn->l.nupvalues = (uint8_t)nuv;
return fn;
}
diff --git a/src/lj_func.h b/src/lj_func.h
index 7a3a2e3f..6ec4ab95 100644
--- a/src/lj_func.h
+++ b/src/lj_func.h
@@ -17,7 +17,7 @@ LJ_FUNC void LJ_FASTCALL lj_func_freeuv(global_State *g, GCupval *uv);
/* Functions (closures). */
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_FUNC void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *c);
diff --git a/src/lj_gc.c b/src/lj_gc.c
index b5614ea2..1985abc7 100644
--- a/src/lj_gc.c
+++ b/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));
if (isluafunc(fn)) {
uint32_t i;
- lua_assert(fn->l.nupvalues == funcproto(fn)->sizeuv);
+ lua_assert(fn->l.nupvalues <= funcproto(fn)->sizeuv);
gc_markobj(g, funcproto(fn));
for (i = 0; i < fn->l.nupvalues; i++) /* Mark Lua function upvalues. */
gc_markobj(g, &gcref(fn->l.uvptr[i])->uv);
diff --git a/src/lj_lex.c b/src/lj_lex.c
index 3411aa56..01e0c641 100644
--- a/src/lj_lex.c
+++ b/src/lj_lex.c
@@ -408,7 +408,7 @@ static int llex(LexState *ls, TValue *tv)
/* -- Lexer API ----------------------------------------------------------- */
/* 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->fs = NULL;
@@ -433,14 +433,11 @@ void lj_lex_setup(lua_State *L, LexState *ls)
if (ls->current == '#') { /* Skip POSIX #! header line. */
do {
next(ls);
- if (ls->current == END_OF_STREAM) return;
+ if (ls->current == END_OF_STREAM) return 0;
} while (!currIsNewline(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. */
diff --git a/src/lj_lex.h b/src/lj_lex.h
index 78a7e1a9..1ddf4b59 100644
--- a/src/lj_lex.h
+++ b/src/lj_lex.h
@@ -71,7 +71,7 @@ typedef struct LexState {
uint32_t level; /* Syntactical nesting level. */
} 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_next(LexState *ls);
LJ_FUNC LexToken lj_lex_lookahead(LexState *ls);
diff --git a/src/lj_obj.h b/src/lj_obj.h
index 3442dc0c..7cdc4a1a 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -310,11 +310,11 @@ typedef struct GCproto {
} GCproto;
/* 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. */
#define PROTO_HAS_RETURN 0x20 /* Already emitted a return. */
#define PROTO_FIXUP_RETURN 0x40 /* Need to fixup emitted returns. */
diff --git a/src/ljamalg.c b/src/ljamalg.c
index fcfb77ff..57ad7851 100644
--- a/src/ljamalg.c
+++ b/src/ljamalg.c
@@ -46,6 +46,8 @@
#include "lj_api.c"
#include "lj_lex.c"
#include "lj_parse.c"
+#include "lj_bcread.c"
+#include "lj_bcwrite.c"
#include "lj_ctype.c"
#include "lj_cdata.c"
#include "lj_cconv.c"