Fixed bug in line info. when using 'not' operator
When creating code for a jump on a 'not' condition, the code generator
was removing an instruction (the OP_NOT) without adjusting its
corresponding line information.
This fix also added tests for this case and extra functionality in
the test library to debug line info. structures.
diff --git a/lcode.c b/lcode.c
index ab91561..d00038d 100644
--- a/lcode.c
+++ b/lcode.c
@@ -1,5 +1,5 @@
/*
-** $Id: lcode.c,v 2.160 2018/03/16 14:22:09 roberto Exp roberto $
+** $Id: lcode.c $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
@@ -309,10 +309,19 @@
}
+/*
+** MAXimum number of successive Instructions WiTHout ABSolute line
+** information.
+*/
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 120
#endif
+
+/* limit for difference between lines in relative line info. */
+#define LIMLINEDIFF 0x80
+
+
/*
** Save line info for a new instruction. If difference from last line
** does not fit in a byte, of after that many instructions, save a new
@@ -320,14 +329,15 @@
** in 'lineinfo' signals the existence of this absolute information.)
** Otherwise, store the difference from last line in 'lineinfo'.
*/
-static void savelineinfo (FuncState *fs, Proto *f, int pc, int line) {
+static void savelineinfo (FuncState *fs, Proto *f, int line) {
int linedif = line - fs->previousline;
- if (abs(linedif) >= 0x80 || fs->iwthabs++ > MAXIWTHABS) {
+ int pc = fs->pc - 1; /* last instruction coded */
+ if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
f->abslineinfo[fs->nabslineinfo].pc = pc;
f->abslineinfo[fs->nabslineinfo++].line = line;
- linedif = ABSLINEINFO; /* signal there is absolute information */
+ linedif = ABSLINEINFO; /* signal that there is absolute information */
fs->iwthabs = 0; /* restart counter */
}
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
@@ -338,6 +348,37 @@
/*
+** Remove line information from the last instruction.
+** If line information for that instruction is absolute, set 'iwthabs'
+** above its max to force the new (replacing) instruction to have
+** absolute line info, too.
+*/
+static void removelastlineinfo (FuncState *fs) {
+ Proto *f = fs->f;
+ int pc = fs->pc - 1; /* last instruction coded */
+ if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */
+ fs->previousline -= f->lineinfo[pc]; /* last line saved */
+ fs->iwthabs--;
+ }
+ else { /* absolute line information */
+ fs->nabslineinfo--; /* remove it */
+ lua_assert(f->abslineinfo[fs->nabslineinfo].pc = pc);
+ fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
+ }
+}
+
+
+/*
+** Remove the last instruction created, correcting line information
+** accordingly.
+*/
+static void removelastinstruction (FuncState *fs) {
+ removelastlineinfo(fs);
+ fs->pc--;
+}
+
+
+/*
** Emit instruction 'i', checking for array sizes and saving also its
** line information. Return 'i' position.
*/
@@ -346,9 +387,9 @@
/* put new instruction in code array */
luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
MAX_INT, "opcodes");
- f->code[fs->pc] = i;
- savelineinfo(fs, f, fs->pc, fs->ls->lastline);
- return fs->pc++;
+ f->code[fs->pc++] = i;
+ savelineinfo(fs, f, fs->ls->lastline);
+ return fs->pc - 1; /* index of new instruction */
}
@@ -986,7 +1027,7 @@
if (e->k == VRELOC) {
Instruction ie = getinstruction(fs, e);
if (GET_OPCODE(ie) == OP_NOT) {
- fs->pc--; /* remove previous OP_NOT */
+ removelastinstruction(fs); /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
}
/* else go through */
@@ -1572,23 +1613,12 @@
/*
-** Change line information associated with current position. If that
-** information is absolute, just change it and correct 'previousline'.
-** Otherwise, restore 'previousline' to its value before saving the
-** current position and than saves the line information again, with the
-** new line.
+** Change line information associated with current position, by removing
+** previous info and adding it again with new line.
*/
void luaK_fixline (FuncState *fs, int line) {
- Proto *f = fs->f;
- if (f->lineinfo[fs->pc - 1] == ABSLINEINFO) {
- lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == fs->pc - 1);
- f->abslineinfo[fs->nabslineinfo - 1].line = line;
- fs->previousline = line;
- }
- else {
- fs->previousline -= f->lineinfo[fs->pc - 1]; /* undo previous info. */
- savelineinfo(fs, f, fs->pc - 1, line); /* redo it */
- }
+ removelastlineinfo(fs);
+ savelineinfo(fs, fs->f, line);
}
diff --git a/ltests.c b/ltests.c
index ac548a9..13717da 100644
--- a/ltests.c
+++ b/ltests.c
@@ -526,7 +526,8 @@
OpCode o = GET_OPCODE(i);
const char *name = opnames[o];
int line = luaG_getfuncline(p, pc);
- sprintf(buff, "(%4d) %4d - ", line, pc);
+ int lineinfo = (p->lineinfo != NULL) ? p->lineinfo[pc] : 0;
+ sprintf(buff, "(%2d - %4d) %4d - ", lineinfo, line, pc);
switch (getOpMode(o)) {
case iABC:
sprintf(buff+strlen(buff), "%-12s%4d %4d %4d%s", name,
@@ -621,6 +622,24 @@
}
+static int listabslineinfo (lua_State *L) {
+ Proto *p;
+ int i;
+ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = getproto(obj_at(L, 1));
+ luaL_argcheck(L, p->abslineinfo != NULL, 1, "function has no debug info");
+ lua_createtable(L, 2 * p->sizeabslineinfo, 0);
+ for (i=0; i < p->sizeabslineinfo; i++) {
+ lua_pushinteger(L, p->abslineinfo[i].pc);
+ lua_rawseti(L, -2, 2 * i + 1);
+ lua_pushinteger(L, p->abslineinfo[i].line);
+ lua_rawseti(L, -2, 2 * i + 2);
+ }
+ return 1;
+}
+
+
static int listlocals (lua_State *L) {
Proto *p;
int pc = cast_int(luaL_checkinteger(L, 2)) - 1;
@@ -1682,6 +1701,7 @@
{"listcode", listcode},
{"printcode", printcode},
{"listk", listk},
+ {"listabslineinfo", listabslineinfo},
{"listlocals", listlocals},
{"loadlib", loadlib},
{"checkpanic", checkpanic},
diff --git a/testes/errors.lua b/testes/errors.lua
index 63a7b74..19a7b6f 100644
--- a/testes/errors.lua
+++ b/testes/errors.lua
@@ -1,4 +1,4 @@
--- $Id: errors.lua,v 1.97 2017/11/28 15:31:56 roberto Exp $
+-- $Id: errors.lua $
-- See Copyright Notice in file all.lua
print("testing errors")
@@ -299,7 +299,7 @@
local function lineerror (s, l)
local err,msg = pcall(load(s))
local line = string.match(msg, ":(%d+):")
- assert((line and line+0) == l)
+ assert(tonumber(line) == l)
end
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
@@ -350,6 +350,23 @@
X=2;lineerror((p), 1)
+lineerror([[
+local b = false
+if not b then
+ error 'test'
+end]], 3)
+
+lineerror([[
+local b = false
+if not b then
+ if not b then
+ if not b then
+ error 'test'
+ end
+ end
+end]], 5)
+
+
if not _soft then
-- several tests that exaust the Lua stack
collectgarbage()