lmtlualib.c /size: 19 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    Some code here originates from the beginning of \LUATEX\ developmentm like the bytecode
10    registers. They provide a way to store (compiled) \LUA\ code in the format file. In the
11    meantime there are plenty of ways to use \LUA\ code in the frontend so an interface at the
12    \TEX\ end makes no longer much sense.
13
14    This module also provides some statistics and control options. Keep in mind that the engine
15    also can act as a \LUA\ engine, so some of that property is reflected in the code.
16
17*/
18
19# define LOAD_BUF_SIZE 64*1024
20# define UINT_MAX32    0xFFFFFFFF
21
22# define LUA_FUNCTIONS          "lua.functions"
23# define LUA_BYTECODES          "lua.bytecodes"
24# define LUA_BYTECODES_INDIRECT "lua.bytecodes.indirect"
25
26typedef struct bytecode {
27    unsigned char *buf;
28    int            size;
29    int            alloc;
30} bytecode;
31
32static bytecode *lmt_bytecode_registers = NULL;
33
34void lmt_dump_registers(dumpstream f)
35{
36    dump_int(f, lmt_lua_state.version_number);
37    dump_int(f, lmt_lua_state.release_number);
38    dump_int(f, lmt_lua_state.integer_size);
39    dump_int(f, lmt_lua_state.bytecode_max);
40    if (lmt_bytecode_registers) {
41        int n = 0;
42        for (int k = 0; k <= lmt_lua_state.bytecode_max; k++) {
43            if (lmt_bytecode_registers[k].size != 0) {
44                n++;
45            }
46        }
47        dump_int(f, n);
48        for (int k = 0; k <= lmt_lua_state.bytecode_max; k++) {
49            bytecode b = lmt_bytecode_registers[k];
50            if (b.size != 0) {
51                dump_int(f, k);
52                dump_int(f, b.size);
53                dump_items(f, (char *) b.buf, 1, b.size);
54            }
55        }
56    }
57}
58
59void lmt_undump_registers(dumpstream f)
60{
61    int version_number = 0;
62    int release_number = 0;
63    int integer_size = 0;
64    undump_int(f, version_number);
65    if (version_number != lmt_lua_state.version_number) {
66        tex_fatal_undump_error("mismatching Lua version number");
67    }
68    undump_int(f, release_number);
69    if (release_number != lmt_lua_state.release_number) {
70        tex_fatal_undump_error("mismatching Lua release number");
71    }
72    undump_int(f, integer_size);
73    if (integer_size != lmt_lua_state.integer_size) {
74        tex_fatal_undump_error("different integer size");
75    }
76    undump_int(f, lmt_lua_state.bytecode_max);
77    if (lmt_lua_state.bytecode_max < 0) {
78        tex_fatal_undump_error("not enough memory for undumping bytecodes"); /* old */
79    } else {
80        size_t s = (lmt_lua_state.bytecode_max + 1) * sizeof(bytecode);
81        int n = (int) s;
82        lmt_bytecode_registers = (bytecode *) lmt_memory_malloc(s);
83        if (lmt_bytecode_registers) {
84            lmt_lua_state.bytecode_bytes = n;
85            for (int j = 0; j <= lmt_lua_state.bytecode_max; j++) {
86                lmt_bytecode_registers[j].buf = NULL;
87                lmt_bytecode_registers[j].size = 0;
88                lmt_bytecode_registers[j].alloc = 0;
89            }
90            undump_int(f, n);
91            for (int j = 0; j < n; j++) {
92                unsigned char *buffer;
93                int slot, size;
94                undump_int(f, slot);
95                undump_int(f, size);
96                buffer = (unsigned char *) lmt_memory_malloc((unsigned) size);
97                if (buffer) {
98                    memset(buffer, 0, (size_t) size);
99                    undump_items(f, buffer, 1, size);
100                    lmt_bytecode_registers[slot].buf = buffer;
101                    lmt_bytecode_registers[slot].size = size;
102                    lmt_bytecode_registers[slot].alloc = size;
103                    lmt_lua_state.bytecode_bytes += size;
104                } else {
105                    tex_fatal_undump_error("not enough memory for undumping bytecodes");
106                }
107            }
108        }
109    }
110}
111
112static void lualib_aux_bytecode_register_shadow_set(lua_State *L, int k)
113{
114    /*tex the stack holds the value to be set */
115    luaL_getmetatable(L, LUA_BYTECODES_INDIRECT);
116    if (lua_istable(L, -1)) {
117        lua_pushvalue(L, -2);
118        lua_rawseti(L, -2, k);
119    }
120    lua_pop(L, 2); /*tex pop table or nil and value */
121}
122
123static int lualib_aux_bytecode_register_shadow_get(lua_State *L, int k)
124{
125    /*tex the stack holds the value to be set */
126    int ret = 0;
127    luaL_getmetatable(L, LUA_BYTECODES_INDIRECT);
128    if (lua_istable(L, -1)) {
129        if (lua_rawgeti(L, -1, k) != LUA_TNIL) {
130            ret = 1;
131        }
132        /*tex store the value or nil, deeper down  */
133        lua_insert(L, -3);
134        /*tex pop the value or nil at top */
135        lua_pop(L, 1);
136    }
137    /*tex pop table or nil */
138    lua_pop(L, 1);
139    return ret;
140}
141
142static int lualib_aux_writer(lua_State *L, const void *b, size_t size, void *B)
143{
144    bytecode *buf = (bytecode *) B;
145    (void) L;
146    if ((int) (buf->size + (int) size) > buf->alloc) {
147        unsigned newalloc = (unsigned) (buf->alloc + (int) size + LOAD_BUF_SIZE);
148        unsigned char *bb = lmt_memory_realloc(buf->buf, newalloc);
149        if (bb) {
150            buf->buf = bb;
151            buf->alloc = newalloc;
152        } else {
153            return luaL_error(L, "something went wrong with handling bytecodes");
154        }
155    }
156    memcpy(buf->buf + buf->size, b, size);
157    buf->size += (int) size;
158    lmt_lua_state.bytecode_bytes += (unsigned) size;
159    return 0;
160}
161
162static const char *lualib_aux_reader(lua_State *L, void *ud, size_t *size)
163{
164    bytecode *buf = (bytecode *) ud;
165    (void) L;
166    *size = (size_t) buf->size;
167    return (const char *) buf->buf;
168}
169
170static int lualib_valid_bytecode(lua_State *L, int slot)
171{
172    if (slot < 0 || slot > lmt_lua_state.bytecode_max) {
173        return luaL_error(L, "bytecode register out of range");
174    } else if (lualib_aux_bytecode_register_shadow_get(L, slot) || ! lmt_bytecode_registers[slot].buf) {
175        return luaL_error(L, "undefined bytecode register");
176    } else if (lua_load(L, lualib_aux_reader, (void *) (lmt_bytecode_registers + slot), "bytecode", NULL)) {
177        return luaL_error(L, "bytecode register doesn't load well");
178    } else {
179        return 1;
180    }
181}
182
183static int lualib_get_bytecode(lua_State *L)
184{
185    int slot = lmt_checkinteger(L, 1);
186    if (lualib_valid_bytecode(L, slot)) {
187        lua_pushvalue(L, -1);
188        lualib_aux_bytecode_register_shadow_set(L, slot);
189        return 1;
190    } else {
191        return 0;
192    }
193}
194
195static int lmt_handle_bytecode_call(lua_State *L, int slot)
196{
197    int stacktop = lua_gettop(L);
198    int error = 1;
199    if (lualib_valid_bytecode(L, slot)) {
200        /*tex function index */
201        lua_pushinteger(L, slot);
202        /*tex push traceback function */
203        lua_pushcfunction(L, lmt_traceback);
204        /*tex put it under chunk  */
205        lua_insert(L, stacktop);
206        ++lmt_lua_state.bytecode_callback_count;
207        error = lua_pcall(L, 1, 0, stacktop);
208        /*tex remove traceback function */
209        lua_remove(L, stacktop);
210        if (error) {
211            lua_gc(L, LUA_GCCOLLECT, 0);
212            lmt_error(L, "bytecode call", slot, (error == LUA_ERRRUN ? 0 : 1));
213        }
214    }
215    lua_settop(L, stacktop);
216    return ! error;
217}
218
219void lmt_bytecode_call(int slot)
220{
221    lmt_handle_bytecode_call(lmt_lua_state.lua_instance, slot);
222}
223
224/*tex
225    We don't report an error so this this permits a loop over the bytecode array.
226*/
227
228static int lualib_call_bytecode(lua_State *L)
229{
230    int k = lmt_checkinteger(L, -1);
231    if (k >= 0 && ! lualib_aux_bytecode_register_shadow_get(L, k)) {
232        if (k <= lmt_lua_state.bytecode_max && lmt_bytecode_registers[k].buf) {
233            lmt_handle_bytecode_call(L, k);
234            /* We can have a function pushed! */
235        } else {
236            k = -1;
237        }
238    } else {
239        k = -1;
240    }
241    lua_pushboolean(L, k != -1);
242    /*tex At most 1. */
243    return 1;
244}
245
246static int lualib_set_bytecode(lua_State *L)
247{
248    int k = lmt_checkinteger(L, 1);
249    int i = k + 1;
250    if ((k < 0) || (k > max_bytecode_index)) {
251        return luaL_error(L, "bytecode register out of range");
252    } else {
253        int ltype = lua_type(L, 2);
254        int strip = lua_toboolean(L, 3);
255        if (ltype != LUA_TFUNCTION && ltype != LUA_TNIL) {
256            return luaL_error(L, "bytecode register should be a function or nil");
257        } else {
258            /*tex Later calls expect the function at the top of the stack. */
259            lua_settop(L, 2);
260            if (k > lmt_lua_state.bytecode_max) {
261                bytecode *r = lmt_memory_realloc(lmt_bytecode_registers, (size_t) i * sizeof(bytecode));
262                if (r) {
263                    lmt_bytecode_registers = r;
264                    lmt_lua_state.bytecode_bytes += ((int) sizeof(bytecode) * (k + 1 - (lmt_lua_state.bytecode_max > 0 ? lmt_lua_state.bytecode_max : 0)));
265                    for (unsigned j = (unsigned) (lmt_lua_state.bytecode_max + 1); j <= (unsigned) k; j++) {
266                        lmt_bytecode_registers[j].buf = NULL;
267                        lmt_bytecode_registers[j].size = 0;
268                        lmt_bytecode_registers[j].alloc = 0;
269                    }
270                    lmt_lua_state.bytecode_max = k;
271                } else {
272                    return luaL_error(L, "bytecode register exceeded memory");
273                }
274            }
275            if (lmt_bytecode_registers[k].buf) {
276                lmt_memory_free(lmt_bytecode_registers[k].buf);
277                lmt_lua_state.bytecode_bytes -= lmt_bytecode_registers[k].size;
278                lmt_bytecode_registers[k].size = 0;
279                lmt_bytecode_registers[k].buf = NULL;
280                lua_pushnil(L);
281                lualib_aux_bytecode_register_shadow_set(L, k);
282            }
283            if (ltype == LUA_TFUNCTION) {
284                lmt_bytecode_registers[k].buf = lmt_memory_calloc(1, LOAD_BUF_SIZE);
285                if (lmt_bytecode_registers[k].buf) {
286                    lmt_bytecode_registers[k].alloc = LOAD_BUF_SIZE;
287                 // memset(lua_bytecode_registers[k].buf, 0, LOAD_BUF_SIZE);
288                    lua_dump(L, lualib_aux_writer, (void *) (lmt_bytecode_registers + k), strip);
289                } else {
290                    return luaL_error(L, "bytecode register exceeded memory");
291                }
292            }
293            lua_pop(L, 1);
294        }
295    }
296    return 0;
297}
298
299void lmt_initialize_functions(int set_size)
300{
301    lua_State *L = lmt_lua_state.lua_instance;
302    if (set_size) {
303        tex_engine_get_config_number("functionsize", &lmt_lua_state.function_table_size);
304        if (lmt_lua_state.function_table_size < 0) {
305            lmt_lua_state.function_table_size = 0;
306        }
307        lua_createtable(L, lmt_lua_state.function_table_size, 0);
308    } else {
309        lua_newtable(L);
310    }
311    lmt_lua_state.function_table_id = luaL_ref(L, LUA_REGISTRYINDEX);
312    /* not needed, so unofficial */
313    lua_pushstring(L, LUA_FUNCTIONS);
314    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
315    lua_settable(L, LUA_REGISTRYINDEX);
316}
317
318static int lualib_get_functions_table(lua_State *L)
319{
320    if (lua_toboolean(L, lua_gettop(L))) {
321        /*tex Beware: this can have side effects when used without care. */
322        lmt_initialize_functions(1);
323    }
324    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
325    return 1;
326}
327
328static int lualib_new_table(lua_State *L)
329{
330    int i = lmt_checkinteger(L, 1);
331    int h = lmt_checkinteger(L, 2);
332    lua_createtable(L, i < 0 ? 0 : i, h < 0 ? 0 : h);
333    return 1;
334}
335
336static int lualib_new_index(lua_State *L)
337{
338    int n = lmt_checkinteger(L, 1);
339    int t = lua_gettop(L);
340    lua_createtable(L, n < 0 ? 0 : n, 0);
341    if (t == 2) {
342        for (lua_Integer i = 1; i <= n; i++) {
343            lua_pushvalue(L, 2);
344            lua_rawseti(L, -2, i);
345        }
346    }
347    return 1;
348}
349
350static int lualib_get_stack_top(lua_State *L)
351{
352    lua_pushinteger(L, lua_gettop(L));
353    return 1;
354}
355
356static int lualib_get_runtime(lua_State *L)
357{
358    lua_pushnumber(L, aux_get_run_time());
359    return 1;
360}
361
362static int lualib_get_currenttime(lua_State *L)
363{
364    lua_pushnumber(L, aux_get_current_time());
365    return 1;
366}
367
368static int lualib_set_exitcode(lua_State *L)
369{
370    lmt_error_state.default_exit_code = lmt_checkinteger(L, 1);
371    return 0;
372}
373
374static int lualib_get_exitcode(lua_State *L)
375{
376    lua_pushinteger(L, lmt_error_state.default_exit_code);
377    return 1;
378}
379
380/*tex
381
382    The |getpreciseticks()| call returns a number. This number has no meaning in itself but
383    successive calls can be used to calculate a delta with a previous call. When the number is fed
384    into |getpreciseseconds(n)| a number is returned representing seconds.
385
386*/
387
388# ifdef _WIN32
389
390#   define clock_inittime()
391
392    static int lualib_get_preciseticks(lua_State *L)
393    {
394        LARGE_INTEGER t;
395        QueryPerformanceCounter(&t);
396        lua_pushnumber(L, (double) t.QuadPart);
397        return 1;
398    }
399
400    static int lualib_get_preciseseconds(lua_State *L)
401    {
402        LARGE_INTEGER t;
403        QueryPerformanceFrequency(&t);
404        lua_pushnumber(L, luaL_optnumber(L, 1, 0) / (double) t.QuadPart);
405        return 1;
406    }
407
408# else
409
410#   if (defined(__MACH__) && ! defined(CLOCK_PROCESS_CPUTIME_ID))
411
412        /* https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x */
413
414#       include <mach/mach_time.h>
415#       define CLOCK_PROCESS_CPUTIME_ID 1
416
417        static double conversion_factor;
418
419        static void clock_inittime()
420        {
421            mach_timebase_info_data_t timebase;
422            mach_timebase_info(&timebase);
423            conversion_factor = (double)timebase.numer / (double)timebase.denom;
424        }
425
426        static int clock_gettime(int clk_id, struct timespec *t)
427        {
428            uint64_t time;
429            double nseconds, seconds;
430            (void) clk_id; /* please the compiler */
431            time = mach_absolute_time();
432            nseconds = ((double)time * conversion_factor);
433            seconds  = ((double)time * conversion_factor / 1e9);
434            t->tv_sec = seconds;
435            t->tv_nsec = nseconds;
436            return 0;
437        }
438
439#   else
440
441#       define clock_inittime()
442
443#   endif
444
445    static int lualib_get_preciseticks(lua_State *L)
446    {
447        struct timespec t;
448        clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);
449        lua_pushnumber(L, t.tv_sec*1000000000.0 + t.tv_nsec);
450        return 1;
451    }
452
453    static int lualib_get_preciseseconds(lua_State *L)
454    {
455        lua_pushnumber(L, ((double) luaL_optnumber(L, 1, 0)) / 1000000000.0);
456        return 1;
457    }
458
459# endif
460
461static int lualib_get_startupfile(lua_State *L)
462{
463    lua_pushstring(L, lmt_engine_state.startup_filename);
464    return 1;
465}
466
467static int lualib_get_version(lua_State *L)
468{
469    lua_pushstring(L, LUA_VERSION);
470    return 1;
471}
472
473/* obsolete:
474static int lualib_get_hashchars(lua_State *L)
475{
476    lua_pushinteger(L, 1 << LUAI_HASHLIMIT);
477    return 1;
478}
479*/
480
481/*
482static int lualib_get_doing_the(lua_State *L)
483{
484    lua_pushboolean(L, lua_state.doing_the);
485    return 1;
486}
487*/
488
489/* This makes the (already old and rusty) profiler 2.5 times faster. */
490
491/*
492static lua_State *getthread (lua_State *L, int *arg) {
493    if (lua_isthread(L, 1)) {
494        *arg = 1;
495        return lua_tothread(L, 1);
496    } else {
497       *arg = 0;
498       return L;
499    }
500}
501
502static int lualib_get_debug_info(lua_State *L) {
503    lua_Debug ar;
504    int arg;
505    lua_State *L1 = getthread(L, &arg);
506    if (lua_getstack(L1, 2, &ar) && lua_getinfo(L1, "nS", &ar)) {
507        ....
508    }
509    return 0;
510}
511*/
512
513/*
514static int lualib_get_debug_info(lua_State *L) {
515    if (! lua_isthread(L, 1)) {
516        lua_Debug ar;
517        if (lua_getstack(L, 2, &ar) && lua_getinfo(L, "nS", &ar)) {
518            lua_pushstring(L, ar.short_src);
519            lua_pushinteger(L, ar.linedefined);
520            if (ar.name) {
521                lua_pushstring(L, ar.name);
522            } else if (! strcmp(ar.what, "C")) {
523                lua_pushliteral(L, "<anonymous>");
524            } else if (ar.namewhat) {
525                lua_pushstring(L, ar.namewhat);
526            } else if (ar.what) {
527                lua_pushstring(L, ar.what);
528            } else {
529                lua_pushliteral(L, "<unknown>");
530            }
531            return 3;
532        }
533    }
534    return 0;
535}
536*/
537
538/*tex
539    I can make it faster if needed but then I need to patch the two lua modules (add some simple
540    helpers) which for now doesn't make much sense. This is an undocumented feature.
541*/
542
543static int lualib_get_debug_info(lua_State *L) {
544    if (! lua_isthread(L, 1)) {
545        lua_Debug ar;
546        if (lua_getstack(L, 2, &ar) && lua_getinfo(L, "nS", &ar)) {
547            lua_pushstring(L, ar.name ? ar.name : (ar.namewhat ? ar.namewhat : (ar.what ? ar.what : "<unknown>")));
548            lua_pushstring(L, ar.short_src);
549            lua_pushinteger(L, ar.linedefined);
550            return 3;
551        }
552    }
553    return 0;
554}
555
556/* */
557
558static const struct luaL_Reg lualib_function_list[] = {
559    { "newtable",            lualib_new_table           },
560    { "newindex",            lualib_new_index           },
561    { "getstacktop",         lualib_get_stack_top       },
562    { "getruntime",          lualib_get_runtime         },
563    { "getcurrenttime",      lualib_get_currenttime     },
564    { "getpreciseticks",     lualib_get_preciseticks    },
565    { "getpreciseseconds",   lualib_get_preciseseconds  },
566    { "getbytecode",         lualib_get_bytecode        },
567    { "setbytecode",         lualib_set_bytecode        },
568    { "callbytecode",        lualib_call_bytecode       },
569    { "getfunctionstable",   lualib_get_functions_table },
570    { "getstartupfile",      lualib_get_startupfile     },
571    { "getversion",          lualib_get_version         },
572 /* { "gethashchars",        lualib_get_hashchars       }, */
573    { "setexitcode",         lualib_set_exitcode        },
574    { "getexitcode",         lualib_get_exitcode        },
575 /* { "doingthe",            lualib_get_doing_the       }, */
576    { "getdebuginfo",        lualib_get_debug_info      },
577    { NULL,                  NULL                       },
578};
579
580static const struct luaL_Reg lualib_function_list_only[] = {
581    { "newtable",            lualib_new_table          },
582    { "newindex",            lualib_new_index          },
583    { "getstacktop",         lualib_get_stack_top      },
584    { "getruntime",          lualib_get_runtime        },
585    { "getcurrenttime",      lualib_get_currenttime    },
586    { "getpreciseticks",     lualib_get_preciseticks   },
587    { "getpreciseseconds",   lualib_get_preciseseconds },
588    { "getstartupfile",      lualib_get_startupfile    },
589    { "getversion",          lualib_get_version        },
590 /* { "gethashchars",        lualib_get_hashchars      }, */
591    { "setexitcode",         lualib_set_exitcode       },
592    { "getexitcode",         lualib_get_exitcode       },
593    { NULL,                  NULL                      },
594};
595
596static int lualib_index_bytecode(lua_State *L)
597{
598    lua_remove(L, 1);
599    return lualib_get_bytecode(L);
600}
601
602static int lualib_newindex_bytecode(lua_State *L)
603{
604    lua_remove(L, 1);
605    return lualib_set_bytecode(L);
606}
607
608int luaopen_lua(lua_State *L)
609{
610    lua_newtable(L);
611    if (lmt_engine_state.lua_only) {
612        luaL_setfuncs(L, lualib_function_list_only, 0);
613    } else {
614        luaL_setfuncs(L, lualib_function_list, 0);
615        lmt_make_table(L, "bytecode", LUA_BYTECODES, lualib_index_bytecode, lualib_newindex_bytecode);
616        lua_newtable(L);
617        lua_setfield(L, LUA_REGISTRYINDEX, LUA_BYTECODES_INDIRECT);
618    }
619    lua_pushstring(L, LUA_VERSION);
620    lua_setfield(L, -2, "version");
621    if (lmt_engine_state.startup_filename) {
622        lua_pushstring(L, lmt_engine_state.startup_filename);
623        lua_setfield(L, -2, "startupfile");
624    }
625    clock_inittime();
626    return 1;
627}
628