Skip to content

Commit 75e5cb6

Browse files
nshylocker
authored andcommitted
lua: prepare utils for box error trace improvement
Here is just of bunch utility functions that used in the patch that make several modules throw box.error with trace set to the caller place. That patch is just a boring huge switch to box.error and setting proper level on error creation. Factor out utility functions so that they and their tests are not get lost. Part of tarantool#9914 NO_CHANGELOG=internal NO_DOC=internal
1 parent 1f75231 commit 75e5cb6

File tree

6 files changed

+534
-9
lines changed

6 files changed

+534
-9
lines changed

src/box/lua/console.lua

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ local urilib = require('uri')
2828
local yaml = require('yaml')
2929
local net_box = require('net.box')
3030
local help = require('help').help
31+
local utils = require('internal.utils')
3132

3233
local DEFAULT_CONNECT_TIMEOUT = 10
3334
local PUSH_TAG_HANDLE = '!push!'
@@ -413,13 +414,6 @@ local function get_command(line)
413414
return nil
414415
end
415416

416-
--
417-
-- return args as table with 'n' set to args number
418-
--
419-
local function table_pack(...)
420-
return {n = select('#', ...), ...}
421-
end
422-
423417
local initial_env
424418

425419
-- Get initial console environment.
@@ -519,7 +513,7 @@ local function local_eval(storage, line)
519513
end
520514
-- box.is_in_txn() is stubbed to throw a error before call to box.cfg{}
521515
local in_txn_before = ffi.C.box_txn()
522-
local res = table_pack(pcall(fun))
516+
local res = utils.table_pack(pcall(fun))
523517
--
524518
-- Rollback if transaction was began in the failed expression.
525519
--

src/lua/utils.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,3 +1135,95 @@ void cord_on_yield(void)
11351135
exit(EXIT_FAILURE);
11361136
}
11371137
}
1138+
1139+
const char *
1140+
luaT_checkstring(struct lua_State *L, int index)
1141+
{
1142+
const char *name = lua_tostring(L, index);
1143+
if (name == NULL) {
1144+
diag_set(IllegalParams, "expected string as %d argument",
1145+
index);
1146+
luaT_error(L);
1147+
}
1148+
return name;
1149+
}
1150+
1151+
const char *
1152+
luaT_checklstring(struct lua_State *L, int index, size_t *len)
1153+
{
1154+
const char *name = lua_tolstring(L, index, len);
1155+
if (name == NULL) {
1156+
diag_set(IllegalParams, "expected string as %d argument",
1157+
index);
1158+
luaT_error(L);
1159+
}
1160+
return name;
1161+
}
1162+
1163+
int
1164+
luaT_checkint(struct lua_State *L, int index)
1165+
{
1166+
int ok;
1167+
int i = lua_tointegerx(L, index, &ok);
1168+
if (!ok) {
1169+
diag_set(IllegalParams, "expected integer as %d argument",
1170+
index);
1171+
luaT_error(L);
1172+
}
1173+
return i;
1174+
}
1175+
1176+
double
1177+
luaT_checknumber(struct lua_State *L, int index)
1178+
{
1179+
int ok;
1180+
double d = lua_tonumberx(L, index, &ok);
1181+
if (!ok) {
1182+
diag_set(IllegalParams, "expected number as %d argument",
1183+
index);
1184+
luaT_error(L);
1185+
}
1186+
return d;
1187+
}
1188+
1189+
void *
1190+
luaT_checkudata(struct lua_State *L, int index, const char *name)
1191+
{
1192+
void *udata = luaL_testudata(L, index, name);
1193+
if (udata == NULL) {
1194+
diag_set(IllegalParams, "expected %s as %d argument",
1195+
name, index);
1196+
luaT_error(L);
1197+
}
1198+
return udata;
1199+
}
1200+
1201+
void
1202+
luaT_checktype(struct lua_State *L, int index, int expected)
1203+
{
1204+
int actual = lua_type(L, index);
1205+
if (actual != expected) {
1206+
diag_set(IllegalParams, "expected %s as %d argument",
1207+
lua_typename(L, expected), index);
1208+
luaT_error(L);
1209+
}
1210+
}
1211+
1212+
int64_t
1213+
luaT_checkint64(struct lua_State *L, int idx)
1214+
{
1215+
int64_t result;
1216+
if (luaL_convertint64(L, idx, false, &result) != 0) {
1217+
diag_set(IllegalParams, "expected int64_t as %d argument", idx);
1218+
luaT_error(L);
1219+
}
1220+
return result;
1221+
}
1222+
1223+
int
1224+
luaT_optint(struct lua_State *L, int index, int deflt)
1225+
{
1226+
if (lua_isnoneornil(L, index))
1227+
return deflt;
1228+
return luaT_checkint(L, index);
1229+
}

src/lua/utils.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ extern "C" {
5454
#include <lj_meta.h>
5555

5656
#include "lua/error.h"
57+
#include "error.h"
58+
#include "diag.h"
5759

5860
struct lua_State;
5961
struct ibuf;
@@ -577,6 +579,38 @@ void luaL_iterator_delete(struct luaL_iterator *it);
577579
int
578580
tarantool_lua_utils_init(struct lua_State *L);
579581

582+
/** Same as luaL_checkstring but raises box error. **/
583+
const char *
584+
luaT_checkstring(struct lua_State *L, int index);
585+
586+
/** Same as luaL_checklstring but raises box error. **/
587+
const char *
588+
luaT_checklstring(struct lua_State *L, int index, size_t *len);
589+
590+
/** Same as luaL_checkint but raises box error. **/
591+
int
592+
luaT_checkint(struct lua_State *L, int index);
593+
594+
/** Same as luaL_checknumber but raises box error. **/
595+
double
596+
luaT_checknumber(struct lua_State *L, int index);
597+
598+
/** Same as luaL_checkudata but raises box error. **/
599+
void *
600+
luaT_checkudata(struct lua_State *L, int index, const char *name);
601+
602+
/** Same as luaL_checktype but raises box error. **/
603+
void
604+
luaT_checktype(struct lua_State *L, int index, int expected);
605+
606+
/** Same as luaL_checkint64 but raises box error. **/
607+
int64_t
608+
luaT_checkint64(struct lua_State *L, int idx);
609+
610+
/** Same as luaL_optint but raises box error. **/
611+
int
612+
luaT_optint(struct lua_State *L, int index, int deflt);
613+
580614
#if defined(__cplusplus)
581615
} /* extern "C" */
582616
#endif /* defined(__cplusplus) */

src/lua/utils.lua

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,43 @@ function utils.box_check_configured()
151151
end
152152
end
153153

154+
-- Checks if value() is valid expression.
155+
function utils.is_callable(value)
156+
if type(value) == 'function' then
157+
return true
158+
end
159+
local mt = debug.getmetatable(value)
160+
if mt ~= nil and type(mt.__call) == 'function' then
161+
return true
162+
end
163+
return false
164+
end
165+
166+
--
167+
-- Return args as table with 'n' set to args number.
168+
--
169+
function utils.table_pack(...)
170+
return {n = select('#', ...), ...}
171+
end
172+
173+
--
174+
-- Call fn with given variable number of arguments. In case of error
175+
-- if error is box.error then update trace frame to the given level.
176+
-- If level is nil then trace frame will not be updated.
177+
--
178+
function utils.call_at(level, fn, ...)
179+
local res = utils.table_pack(pcall(fn, ...))
180+
if not res[1] then
181+
local err = res[2]
182+
if box.error.is(err) then
183+
box.error(err, level and level + 1)
184+
else
185+
-- Keep original trace and append trace of level + 1 as
186+
-- in case of box.error.
187+
error(tostring(err), level and level + 1)
188+
end
189+
end
190+
return unpack(res, 2, res.n)
191+
end
192+
154193
return utils

test/app-luatest/utils_test.lua

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,67 @@ g.test_update_param_table = function()
6565
c = 'y',
6666
})
6767
end
68+
69+
g.test_is_callable = function()
70+
t.assert(utils.is_callable(function() end))
71+
local functor = setmetatable({}, {
72+
__call = function() end
73+
})
74+
t.assert(utils.is_callable(functor))
75+
t.assert_not(utils.is_callable(1))
76+
t.assert_not(utils.is_callable("foo"))
77+
end
78+
79+
g.test_table_pack = function()
80+
t.assert_equals(utils.table_pack(), {n = 0})
81+
t.assert_equals(utils.table_pack(1), {n = 1, 1})
82+
t.assert_equals(utils.table_pack(1, 2), {n = 2, 1, 2})
83+
t.assert_equals(utils.table_pack(1, 2, nil), {n = 3, 1, 2})
84+
t.assert_equals(utils.table_pack(1, 2, nil, 3), {n = 4, 1, 2, nil, 3})
85+
end
86+
87+
g.test_call_at = function()
88+
local f = function(...) return ... end
89+
t.assert_equals(utils.call_at(1, f, nil))
90+
t.assert_equals(utils.call_at(1, f, 1), 1)
91+
t.assert_equals(utils.table_pack(utils.call_at(1, f, 1, 2)),
92+
utils.table_pack(1, 2))
93+
t.assert_equals(utils.table_pack(utils.call_at(1, f, 1, 2, nil)),
94+
utils.table_pack(1, 2, nil))
95+
t.assert_equals(utils.table_pack(utils.call_at(1, f, 1, 2, nil, 3)),
96+
utils.table_pack(1, 2, nil, 3))
97+
98+
local this_file = debug.getinfo(1, 'S').short_src
99+
local line1 = debug.getinfo(1, 'l').currentline + 1
100+
local f1 = function() box.error(box.error.UNKNOWN, 1) end
101+
local line2 = debug.getinfo(1, 'l').currentline + 1
102+
local f2 = function(level, ...) utils.call_at(level, f1, ...) end
103+
local line3 = debug.getinfo(1, 'l').currentline + 1
104+
local f3 = function(...) f2(...) end
105+
106+
local check_box_error = function(level, line)
107+
local ok, err = pcall(f3, level)
108+
t.assert_not(ok)
109+
t.assert(box.error.is(err))
110+
t.assert_equals(err.trace[1].file, this_file)
111+
t.assert_equals(err.trace[1].line, line)
112+
end
113+
114+
check_box_error(nil, line1)
115+
check_box_error(1, line2)
116+
check_box_error(2, line3)
117+
118+
line1 = debug.getinfo(1, 'l').currentline + 1
119+
f1 = function() error('foo', 1) end
120+
121+
local check_box_error = function(level, line_src, line_level)
122+
local ok, err = pcall(f3, level)
123+
t.assert_not(ok)
124+
t.assert_equals(tostring(err),
125+
string.format('%s:%s: %s:%s: foo',
126+
this_file, line_level, this_file, line_src))
127+
end
128+
129+
check_box_error(1, line1, line2)
130+
check_box_error(2, line1, line3)
131+
end

0 commit comments

Comments
 (0)