From 4d5de1c1fb2decb39d74dfb092ca5643ce47176f Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 11 Jul 2018 12:53:23 -0300 Subject: [PATCH] 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. --- lcode.c | 76 +++++++++++++++++++++++++++++++++-------------- ltests.c | 22 +++++++++++++- testes/errors.lua | 21 +++++++++++-- 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/lcode.c b/lcode.c index ab91561c..d00038dd 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 @@ void luaK_patchclose (FuncState *fs, int list) { } +/* +** 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 @@ void luaK_patchclose (FuncState *fs, int list) { ** 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, @@ -337,6 +347,37 @@ static void savelineinfo (FuncState *fs, Proto *f, int pc, int line) { } +/* +** 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 @@ static int luaK_code (FuncState *fs, Instruction i) { /* 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 @@ static int jumponcond (FuncState *fs, expdesc *e, int cond) { 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 @@ void luaK_posfix (FuncState *fs, BinOpr opr, /* -** 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 ac548a95..13717da9 100644 --- a/ltests.c +++ b/ltests.c @@ -526,7 +526,8 @@ static char *buildop (Proto *p, int pc, char *buff) { 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 listk (lua_State *L) { } +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 @@ static const struct luaL_Reg tests_funcs[] = { {"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 63a7b740..19a7b6fa 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 @@ end 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=1;lineerror((p), 2) 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()