Skip to content
Snippets Groups Projects
Commit 6161ad06 authored by Aleksandr Lyapunov's avatar Aleksandr Lyapunov Committed by Aleksandr Lyapunov
Browse files

box: support brackets in name resolution for Lua calls

Fefactor lua name resolution, simplify and comment.
Add an ability to specify path with brackets, for example in
'box.space[512]:get' or 'box.space["test"]:get'.
Only literals (strings and numbers) are supported.

Closes #8604

@TarantoolBot document
Title: square brackets in procedure resolution for Lua calls

Square brackets are now supported in Lua call procedure resolution. This is
applicable to `net.box` connection objects `call` method as well as
`box.schema.func.call`.

Examples of function calls with square brackets can be found in the test to
this patch.
parent 246e8232
No related branches found
No related tags found
No related merge requests found
## feature/box
* Added support for square brackets in procedure resolution for Lua calls
(gh-8604).
......@@ -93,74 +93,86 @@ get_call_serializer(void)
}
/**
* A helper to find a Lua function by name and put it
* on top of the stack.
* A helper to resolve a Lua function by full name, for example like:
* foo.bar['biz']["baz"][3].object:function
* Puts the function on top of the stack, followed by an object (if present).
* Returns number of items pushed (1 or 2) or -1 in case of error (diag is set).
*/
static int
box_lua_find(lua_State *L, const char *name, const char *name_end)
{
int index = LUA_GLOBALSINDEX;
int objstack = 0, top = lua_gettop(L);
const char *start = name, *end;
while ((end = (const char *) memchr(start, '.', name_end - start))) {
lua_checkstack(L, 3);
lua_pushlstring(L, start, end - start);
lua_gettable(L, index);
if (! lua_istable(L, -1)) {
diag_set(ClientError, ER_NO_SUCH_PROC,
name_end - name, name);
return -1;
}
start = end + 1; /* next piece of a.b.c */
index = lua_gettop(L); /* top of the stack */
}
lua_checkstack(L, 2); /* No more than 2 entries are needed. */
int top = lua_gettop(L);
/* box.something:method */
if ((end = (const char *) memchr(start, ':', name_end - start))) {
lua_checkstack(L, 3);
lua_pushlstring(L, start, end - start);
lua_gettable(L, index);
if (! (lua_istable(L, -1) ||
lua_islightuserdata(L, -1) || lua_isuserdata(L, -1) )) {
diag_set(ClientError, ER_NO_SUCH_PROC,
name_end - name, name);
return -1;
/* Take the first token. */
const char *start = name;
while (start != name_end && *start != '.' &&
*start != ':' && *start != '[')
start++;
lua_pushlstring(L, name, start - name);
lua_gettable(L, LUA_GLOBALSINDEX);
/* Take the rest tokens. */
while (start != name_end) {
if (!lua_istable(L, -1) &&
!lua_islightuserdata(L, -1) && !lua_isuserdata(L, -1))
goto no_such_proc;
char delim = *start++; /* skip delimiter. */
if (delim == '.') {
/* Look for the next token. */
const char *end = start;
while (end != name_end && *end != '.' &&
*end != ':' && *end != '[')
end++;
lua_pushlstring(L, start, end - start);
start = end;
} else if (delim == ':') {
lua_pushlstring(L, start, name_end - start);
lua_gettable(L, -2); /* get function from object. */
lua_insert(L, -2); /* swap function and object. */
break;
} else if (delim == '[') {
const char *end = memchr(start, ']', name_end - start);
if (end == NULL)
goto no_such_proc;
if (end - start >= 2 && start[0] == end[-1] &&
(start[0] == '"' || start[0] == '\'')) {
/* Quoted string, just extract it. */
lua_pushlstring(L, start + 1, end - start - 2);
} else {
/* Must be a number, convert from string. */
lua_pushlstring(L, start, end - start);
int success;
lua_Number num = lua_tonumberx(L, -1, &success);
if (!success)
goto no_such_proc;
lua_pop(L, 1);
lua_pushnumber(L, num);
}
start = end + 1; /* skip closing bracket. */
} else {
goto no_such_proc;
}
start = end + 1; /* next piece of a.b.c */
index = lua_gettop(L); /* top of the stack */
objstack = index - top;
lua_gettable(L, -2); /* get child object from parent object. */
lua_remove(L, -2); /* drop previous parent object. */
}
lua_pushlstring(L, start, name_end - start);
lua_gettable(L, index);
if (!lua_isfunction(L, -1) && !lua_istable(L, -1)) {
/* Now at top+1 must be the function, and at top+2 may be the object. */
assert(lua_gettop(L) - top >= 1 && lua_gettop(L) - top <= 2);
if (!lua_isfunction(L, top + 1) && !lua_istable(L, top + 1)) {
/* lua_call or lua_gettable would raise a type error
* for us, but our own message is more verbose. */
diag_set(ClientError, ER_NO_SUCH_PROC,
name_end - name, name);
return -1;
goto no_such_proc;
}
/* setting stack that it would contain only
* the function pointer. */
if (index != LUA_GLOBALSINDEX) {
if (objstack == 0) { /* no object, only a function */
lua_replace(L, top + 1);
lua_pop(L, lua_gettop(L) - top - 1);
} else if (objstack == 1) { /* just two values, swap them */
lua_insert(L, -2);
lua_pop(L, lua_gettop(L) - top - 2);
} else { /* long path */
lua_insert(L, top + 1);
lua_insert(L, top + 2);
lua_pop(L, objstack - 1);
objstack = 1;
}
}
return 1 + objstack;
return lua_gettop(L) - top;
no_such_proc:
diag_set(ClientError, ER_NO_SUCH_PROC, name_end - name, name);
return -1;
}
/**
......
local t = require('luatest')
local g = t.group()
g.before_all(function()
local netbox = require('net.box')
local a = {
b = {
c = function() return 'c' end,
[555] = function() return 555 end
},
[777] = {
d = {
[444] = function() return 444 end,
e = function() return 'e' end
},
[666] = function() return 666 end
},
[555] = function() return 555 end,
[-1] = function() return -1 end,
[333] = netbox.self,
f = function() return 'f' end,
g = netbox.self
}
rawset(_G, 'a', a)
end)
g.after_all(function()
rawset(_G, 'a', nil)
end)
-- Checks that procedure resolution for Lua calls works correctly.
g.test_procedure_resolution = function()
local netbox = require('net.box')
local function test(proc)
t.assert_equals(netbox.self:call(proc),
netbox.self:eval('return ' .. proc .. '()'))
end
test('a.b.c')
test('a.b.c')
test('a.b["c"]')
test('a.b[\'c\']')
test('a.b[555]')
test('a[777].d[444]')
test('a[777].d.e')
test('a[777][666]')
test('a[555]')
test('a[555.]')
test('a[-1]')
test('a[333]:ping')
test('a.f')
test('a.g:ping')
end
-- Checks that error detection in procedure resolution for Lua calls works
-- correctly.
g.test_procedure_resolution_errors = function()
local netbox = require('net.box')
local function test(proc)
t.assert_error(function() netbox.self:call(proc) end)
end
test('')
test('.')
test(':')
test('[')
test(']')
test('[]')
test('a.')
test('l:')
test('a.b.')
test('a[b]')
test('a[[]')
test('a[[777]')
test('a["b]')
test('a["b\']')
test('a[\'b]')
test('a[\'b"]')
test('a[\'\']')
test('a[""]')
test('a[\'\']')
test('a["b""]')
test('a["b"\']')
test('a[\'b"\']')
test('a["b\'"]')
test('a[333]:')
test('a[333]:ping:')
test('a:[333]:ping:')
test('a:[333]:')
test('a[555].')
test('a[555].')
test('a[777].[666]')
test('a[777]d[444]')
test('a[777].d.[444]')
test('a[777][666]e')
test('a[555')
test('a[555]..')
test('a[555]..')
test('a[777]..[666]')
test('a[777].][666]')
test('a]555[')
test('a]555]')
test('a]]')
test('a[[555]')
test('a[[555]]')
test('a.b[c]')
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment