Skip to content

Commit

Permalink
add basic error handler function to validate approach (#257)
Browse files Browse the repository at this point in the history
* add basic error handler function to validate approach

* do not print backtrace but save in registry

* bump lua version and fix travis setup so test suite can find titan runtime library

* abstract throwing of errors by titan code to generator function instead of directly using luaL_error

* reserve first stack slot for error value
  • Loading branch information
mascarenhas committed Jan 29, 2019
1 parent cc0647a commit 11a2109
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# lua
lua/src/lua
lua/src/luac
lua-5.3.4
lua-5.3.5
20 changes: 11 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ matrix:
- INSTALL_LUA="lua=5.3"
- LUA="lua"
- LUA_OS="linux"
# - os: osx
# language: generic
# env:
# - INSTALL_LUA="lua=5.3"
# - LUA="lua"
# - LUA_OS="macosx"
- os: osx
language: generic
env:
- INSTALL_LUA="lua=5.3"
- LUA="lua"
- LUA_OS="macosx"

before_install:
- if [ ! -f lua_install/bin/luarocks ]; then pip2 install hererocks; fi
- if [ ! -f lua_install/bin/luarocks ]; then hererocks lua_install -r^ --$INSTALL_LUA; fi
- export PATH=$PATH:$PWD/lua_install/bin # Add directory with all installed binaries to PATH
- wget http://www.lua.org/ftp/lua-5.3.4.tar.gz
- tar zxvf lua-5.3.4.tar.gz
- cd lua-5.3.4; make $LUA_OS MYCFLAGS=-fPIC; cd ..
- export LUA_PATH="./?.lua;$PWD/lua_install/share/lua/5.3/?.lua"
- export LUA_CPATH="./?.so;$PWD/lua_install/lib/lua/5.3/?.so"
- wget http://www.lua.org/ftp/lua-5.3.5.tar.gz
- tar zxvf lua-5.3.5.tar.gz
- cd lua-5.3.5; make $LUA_OS MYCFLAGS=-fPIC; cd ..

install:
- if [ ! -f lua_install/bin/busted ]; then luarocks install busted; fi
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ application programming language with a focus on performance.

# Install

First you need to download, extract and build the [sources to Lua 5.3.4](http://www.lua.org/ftp/lua-5.3.4.tar.gz)
First you need to download, extract and build the [sources to Lua 5.3.5](http://www.lua.org/ftp/lua-5.3.5.tar.gz)
inside the folder where you cloned this repository. The Lua tarball will extract
to a `lua-5.3.4` folder. Enter it and build Lua with `make linux MYCFLAGS=-fPIC`.
to a `lua-5.3.5` folder. Enter it and build Lua with `make linux MYCFLAGS=-fPIC`.

You can install the Titan compiler itself using [LuaRocks](http://luarocks.org)
this will also install all dependencies automatically.
Expand Down Expand Up @@ -76,7 +76,7 @@ You may need to adapt the invocation of `make` above to your platform.
# Compiler options

--print-ast Print the AST.
--lua <path> Path to the Lua sources (default 'lua-5.3.4/src')
--lua <path> Path to the Lua sources (default 'lua-5.3.5/src')
--tree <path> Path to the source tree for your Titan modules (default '.')
-h, --help Show this help message and exit.
Expand Down
13 changes: 13 additions & 0 deletions examples/artisanal.titan
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,16 @@ end
function setindent(a: string)
tab.indent = a
end

function fa(): integer
return fb()[1]
end

function fb(): {integer}
return {fc()}
end

function fc(): integer
local x: value = "foo"
return x
end
15 changes: 14 additions & 1 deletion spec/coder_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ local function generate_modules(modules, main, forapp)
end

local function call(modname, code)
local cmd = string.format("lua-5.3.4/src/lua -l %s -e \"print(pcall(function () %s end))\"",
local cmd = string.format("lua-5.3.5/src/lua -l %s -e \"print(pcall(function () %s end))\"",
modname, code)
local f = io.popen(cmd)
local out = f:read()
Expand Down Expand Up @@ -2047,6 +2047,19 @@ describe("Titan code generator", function()
end)
end

it("check that backtrace is being set on error", function ()
run_coder([[
function wrong(): integer
local x: value = {}
return x
end
]], [[
local titan = require 'titan'
local ok, err = pcall(test.wrong)
assert(not ok)
assert(titan.backtrace():find(err, 1, true))
]])
end)

describe("Lua vs C operator semantics", function()
it("unary (-)", function()
Expand Down
71 changes: 54 additions & 17 deletions titan-compiler/coder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ end

local checkandset

local function codethrow(ctx, template, params)
return render([[
luaL_error(L, "$TEMPLATE", $PARAMS);
]], {
TEMPLATE = template,
PARAMS = table.concat(params, ',')
}, true)
end

local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:string]], loc --[[:table]])
local tag
if typ._tag == "Type.Integer" then
Expand All @@ -172,19 +181,21 @@ local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:s
float _v = fltvalue($EXP);
float _flt = l_floor(_v);
if (TITAN_UNLIKELY(_v != _flt)) {
luaL_error(L, "%s:%d:%d: type error, number '%f' has no integer representation", $FILE, $LINE, $COL, _v);
$THROWFLOAT
} else {
lua_numbertointeger(_flt, &$VAR);
}
} else {
luaL_error(L, "%s:%d:%d: type error, expected integer but found %s", $FILE, $LINE, $COL, lua_typename(L, ttnov($EXP)));
$THROWINTEGER
}
]], {
EXP = exp,
VAR = cvar,
FILE = c_string_literal(loc.filename),
LINE = c_integer_literal(loc.line),
COL = c_integer_literal(loc.col),
THROWFLOAT = codethrow(ctx, "%s:%d:%d: type error, number '%f' has no integer representation", { "$FILE", "$LINE", "$COL", "_v" }),
THROWINTEGER = codethrow(ctx, "%s:%d:%d: type error, expected integer but found %s", { "$FILE", "$LINE", "$COL", "lua_typename(L, ttnov($EXP))" })
})
elseif typ._tag == "Type.Float" then
return render([[
Expand All @@ -193,14 +204,15 @@ local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:s
} else if (ttisinteger($EXP)) {
$VAR = (lua_Number)ivalue($EXP);
} else {
luaL_error(L, "%s:%d:%d: type error, expected float but found %s", $FILE, $LINE, $COL, lua_typename(L, ttnov($EXP)));
$THROW
}
]], {
EXP = exp,
VAR = cvar,
FILE = c_string_literal(loc.filename),
LINE = c_integer_literal(loc.line),
COL = c_integer_literal(loc.col),
THROW = codethrow(ctx, "%s:%d:%d: type error, expected float but found %s", { "$FILE", "$LINE", "$COL", "lua_typename(L, ttnov($EXP))" })
})
elseif typ._tag == "Type.Boolean" then
return render([[
Expand Down Expand Up @@ -247,13 +259,13 @@ local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:s
lua_pushlightuserdata(L, _tag);
if(lua_rawget(L, LUA_REGISTRYINDEX) == LUA_TNIL) {
setuvalue(L, L->top, _ud);
luaL_error(L, "%s:%d:%d: type error, expected %s but found %s", $FILE, $LINE, $COL, "$NAME", luaL_tolstring(L, -1, NULL));
$THROWNIL
} else {
luaL_error(L, "%s:%d:%d: type error, expected %s but found %s", $FILE, $LINE, $COL, "$NAME", lua_tostring(L, -1));
$THROWUD
}
}
} else {
luaL_error(L, "%s:%d:%d: type error, expected %s but found %s", $FILE, $LINE, $COL, "$NAME", lua_typename(L, ttnov($EXP)));
$THROWPRIM
}
]], {
TAG = type2tagname(ctx, typ),
Expand All @@ -262,7 +274,10 @@ local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:s
COL = c_integer_literal(loc.col),
EXP = exp,
VAR = cvar,
NAME = types.tostring(typ)
NAME = types.tostring(typ),
THROWNIL = codethrow(ctx, "%s:%d:%d: type error, expected %s but found %s", { "$FILE", "$LINE", "$COL", '"$NAME"', "luaL_tolstring(L, -1, NULL)" }),
THROWUD = codethrow(ctx, "%s:%d:%d: type error, expected %s but found %s", { "$FILE", "$LINE", "$COL", '"$NAME"', "lua_tostring(L, -1)" }),
THROWPRIM = codethrow(ctx, "%s:%d:%d: type error, expected %s but found %s", { "$FILE", "$LINE", "$COL", '"$NAME"', "lua_typename(L, ttnov($EXP))" })
})
else
error("invalid type " .. types.tostring(typ))
Expand All @@ -271,7 +286,7 @@ local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:s
if (TITAN_LIKELY($PREDICATE($EXP))) {
$GETSLOT;
} else {
luaL_error(L, "%s:%d:%d: type error, expected %s but found %s", $FILE, $LINE, $COL, $TAG, lua_typename(L, ttnov($EXP)));
$THROW
}
]], {
EXP = exp,
Expand All @@ -281,7 +296,8 @@ local function checkandget(ctx, typ --[[:table]], cvar --[[:string]], exp --[[:s
FILE = c_string_literal(loc.filename),
LINE = c_integer_literal(loc.line),
COL = c_integer_literal(loc.col),
})
THROW = codethrow(ctx, "%s:%d:%d: type error, expected %s but found %s", { "$FILE", "$LINE", "$COL", "$TAG", "lua_typename(L, ttnov($EXP))" })
})
end

function checkandset(ctx, typ --[[:table]], dst --[[:string]], src --[[:string]], loc --[[:table]])
Expand All @@ -294,6 +310,7 @@ function checkandset(ctx, typ --[[:table]], dst --[[:string]], src --[[:string]]
} else if (ttisinteger($SRC)) {
setfltvalue($DST, ((lua_Number)ivalue($SRC)));
} else {
$THROW
luaL_error(L, "%s:%d:%d: type error, expected float but found %s", $FILE, $LINE, $COL, lua_typename(L, ttnov($SRC)));
}
]], {
Expand All @@ -302,6 +319,7 @@ function checkandset(ctx, typ --[[:table]], dst --[[:string]], src --[[:string]]
FILE = c_string_literal(loc.filename),
LINE = c_integer_literal(loc.line),
COL = c_integer_literal(loc.col),
THROW = codethrow(ctx, "%s:%d:%d: type error, expected float but found %s", { "$FILE", "$LINE", "$COL", "lua_typename(L, ttnov($SRC))" })
})
elseif typ._tag == "Type.Boolean" then tag = "boolean"
elseif typ._tag == "Type.Nil" then tag = "nil"
Expand Down Expand Up @@ -345,7 +363,7 @@ function checkandset(ctx, typ --[[:table]], dst --[[:string]], src --[[:string]]
if (TITAN_LIKELY($PREDICATE($SRC))) {
setobj2t(L, $DST, $SRC);
} else {
luaL_error(L, "%s:%d:%d: type error, expected %s but found %s", $FILE, $LINE, $COL, $TAG, lua_typename(L, ttnov($SRC)));
$THROW
}
]], {
TAG = c_string_literal(tag),
Expand All @@ -355,7 +373,8 @@ function checkandset(ctx, typ --[[:table]], dst --[[:string]], src --[[:string]]
FILE = c_string_literal(loc.filename),
LINE = c_integer_literal(loc.line),
COL = c_integer_literal(loc.col),
})
THROW = codethrow(ctx, "%s:%d:%d: type error, expected %s but found %s", { "$FILE", "$LINE", "$COL", "$TAG", "lua_typename(L, ttnov($SRC))" })
})
end

local function setslot(typ --[[:table]], dst --[[:string]], src --[[:string]])
Expand Down Expand Up @@ -1704,7 +1723,7 @@ local function generate_binop_idiv_int(exp, ctx)
${Q_DECL}
if (l_castS2U(${N}) + 1u <= 1u) {
if (${N} == 0){
luaL_error(L, "error at line %d, divide by zero", $LINE);
$THROW_DIV0
} else {
${Q} = intop(-, 0, ${M});
}
Expand All @@ -1722,6 +1741,7 @@ local function generate_binop_idiv_int(exp, ctx)
Q = qname,
Q_DECL = qdecl,
LINE = c_integer_literal(exp.loc.line),
THROW_DIV0 = codethrow(ctx, "error at line %d, divide by zero", { "$LINE" })
})
return cstats, qname
end
Expand Down Expand Up @@ -1759,7 +1779,7 @@ local function generate_binop_mod_int(exp, ctx)
${R_DECL};
if (l_castS2U(${N}) + 1u <= 1u) {
if (${N} == 0){
luaL_error(L, "error at line %d, % by zero", $LINE);
$THROW_MOD0
} else {
${R} = 0;
}
Expand All @@ -1777,6 +1797,7 @@ local function generate_binop_mod_int(exp, ctx)
R = rname,
R_DECL = rdecl,
LINE = c_integer_literal(exp.loc.line),
THROW_MOD0 = codethrow(ctx, "error at line %d, % by zero", { "$LINE" })
})
return cstats, rname
end
Expand Down Expand Up @@ -2169,7 +2190,7 @@ function codeexp(ctx, node, iscondition, target)
$TMPNAME1 = $CEXP;
$TMPNAME2 = l_floor($TMPNAME1);
if ($TMPNAME1 != $TMPNAME2) {
luaL_error(L, "type error at line %d, number '%f' has no integer representation", $LINE, $TMPNAME1);
$THROW_WRONG_INT
} else {
lua_numbertointeger($TMPNAME2, &$TMPNAME3);
}
Expand All @@ -2182,7 +2203,8 @@ function codeexp(ctx, node, iscondition, target)
TMPNAME1 = tmpname1,
TMPNAME2 = tmpname2,
TMPNAME3 = tmpname3,
LINE = c_integer_literal(node.loc.line)
LINE = c_integer_literal(node.loc.line),
THROW_WRONG_INT = codethrow(ctx, "type error at line %d, number '%f' has no integer representation", { "$LINE", "$TMPNAME1" })
})
return cfloor, tmpname3
elseif tag == "Ast.ExpCast" and node._type._tag == "Type.String" then
Expand Down Expand Up @@ -2243,7 +2265,7 @@ function codeexp(ctx, node, iscondition, target)
$CSTATS
$CTMP
if(TITAN_UNLIKELY(ttisnil(&$CEXP))) {
luaL_error(L, "%s:%d:%d: type error, expected %s but found nil", $FILE, $LINE, $COL, $TYPENAME);
$THROW_WRONG_CAST
} else {
$GETSLOT
}
Expand All @@ -2255,7 +2277,8 @@ function codeexp(ctx, node, iscondition, target)
LINE = node.loc.line,
COL = node.loc.col,
TYPENAME = c_string_literal(types.tostring(node._type)),
GETSLOT = getslot(node._type, tmpname, "&" .. cexp)
GETSLOT = getslot(node._type, tmpname, "&" .. cexp),
THROW_WRONG_CAST = codethrow(ctx, "%s:%d:%d: type error, expected %s but found nil", { "$FILE", "$LINE", "$COL", "$TYPENAME" })
}), tmpname
elseif tag == "Ast.ExpCast" and node._type._tag == "Type.Pointer" then
local cstats, cexp = codeexp(ctx, node.exp)
Expand Down Expand Up @@ -2367,6 +2390,13 @@ local function genluaentry(tlcontext, titan_name, titan_entry, typ, loc, lua_nam
table.insert(stats, ctype(ptype) .. " " .. pname .. " = " .. initval(ptype) .. ";")
end
local rettype = typ.rettypes[1]
if #params > 0 then
table.insert(stats, [[
lua_pushvalue(L, 1); /* make space for error slot */
lua_pushnil(L);
lua_replace(L, 1); /* replace first stack element with nil, this is our error slot */
]])
end
table.insert(stats, render([[
lua_checkstack(L, $NRET);
TValue *_firstret = L->top;
Expand All @@ -2390,6 +2420,7 @@ local function genluaentry(tlcontext, titan_name, titan_entry, typ, loc, lua_nam
}))
end
table.insert(stats, render([[
L->errfunc = __save_errfunc;
return $NRET;
]], {
NRET = #typ.rettypes
Expand All @@ -2400,11 +2431,16 @@ local function genluaentry(tlcontext, titan_name, titan_entry, typ, loc, lua_nam
if((L->top - func - 1) != $EXPECTED) {
luaL_error(L, "calling Titan function %s with %d arguments, but expected %d", $NAME, L->top - func - 1, $EXPECTED);
}
$ERRORSLOT
int __save_errfunc = L->errfunc;
lua_pushcclosure(L, errorhandler, 0);
L->errfunc = savestack(L, L->top-1);
CClosure *_mod = clCvalue(&clCvalue(func)->upvalue[0]);
$BODY
}]], {
LUANAME = lua_name,
EXPECTED = c_integer_literal(#params),
ERRORSLOT = #params == 0 and "lua_pushnil(L);" or "",
NAME = c_string_literal(titan_name),
BODY = table.concat(stats, "\n"),
})
Expand Down Expand Up @@ -2567,6 +2603,7 @@ local preamble = [[
#include "lstring.h"
#include "lvm.h"
#include "lobject.h"
#include "ldo.h"
#include "titan.h"
Expand Down
6 changes: 4 additions & 2 deletions titan-compiler/driver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ driver.imported = {}

driver.TITAN_BIN_PATH = os.getenv("TITAN_PATH_0_5") or os.getenv("TITAN_PATH") or ".;/usr/local/lib/titan/0.5"
driver.TITAN_SOURCE_PATH = "."
driver.LUA_SOURCE_PATH = "lua-5.3.4/src/"
driver.LUA_SOURCE_PATH = "lua-5.3.5/src/"
driver.TITAN_RUNTIME_PATH = "titan-runtime/"
driver.CFLAGS = "--std=c99 -O2 -Wall -fPIC"
driver.CC = "cc"
Expand Down Expand Up @@ -139,7 +139,9 @@ end
local function check_runtime()
local runtime_c = driver.TITAN_RUNTIME_PATH .. "titan.c"
local runtime_o = driver.TITAN_RUNTIME_PATH .. "titan.o"
if not lfs.attributes(runtime_o, "size") then
local titanc_mtime = lfs.attributes(runtime_c, "modification")
local titano_mtime = lfs.attributes(runtime_o, "modification")
if (not titano_mtime) or (titano_mtime < titanc_mtime) then
local args = {driver.CC, driver.CFLAGS, "-c", runtime_c,
"-I", driver.TITAN_RUNTIME_PATH, "-I", driver.LUA_SOURCE_PATH,
"-o", runtime_o }
Expand Down
Loading

0 comments on commit 11a2109

Please sign in to comment.