summaryrefslogtreecommitdiff
path: root/source/luametatex/source/lua/lmtlualib.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/lua/lmtlualib.c')
-rw-r--r--source/luametatex/source/lua/lmtlualib.c627
1 files changed, 627 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmtlualib.c b/source/luametatex/source/lua/lmtlualib.c
new file mode 100644
index 000000000..b82ddc649
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlualib.c
@@ -0,0 +1,627 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ Some code here originates from the beginning of \LUATEX\ developmentm like the bytecode
+ registers. They provide a way to store (compiled) \LUA\ code in the format file. In the
+ meantime there are plenty of ways to use \LUA\ code in the frontend so an interface at the
+ \TEX\ end makes no longer much sense.
+
+ This module also provides some statistics and control options. Keep in mind that the engine
+ also can act as a \LUA\ engine, so some of that property is reflected in the code.
+
+*/
+
+# define LOAD_BUF_SIZE 64*1024
+# define UINT_MAX32 0xFFFFFFFF
+
+# define LUA_FUNCTIONS "lua.functions"
+# define LUA_BYTECODES "lua.bytecodes"
+# define LUA_BYTECODES_INDIRECT "lua.bytecodes.indirect"
+
+typedef struct bytecode {
+ unsigned char *buf;
+ int size;
+ int alloc;
+} bytecode;
+
+static bytecode *lmt_bytecode_registers = NULL;
+
+void lmt_dump_registers(dumpstream f)
+{
+ dump_int(f, lmt_lua_state.version_number);
+ dump_int(f, lmt_lua_state.release_number);
+ dump_int(f, lmt_lua_state.integer_size);
+ dump_int(f, lmt_lua_state.bytecode_max);
+ if (lmt_bytecode_registers) {
+ int n = 0;
+ for (int k = 0; k <= lmt_lua_state.bytecode_max; k++) {
+ if (lmt_bytecode_registers[k].size != 0) {
+ n++;
+ }
+ }
+ dump_int(f, n);
+ for (int k = 0; k <= lmt_lua_state.bytecode_max; k++) {
+ bytecode b = lmt_bytecode_registers[k];
+ if (b.size != 0) {
+ dump_int(f, k);
+ dump_int(f, b.size);
+ dump_items(f, (char *) b.buf, 1, b.size);
+ }
+ }
+ }
+}
+
+void lmt_undump_registers(dumpstream f)
+{
+ int version_number = 0;
+ int release_number = 0;
+ int integer_size = 0;
+ undump_int(f, version_number);
+ if (version_number != lmt_lua_state.version_number) {
+ tex_fatal_undump_error("mismatching Lua version number");
+ }
+ undump_int(f, release_number);
+ if (release_number != lmt_lua_state.release_number) {
+ tex_fatal_undump_error("mismatching Lua release number");
+ }
+ undump_int(f, integer_size);
+ if (integer_size != lmt_lua_state.integer_size) {
+ tex_fatal_undump_error("different integer size");
+ }
+ undump_int(f, lmt_lua_state.bytecode_max);
+ if (lmt_lua_state.bytecode_max < 0) {
+ tex_fatal_undump_error("not enough memory for undumping bytecodes"); /* old */
+ } else {
+ size_t s = (lmt_lua_state.bytecode_max + 1) * sizeof(bytecode);
+ int n = (int) s;
+ lmt_bytecode_registers = (bytecode *) lmt_memory_malloc(s);
+ if (lmt_bytecode_registers) {
+ lmt_lua_state.bytecode_bytes = n;
+ for (int j = 0; j <= lmt_lua_state.bytecode_max; j++) {
+ lmt_bytecode_registers[j].buf = NULL;
+ lmt_bytecode_registers[j].size = 0;
+ lmt_bytecode_registers[j].alloc = 0;
+ }
+ undump_int(f, n);
+ for (int j = 0; j < n; j++) {
+ unsigned char *buffer;
+ int slot, size;
+ undump_int(f, slot);
+ undump_int(f, size);
+ buffer = (unsigned char *) lmt_memory_malloc((unsigned) size);
+ if (buffer) {
+ memset(buffer, 0, (size_t) size);
+ undump_items(f, buffer, 1, size);
+ lmt_bytecode_registers[slot].buf = buffer;
+ lmt_bytecode_registers[slot].size = size;
+ lmt_bytecode_registers[slot].alloc = size;
+ lmt_lua_state.bytecode_bytes += size;
+ } else {
+ tex_fatal_undump_error("not enough memory for undumping bytecodes");
+ }
+ }
+ }
+ }
+}
+
+static void lualib_aux_bytecode_register_shadow_set(lua_State *L, int k)
+{
+ /*tex the stack holds the value to be set */
+ luaL_getmetatable(L, LUA_BYTECODES_INDIRECT);
+ if (lua_istable(L, -1)) {
+ lua_pushvalue(L, -2);
+ lua_rawseti(L, -2, k);
+ }
+ lua_pop(L, 2); /*tex pop table or nil and value */
+}
+
+static int lualib_aux_bytecode_register_shadow_get(lua_State *L, int k)
+{
+ /*tex the stack holds the value to be set */
+ int ret = 0;
+ luaL_getmetatable(L, LUA_BYTECODES_INDIRECT);
+ if (lua_istable(L, -1)) {
+ if (lua_rawgeti(L, -1, k) != LUA_TNIL) {
+ ret = 1;
+ }
+ /*tex store the value or nil, deeper down */
+ lua_insert(L, -3);
+ /*tex pop the value or nil at top */
+ lua_pop(L, 1);
+ }
+ /*tex pop table or nil */
+ lua_pop(L, 1);
+ return ret;
+}
+
+static int lualib_aux_writer(lua_State *L, const void *b, size_t size, void *B)
+{
+ bytecode *buf = (bytecode *) B;
+ (void) L;
+ if ((int) (buf->size + (int) size) > buf->alloc) {
+ unsigned newalloc = (unsigned) (buf->alloc + (int) size + LOAD_BUF_SIZE);
+ unsigned char *bb = lmt_memory_realloc(buf->buf, newalloc);
+ if (bb) {
+ buf->buf = bb;
+ buf->alloc = newalloc;
+ } else {
+ return luaL_error(L, "something went wrong with handling bytecodes");
+ }
+ }
+ memcpy(buf->buf + buf->size, b, size);
+ buf->size += (int) size;
+ lmt_lua_state.bytecode_bytes += (unsigned) size;
+ return 0;
+}
+
+static const char *lualib_aux_reader(lua_State *L, void *ud, size_t *size)
+{
+ bytecode *buf = (bytecode *) ud;
+ (void) L;
+ *size = (size_t) buf->size;
+ return (const char *) buf->buf;
+}
+
+static int lualib_valid_bytecode(lua_State *L, int slot)
+{
+ if (slot < 0 || slot > lmt_lua_state.bytecode_max) {
+ return luaL_error(L, "bytecode register out of range");
+ } else if (lualib_aux_bytecode_register_shadow_get(L, slot) || ! lmt_bytecode_registers[slot].buf) {
+ return luaL_error(L, "undefined bytecode register");
+ } else if (lua_load(L, lualib_aux_reader, (void *) (lmt_bytecode_registers + slot), "bytecode", NULL)) {
+ return luaL_error(L, "bytecode register doesn't load well");
+ } else {
+ return 1;
+ }
+}
+
+static int lualib_get_bytecode(lua_State *L)
+{
+ int slot = lmt_checkinteger(L, 1);
+ if (lualib_valid_bytecode(L, slot)) {
+ lua_pushvalue(L, -1);
+ lualib_aux_bytecode_register_shadow_set(L, slot);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int lmt_handle_bytecode_call(lua_State *L, int slot)
+{
+ int stacktop = lua_gettop(L);
+ int error = 1;
+ if (lualib_valid_bytecode(L, slot)) {
+ /*tex function index */
+ lua_pushinteger(L, slot);
+ /*tex push traceback function */
+ lua_pushcfunction(L, lmt_traceback);
+ /*tex put it under chunk */
+ lua_insert(L, stacktop);
+ ++lmt_lua_state.bytecode_callback_count;
+ error = lua_pcall(L, 1, 0, stacktop);
+ /*tex remove traceback function */
+ lua_remove(L, stacktop);
+ if (error) {
+ lua_gc(L, LUA_GCCOLLECT, 0);
+ lmt_error(L, "bytecode call", slot, (error == LUA_ERRRUN ? 0 : 1));
+ }
+ }
+ lua_settop(L, stacktop);
+ return ! error;
+}
+
+void lmt_bytecode_call(int slot)
+{
+ lmt_handle_bytecode_call(lmt_lua_state.lua_instance, slot);
+}
+
+/*tex
+ We don't report an error so this this permits a loop over the bytecode array.
+*/
+
+static int lualib_call_bytecode(lua_State *L)
+{
+ int k = lmt_checkinteger(L, -1);
+ if (k >= 0 && ! lualib_aux_bytecode_register_shadow_get(L, k)) {
+ if (k <= lmt_lua_state.bytecode_max && lmt_bytecode_registers[k].buf) {
+ lmt_handle_bytecode_call(L, k);
+ /* We can have a function pushed! */
+ } else {
+ k = -1;
+ }
+ } else {
+ k = -1;
+ }
+ lua_pushboolean(L, k != -1);
+ /*tex At most 1. */
+ return 1;
+}
+
+static int lualib_set_bytecode(lua_State *L)
+{
+ int k = lmt_checkinteger(L, 1);
+ int i = k + 1;
+ if ((k < 0) || (k > max_bytecode_index)) {
+ return luaL_error(L, "bytecode register out of range");
+ } else {
+ int ltype = lua_type(L, 2);
+ int strip = lua_toboolean(L, 3);
+ if (ltype != LUA_TFUNCTION && ltype != LUA_TNIL) {
+ return luaL_error(L, "bytecode register should be a function or nil");
+ } else {
+ /*tex Later calls expect the function at the top of the stack. */
+ lua_settop(L, 2);
+ if (k > lmt_lua_state.bytecode_max) {
+ bytecode *r = lmt_memory_realloc(lmt_bytecode_registers, (size_t) i * sizeof(bytecode));
+ if (r) {
+ lmt_bytecode_registers = r;
+ lmt_lua_state.bytecode_bytes += ((int) sizeof(bytecode) * (k + 1 - (lmt_lua_state.bytecode_max > 0 ? lmt_lua_state.bytecode_max : 0)));
+ for (unsigned j = (unsigned) (lmt_lua_state.bytecode_max + 1); j <= (unsigned) k; j++) {
+ lmt_bytecode_registers[j].buf = NULL;
+ lmt_bytecode_registers[j].size = 0;
+ lmt_bytecode_registers[j].alloc = 0;
+ }
+ lmt_lua_state.bytecode_max = k;
+ } else {
+ return luaL_error(L, "bytecode register exceeded memory");
+ }
+ }
+ if (lmt_bytecode_registers[k].buf) {
+ lmt_memory_free(lmt_bytecode_registers[k].buf);
+ lmt_lua_state.bytecode_bytes -= lmt_bytecode_registers[k].size;
+ lmt_bytecode_registers[k].size = 0;
+ lmt_bytecode_registers[k].buf = NULL;
+ lua_pushnil(L);
+ lualib_aux_bytecode_register_shadow_set(L, k);
+ }
+ if (ltype == LUA_TFUNCTION) {
+ lmt_bytecode_registers[k].buf = lmt_memory_calloc(1, LOAD_BUF_SIZE);
+ if (lmt_bytecode_registers[k].buf) {
+ lmt_bytecode_registers[k].alloc = LOAD_BUF_SIZE;
+ // memset(lua_bytecode_registers[k].buf, 0, LOAD_BUF_SIZE);
+ lua_dump(L, lualib_aux_writer, (void *) (lmt_bytecode_registers + k), strip);
+ } else {
+ return luaL_error(L, "bytecode register exceeded memory");
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ return 0;
+}
+
+void lmt_initialize_functions(int set_size)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (set_size) {
+ tex_engine_get_config_number("functionsize", &lmt_lua_state.function_table_size);
+ if (lmt_lua_state.function_table_size < 0) {
+ lmt_lua_state.function_table_size = 0;
+ }
+ lua_createtable(L, lmt_lua_state.function_table_size, 0);
+ } else {
+ lua_newtable(L);
+ }
+ lmt_lua_state.function_table_id = luaL_ref(L, LUA_REGISTRYINDEX);
+ /* not needed, so unofficial */
+ lua_pushstring(L, LUA_FUNCTIONS);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
+ lua_settable(L, LUA_REGISTRYINDEX);
+}
+
+static int lualib_get_functions_table(lua_State *L)
+{
+ if (lua_toboolean(L, lua_gettop(L))) {
+ /*tex Beware: this can have side effects when used without care. */
+ lmt_initialize_functions(1);
+ }
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
+ return 1;
+}
+
+static int lualib_new_table(lua_State *L)
+{
+ int i = lmt_checkinteger(L, 1);
+ int h = lmt_checkinteger(L, 2);
+ lua_createtable(L, i < 0 ? 0 : i, h < 0 ? 0 : h);
+ return 1;
+}
+
+static int lualib_new_index(lua_State *L)
+{
+ int n = lmt_checkinteger(L, 1);
+ int t = lua_gettop(L);
+ lua_createtable(L, n < 0 ? 0 : n, 0);
+ if (t == 2) {
+ for (lua_Integer i = 1; i <= n; i++) {
+ lua_pushvalue(L, 2);
+ lua_rawseti(L, -2, i);
+ }
+ }
+ return 1;
+}
+
+static int lualib_get_stack_top(lua_State *L)
+{
+ lua_pushinteger(L, lua_gettop(L));
+ return 1;
+}
+
+static int lualib_get_runtime(lua_State *L)
+{
+ lua_pushnumber(L, aux_get_run_time());
+ return 1;
+}
+
+static int lualib_get_currenttime(lua_State *L)
+{
+ lua_pushnumber(L, aux_get_current_time());
+ return 1;
+}
+
+static int lualib_set_exitcode(lua_State *L)
+{
+ lmt_error_state.default_exit_code = lmt_checkinteger(L, 1);
+ return 0;
+}
+
+static int lualib_get_exitcode(lua_State *L)
+{
+ lua_pushinteger(L, lmt_error_state.default_exit_code);
+ return 1;
+}
+
+/*tex
+
+ The |getpreciseticks()| call returns a number. This number has no meaning in itself but
+ successive calls can be used to calculate a delta with a previous call. When the number is fed
+ into |getpreciseseconds(n)| a number is returned representing seconds.
+
+*/
+
+# ifdef _WIN32
+
+# define clock_inittime()
+
+ static int lualib_get_preciseticks(lua_State *L)
+ {
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ lua_pushnumber(L, (double) t.QuadPart);
+ return 1;
+ }
+
+ static int lualib_get_preciseseconds(lua_State *L)
+ {
+ LARGE_INTEGER t;
+ QueryPerformanceFrequency(&t);
+ lua_pushnumber(L, luaL_optnumber(L, 1, 0) / (double) t.QuadPart);
+ return 1;
+ }
+
+# else
+
+# if (defined(__MACH__) && ! defined(CLOCK_PROCESS_CPUTIME_ID))
+
+ /* https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x */
+
+# include <mach/mach_time.h>
+# define CLOCK_PROCESS_CPUTIME_ID 1
+
+ static double conversion_factor;
+
+ static void clock_inittime()
+ {
+ mach_timebase_info_data_t timebase;
+ mach_timebase_info(&timebase);
+ conversion_factor = (double)timebase.numer / (double)timebase.denom;
+ }
+
+ static int clock_gettime(int clk_id, struct timespec *t)
+ {
+ uint64_t time;
+ double nseconds, seconds;
+ (void) clk_id; /* please the compiler */
+ time = mach_absolute_time();
+ nseconds = ((double)time * conversion_factor);
+ seconds = ((double)time * conversion_factor / 1e9);
+ t->tv_sec = seconds;
+ t->tv_nsec = nseconds;
+ return 0;
+ }
+
+# else
+
+# define clock_inittime()
+
+# endif
+
+ static int lualib_get_preciseticks(lua_State *L)
+ {
+ struct timespec t;
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);
+ lua_pushnumber(L, t.tv_sec*1000000000.0 + t.tv_nsec);
+ return 1;
+ }
+
+ static int lualib_get_preciseseconds(lua_State *L)
+ {
+ lua_pushnumber(L, ((double) luaL_optnumber(L, 1, 0)) / 1000000000.0);
+ return 1;
+ }
+
+# endif
+
+static int lualib_get_startupfile(lua_State *L)
+{
+ lua_pushstring(L, lmt_engine_state.startup_filename);
+ return 1;
+}
+
+static int lualib_get_version(lua_State *L)
+{
+ lua_pushstring(L, LUA_VERSION);
+ return 1;
+}
+
+/* obsolete:
+static int lualib_get_hashchars(lua_State *L)
+{
+ lua_pushinteger(L, 1 << LUAI_HASHLIMIT);
+ return 1;
+}
+*/
+
+/*
+static int lualib_get_doing_the(lua_State *L)
+{
+ lua_pushboolean(L, lua_state.doing_the);
+ return 1;
+}
+*/
+
+/* This makes the (already old and rusty) profiler 2.5 times faster. */
+
+/*
+static lua_State *getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ } else {
+ *arg = 0;
+ return L;
+ }
+}
+
+static int lualib_get_debug_info(lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_getstack(L1, 2, &ar) && lua_getinfo(L1, "nS", &ar)) {
+ ....
+ }
+ return 0;
+}
+*/
+
+/*
+static int lualib_get_debug_info(lua_State *L) {
+ if (! lua_isthread(L, 1)) {
+ lua_Debug ar;
+ if (lua_getstack(L, 2, &ar) && lua_getinfo(L, "nS", &ar)) {
+ lua_pushstring(L, ar.short_src);
+ lua_pushinteger(L, ar.linedefined);
+ if (ar.name) {
+ lua_pushstring(L, ar.name);
+ } else if (! strcmp(ar.what, "C")) {
+ lua_pushliteral(L, "<anonymous>");
+ } else if (ar.namewhat) {
+ lua_pushstring(L, ar.namewhat);
+ } else if (ar.what) {
+ lua_pushstring(L, ar.what);
+ } else {
+ lua_pushliteral(L, "<unknown>");
+ }
+ return 3;
+ }
+ }
+ return 0;
+}
+*/
+
+/*tex
+ I can make it faster if needed but then I need to patch the two lua modules (add some simple
+ helpers) which for now doesn't make much sense. This is an undocumented feature.
+*/
+
+static int lualib_get_debug_info(lua_State *L) {
+ if (! lua_isthread(L, 1)) {
+ lua_Debug ar;
+ if (lua_getstack(L, 2, &ar) && lua_getinfo(L, "nS", &ar)) {
+ lua_pushstring(L, ar.name ? ar.name : (ar.namewhat ? ar.namewhat : (ar.what ? ar.what : "<unknown>")));
+ lua_pushstring(L, ar.short_src);
+ lua_pushinteger(L, ar.linedefined);
+ return 3;
+ }
+ }
+ return 0;
+}
+
+/* */
+
+static const struct luaL_Reg lualib_function_list[] = {
+ { "newtable", lualib_new_table },
+ { "newindex", lualib_new_index },
+ { "getstacktop", lualib_get_stack_top },
+ { "getruntime", lualib_get_runtime },
+ { "getcurrenttime", lualib_get_currenttime },
+ { "getpreciseticks", lualib_get_preciseticks },
+ { "getpreciseseconds", lualib_get_preciseseconds },
+ { "getbytecode", lualib_get_bytecode },
+ { "setbytecode", lualib_set_bytecode },
+ { "callbytecode", lualib_call_bytecode },
+ { "getfunctionstable", lualib_get_functions_table },
+ { "getstartupfile", lualib_get_startupfile },
+ { "getversion", lualib_get_version },
+ /* { "gethashchars", lualib_get_hashchars }, */
+ { "setexitcode", lualib_set_exitcode },
+ { "getexitcode", lualib_get_exitcode },
+ /* { "doingthe", lualib_get_doing_the }, */
+ { "getdebuginfo", lualib_get_debug_info },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg lualib_function_list_only[] = {
+ { "newtable", lualib_new_table },
+ { "newindex", lualib_new_index },
+ { "getstacktop", lualib_get_stack_top },
+ { "getruntime", lualib_get_runtime },
+ { "getcurrenttime", lualib_get_currenttime },
+ { "getpreciseticks", lualib_get_preciseticks },
+ { "getpreciseseconds", lualib_get_preciseseconds },
+ { "getstartupfile", lualib_get_startupfile },
+ { "getversion", lualib_get_version },
+ /* { "gethashchars", lualib_get_hashchars }, */
+ { "setexitcode", lualib_set_exitcode },
+ { "getexitcode", lualib_get_exitcode },
+ { NULL, NULL },
+};
+
+static int lualib_index_bytecode(lua_State *L)
+{
+ lua_remove(L, 1);
+ return lualib_get_bytecode(L);
+}
+
+static int lualib_newindex_bytecode(lua_State *L)
+{
+ lua_remove(L, 1);
+ return lualib_set_bytecode(L);
+}
+
+int luaopen_lua(lua_State *L)
+{
+ lua_newtable(L);
+ if (lmt_engine_state.lua_only) {
+ luaL_setfuncs(L, lualib_function_list_only, 0);
+ } else {
+ luaL_setfuncs(L, lualib_function_list, 0);
+ lmt_make_table(L, "bytecode", LUA_BYTECODES, lualib_index_bytecode, lualib_newindex_bytecode);
+ lua_newtable(L);
+ lua_setfield(L, LUA_REGISTRYINDEX, LUA_BYTECODES_INDIRECT);
+ }
+ lua_pushstring(L, LUA_VERSION);
+ lua_setfield(L, -2, "version");
+ if (lmt_engine_state.startup_filename) {
+ lua_pushstring(L, lmt_engine_state.startup_filename);
+ lua_setfield(L, -2, "startupfile");
+ }
+ clock_inittime();
+ return 1;
+}