From 4d315d5f248ac2a1fea77e4ab431bd3bbb78384a Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Tue, 28 May 2024 17:33:01 +0600 Subject: [PATCH 1/7] Initial Pallene Tracer implementation with custom debug traceback fn --- src/pallene/coder.lua | 43 +++++-- src/pallene/pallenelib.lua | 253 ++++++++++++++++++++++++++++++++++++- 2 files changed, 281 insertions(+), 15 deletions(-) diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index ed70a210..45d2fbe8 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -395,6 +395,7 @@ function Coder:pallene_entry_point_declaration(f_id) table.insert(args, {"lua_State *" , "L", ""}) table.insert(args, {"Udata * restrict " , "K", ""}) -- constants table table.insert(args, {"TValue * restrict ", "U" , ""}) -- upvalue array + table.insert(args, {"ptrdiff_t ", "frame_sig", ""}) for i = 1, #arg_types do local v_id = ir.arg_var(func, i) @@ -440,17 +441,25 @@ function Coder:pallene_entry_point_definition(f_id) local max_frame_size = self.gc[func].max_frame_size local slots_needed = max_frame_size + self.max_lua_call_stack_usage[func] + table.insert(prologue, util.render([[ + PALLENE_FRAMEENTER(L, "$name", frame_sig); + /**/ + ]], { + name = func.name + })); if slots_needed > 0 then table.insert(prologue, util.render([[ - if (!lua_checkstack(L, $n)) { pallene_runtime_cant_grow_stack_error(L); } + if (!lua_checkstack(L, $n)) { pallene_runtime_cant_grow_stack_error(L, $line); } ]], { - n = C.integer(slots_needed) + n = C.integer(slots_needed), + line = C.integer(func.loc and func.loc.line or 0) })) end table.insert(prologue, "StackValue *base = L->top.p;"); table.insert(prologue, self:savestack()) table.insert(prologue, "/**/") + for v_id = #arg_types + 1, #func.vars do -- To avoid -Wmaybe-uninitialized warnings we have to initialize our local variables of type -- "Any". Nils and Booleans only set the type tag of the TValue and leave the "._value" @@ -470,16 +479,18 @@ function Coder:pallene_entry_point_definition(f_id) ${prologue} /**/ ${body} + ${void_frameexit} } ]], { name_comment = C.comment(name_comment), fun_decl = self:pallene_entry_point_declaration(f_id), prologue = table.concat(prologue, "\n"), body = body, + void_frameexit = #func.typ.ret_types == 0 and "PALLENE_FRAMEEXIT(L);" or "" })) end -function Coder:call_pallene_function(dsts, f_id, cclosure, xs) +function Coder:call_pallene_function(dsts, f_id, cclosure, xs, lua_entry) local func = self.module.functions[f_id] local n_upvalues = #func.captured_vars @@ -498,6 +509,9 @@ function Coder:call_pallene_function(dsts, f_id, cclosure, xs) end table.insert(args, upvals) + -- The frame signature. + table.insert(args, "(ptrdiff_t) "..(lua_entry ~= nil and lua_entry or "NULL")) + for _, x in ipairs(xs) do table.insert(args, x) end @@ -555,11 +569,12 @@ function Coder:lua_entry_point_definition(f_id) local arity_check = util.render([[ int nargs = lua_gettop(L); if (l_unlikely(nargs != $nargs)) { - pallene_runtime_arity_error(L, $fname, $nargs, nargs); + pallene_runtime_arity_error(L, $fname, $nargs, nargs, $line); } ]], { nargs = C.integer(#arg_types), fname = C.string(fname), + line = C.integer(func.loc and func.loc.line or 0) }) local arg_vars = {} @@ -590,7 +605,8 @@ function Coder:lua_entry_point_definition(f_id) table.insert(ret_decls, C.declaration(ctype(typ), ret)..";") end - local call_pallene = self:call_pallene_function(ret_vars, f_id, "func", arg_vars) + local call_pallene = self:call_pallene_function(ret_vars, f_id, "func", arg_vars, + self:lua_entry_point_name(f_id)) local push_results = {} @@ -1392,7 +1408,9 @@ gen_cmd["CallStatic"] = function(self, cmd, func) end table.insert(parts, self:update_stack_top(func, cmd)) - table.insert(parts, self:call_pallene_function(dsts, f_id, cclosure, xs)) + table.insert(parts, string.format("PALLENE_SETLINE(%d);\n", + func.loc and func.loc.line or 0)) + table.insert(parts, self:call_pallene_function(dsts, f_id, cclosure, xs, nil)) table.insert(parts, self:restorestack()) return table.concat(parts, "\n") end @@ -1429,12 +1447,14 @@ gen_cmd["CallDyn"] = function(self, cmd, func) return util.render([[ ${update_stack_top} ${push_arguments} + PALLENE_SETLINE($line); lua_call(L, $nargs, $nrets); ${pop_results} ${restore_stack} ]], { update_stack_top = self:update_stack_top(func, cmd), push_arguments = table.concat(push_arguments, "\n"), + line = C.integer(func.loc and func.loc.line or 0), pop_results = table.concat(pop_results, "\n"), nargs = C.integer(#f_typ.arg_types), nrets = C.integer(#f_typ.ret_types), @@ -1573,7 +1593,7 @@ end gen_cmd["Return"] = function(self, cmd) if #cmd.srcs == 0 then - return [[ return; ]] + return [[ PALLENE_FRAMEEXIT(L); ]] else -- We assign the dsts from right to left, in order to match Lua's semantics when a -- destination variable appears more than once in the LHS. For example, in `x,x = f()`. @@ -1585,7 +1605,7 @@ gen_cmd["Return"] = function(self, cmd) util.render([[ *$reti = $v; ]], { reti = self:c_ret_var(i), v = src })) end local src1 = self:c_value(cmd.srcs[1]) - table.insert(returns, util.render([[ return $v; ]], { v = src1 })) + table.insert(returns, util.render([[ PALLENE_FRAMEEXIT(L, $v); ]], { v = src1 })) return table.concat(returns, "\n") end end @@ -1715,7 +1735,8 @@ function Coder:generate_module_header() table.insert(out, "/* This file was generated by the Pallene compiler. Do not edit by hand */") table.insert(out, "") - table.insert(out, string.format("#define PALLENE_SOURCE_FILE %s", C.string(self.filename))) + table.insert(out, string.format("#define PALLENE_SOURCE_FILE %s", C.string(self.filename))) + table.insert(out, string.format("#define PALLENE_SOURCE_FILE_WO_EXT %s", C.string(self.filename:match('([^%.]+)')))); table.insert(out, "") table.insert(out, "/* ------------------------ */") @@ -1821,6 +1842,10 @@ function Coder:generate_luaopen_function() #error "Lua version must be exactly 5.4.6" #endif + /* Initialize Pallene Tracer. */ + /**/ + pallene_tracer_init(L); + /**/ luaL_checkcoreversion(L); /**/ diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index 097892d0..d6f59bfa 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -43,9 +43,46 @@ return [==[ #include #include #include +#include #define PALLENE_UNREACHABLE __builtin_unreachable() +/* Part of Pallene Tracer. */ +#define PALLENE_FRAMEENTER(L, name, sig) pt_frame_t _frame = { \ + .fn_name = PALLENE_SOURCE_FILE_WO_EXT "." name, \ + .mod_name = PALLENE_SOURCE_FILE, \ + .line = -1 \ + }; \ + pallene_tracer_frameenter(L, &_frame, sig) +#define PALLENE_GLOBAL_SETLINE(L, line) pallene_tracer_global_setline(L, line) +#define PALLENE_SETLINE(line) pallene_tracer_setline(&_frame, line) +#define PALLENE_FRAMEEXIT(L, ...) pallene_tracer_frameexit(L); \ + return __VA_ARGS__ + +/* Pallene Tracer related data-structures. */ +typedef struct pt_frame { + /* Max name length is 127. */ + char fn_name[128]; + char mod_name[128]; + + /* Line number. */ + int line; + + /* The frame signature. */ + ptrdiff_t frame_sig; + + struct pt_frame *next; + struct pt_frame *prev; +} pt_frame_t; + +/* Pallene Tracer. */ +static void pallene_tracer_frameenter(lua_State *L, pt_frame_t * restrict frame, ptrdiff_t sig); +static void pallene_tracer_global_setline(lua_State *L, int line); +static void pallene_tracer_setline(pt_frame_t * restrict frame, int line); +static void pallene_tracer_frameexit(lua_State *L); +static int pallene_tracer_debug_traceback(lua_State *L); +static void pallene_tracer_init(lua_State *L); + /* Type tags */ static const char *pallene_type_name(lua_State *L, const TValue *v); static int pallene_is_truthy(const TValue *v); @@ -59,12 +96,12 @@ static void pallene_barrierback_unboxed(lua_State *L, GCObject *p, GCObject *v); /* Runtime errors */ static l_noret pallene_runtime_tag_check_error(lua_State *L, const char* file, int line, const char *expected_type_name, const TValue *received_type, const char *description_fmt, ...); -static l_noret pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received); +static l_noret pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received, int line); static l_noret pallene_runtime_divide_by_zero_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_mod_by_zero_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_number_to_integer_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_array_metatable_error(lua_State *L, const char* file, int line); -static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L); +static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L, int line); /* Arithmetic operators */ static lua_Integer pallene_int_divi(lua_State *L, lua_Integer m, lua_Integer n, const char* file, int line); @@ -90,12 +127,209 @@ static lua_Number pallene_math_log(lua_Integer x, lua_Integer base); static lua_Integer pallene_math_modf(lua_State *L, const char* file, int line, lua_Number n, lua_Number* out); /* Other builtins */ -static TString* pallene_string_char(lua_State *L, const char* file, int line, lua_Integer c); -static TString* pallene_string_sub(lua_State *L, TString *str, lua_Integer start, lua_Integer end); +static TString *pallene_string_char(lua_State *L, const char* file, int line, lua_Integer c); +static TString *pallene_string_sub(lua_State *L, TString *str, lua_Integer start, lua_Integer end); static TString *pallene_type_builtin(lua_State *L, TValue v); static TString *pallene_tostring(lua_State *L, const char* file, int line, TValue v); static void pallene_io_write(lua_State *L, TString *str); +/* Pallene tracer implementation. */ + +static void pallene_tracer_frameenter(lua_State *L, pt_frame_t * restrict frame, ptrdiff_t sig) { + pt_frame_t *head, *tail; + + lua_getglobal(L, "__pallene_tracer_stack_head"); + head = (pt_frame_t *) lua_topointer(L, -1); + lua_getglobal(L, "__pallene_tracer_stack_tail"); + tail = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 2); + + /* If there is no frame in the stack. */ + if(head == NULL) { + /* Just to be safe. */ + frame -> prev = NULL; + frame -> next = NULL; + + /* There is no other signature. */ + frame -> frame_sig = sig; + + head = frame; + tail = frame; + + goto out; + } + + /* The frame signature. */ + /* If we don't have any frame signature, that denotes the call was made from Pallene + environment. */ + frame -> frame_sig = sig == (ptrdiff_t) NULL ? tail -> frame_sig : sig; + + tail -> next = frame; + frame -> prev = tail; + + tail = frame; + +out: + /* Now update the registry. */ + lua_pushlightuserdata(L, head); + lua_setglobal(L, "__pallene_tracer_stack_head"); + lua_pushlightuserdata(L, tail); + lua_setglobal(L, "__pallene_tracer_stack_tail"); +} + +static void pallene_tracer_global_setline(lua_State *L, int line) { + lua_getglobal(L, "__pallene_tracer_stack_tail"); + pt_frame_t *tail = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 1); + + if(tail != NULL) + tail -> line = line; +} + +static void pallene_tracer_setline(pt_frame_t * restrict frame, int line) { + frame -> line = line; +} + +static void pallene_tracer_frameexit(lua_State *L) { + pt_frame_t *head, *tail; + + lua_getglobal(L, "__pallene_tracer_stack_head"); + head = (pt_frame_t *) lua_topointer(L, -1); + lua_getglobal(L, "__pallene_tracer_stack_tail"); + tail = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 2); + + /* We are popping the very last frame. */ + if(tail -> prev == NULL) { + tail = NULL; + head = NULL; + + goto out; + } + + tail -> prev -> next = NULL; + tail = tail -> prev; + +out: + /* Now update the registry. */ + lua_pushlightuserdata(L, head); + lua_setglobal(L, "__pallene_tracer_stack_head"); + lua_pushlightuserdata(L, tail); + lua_setglobal(L, "__pallene_tracer_stack_tail"); +} + +static int pallene_tracer_debug_traceback(lua_State *L) { + /* The debug traceback function frame. */ + pt_frame_t self = { + .fn_name = "pallene_tracer_debug_traceback", + .mod_name = "pallene_debug", + .line = 0 + }; + pallene_tracer_frameenter(L, &self, (ptrdiff_t) pallene_tracer_debug_traceback); + + const char *message = lua_tostring(L, 1); + fprintf(stderr, "Runtime error: %s\nStack traceback: \n", message); + + lua_getglobal(L, "__pallene_tracer_stack_tail"); + pt_frame_t *frame = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 1); + + /* For context switch from Pallene to Lua or vice versa. */ + /* We use the respective call-stack depending on the context. + * In Lua context we use the Lua call-stack and in Pallene context + * we use our self-maintained call stack. */ + /* 1: Lua, 0: Pallene */ + int context = 1; + + /* Current level of depth we are at in the Lua call stack. */ + int level = 0; + + /* Are we done iterating through all the call-frames in the Lua stack? */ + bool gstack = 0; + + /* Which context we were in previously? */ + /* The numeric representation is same as `context`. */ + int prev_context = 1; + + /* The frame signature. */ + ptrdiff_t frame_sig = frame -> frame_sig; + + while(true) { + /* If we are done iterating through the Lua stack and + there is no more frame left in Pallene Tracer stack, + we are done with the traceback. */ + if(!gstack && frame == NULL) + break; + + if(context) { + /* Get lua call stack information. */ + lua_Debug ar; + + if(!(gstack = lua_getstack(L, level, &ar))) + continue; + + level++; + + /* We need more info for a good traceback entry. */ + lua_getinfo(L, "Slnt", &ar); + + /* We have got a C frame. Time to make a context switch. */ + if(ar.what && !strcmp("C", ar.what)) { + lua_getinfo(L, "f", &ar); + frame_sig = (ptrdiff_t) lua_tocfunction(L, -1); + lua_pop(L, 1); + + context = 0; + } else { + fprintf(stderr, " %s:%d: in function '%s'\n", ar.short_src, ar.currentline, + ar.name != NULL ? ar.name : ""); + } + + prev_context = 1; + } else { + /* If the frame signature does not match, then it's just a normal + C function */ + if(frame == NULL || frame -> frame_sig != frame_sig) { + /* If we switched from Lua and the frame signature is + not known, then function is just a C function oblivious to Pallene and Lua. */ + if(prev_context == 1) + fprintf(stderr, " C Function: 0x%lx\n", (void *) frame_sig); + + context = 1; + goto pallene_stack_done; + } + + fprintf(stderr, " %s:%d: in function '%s'\n", frame -> mod_name, frame -> line, + frame -> fn_name); + + /* We are done, now go to the previous frame. */ + frame = frame -> prev; + +pallene_stack_done: + prev_context = 0; + } + } + + /* Self frame. */ + pallene_tracer_frameexit(L); + + return 0; +} + +static void pallene_tracer_init(lua_State *L) { + lua_getglobal(L, "__pallene_tracer_stack_head"); + + /* Setup the stack head, tail and custom pallene traceback fn. */ + if(l_likely(lua_isnil(L, -1) == true)) { + lua_pushlightuserdata(L, NULL); + lua_setglobal(L, "__pallene_tracer_stack_head"); + lua_pushlightuserdata(L, NULL); + lua_setglobal(L, "__pallene_tracer_stack_tail"); + + /* The debug traceback fn. */ + lua_register(L, "pallene_tracer_debug_traceback", pallene_tracer_debug_traceback); + } +} static const char *pallene_type_name(lua_State *L, const TValue *v) { @@ -172,12 +406,14 @@ static void pallene_runtime_tag_check_error( lua_pushfstring(L, ", expected %s but found %s", expected_type_name, received_type_name); lua_concat(L, 5); + PALLENE_GLOBAL_SETLINE(L, line); lua_error(L); PALLENE_UNREACHABLE; } -static void pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received) +static void pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received, int line) { + PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "wrong number of arguments to function '%s', expected %d but received %d", name, expected, received @@ -187,30 +423,35 @@ static void pallene_runtime_arity_error(lua_State *L, const char *name, int expe static void pallene_runtime_divide_by_zero_error(lua_State *L, const char* file, int line) { + PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: attempt to divide by zero", file, line); PALLENE_UNREACHABLE; } static void pallene_runtime_mod_by_zero_error(lua_State *L, const char* file, int line) { + PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: attempt to perform 'n%%0'", file, line); PALLENE_UNREACHABLE; } static void pallene_runtime_number_to_integer_error(lua_State *L, const char* file, int line) { + PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: conversion from float does not fit into integer", file, line); PALLENE_UNREACHABLE; } static void pallene_runtime_array_metatable_error(lua_State *L, const char* file, int line) { + PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: arrays in Pallene must not have a metatable", file, line); PALLENE_UNREACHABLE; } -static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L) +static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L, int line) { + PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "stack overflow"); PALLENE_UNREACHABLE; } From 9492e345cc09c3e88f725ef072bcfd36c5514dc4 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Sat, 1 Jun 2024 15:01:33 +0600 Subject: [PATCH 2/7] Some improvements in the code quality --- src/pallene/coder.lua | 1 - src/pallene/pallenelib.lua | 54 +++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index 45d2fbe8..8bb6ab84 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -1843,7 +1843,6 @@ function Coder:generate_luaopen_function() #endif /* Initialize Pallene Tracer. */ - /**/ pallene_tracer_init(L); /**/ luaL_checkcoreversion(L); diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index d6f59bfa..db4e48f5 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -61,9 +61,9 @@ return [==[ /* Pallene Tracer related data-structures. */ typedef struct pt_frame { - /* Max name length is 127. */ - char fn_name[128]; - char mod_name[128]; + /* Here we would store the function name and the module name. */ + const char *const fn_name; + const char *const mod_name; /* Line number. */ int line; @@ -147,11 +147,11 @@ static void pallene_tracer_frameenter(lua_State *L, pt_frame_t * restrict frame, /* If there is no frame in the stack. */ if(head == NULL) { /* Just to be safe. */ - frame -> prev = NULL; - frame -> next = NULL; + frame->prev = NULL; + frame->next = NULL; /* There is no other signature. */ - frame -> frame_sig = sig; + frame->frame_sig = sig; head = frame; tail = frame; @@ -162,10 +162,11 @@ static void pallene_tracer_frameenter(lua_State *L, pt_frame_t * restrict frame, /* The frame signature. */ /* If we don't have any frame signature, that denotes the call was made from Pallene environment. */ - frame -> frame_sig = sig == (ptrdiff_t) NULL ? tail -> frame_sig : sig; + if(sig) frame->frame_sig = sig; + else frame->frame_sig = tail->frame_sig; - tail -> next = frame; - frame -> prev = tail; + tail ->next = frame; + frame->prev = tail; tail = frame; @@ -183,11 +184,11 @@ static void pallene_tracer_global_setline(lua_State *L, int line) { lua_pop(L, 1); if(tail != NULL) - tail -> line = line; + tail->line = line; } static void pallene_tracer_setline(pt_frame_t * restrict frame, int line) { - frame -> line = line; + frame->line = line; } static void pallene_tracer_frameexit(lua_State *L) { @@ -200,15 +201,15 @@ static void pallene_tracer_frameexit(lua_State *L) { lua_pop(L, 2); /* We are popping the very last frame. */ - if(tail -> prev == NULL) { + if(tail->prev == NULL) { tail = NULL; head = NULL; goto out; } - tail -> prev -> next = NULL; - tail = tail -> prev; + tail->prev->next = NULL; + tail = tail->prev; out: /* Now update the registry. */ @@ -252,15 +253,9 @@ static int pallene_tracer_debug_traceback(lua_State *L) { int prev_context = 1; /* The frame signature. */ - ptrdiff_t frame_sig = frame -> frame_sig; - - while(true) { - /* If we are done iterating through the Lua stack and - there is no more frame left in Pallene Tracer stack, - we are done with the traceback. */ - if(!gstack && frame == NULL) - break; + ptrdiff_t frame_sig = frame->frame_sig; + while(gstack || frame != NULL) { if(context) { /* Get lua call stack information. */ lua_Debug ar; @@ -271,13 +266,11 @@ static int pallene_tracer_debug_traceback(lua_State *L) { level++; /* We need more info for a good traceback entry. */ - lua_getinfo(L, "Slnt", &ar); + lua_getinfo(L, "Slntf", &ar); /* We have got a C frame. Time to make a context switch. */ - if(ar.what && !strcmp("C", ar.what)) { - lua_getinfo(L, "f", &ar); + if(lua_iscfunction(L, -1)) { frame_sig = (ptrdiff_t) lua_tocfunction(L, -1); - lua_pop(L, 1); context = 0; } else { @@ -285,11 +278,12 @@ static int pallene_tracer_debug_traceback(lua_State *L) { ar.name != NULL ? ar.name : ""); } + lua_pop(L, 1); prev_context = 1; } else { /* If the frame signature does not match, then it's just a normal C function */ - if(frame == NULL || frame -> frame_sig != frame_sig) { + if(frame == NULL || frame->frame_sig != frame_sig) { /* If we switched from Lua and the frame signature is not known, then function is just a C function oblivious to Pallene and Lua. */ if(prev_context == 1) @@ -299,11 +293,11 @@ static int pallene_tracer_debug_traceback(lua_State *L) { goto pallene_stack_done; } - fprintf(stderr, " %s:%d: in function '%s'\n", frame -> mod_name, frame -> line, - frame -> fn_name); + fprintf(stderr, " %s:%d: in function '%s'\n", frame->mod_name, frame->line, + frame->fn_name); /* We are done, now go to the previous frame. */ - frame = frame -> prev; + frame = frame->prev; pallene_stack_done: prev_context = 0; From 4758fe5db63a9510e181607c2bb9947e6c14d323 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Tue, 4 Jun 2024 14:47:16 +0600 Subject: [PATCH 3/7] Traceback improvements, added global function name resover and --no-traceback option --- src/pallene/coder.lua | 98 +++++++--- src/pallene/driver.lua | 9 +- src/pallene/pallenec.lua | 29 +-- src/pallene/pallenelib.lua | 355 +++++++++++++++++++++++-------------- 4 files changed, 315 insertions(+), 176 deletions(-) diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index 8bb6ab84..b64917b7 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -23,8 +23,8 @@ local define_union = tagged_union.in_namespace(coder, "coder") local Coder local RecordCoder -function coder.generate(module, modname, pallene_filename) - local c = Coder.new(module, modname, pallene_filename) +function coder.generate(module, modname, pallene_filename, flags) + local c = Coder.new(module, modname, pallene_filename, flags) local code = c:generate_module_header() .. c:generate_module_body() return code, {} end @@ -57,10 +57,11 @@ end -- Coder = util.Class() -function Coder:init(module, modname, filename) +function Coder:init(module, modname, filename, flags) self.module = module self.modname = modname self.filename = filename + self.flags = flags self.current_func = false @@ -395,7 +396,6 @@ function Coder:pallene_entry_point_declaration(f_id) table.insert(args, {"lua_State *" , "L", ""}) table.insert(args, {"Udata * restrict " , "K", ""}) -- constants table table.insert(args, {"TValue * restrict ", "U" , ""}) -- upvalue array - table.insert(args, {"ptrdiff_t ", "frame_sig", ""}) for i = 1, #arg_types do local v_id = ir.arg_var(func, i) @@ -441,12 +441,15 @@ function Coder:pallene_entry_point_definition(f_id) local max_frame_size = self.gc[func].max_frame_size local slots_needed = max_frame_size + self.max_lua_call_stack_usage[func] - table.insert(prologue, util.render([[ - PALLENE_FRAMEENTER(L, "$name", frame_sig); - /**/ - ]], { - name = func.name - })); + if not self.flags.no_traceback then + table.insert(prologue, util.render([[ + PALLENE_C_FRAMEENTER(L, "$name"); + /**/ + ]], { + name = self.modname.."."..func.name + })); + end + if slots_needed > 0 then table.insert(prologue, util.render([[ if (!lua_checkstack(L, $n)) { pallene_runtime_cant_grow_stack_error(L, $line); } @@ -473,24 +476,29 @@ function Coder:pallene_entry_point_definition(f_id) local body = self:generate_cmd(func, func.body) + local void_frameexit = "" + if #func.typ.ret_types == 0 and not self.flags.no_traceback then + void_frameexit = "PALLENE_FRAMEEXIT(L);" + end + return (util.render([[ ${name_comment} ${fun_decl} { ${prologue} /**/ ${body} - ${void_frameexit} + ${void_fe} } ]], { name_comment = C.comment(name_comment), fun_decl = self:pallene_entry_point_declaration(f_id), prologue = table.concat(prologue, "\n"), body = body, - void_frameexit = #func.typ.ret_types == 0 and "PALLENE_FRAMEEXIT(L);" or "" + void_fe = void_frameexit })) end -function Coder:call_pallene_function(dsts, f_id, cclosure, xs, lua_entry) +function Coder:call_pallene_function(dsts, f_id, cclosure, xs) local func = self.module.functions[f_id] local n_upvalues = #func.captured_vars @@ -509,9 +517,6 @@ function Coder:call_pallene_function(dsts, f_id, cclosure, xs, lua_entry) end table.insert(args, upvals) - -- The frame signature. - table.insert(args, "(ptrdiff_t) "..(lua_entry ~= nil and lua_entry or "NULL")) - for _, x in ipairs(xs) do table.insert(args, x) end @@ -614,9 +619,23 @@ function Coder:lua_entry_point_definition(f_id) table.insert(push_results, self:push_to_stack(typ, ret_vars[i])) end + local frameenter = "" + if not self.flags.no_traceback then + frameenter = util.render([[ PALLENE_LUA_FRAMEENTER(L, (void *) $fun_name); ]], { + fun_name = self:lua_entry_point_name(f_id), + }) + end + + local frameexit = util.render(not self.flags.no_traceback + and [[ PALLENE_FRAMEEXIT(L, $nresults); ]] + or [[ return $nresults; ]], { + nresults = C.integer(#ret_types) + }); + return (util.render([[ ${fun_decl} { + ${lua_fenter} StackValue *base = L->ci->func.p; ${init_global_userdata} /**/ @@ -629,10 +648,11 @@ function Coder:lua_entry_point_definition(f_id) ${ret_decls} ${call_pallene} ${push_results} - return $nresults; + ${lua_fexit} } ]], { fun_decl = self:lua_entry_point_declaration(f_id), + lua_fenter = frameenter, init_global_userdata = init_global_userdata, arity_check = arity_check, arg_decls = table.concat(arg_decls, "\n"), @@ -640,7 +660,7 @@ function Coder:lua_entry_point_definition(f_id) ret_decls = table.concat(ret_decls, "\n"), call_pallene = call_pallene, push_results = table.concat(push_results, "\n"), - nresults = C.integer(#ret_types), + lua_fexit = frameexit })) end @@ -1408,8 +1428,12 @@ gen_cmd["CallStatic"] = function(self, cmd, func) end table.insert(parts, self:update_stack_top(func, cmd)) - table.insert(parts, string.format("PALLENE_SETLINE(%d);\n", - func.loc and func.loc.line or 0)) + + if not self.flags.no_traceback then + table.insert(parts, string.format("PALLENE_SETLINE(%d);\n", + func.loc and func.loc.line or 0)) + end + table.insert(parts, self:call_pallene_function(dsts, f_id, cclosure, xs, nil)) table.insert(parts, self:restorestack()) return table.concat(parts, "\n") @@ -1444,17 +1468,24 @@ gen_cmd["CallDyn"] = function(self, cmd, func) })) end + local setline = "" + if not self.flags.no_traceback then + setline = util.render([[ PALLENE_SETLINE($line); ]], { + line = C.integer(func.loc and func.loc.line or 0) + }) + end + return util.render([[ ${update_stack_top} ${push_arguments} - PALLENE_SETLINE($line); + ${setline} lua_call(L, $nargs, $nrets); ${pop_results} ${restore_stack} ]], { update_stack_top = self:update_stack_top(func, cmd), push_arguments = table.concat(push_arguments, "\n"), - line = C.integer(func.loc and func.loc.line or 0), + setline = setline, pop_results = table.concat(pop_results, "\n"), nargs = C.integer(#f_typ.arg_types), nrets = C.integer(#f_typ.ret_types), @@ -1593,7 +1624,7 @@ end gen_cmd["Return"] = function(self, cmd) if #cmd.srcs == 0 then - return [[ PALLENE_FRAMEEXIT(L); ]] + return self.flags.no_traceback and [[ return; ]] or [[ PALLENE_FRAMEEXIT(L); ]] else -- We assign the dsts from right to left, in order to match Lua's semantics when a -- destination variable appears more than once in the LHS. For example, in `x,x = f()`. @@ -1605,7 +1636,7 @@ gen_cmd["Return"] = function(self, cmd) util.render([[ *$reti = $v; ]], { reti = self:c_ret_var(i), v = src })) end local src1 = self:c_value(cmd.srcs[1]) - table.insert(returns, util.render([[ PALLENE_FRAMEEXIT(L, $v); ]], { v = src1 })) + table.insert(returns, util.render(self.flags.no_traceback and [[ return $v; ]] or [[ PALLENE_FRAMEEXIT(L, $v); ]], { v = src1 })) return table.concat(returns, "\n") end end @@ -1735,8 +1766,7 @@ function Coder:generate_module_header() table.insert(out, "/* This file was generated by the Pallene compiler. Do not edit by hand */") table.insert(out, "") - table.insert(out, string.format("#define PALLENE_SOURCE_FILE %s", C.string(self.filename))) - table.insert(out, string.format("#define PALLENE_SOURCE_FILE_WO_EXT %s", C.string(self.filename:match('([^%.]+)')))); + table.insert(out, string.format("#define PALLENE_SOURCE_FILE %s", C.string(self.filename))) table.insert(out, "") table.insert(out, "/* ------------------------ */") @@ -1827,6 +1857,17 @@ function Coder:generate_luaopen_function() init_function = self:lua_entry_point_name(1), }) + local init_pt = "" + + if not self.flags.no_traceback then + init_pt = [[ + /* Initialize Pallene Tracer. */ + pallene_tracer_init(L); + /**/ + ]] + end + + -- NOTE: Version compatibility -- --------------------------- -- We have both a compile-time and a run-time test. The compile-time test ensures that the @@ -1842,9 +1883,7 @@ function Coder:generate_luaopen_function() #error "Lua version must be exactly 5.4.6" #endif - /* Initialize Pallene Tracer. */ - pallene_tracer_init(L); - /**/ + ${init_pt} luaL_checkcoreversion(L); /**/ @@ -1869,6 +1908,7 @@ function Coder:generate_luaopen_function() } ]], { name = "luaopen_" .. self.modname, + init_pt = init_pt, n_upvalues = C.integer(#self.constants), init_constants = table.concat(init_constants, "\n"), init_initializers = init_initializers, diff --git a/src/pallene/driver.lua b/src/pallene/driver.lua index b16300a8..161c6613 100644 --- a/src/pallene/driver.lua +++ b/src/pallene/driver.lua @@ -99,7 +99,7 @@ function driver.compile_internal(filename, input, stop_after, opt_level) error("impossible") end -local function compile_pallene_to_c(pallene_filename, c_filename, mod_name, opt_level) +local function compile_pallene_to_c(pallene_filename, c_filename, mod_name, opt_level, flags) local input, err = driver.load_input(pallene_filename) if not input then return false, { err } @@ -111,7 +111,7 @@ local function compile_pallene_to_c(pallene_filename, c_filename, mod_name, opt_ end local c_code - c_code, errs = coder.generate(module, mod_name, pallene_filename) + c_code, errs = coder.generate(module, mod_name, pallene_filename, flags) if not c_code then return false, errs end @@ -178,7 +178,8 @@ end -- Writes the resulting output to [output_file_name] with extension [output_ext]. -- If [output_file_name] is nil then the output is written to a file in the same -- directory as [input_file_name] and having the same base name as the input file. -function driver.compile(argv0, opt_level, input_ext, output_ext, input_file_name, output_file_name) +function driver.compile(argv0, opt_level, input_ext, output_ext, + input_file_name, output_file_name, flags) local input_base_name, err = check_source_filename(argv0, input_file_name, input_ext) if not input_base_name then return false, {err} end @@ -212,7 +213,7 @@ function driver.compile(argv0, opt_level, input_ext, output_ext, input_file_name local f = compiler_steps[i].f local src = file_names[i] local out = file_names[i+1] - ok, errs = f(src, out, mod_name, opt_level) + ok, errs = f(src, out, mod_name, opt_level, flags) if not ok then break end end diff --git a/src/pallene/pallenec.lua b/src/pallene/pallenec.lua index 85e650ed..261427eb 100644 --- a/src/pallene/pallenec.lua +++ b/src/pallene/pallenec.lua @@ -26,13 +26,16 @@ do -- What the compiler should output. p:mutex( - p:flag("--emit-c", "Generate a .c file instead of an executable"), - p:flag("--emit-lua", "Generate a .lua file instead of an executable"), - p:flag("--compile-c", "Compile a .c file generated by --emit-c"), - p:flag("--only-check","Check for syntax or type errors, without compiling"), - p:flag("--print-ir", "Show the intermediate representation for a program") + p:flag("--emit-c", "Generate a .c file instead of an executable"), + p:flag("--emit-lua", "Generate a .lua file instead of an executable"), + p:flag("--compile-c", "Compile a .c file generated by --emit-c"), + p:flag("--only-check", "Check for syntax or type errors, without compiling"), + p:flag("--print-ir", "Show the intermediate representation for a program") ) + -- No Pallene tracebacks + p:flag("--no-traceback", "No function traceback using Pallene Tracer") + p:option("-O", "Optimization level") :args(1):convert(tonumber) :choices({"0", "1", "2", "3"}) @@ -43,9 +46,9 @@ do opts = p:parse() end -local function compile(in_ext, out_ext) +local function compile(in_ext, out_ext, flags) local ok, errs = driver.compile(compiler_name, opts.O, in_ext, out_ext, opts.source_file, - opts.output) + opts.output, flags) if not ok then util.abort(table.concat(errs, "\n")) end end @@ -69,12 +72,16 @@ local function do_print_ir() end function pallenec.main() - if opts.emit_c then compile("pln", "c") - elseif opts.emit_lua then compile("pln", "lua") - elseif opts.compile_c then compile("c" , "so") + local flags = { + no_traceback = opts.no_traceback and true or false + } + + if opts.emit_c then compile("pln", "c", flags) + elseif opts.emit_lua then compile("pln", "lua", flags) + elseif opts.compile_c then compile("c" , "so", flags) elseif opts.only_check then do_check() elseif opts.print_ir then do_print_ir() - else --[[default]] compile("pln", "so") + else --[[default]] compile("pln", "so", flags) end end diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index db4e48f5..3aec6615 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -48,37 +48,64 @@ return [==[ #define PALLENE_UNREACHABLE __builtin_unreachable() /* Part of Pallene Tracer. */ -#define PALLENE_FRAMEENTER(L, name, sig) pt_frame_t _frame = { \ - .fn_name = PALLENE_SOURCE_FILE_WO_EXT "." name, \ - .mod_name = PALLENE_SOURCE_FILE, \ - .line = -1 \ - }; \ - pallene_tracer_frameenter(L, &_frame, sig) +#define PALLENE_C_FRAMEENTER(L, name) pt_fn_details_t _details = { \ + .fn_name = name, \ + .mod_name = PALLENE_SOURCE_FILE \ + }; \ + pt_frame_t _frame = { \ + .type = PALLENE_TRACER_FRAME_TYPE_C, \ + .shared = { \ + .details = &_details \ + } \ + }; \ + pallene_tracer_frameenter(L, &_frame) + +#define PALLENE_LUA_FRAMEENTER(L, sig) pt_frame_t _frame = { \ + .type = PALLENE_TRACER_FRAME_TYPE_LUA, \ + .shared = { \ + .frame_sig = sig \ + } \ + }; \ + pallene_tracer_frameenter(L, &_frame) + #define PALLENE_GLOBAL_SETLINE(L, line) pallene_tracer_global_setline(L, line) #define PALLENE_SETLINE(line) pallene_tracer_setline(&_frame, line) -#define PALLENE_FRAMEEXIT(L, ...) pallene_tracer_frameexit(L); \ +#define PALLENE_FRAMEEXIT(L, ...) pallene_tracer_frameexit(L); \ return __VA_ARGS__ -/* Pallene Tracer related data-structures. */ -typedef struct pt_frame { - /* Here we would store the function name and the module name. */ + +/* PALLENE TRACER RELATED DATA-STRUCTURES. */ + +/* Whether the frame is a Pallene->Pallene or Lua->Pallene call. */ +typedef enum frame_type { + PALLENE_TRACER_FRAME_TYPE_C, + PALLENE_TRACER_FRAME_TYPE_LUA +} frame_type_t; + +/* Details of a single function such as what is the name + and where it is from. */ +typedef struct pt_fn_details { const char *const fn_name; const char *const mod_name; +} pt_fn_details_t; - /* Line number. */ +/* A single frame representation. */ +typedef struct pt_frame { + frame_type_t type; int line; - /* The frame signature. */ - ptrdiff_t frame_sig; + union { + const pt_fn_details_t *details; + const void *frame_sig; + } shared; - struct pt_frame *next; struct pt_frame *prev; } pt_frame_t; /* Pallene Tracer. */ -static void pallene_tracer_frameenter(lua_State *L, pt_frame_t * restrict frame, ptrdiff_t sig); +static void pallene_tracer_frameenter(lua_State *L, pt_frame_t *restrict frame); static void pallene_tracer_global_setline(lua_State *L, int line); -static void pallene_tracer_setline(pt_frame_t * restrict frame, int line); +static void pallene_tracer_setline(pt_frame_t *restrict frame, int line); static void pallene_tracer_frameexit(lua_State *L); static int pallene_tracer_debug_traceback(lua_State *L); static void pallene_tracer_init(lua_State *L); @@ -135,190 +162,254 @@ static void pallene_io_write(lua_State *L, TString *str); /* Pallene tracer implementation. */ -static void pallene_tracer_frameenter(lua_State *L, pt_frame_t * restrict frame, ptrdiff_t sig) { - pt_frame_t *head, *tail; +/* Private routines. */ - lua_getglobal(L, "__pallene_tracer_stack_head"); - head = (pt_frame_t *) lua_topointer(L, -1); - lua_getglobal(L, "__pallene_tracer_stack_tail"); - tail = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 2); +static bool _findfield(lua_State *L, int fn_idx, int level) { + if(level == 0 || !lua_istable(L, -1)) + return false; - /* If there is no frame in the stack. */ - if(head == NULL) { - /* Just to be safe. */ - frame->prev = NULL; - frame->next = NULL; + lua_pushnil(L); /* Initial key. */ - /* There is no other signature. */ - frame->frame_sig = sig; + while(lua_next(L, -2)) { + /* We are only interested in String keys. */ + if(lua_type(L, -2) == LUA_TSTRING) { + /* Avoid "_G" recursion in global table. The global table is also part of + global table. */ + if(!strcmp(lua_tostring(L, -2), "_G")) { + /* Remove value and continue. */ + lua_pop(L, 1); + continue; + } - head = frame; - tail = frame; + /* Is it the function we are looking for? */ + if(lua_rawequal(L, fn_idx, -1)) { + /* Remove value and keep name. */ + lua_pop(L, 1); - goto out; + return true; + } + /* If not go one level deeper and get the value recursively. */ + if(_findfield(L, fn_idx, level - 1)) { + /* Remove the table but keep name. */ + lua_remove(L, -2); + + /* Add a "." in between. */ + lua_pushliteral(L, "."); + lua_insert(L, -2); + + /* Concatenate last 3 values, resulting "table.some_func". */ + lua_concat(L, 3); + + return true; + } + } + + /* Pop the value. */ + lua_pop(L, 1); } - /* The frame signature. */ - /* If we don't have any frame signature, that denotes the call was made from Pallene - environment. */ - if(sig) frame->frame_sig = sig; - else frame->frame_sig = tail->frame_sig; + return false; +} + +/* Pushes a function name if found in the global table and returns true. + Returns false otherwise. */ +/* Expects the funtion to be pushed in the stack. */ +static bool _pgf_name(lua_State *L) { + int top = lua_gettop(L); + + lua_pushglobaltable(L); + + if(_findfield(L, top, 2)) { + lua_remove(L, -2); + + return true; + } + + lua_pop(L, 1); + return false; +} + +/* Private routines end. */ + +static void pallene_tracer_frameenter(lua_State *L, pt_frame_t *restrict frame) { + /* Retrieve the end of the stack. */ + lua_getglobal(L, "__pallene_tracer_stack"); + pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 1); + + /* If there is no frame in the stack. */ + /* No matter what type of frame we got (Lua or plain C), it will be + in the general stack. */ + if(l_unlikely(stack == NULL)) { + frame->prev = NULL; + stack = frame; - tail ->next = frame; - frame->prev = tail; + goto out; + } - tail = frame; + frame->prev = stack; + stack = frame; out: - /* Now update the registry. */ - lua_pushlightuserdata(L, head); - lua_setglobal(L, "__pallene_tracer_stack_head"); - lua_pushlightuserdata(L, tail); - lua_setglobal(L, "__pallene_tracer_stack_tail"); + lua_pushlightuserdata(L, stack); + lua_setglobal(L, "__pallene_tracer_stack"); } static void pallene_tracer_global_setline(lua_State *L, int line) { - lua_getglobal(L, "__pallene_tracer_stack_tail"); - pt_frame_t *tail = (pt_frame_t *) lua_topointer(L, -1); + lua_getglobal(L, "__pallene_tracer_stack"); + pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); lua_pop(L, 1); - if(tail != NULL) - tail->line = line; + if(stack != NULL) + stack->line = line; } -static void pallene_tracer_setline(pt_frame_t * restrict frame, int line) { +static void pallene_tracer_setline(pt_frame_t *restrict frame, int line) { frame->line = line; } static void pallene_tracer_frameexit(lua_State *L) { - pt_frame_t *head, *tail; - - lua_getglobal(L, "__pallene_tracer_stack_head"); - head = (pt_frame_t *) lua_topointer(L, -1); - lua_getglobal(L, "__pallene_tracer_stack_tail"); - tail = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 2); + /* Retrieve the end of the stack. */ + lua_getglobal(L, "__pallene_tracer_stack"); + pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 1); /* We are popping the very last frame. */ - if(tail->prev == NULL) { - tail = NULL; - head = NULL; - + if(stack->prev == NULL) { + stack = NULL; goto out; } - tail->prev->next = NULL; - tail = tail->prev; + stack = stack->prev; out: - /* Now update the registry. */ - lua_pushlightuserdata(L, head); - lua_setglobal(L, "__pallene_tracer_stack_head"); - lua_pushlightuserdata(L, tail); - lua_setglobal(L, "__pallene_tracer_stack_tail"); + lua_pushlightuserdata(L, stack); + lua_setglobal(L, "__pallene_tracer_stack"); } static int pallene_tracer_debug_traceback(lua_State *L) { - /* The debug traceback function frame. */ - pt_frame_t self = { - .fn_name = "pallene_tracer_debug_traceback", - .mod_name = "pallene_debug", - .line = 0 - }; - pallene_tracer_frameenter(L, &self, (ptrdiff_t) pallene_tracer_debug_traceback); - const char *message = lua_tostring(L, 1); fprintf(stderr, "Runtime error: %s\nStack traceback: \n", message); - lua_getglobal(L, "__pallene_tracer_stack_tail"); - pt_frame_t *frame = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 1); - - /* For context switch from Pallene to Lua or vice versa. */ - /* We use the respective call-stack depending on the context. - * In Lua context we use the Lua call-stack and in Pallene context - * we use our self-maintained call stack. */ - /* 1: Lua, 0: Pallene */ + /* Lua: 1, Pallene: 0 */ int context = 1; + int level = 1; + bool l_stack = true; + void *f_sig = NULL; - /* Current level of depth we are at in the Lua call stack. */ - int level = 0; - - /* Are we done iterating through all the call-frames in the Lua stack? */ - bool gstack = 0; - - /* Which context we were in previously? */ - /* The numeric representation is same as `context`. */ - int prev_context = 1; + lua_getglobal(L, "__pallene_tracer_stack"); + pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); + lua_pop(L, 1); - /* The frame signature. */ - ptrdiff_t frame_sig = frame->frame_sig; + /* To store lua call stack information, to use it in both contexts. */ + lua_Debug ar; - while(gstack || frame != NULL) { - if(context) { - /* Get lua call stack information. */ - lua_Debug ar; + /* We will restore to this top everytime. */ + int top = lua_gettop(L); - if(!(gstack = lua_getstack(L, level, &ar))) + while(l_stack || stack != NULL) { + /* Generally, we would spend most of our time dealing with Pallene->Pallene calls. */ + if(l_unlikely(context == 1)) { + if(!(l_stack = lua_getstack(L, level++, &ar))) continue; - level++; - /* We need more info for a good traceback entry. */ + /* Also push the function on the stack. */ lua_getinfo(L, "Slntf", &ar); /* We have got a C frame. Time to make a context switch. */ if(lua_iscfunction(L, -1)) { - frame_sig = (ptrdiff_t) lua_tocfunction(L, -1); - + /* Set the signature and switch to Pallene stack. */ + f_sig = (void *) lua_tocfunction(L, -1); context = 0; } else { - fprintf(stderr, " %s:%d: in function '%s'\n", ar.short_src, ar.currentline, - ar.name != NULL ? ar.name : ""); + /* It's a regular Lua function. */ + + /* Do we have a name? */ + if(*ar.namewhat != '\0') + lua_pushfstring(L, "function '%s'", ar.name); + /* Is it the main chunk? */ + else if(*ar.what == 'm') + lua_pushliteral(L, "
"); + /* Can we deduce the name from the global table? */ + else if(_pgf_name(L)) + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + else lua_pushliteral(L, "function ''"); + + fprintf(stderr, " %s:%d: in %s\n", ar.short_src, + ar.currentline, lua_tostring(L, -1)); } - lua_pop(L, 1); - prev_context = 1; + lua_settop(L, top); } else { - /* If the frame signature does not match, then it's just a normal - C function */ - if(frame == NULL || frame->frame_sig != frame_sig) { - /* If we switched from Lua and the frame signature is - not known, then function is just a C function oblivious to Pallene and Lua. */ - if(prev_context == 1) - fprintf(stderr, " C Function: 0x%lx\n", (void *) frame_sig); + /* We can still use the debug structure storing the last Lua call info. */ + lua_getinfo(L, "f", &ar); + + /* Deduce name from global table. */ + if(_pgf_name(L)) + lua_pushfstring(L, "C: in function '%s'", lua_tostring(L, -1)); + else lua_pushfstring(L, "C: in function '0x%p'", f_sig); + + if(stack == NULL) { + if(f_sig != NULL) + fprintf(stderr, " %s\n", lua_tostring(L, -1)); context = 1; - goto pallene_stack_done; + goto end; } - fprintf(stderr, " %s:%d: in function '%s'\n", frame->mod_name, frame->line, - frame->fn_name); + if(f_sig != NULL) { + /* Check if the frame signature matches. */ + pt_frame_t *check = stack; + + while(check->type != PALLENE_TRACER_FRAME_TYPE_LUA) + check = check->prev; + + /* It's an untracked C function. */ + if(f_sig != check->shared.frame_sig) { + fprintf(stderr, " %s\n", lua_tostring(L, -1)); + + /* Now we switch to Lua stack. */ + context = 1; + goto end; + } + + /* Bingo! We have found a signature. Erase the signature so that + in the next iteration we don't care about rechecking the + frame signature agian. */ + f_sig = NULL; + } - /* We are done, now go to the previous frame. */ - frame = frame->prev; + /* If we find a Lua interface, we simply ignore and switch. */ + if(stack->type == PALLENE_TRACER_FRAME_TYPE_LUA) { + stack = stack->prev; -pallene_stack_done: - prev_context = 0; + context = 1; + goto end; + } + + fprintf(stderr, " %s:%d: in function '%s'\n", stack->shared.details->mod_name, + stack->line, stack->shared.details->fn_name); + + stack = stack->prev; + + end: + lua_settop(L, top); } } - /* Self frame. */ - pallene_tracer_frameexit(L); - return 0; } static void pallene_tracer_init(lua_State *L) { - lua_getglobal(L, "__pallene_tracer_stack_head"); + lua_getglobal(L, "__pallene_tracer_stack"); - /* Setup the stack head, tail and custom pallene traceback fn. */ + /* Setup the state and pallene traceback fn. */ if(l_likely(lua_isnil(L, -1) == true)) { + /* The first value is the tail of the LinkedList stack. */ lua_pushlightuserdata(L, NULL); - lua_setglobal(L, "__pallene_tracer_stack_head"); - lua_pushlightuserdata(L, NULL); - lua_setglobal(L, "__pallene_tracer_stack_tail"); + + lua_setglobal(L, "__pallene_tracer_stack"); /* The debug traceback fn. */ lua_register(L, "pallene_tracer_debug_traceback", pallene_tracer_debug_traceback); From dd685259fd58910ddd72e3eaf162e72a39971c27 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Fri, 7 Jun 2024 19:16:05 +0600 Subject: [PATCH 4/7] Added traceback tests and code improvements --- spec/traceback/rect/main.lua | 12 +++++ spec/traceback/rect/rect.pln | 17 +++++++ spec/traceback_spec.lua | 30 +++++++++++ src/pallene/coder.lua | 99 +++++++++++++++++++++++------------- src/pallene/pallenec.lua | 4 +- src/pallene/pallenelib.lua | 80 +++++++++++------------------ 6 files changed, 157 insertions(+), 85 deletions(-) create mode 100644 spec/traceback/rect/main.lua create mode 100644 spec/traceback/rect/rect.pln create mode 100644 spec/traceback_spec.lua diff --git a/spec/traceback/rect/main.lua b/spec/traceback/rect/main.lua new file mode 100644 index 00000000..e10f30d9 --- /dev/null +++ b/spec/traceback/rect/main.lua @@ -0,0 +1,12 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local rect = require 'spec.traceback.rect.rect' + +function wrapper() + print(rect.area { width = "Huh, gotcha!", height = 16.0 }) +end + +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/rect/rect.pln b/spec/traceback/rect/rect.pln new file mode 100644 index 00000000..91cb86eb --- /dev/null +++ b/spec/traceback/rect/rect.pln @@ -0,0 +1,17 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} +typealias rect = { width: any, height: any } + +function mod.universal_calc_area(x: any, y: any): any + return (x as float * y as float) as any +end + +function mod.area(r: rect): float + return mod.universal_calc_area(r.width, r.height) as float +end + +return mod diff --git a/spec/traceback_spec.lua b/spec/traceback_spec.lua new file mode 100644 index 00000000..8bc12106 --- /dev/null +++ b/spec/traceback_spec.lua @@ -0,0 +1,30 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local util = require "pallene.util" + +local function assert_test(test, expected_traceback) + local plnfile = util.shell_quote("spec/traceback/"..test.."/"..test..".pln") + local ok, err = util.execute("pallenec "..plnfile.." --use-traceback") + assert(ok, err) + + local luafile = util.shell_quote("spec/traceback/"..test.."/main.lua") + local ok, err, _, err_content = util.outputs_of_execute("lua "..luafile) + assert(ok, err) + assert.are.same(expected_traceback, err_content) +end + +it("rect", function() + assert_test("rect", [[ +Runtime error: spec/traceback/rect/main.lua:9: file spec/traceback/rect/rect.pln: line 10: wrong type for downcasted value, expected float but found string +Stack traceback: + spec/traceback/rect/rect.pln:10: in function 'universal_calc_area' + spec/traceback/rect/rect.pln:13: in function 'area' + spec/traceback/rect/main.lua:9: in function 'wrapper' + C: in function 'xpcall' + spec/traceback/rect/main.lua:12: in
+ C: in function '' +]]) +end) diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index b64917b7..d1d1cc20 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -213,13 +213,20 @@ end -- Raise an error if the given table contains a metatable. Pallene would rather raise an error in -- these cases instead of invoking the metatable operations, which may impair program optimization -- even if they are never called. -local function check_no_metatable(src, loc) +local function check_no_metatable(self, src, loc) + local setline = "" + if self.flags.use_traceback then + setline = string.format("PALLENE_SETLINE(%d);", loc.line) + end + return (util.render([[ if ($src->metatable) { + ${setline} pallene_runtime_array_metatable_error(L, PALLENE_SOURCE_FILE, $line); } ]], { src = src, + setline = setline, line = C.integer(loc.line), })) end @@ -246,14 +253,22 @@ function Coder:get_stack_slot(typ, dst, slot, loc, description_fmt, ...) else assert(not typ.is_upvalue_box) local extra_args = table.pack(...) + + local setline = "" + if self.flags.use_traceback then + setline = string.format("PALLENE_SETLINE(%d);", loc.line) + end + check_tag = util.render([[ if (l_unlikely(!$test)) { + ${setline} pallene_runtime_tag_check_error(L, $file, $line, $expected_type, $slot, ${description_fmt}${opt_comma}${extra_args}); } ]], { test = self:test_tag(typ, slot), + setline = setline, file = C.string(loc and loc.file_name or ""), line = C.integer(loc and loc.line or 0), expected_type = C.string(pallene_type_tag(typ)), @@ -290,7 +305,7 @@ function Coder:get_luatable_slot(typ, dst, slot, tab, loc, description_fmt, ...) } ]], { slot = slot, - check_no_metatable = check_no_metatable(tab, loc), + check_no_metatable = check_no_metatable(self, tab, loc), })) end @@ -441,21 +456,33 @@ function Coder:pallene_entry_point_definition(f_id) local max_frame_size = self.gc[func].max_frame_size local slots_needed = max_frame_size + self.max_lua_call_stack_usage[func] - if not self.flags.no_traceback then + + local setline = "" + local void_frameexit = "" + if self.flags.use_traceback then table.insert(prologue, util.render([[ PALLENE_C_FRAMEENTER(L, "$name"); /**/ ]], { - name = self.modname.."."..func.name + name = func.name })); + + setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0) + + if #func.typ.ret_types == 0 then + void_frameexit = "PALLENE_FRAMEEXIT(L);" + end end if slots_needed > 0 then table.insert(prologue, util.render([[ - if (!lua_checkstack(L, $n)) { pallene_runtime_cant_grow_stack_error(L, $line); } + if (!lua_checkstack(L, $n)) { + ${setline} + pallene_runtime_cant_grow_stack_error(L); + } ]], { n = C.integer(slots_needed), - line = C.integer(func.loc and func.loc.line or 0) + setline = setline, })) end table.insert(prologue, "StackValue *base = L->top.p;"); @@ -476,11 +503,6 @@ function Coder:pallene_entry_point_definition(f_id) local body = self:generate_cmd(func, func.body) - local void_frameexit = "" - if #func.typ.ret_types == 0 and not self.flags.no_traceback then - void_frameexit = "PALLENE_FRAMEEXIT(L);" - end - return (util.render([[ ${name_comment} ${fun_decl} { @@ -571,15 +593,29 @@ function Coder:lua_entry_point_definition(f_id) Udata *K = uvalue(&func->upvalue[0]); ]] + local frameenter = "" + local setline = "" + local frameexit = "" + if self.flags.use_traceback then + frameenter = util.render([[ PALLENE_LUA_FRAMEENTER(L, $fun_name); ]], { + fun_name = self:lua_entry_point_name(f_id), + }) + + setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0) + + frameexit = "PALLENE_FRAMEEXIT(L);" + end + local arity_check = util.render([[ int nargs = lua_gettop(L); if (l_unlikely(nargs != $nargs)) { - pallene_runtime_arity_error(L, $fname, $nargs, nargs, $line); + ${setline} + pallene_runtime_arity_error(L, $fname, $nargs, nargs); } ]], { nargs = C.integer(#arg_types), + setline = setline, fname = C.string(fname), - line = C.integer(func.loc and func.loc.line or 0) }) local arg_vars = {} @@ -613,25 +649,11 @@ function Coder:lua_entry_point_definition(f_id) local call_pallene = self:call_pallene_function(ret_vars, f_id, "func", arg_vars, self:lua_entry_point_name(f_id)) - local push_results = {} for i, typ in ipairs(ret_types) do table.insert(push_results, self:push_to_stack(typ, ret_vars[i])) end - local frameenter = "" - if not self.flags.no_traceback then - frameenter = util.render([[ PALLENE_LUA_FRAMEENTER(L, (void *) $fun_name); ]], { - fun_name = self:lua_entry_point_name(f_id), - }) - end - - local frameexit = util.render(not self.flags.no_traceback - and [[ PALLENE_FRAMEEXIT(L, $nresults); ]] - or [[ return $nresults; ]], { - nresults = C.integer(#ret_types) - }); - return (util.render([[ ${fun_decl} { @@ -649,6 +671,7 @@ function Coder:lua_entry_point_definition(f_id) ${call_pallene} ${push_results} ${lua_fexit} + return $nresults; } ]], { fun_decl = self:lua_entry_point_declaration(f_id), @@ -660,7 +683,8 @@ function Coder:lua_entry_point_definition(f_id) ret_decls = table.concat(ret_decls, "\n"), call_pallene = call_pallene, push_results = table.concat(push_results, "\n"), - lua_fexit = frameexit + lua_fexit = frameexit, + nresults = C.integer(#ret_types) })) end @@ -973,7 +997,7 @@ gen_cmd["Unop"] = function(self, cmd, _func) ${check_no_metatable} $dst = luaH_getn($x); ]], { - check_no_metatable = check_no_metatable(x, cmd.loc), + check_no_metatable = check_no_metatable(self, x, cmd.loc), line = C.integer(cmd.loc.line), dst = dst, x = x @@ -1429,7 +1453,7 @@ gen_cmd["CallStatic"] = function(self, cmd, func) table.insert(parts, self:update_stack_top(func, cmd)) - if not self.flags.no_traceback then + if self.flags.use_traceback then table.insert(parts, string.format("PALLENE_SETLINE(%d);\n", func.loc and func.loc.line or 0)) end @@ -1469,7 +1493,7 @@ gen_cmd["CallDyn"] = function(self, cmd, func) end local setline = "" - if not self.flags.no_traceback then + if self.flags.use_traceback then setline = util.render([[ PALLENE_SETLINE($line); ]], { line = C.integer(func.loc and func.loc.line or 0) }) @@ -1623,8 +1647,14 @@ gen_cmd["Seq"] = function(self, cmd, func) end gen_cmd["Return"] = function(self, cmd) + local frameexit = "" + if self.flags.use_traceback then + frameexit = "PALLENE_FRAMEEXIT(L);" + end + if #cmd.srcs == 0 then - return self.flags.no_traceback and [[ return; ]] or [[ PALLENE_FRAMEEXIT(L); ]] + return util.render([[ ${fexit} + return; ]], { fexit = frameexit }) else -- We assign the dsts from right to left, in order to match Lua's semantics when a -- destination variable appears more than once in the LHS. For example, in `x,x = f()`. @@ -1636,7 +1666,8 @@ gen_cmd["Return"] = function(self, cmd) util.render([[ *$reti = $v; ]], { reti = self:c_ret_var(i), v = src })) end local src1 = self:c_value(cmd.srcs[1]) - table.insert(returns, util.render(self.flags.no_traceback and [[ return $v; ]] or [[ PALLENE_FRAMEEXIT(L, $v); ]], { v = src1 })) + table.insert(returns, util.render([[ ${fexit} + return $v; ]], { fexit = frameexit, v = src1 })) return table.concat(returns, "\n") end end @@ -1859,7 +1890,7 @@ function Coder:generate_luaopen_function() local init_pt = "" - if not self.flags.no_traceback then + if self.flags.use_traceback then init_pt = [[ /* Initialize Pallene Tracer. */ pallene_tracer_init(L); diff --git a/src/pallene/pallenec.lua b/src/pallene/pallenec.lua index 261427eb..281a4cf2 100644 --- a/src/pallene/pallenec.lua +++ b/src/pallene/pallenec.lua @@ -34,7 +34,7 @@ do ) -- No Pallene tracebacks - p:flag("--no-traceback", "No function traceback using Pallene Tracer") + p:flag("--use-traceback", "Use Pallene Tracer function traceback for debugging") p:option("-O", "Optimization level") :args(1):convert(tonumber) @@ -73,7 +73,7 @@ end function pallenec.main() local flags = { - no_traceback = opts.no_traceback and true or false + use_traceback = opts.use_traceback and true or false } if opts.emit_c then compile("pln", "c", flags) diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index 3aec6615..d20b7d35 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -48,31 +48,30 @@ return [==[ #define PALLENE_UNREACHABLE __builtin_unreachable() /* Part of Pallene Tracer. */ -#define PALLENE_C_FRAMEENTER(L, name) pt_fn_details_t _details = { \ - .fn_name = name, \ - .mod_name = PALLENE_SOURCE_FILE \ - }; \ - pt_frame_t _frame = { \ - .type = PALLENE_TRACER_FRAME_TYPE_C, \ - .shared = { \ - .details = &_details \ - } \ - }; \ - pallene_tracer_frameenter(L, &_frame) - -#define PALLENE_LUA_FRAMEENTER(L, sig) pt_frame_t _frame = { \ - .type = PALLENE_TRACER_FRAME_TYPE_LUA, \ - .shared = { \ - .frame_sig = sig \ - } \ - }; \ - pallene_tracer_frameenter(L, &_frame) - -#define PALLENE_GLOBAL_SETLINE(L, line) pallene_tracer_global_setline(L, line) -#define PALLENE_SETLINE(line) pallene_tracer_setline(&_frame, line) -#define PALLENE_FRAMEEXIT(L, ...) pallene_tracer_frameexit(L); \ - return __VA_ARGS__ +#define PALLENE_C_FRAMEENTER(L, name) \ + static pt_fn_details_t _details = { \ + .fn_name = name, \ + .mod_name = PALLENE_SOURCE_FILE \ + }; \ + static pt_frame_t _frame = { \ + .type = PALLENE_TRACER_FRAME_TYPE_C, \ + .shared = { \ + .details = &_details \ + } \ + }; \ + pallene_tracer_frameenter(L, &_frame) + +#define PALLENE_LUA_FRAMEENTER(L, sig) \ + static pt_frame_t _frame = { \ + .type = PALLENE_TRACER_FRAME_TYPE_LUA, \ + .shared = { \ + .frame_sig = sig \ + } \ + }; \ + pallene_tracer_frameenter(L, &_frame) +#define PALLENE_SETLINE(line) pallene_tracer_setline(&_frame, line) +#define PALLENE_FRAMEEXIT(...) pallene_tracer_frameexit(L); /* PALLENE TRACER RELATED DATA-STRUCTURES. */ @@ -96,7 +95,7 @@ typedef struct pt_frame { union { const pt_fn_details_t *details; - const void *frame_sig; + const lua_CFunction frame_sig; } shared; struct pt_frame *prev; @@ -104,7 +103,6 @@ typedef struct pt_frame { /* Pallene Tracer. */ static void pallene_tracer_frameenter(lua_State *L, pt_frame_t *restrict frame); -static void pallene_tracer_global_setline(lua_State *L, int line); static void pallene_tracer_setline(pt_frame_t *restrict frame, int line); static void pallene_tracer_frameexit(lua_State *L); static int pallene_tracer_debug_traceback(lua_State *L); @@ -123,12 +121,12 @@ static void pallene_barrierback_unboxed(lua_State *L, GCObject *p, GCObject *v); /* Runtime errors */ static l_noret pallene_runtime_tag_check_error(lua_State *L, const char* file, int line, const char *expected_type_name, const TValue *received_type, const char *description_fmt, ...); -static l_noret pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received, int line); +static l_noret pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received); static l_noret pallene_runtime_divide_by_zero_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_mod_by_zero_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_number_to_integer_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_array_metatable_error(lua_State *L, const char* file, int line); -static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L, int line); +static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L); /* Arithmetic operators */ static lua_Integer pallene_int_divi(lua_State *L, lua_Integer m, lua_Integer n, const char* file, int line); @@ -255,15 +253,6 @@ out: lua_setglobal(L, "__pallene_tracer_stack"); } -static void pallene_tracer_global_setline(lua_State *L, int line) { - lua_getglobal(L, "__pallene_tracer_stack"); - pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 1); - - if(stack != NULL) - stack->line = line; -} - static void pallene_tracer_setline(pt_frame_t *restrict frame, int line) { frame->line = line; } @@ -295,7 +284,7 @@ static int pallene_tracer_debug_traceback(lua_State *L) { int context = 1; int level = 1; bool l_stack = true; - void *f_sig = NULL; + lua_CFunction f_sig = NULL; lua_getglobal(L, "__pallene_tracer_stack"); pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); @@ -320,7 +309,7 @@ static int pallene_tracer_debug_traceback(lua_State *L) { /* We have got a C frame. Time to make a context switch. */ if(lua_iscfunction(L, -1)) { /* Set the signature and switch to Pallene stack. */ - f_sig = (void *) lua_tocfunction(L, -1); + f_sig = lua_tocfunction(L, -1); context = 0; } else { /* It's a regular Lua function. */ @@ -348,7 +337,7 @@ static int pallene_tracer_debug_traceback(lua_State *L) { /* Deduce name from global table. */ if(_pgf_name(L)) lua_pushfstring(L, "C: in function '%s'", lua_tostring(L, -1)); - else lua_pushfstring(L, "C: in function '0x%p'", f_sig); + else lua_pushliteral(L, "C: in function ''"); if(stack == NULL) { if(f_sig != NULL) @@ -491,14 +480,12 @@ static void pallene_runtime_tag_check_error( lua_pushfstring(L, ", expected %s but found %s", expected_type_name, received_type_name); lua_concat(L, 5); - PALLENE_GLOBAL_SETLINE(L, line); lua_error(L); PALLENE_UNREACHABLE; } -static void pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received, int line) +static void pallene_runtime_arity_error(lua_State *L, const char *name, int expected, int received) { - PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "wrong number of arguments to function '%s', expected %d but received %d", name, expected, received @@ -508,35 +495,30 @@ static void pallene_runtime_arity_error(lua_State *L, const char *name, int expe static void pallene_runtime_divide_by_zero_error(lua_State *L, const char* file, int line) { - PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: attempt to divide by zero", file, line); PALLENE_UNREACHABLE; } static void pallene_runtime_mod_by_zero_error(lua_State *L, const char* file, int line) { - PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: attempt to perform 'n%%0'", file, line); PALLENE_UNREACHABLE; } static void pallene_runtime_number_to_integer_error(lua_State *L, const char* file, int line) { - PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: conversion from float does not fit into integer", file, line); PALLENE_UNREACHABLE; } static void pallene_runtime_array_metatable_error(lua_State *L, const char* file, int line) { - PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "file %s: line %d: arrays in Pallene must not have a metatable", file, line); PALLENE_UNREACHABLE; } -static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L, int line) +static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L) { - PALLENE_GLOBAL_SETLINE(L, line); luaL_error(L, "stack overflow"); PALLENE_UNREACHABLE; } From ed41c08e45cd678981012551815502460dc8ecab Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Fri, 7 Jun 2024 21:49:47 +0600 Subject: [PATCH 5/7] Added more traceback tests and bug fixes --- .../depth_recursion/depth_recursion.pln | 17 +++++ spec/traceback/depth_recursion/main.lua | 20 ++++++ spec/traceback/module_lua/another_module.lua | 12 ++++ spec/traceback/module_lua/main.lua | 27 +++++++ spec/traceback/module_lua/module_lua.pln | 16 +++++ spec/traceback/module_pallene/main.lua | 21 ++++++ .../module_pallene/module_pallene.pln | 12 ++++ .../module_pallene/module_pallene_alt.pln | 13 ++++ spec/traceback/rect/main.lua | 2 +- spec/traceback_spec.lua | 70 ++++++++++++++++++- src/pallene/pallenelib.lua | 6 +- 11 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 spec/traceback/depth_recursion/depth_recursion.pln create mode 100644 spec/traceback/depth_recursion/main.lua create mode 100644 spec/traceback/module_lua/another_module.lua create mode 100644 spec/traceback/module_lua/main.lua create mode 100644 spec/traceback/module_lua/module_lua.pln create mode 100644 spec/traceback/module_pallene/main.lua create mode 100644 spec/traceback/module_pallene/module_pallene.pln create mode 100644 spec/traceback/module_pallene/module_pallene_alt.pln diff --git a/spec/traceback/depth_recursion/depth_recursion.pln b/spec/traceback/depth_recursion/depth_recursion.pln new file mode 100644 index 00000000..855dacb3 --- /dev/null +++ b/spec/traceback/depth_recursion/depth_recursion.pln @@ -0,0 +1,17 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} + +function mod.pallene_fn(lua_fn: integer -> (), depth: integer) + if depth == 0 then + -- Call 'lua_fn' for the last time so that we can raise an error. + lua_fn(depth) + end + + lua_fn(depth - 1) +end + +return mod diff --git a/spec/traceback/depth_recursion/main.lua b/spec/traceback/depth_recursion/main.lua new file mode 100644 index 00000000..297542e9 --- /dev/null +++ b/spec/traceback/depth_recursion/main.lua @@ -0,0 +1,20 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local pallene = require 'spec.traceback.depth_recursion.depth_recursion' + +function lua_fn(depth) + if depth == 0 then + error "Depth reached 0!" + end + + pallene.pallene_fn(lua_fn, depth - 1) +end + +local function wrapper() + lua_fn(10) +end + +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_lua/another_module.lua b/spec/traceback/module_lua/another_module.lua new file mode 100644 index 00000000..98307fe0 --- /dev/null +++ b/spec/traceback/module_lua/another_module.lua @@ -0,0 +1,12 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local function call_lua_callback(callback) + callback() +end + +return { + call_lua_callback = call_lua_callback +} diff --git a/spec/traceback/module_lua/main.lua b/spec/traceback/module_lua/main.lua new file mode 100644 index 00000000..0c834f0e --- /dev/null +++ b/spec/traceback/module_lua/main.lua @@ -0,0 +1,27 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local another_module = require 'spec.traceback.module_lua.another_module' +local pallene = require 'spec.traceback.module_lua.module_lua' + +function lua_1() + pallene.pallene_1(lua_2) +end + +function lua_2() + pallene.pallene_2(lua_3, 33, 79) +end + +function lua_3(sum) + print("The summation is: ", sum) + + error "Any normal error from Lua!" +end + +local function wrapper() + another_module.call_lua_callback(lua_1) +end + +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_lua/module_lua.pln b/spec/traceback/module_lua/module_lua.pln new file mode 100644 index 00000000..0c756db3 --- /dev/null +++ b/spec/traceback/module_lua/module_lua.pln @@ -0,0 +1,16 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} + +function mod.pallene_1(lua_2: () -> ()) + lua_2() +end + +function mod.pallene_2(lua_3: integer -> (), a: integer, b: integer) + lua_3(a + b) +end + +return mod diff --git a/spec/traceback/module_pallene/main.lua b/spec/traceback/module_pallene/main.lua new file mode 100644 index 00000000..401a4933 --- /dev/null +++ b/spec/traceback/module_pallene/main.lua @@ -0,0 +1,21 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local pallene = require 'spec.traceback.module_pallene.module_pallene' +local pallene_alt = require 'spec.traceback.module_pallene.module_pallene_alt' + +function lua_2() + error "There's an error in everyday life. Shame!" +end + +function lua_1() + pallene_alt.alternate_everyday_fn(lua_2) +end + +local function wrapper() + pallene.normal_everyday_fn(lua_1) +end + +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_pallene/module_pallene.pln b/spec/traceback/module_pallene/module_pallene.pln new file mode 100644 index 00000000..ffc4d707 --- /dev/null +++ b/spec/traceback/module_pallene/module_pallene.pln @@ -0,0 +1,12 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} + +function mod.normal_everyday_fn(callback: () -> ()) + callback() +end + +return mod diff --git a/spec/traceback/module_pallene/module_pallene_alt.pln b/spec/traceback/module_pallene/module_pallene_alt.pln new file mode 100644 index 00000000..cf933ff6 --- /dev/null +++ b/spec/traceback/module_pallene/module_pallene_alt.pln @@ -0,0 +1,13 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} + +function mod.alternate_everyday_fn(callback: () -> ()) + callback() +end + +return mod + diff --git a/spec/traceback/rect/main.lua b/spec/traceback/rect/main.lua index e10f30d9..90e2a088 100644 --- a/spec/traceback/rect/main.lua +++ b/spec/traceback/rect/main.lua @@ -5,7 +5,7 @@ local rect = require 'spec.traceback.rect.rect' -function wrapper() +local function wrapper() print(rect.area { width = "Huh, gotcha!", height = 16.0 }) end diff --git a/spec/traceback_spec.lua b/spec/traceback_spec.lua index 8bc12106..2dfeab7a 100644 --- a/spec/traceback_spec.lua +++ b/spec/traceback_spec.lua @@ -10,21 +10,87 @@ local function assert_test(test, expected_traceback) local ok, err = util.execute("pallenec "..plnfile.." --use-traceback") assert(ok, err) + -- Compile the second Pallene file if exists. + local plnfile = util.shell_quote("spec/traceback/"..test.."/"..test.."_alt.pln") + local ok, err = util.execute("test -f "..plnfile) + if ok then + local ok, err = util.execute("pallenec "..plnfile.." --use-traceback") + assert(ok, err) + end + local luafile = util.shell_quote("spec/traceback/"..test.."/main.lua") local ok, err, _, err_content = util.outputs_of_execute("lua "..luafile) assert(ok, err) assert.are.same(expected_traceback, err_content) end -it("rect", function() +it("Rectangle", function() assert_test("rect", [[ Runtime error: spec/traceback/rect/main.lua:9: file spec/traceback/rect/rect.pln: line 10: wrong type for downcasted value, expected float but found string Stack traceback: spec/traceback/rect/rect.pln:10: in function 'universal_calc_area' spec/traceback/rect/rect.pln:13: in function 'area' - spec/traceback/rect/main.lua:9: in function 'wrapper' + spec/traceback/rect/main.lua:9: in function '' C: in function 'xpcall' spec/traceback/rect/main.lua:12: in
C: in function '' ]]) end) + +it("Multi-module Lua", function() + assert_test("module_lua", [[ +Runtime error: spec/traceback/module_lua/main.lua:20: Any normal error from Lua! +Stack traceback: + C: in function 'error' + spec/traceback/module_lua/main.lua:20: in function 'lua_3' + spec/traceback/module_lua/module_lua.pln:12: in function 'pallene_2' + spec/traceback/module_lua/main.lua:14: in function 'lua_2' + spec/traceback/module_lua/module_lua.pln:8: in function 'pallene_1' + spec/traceback/module_lua/main.lua:10: in function 'callback' + ./spec/traceback/module_lua/another_module.lua:7: in function 'call_lua_callback' + spec/traceback/module_lua/main.lua:24: in function '' + C: in function 'xpcall' + spec/traceback/module_lua/main.lua:27: in
+ C: in function '' +]]) +end) + +it("Multi-module Pallene", function() + assert_test("module_pallene", [[ +Runtime error: spec/traceback/module_pallene/main.lua:10: There's an error in everyday life. Shame! +Stack traceback: + C: in function 'error' + spec/traceback/module_pallene/main.lua:10: in function 'lua_2' + spec/traceback/module_pallene/module_pallene_alt.pln:8: in function 'alternate_everyday_fn' + spec/traceback/module_pallene/main.lua:14: in function 'lua_1' + spec/traceback/module_pallene/module_pallene.pln:8: in function 'normal_everyday_fn' + spec/traceback/module_pallene/main.lua:18: in function '' + C: in function 'xpcall' + spec/traceback/module_pallene/main.lua:21: in
+ C: in function '' +]]) +end) + +it("Depth recursion", function() + assert_test("depth_recursion", [[ +Runtime error: spec/traceback/depth_recursion/main.lua:10: Depth reached 0! +Stack traceback: + C: in function 'error' + spec/traceback/depth_recursion/main.lua:10: in function 'lua_fn' + spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' + spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' + spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' + spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' + spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' + spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:17: in function '' + C: in function 'xpcall' + spec/traceback/depth_recursion/main.lua:20: in
+ C: in function '' +]]) +end) + diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index d20b7d35..17c891c5 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -53,7 +53,7 @@ return [==[ .fn_name = name, \ .mod_name = PALLENE_SOURCE_FILE \ }; \ - static pt_frame_t _frame = { \ + pt_frame_t _frame = { \ .type = PALLENE_TRACER_FRAME_TYPE_C, \ .shared = { \ .details = &_details \ @@ -62,7 +62,7 @@ return [==[ pallene_tracer_frameenter(L, &_frame) #define PALLENE_LUA_FRAMEENTER(L, sig) \ - static pt_frame_t _frame = { \ + pt_frame_t _frame = { \ .type = PALLENE_TRACER_FRAME_TYPE_LUA, \ .shared = { \ .frame_sig = sig \ @@ -278,7 +278,7 @@ out: static int pallene_tracer_debug_traceback(lua_State *L) { const char *message = lua_tostring(L, 1); - fprintf(stderr, "Runtime error: %s\nStack traceback: \n", message); + fprintf(stderr, "Runtime error: %s\nStack traceback:\n", message); /* Lua: 1, Pallene: 0 */ int context = 1; From 07b975f2d7ea378192a5ccfaba2fdc344a9361cb Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Fri, 7 Jun 2024 22:29:43 +0600 Subject: [PATCH 6/7] Improvements in the traceback test samples --- spec/traceback/depth_recursion/main.lua | 12 +++++++----- spec/traceback/module_lua/main.lua | 18 ++++++++++-------- spec/traceback/module_pallene/main.lua | 14 ++++++++------ spec/traceback/rect/main.lua | 6 ++++-- spec/traceback_spec.lua | 24 ++++++++++++------------ 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/spec/traceback/depth_recursion/main.lua b/spec/traceback/depth_recursion/main.lua index 297542e9..54959966 100644 --- a/spec/traceback/depth_recursion/main.lua +++ b/spec/traceback/depth_recursion/main.lua @@ -5,16 +5,18 @@ local pallene = require 'spec.traceback.depth_recursion.depth_recursion' -function lua_fn(depth) +function _G.lua_fn(depth) if depth == 0 then error "Depth reached 0!" end - pallene.pallene_fn(lua_fn, depth - 1) + pallene.pallene_fn(_G.lua_fn, depth - 1) end -local function wrapper() - lua_fn(10) +-- Should be local. +-- Making it global so that it is visible in the traceback. +function _G.wrapper() + _G.lua_fn(10) end -xpcall(wrapper, pallene_tracer_debug_traceback) +xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_lua/main.lua b/spec/traceback/module_lua/main.lua index 0c834f0e..2a69241a 100644 --- a/spec/traceback/module_lua/main.lua +++ b/spec/traceback/module_lua/main.lua @@ -6,22 +6,24 @@ local another_module = require 'spec.traceback.module_lua.another_module' local pallene = require 'spec.traceback.module_lua.module_lua' -function lua_1() - pallene.pallene_1(lua_2) +function _G.lua_1() + pallene.pallene_1(_G.lua_2) end -function lua_2() - pallene.pallene_2(lua_3, 33, 79) +function _G.lua_2() + pallene.pallene_2(_G.lua_3, 33, 79) end -function lua_3(sum) +function _G.lua_3(sum) print("The summation is: ", sum) error "Any normal error from Lua!" end -local function wrapper() - another_module.call_lua_callback(lua_1) +-- Should be local. +-- Making it global so that it is visible in the traceback. +function _G.wrapper() + another_module.call_lua_callback(_G.lua_1) end -xpcall(wrapper, pallene_tracer_debug_traceback) +xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_pallene/main.lua b/spec/traceback/module_pallene/main.lua index 401a4933..8ff36a0c 100644 --- a/spec/traceback/module_pallene/main.lua +++ b/spec/traceback/module_pallene/main.lua @@ -6,16 +6,18 @@ local pallene = require 'spec.traceback.module_pallene.module_pallene' local pallene_alt = require 'spec.traceback.module_pallene.module_pallene_alt' -function lua_2() +function _G.lua_2() error "There's an error in everyday life. Shame!" end -function lua_1() - pallene_alt.alternate_everyday_fn(lua_2) +function _G.lua_1() + pallene_alt.alternate_everyday_fn(_G.lua_2) end -local function wrapper() - pallene.normal_everyday_fn(lua_1) +-- Should be local. +-- Making it global so that it is visible in the traceback. +function _G.wrapper() + pallene.normal_everyday_fn(_G.lua_1) end -xpcall(wrapper, pallene_tracer_debug_traceback) +xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) diff --git a/spec/traceback/rect/main.lua b/spec/traceback/rect/main.lua index 90e2a088..037e2d2b 100644 --- a/spec/traceback/rect/main.lua +++ b/spec/traceback/rect/main.lua @@ -5,8 +5,10 @@ local rect = require 'spec.traceback.rect.rect' -local function wrapper() +-- Should be local. +-- Making it global so that it is visible in the traceback. +function _G.wrapper() print(rect.area { width = "Huh, gotcha!", height = 16.0 }) end -xpcall(wrapper, pallene_tracer_debug_traceback) +xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) diff --git a/spec/traceback_spec.lua b/spec/traceback_spec.lua index 2dfeab7a..8d01b99f 100644 --- a/spec/traceback_spec.lua +++ b/spec/traceback_spec.lua @@ -11,10 +11,10 @@ local function assert_test(test, expected_traceback) assert(ok, err) -- Compile the second Pallene file if exists. - local plnfile = util.shell_quote("spec/traceback/"..test.."/"..test.."_alt.pln") - local ok, err = util.execute("test -f "..plnfile) + local alt_plnfile = util.shell_quote("spec/traceback/"..test.."/"..test.."_alt.pln") + local ok, _ = util.execute("test -f "..alt_plnfile) if ok then - local ok, err = util.execute("pallenec "..plnfile.." --use-traceback") + local ok, err = util.execute("pallenec "..alt_plnfile.." --use-traceback") assert(ok, err) end @@ -26,13 +26,13 @@ end it("Rectangle", function() assert_test("rect", [[ -Runtime error: spec/traceback/rect/main.lua:9: file spec/traceback/rect/rect.pln: line 10: wrong type for downcasted value, expected float but found string +Runtime error: spec/traceback/rect/main.lua:11: file spec/traceback/rect/rect.pln: line 10: wrong type for downcasted value, expected float but found string Stack traceback: spec/traceback/rect/rect.pln:10: in function 'universal_calc_area' spec/traceback/rect/rect.pln:13: in function 'area' - spec/traceback/rect/main.lua:9: in function '' + spec/traceback/rect/main.lua:11: in function 'wrapper' C: in function 'xpcall' - spec/traceback/rect/main.lua:12: in
+ spec/traceback/rect/main.lua:14: in
C: in function '' ]]) end) @@ -48,9 +48,9 @@ Stack traceback: spec/traceback/module_lua/module_lua.pln:8: in function 'pallene_1' spec/traceback/module_lua/main.lua:10: in function 'callback' ./spec/traceback/module_lua/another_module.lua:7: in function 'call_lua_callback' - spec/traceback/module_lua/main.lua:24: in function '' + spec/traceback/module_lua/main.lua:26: in function 'wrapper' C: in function 'xpcall' - spec/traceback/module_lua/main.lua:27: in
+ spec/traceback/module_lua/main.lua:29: in
C: in function '' ]]) end) @@ -64,9 +64,9 @@ Stack traceback: spec/traceback/module_pallene/module_pallene_alt.pln:8: in function 'alternate_everyday_fn' spec/traceback/module_pallene/main.lua:14: in function 'lua_1' spec/traceback/module_pallene/module_pallene.pln:8: in function 'normal_everyday_fn' - spec/traceback/module_pallene/main.lua:18: in function '' + spec/traceback/module_pallene/main.lua:20: in function 'wrapper' C: in function 'xpcall' - spec/traceback/module_pallene/main.lua:21: in
+ spec/traceback/module_pallene/main.lua:23: in
C: in function '' ]]) end) @@ -87,9 +87,9 @@ Stack traceback: spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' - spec/traceback/depth_recursion/main.lua:17: in function '' + spec/traceback/depth_recursion/main.lua:19: in function 'wrapper' C: in function 'xpcall' - spec/traceback/depth_recursion/main.lua:20: in
+ spec/traceback/depth_recursion/main.lua:22: in
C: in function '' ]]) end) From 752686a111aef90fb77d1983a71086ec7a58058e Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Fri, 7 Jun 2024 22:47:17 +0600 Subject: [PATCH 7/7] Traceback test sample code refactoring --- spec/traceback/depth_recursion/main.lua | 13 ++++--- spec/traceback/module_lua/main.lua | 23 ++++++++----- spec/traceback/module_pallene/main.lua | 16 +++++---- spec/traceback/rect/main.lua | 6 ++-- spec/traceback_spec.lua | 46 ++++++++++++------------- 5 files changed, 60 insertions(+), 44 deletions(-) diff --git a/spec/traceback/depth_recursion/main.lua b/spec/traceback/depth_recursion/main.lua index 54959966..1688ee67 100644 --- a/spec/traceback/depth_recursion/main.lua +++ b/spec/traceback/depth_recursion/main.lua @@ -5,18 +5,21 @@ local pallene = require 'spec.traceback.depth_recursion.depth_recursion' -function _G.lua_fn(depth) +-- luacheck: globals lua_fn +function lua_fn(depth) if depth == 0 then error "Depth reached 0!" end - pallene.pallene_fn(_G.lua_fn, depth - 1) + pallene.pallene_fn(lua_fn, depth - 1) end -- Should be local. -- Making it global so that it is visible in the traceback. -function _G.wrapper() - _G.lua_fn(10) +-- luacheck: globals wrapper +function wrapper() + lua_fn(10) end -xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) +-- luacheck: globals pallene_tracer_debug_traceback +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_lua/main.lua b/spec/traceback/module_lua/main.lua index 2a69241a..ef764119 100644 --- a/spec/traceback/module_lua/main.lua +++ b/spec/traceback/module_lua/main.lua @@ -6,15 +6,20 @@ local another_module = require 'spec.traceback.module_lua.another_module' local pallene = require 'spec.traceback.module_lua.module_lua' -function _G.lua_1() - pallene.pallene_1(_G.lua_2) +-- luacheck: globals lua_1 +function lua_1() + -- luacheck: globals lua_2 + pallene.pallene_1(lua_2) end -function _G.lua_2() - pallene.pallene_2(_G.lua_3, 33, 79) +-- luacheck: globals lua_2 +function lua_2() + -- luacheck: globals lua_3 + pallene.pallene_2(lua_3, 33, 79) end -function _G.lua_3(sum) +-- luacheck: globals lua_3 +function lua_3(sum) print("The summation is: ", sum) error "Any normal error from Lua!" @@ -22,8 +27,10 @@ end -- Should be local. -- Making it global so that it is visible in the traceback. -function _G.wrapper() - another_module.call_lua_callback(_G.lua_1) +-- luacheck: globals wrapper +function wrapper() + another_module.call_lua_callback(lua_1) end -xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) +-- luacheck: globals pallene_tracer_debug_traceback +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/module_pallene/main.lua b/spec/traceback/module_pallene/main.lua index 8ff36a0c..62f7902c 100644 --- a/spec/traceback/module_pallene/main.lua +++ b/spec/traceback/module_pallene/main.lua @@ -6,18 +6,22 @@ local pallene = require 'spec.traceback.module_pallene.module_pallene' local pallene_alt = require 'spec.traceback.module_pallene.module_pallene_alt' -function _G.lua_2() +-- luacheck: globals lua_2 +function lua_2() error "There's an error in everyday life. Shame!" end -function _G.lua_1() - pallene_alt.alternate_everyday_fn(_G.lua_2) +-- luacheck: globals lua_1 +function lua_1() + pallene_alt.alternate_everyday_fn(lua_2) end -- Should be local. -- Making it global so that it is visible in the traceback. -function _G.wrapper() - pallene.normal_everyday_fn(_G.lua_1) +-- luacheck: globals wrapper +function wrapper() + pallene.normal_everyday_fn(lua_1) end -xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) +-- luacheck: globals pallene_tracer_debug_traceback +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/rect/main.lua b/spec/traceback/rect/main.lua index 037e2d2b..3a4da419 100644 --- a/spec/traceback/rect/main.lua +++ b/spec/traceback/rect/main.lua @@ -7,8 +7,10 @@ local rect = require 'spec.traceback.rect.rect' -- Should be local. -- Making it global so that it is visible in the traceback. -function _G.wrapper() +-- luacheck: globals wrapper +function wrapper() print(rect.area { width = "Huh, gotcha!", height = 16.0 }) end -xpcall(_G.wrapper, _G.pallene_tracer_debug_traceback) +-- luacheck: globals pallene_tracer_debug_traceback +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback_spec.lua b/spec/traceback_spec.lua index 8d01b99f..af7666fe 100644 --- a/spec/traceback_spec.lua +++ b/spec/traceback_spec.lua @@ -26,70 +26,70 @@ end it("Rectangle", function() assert_test("rect", [[ -Runtime error: spec/traceback/rect/main.lua:11: file spec/traceback/rect/rect.pln: line 10: wrong type for downcasted value, expected float but found string +Runtime error: spec/traceback/rect/main.lua:12: file spec/traceback/rect/rect.pln: line 10: wrong type for downcasted value, expected float but found string Stack traceback: spec/traceback/rect/rect.pln:10: in function 'universal_calc_area' spec/traceback/rect/rect.pln:13: in function 'area' - spec/traceback/rect/main.lua:11: in function 'wrapper' + spec/traceback/rect/main.lua:12: in function 'wrapper' C: in function 'xpcall' - spec/traceback/rect/main.lua:14: in
+ spec/traceback/rect/main.lua:16: in
C: in function '' ]]) end) it("Multi-module Lua", function() assert_test("module_lua", [[ -Runtime error: spec/traceback/module_lua/main.lua:20: Any normal error from Lua! +Runtime error: spec/traceback/module_lua/main.lua:25: Any normal error from Lua! Stack traceback: C: in function 'error' - spec/traceback/module_lua/main.lua:20: in function 'lua_3' + spec/traceback/module_lua/main.lua:25: in function 'lua_3' spec/traceback/module_lua/module_lua.pln:12: in function 'pallene_2' - spec/traceback/module_lua/main.lua:14: in function 'lua_2' + spec/traceback/module_lua/main.lua:18: in function 'lua_2' spec/traceback/module_lua/module_lua.pln:8: in function 'pallene_1' - spec/traceback/module_lua/main.lua:10: in function 'callback' + spec/traceback/module_lua/main.lua:12: in function 'callback' ./spec/traceback/module_lua/another_module.lua:7: in function 'call_lua_callback' - spec/traceback/module_lua/main.lua:26: in function 'wrapper' + spec/traceback/module_lua/main.lua:32: in function 'wrapper' C: in function 'xpcall' - spec/traceback/module_lua/main.lua:29: in
+ spec/traceback/module_lua/main.lua:36: in
C: in function '' ]]) end) it("Multi-module Pallene", function() assert_test("module_pallene", [[ -Runtime error: spec/traceback/module_pallene/main.lua:10: There's an error in everyday life. Shame! +Runtime error: spec/traceback/module_pallene/main.lua:11: There's an error in everyday life. Shame! Stack traceback: C: in function 'error' - spec/traceback/module_pallene/main.lua:10: in function 'lua_2' + spec/traceback/module_pallene/main.lua:11: in function 'lua_2' spec/traceback/module_pallene/module_pallene_alt.pln:8: in function 'alternate_everyday_fn' - spec/traceback/module_pallene/main.lua:14: in function 'lua_1' + spec/traceback/module_pallene/main.lua:16: in function 'lua_1' spec/traceback/module_pallene/module_pallene.pln:8: in function 'normal_everyday_fn' - spec/traceback/module_pallene/main.lua:20: in function 'wrapper' + spec/traceback/module_pallene/main.lua:23: in function 'wrapper' C: in function 'xpcall' - spec/traceback/module_pallene/main.lua:23: in
+ spec/traceback/module_pallene/main.lua:27: in
C: in function '' ]]) end) it("Depth recursion", function() assert_test("depth_recursion", [[ -Runtime error: spec/traceback/depth_recursion/main.lua:10: Depth reached 0! +Runtime error: spec/traceback/depth_recursion/main.lua:11: Depth reached 0! Stack traceback: C: in function 'error' - spec/traceback/depth_recursion/main.lua:10: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:11: in function 'lua_fn' spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' - spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:14: in function 'lua_fn' spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' - spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:14: in function 'lua_fn' spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' - spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:14: in function 'lua_fn' spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' - spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:14: in function 'lua_fn' spec/traceback/depth_recursion/depth_recursion.pln:8: in function 'pallene_fn' - spec/traceback/depth_recursion/main.lua:13: in function 'lua_fn' - spec/traceback/depth_recursion/main.lua:19: in function 'wrapper' + spec/traceback/depth_recursion/main.lua:14: in function 'lua_fn' + spec/traceback/depth_recursion/main.lua:21: in function 'wrapper' C: in function 'xpcall' - spec/traceback/depth_recursion/main.lua:22: in
+ spec/traceback/depth_recursion/main.lua:25: in
C: in function '' ]]) end)