/src/tarantool/src/box/lua/func_adapter.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SPDX-License-Identifier: BSD-2-Clause |
3 | | * |
4 | | * Copyright 2010-2023, Tarantool AUTHORS, please see AUTHORS file. |
5 | | */ |
6 | | #include "box/lua/func_adapter.h" |
7 | | #include "box/lua/tuple.h" |
8 | | #include "box/tuple.h" |
9 | | #include "core/func_adapter.h" |
10 | | #include "lua/msgpack.h" |
11 | | #include "lua/utils.h" |
12 | | |
13 | | #include "box/port.h" |
14 | | |
15 | | /** |
16 | | * Specialization of func_adapter for Lua functions and other callable objects. |
17 | | */ |
18 | | struct func_adapter_lua { |
19 | | /** |
20 | | * Virtual table. |
21 | | */ |
22 | | const struct func_adapter_vtab *vtab; |
23 | | /** |
24 | | * Reference to the function in Lua registry. |
25 | | */ |
26 | | int func_ref; |
27 | | }; |
28 | | |
29 | | /** |
30 | | * Call the function with ports. |
31 | | */ |
32 | | static int |
33 | | func_adapter_lua_call(struct func_adapter *func, struct port *args, struct port *ret) |
34 | 0 | { |
35 | 0 | struct func_adapter_lua *lua_func = |
36 | 0 | (struct func_adapter_lua *)func; |
37 | 0 | int coro_ref = LUA_REFNIL; |
38 | 0 | struct lua_State *L = fiber_lua_state(fiber()); |
39 | | /* |
40 | | * Do not use Lua stack from fiber storage if it is used by args |
41 | | * because port_lua is not allowed to be dumped on the same Lua stack. |
42 | | */ |
43 | 0 | if (args != NULL && port_is_lua(args)) { |
44 | 0 | struct port_lua *args_lua = (struct port_lua *)args; |
45 | 0 | struct lua_State *args_L = args_lua->L; |
46 | 0 | if (args_L == L) |
47 | 0 | L = NULL; |
48 | 0 | } |
49 | 0 | if (L == NULL) { |
50 | 0 | L = luaT_newthread(tarantool_L); |
51 | 0 | if (L == NULL) |
52 | 0 | panic("Cannot create Lua thread"); |
53 | 0 | coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX); |
54 | 0 | } |
55 | 0 | int top_svp = lua_gettop(L); |
56 | |
|
57 | 0 | lua_rawgeti(L, LUA_REGISTRYINDEX, lua_func->func_ref); |
58 | 0 | if (args != NULL) |
59 | 0 | port_dump_lua(args, L, PORT_DUMP_LUA_MODE_FLAT); |
60 | |
|
61 | 0 | int nargs = lua_gettop(L) - top_svp - 1; |
62 | 0 | bool ok = luaT_call(L, nargs, LUA_MULTRET) == 0; |
63 | |
|
64 | 0 | if (!ok || ret == NULL) { |
65 | 0 | luaL_unref(tarantool_L, LUA_REGISTRYINDEX, coro_ref); |
66 | 0 | } else { |
67 | 0 | port_lua_create_at(ret, L, top_svp + 1); |
68 | 0 | struct port_lua *port_lua = (struct port_lua *)ret; |
69 | 0 | port_lua->ref = coro_ref; |
70 | 0 | } |
71 | 0 | return ok ? 0 : -1; |
72 | 0 | } |
73 | | |
74 | | /** |
75 | | * Virtual destructor. |
76 | | */ |
77 | | static void |
78 | | func_adapter_lua_destroy(struct func_adapter *func_base) |
79 | 0 | { |
80 | 0 | struct func_adapter_lua *func = (struct func_adapter_lua *)func_base; |
81 | 0 | luaL_unref(tarantool_L, LUA_REGISTRYINDEX, func->func_ref); |
82 | 0 | free(func); |
83 | 0 | } |
84 | | |
85 | | bool |
86 | | func_adapter_is_lua(struct func_adapter *func) |
87 | 0 | { |
88 | 0 | assert(func != NULL); |
89 | 0 | return func->vtab->destroy == func_adapter_lua_destroy; |
90 | 0 | } |
91 | | |
92 | | void |
93 | | func_adapter_lua_get_func(struct func_adapter *func, lua_State *L) |
94 | 0 | { |
95 | 0 | assert(func != NULL); |
96 | 0 | assert(func->vtab->destroy == func_adapter_lua_destroy); |
97 | 0 | struct func_adapter_lua *lua_func = |
98 | 0 | (struct func_adapter_lua *)func; |
99 | 0 | lua_rawgeti(L, LUA_REGISTRYINDEX, lua_func->func_ref); |
100 | 0 | } |
101 | | |
102 | | struct func_adapter * |
103 | | func_adapter_lua_create(lua_State *L, int idx) |
104 | 0 | { |
105 | 0 | static const struct func_adapter_vtab vtab = { |
106 | 0 | .call = func_adapter_lua_call, |
107 | 0 | .destroy = func_adapter_lua_destroy, |
108 | 0 | }; |
109 | 0 | struct func_adapter_lua *func = xmalloc(sizeof(*func)); |
110 | 0 | assert(luaL_iscallable(L, idx)); |
111 | 0 | lua_pushvalue(L, idx); |
112 | 0 | func->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); |
113 | 0 | func->vtab = &vtab; |
114 | 0 | return (struct func_adapter *)func; |
115 | 0 | } |