forked from ThePhD/sol2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstack.hpp
365 lines (328 loc) · 14.6 KB
/
stack.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2022 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SOL_STACK_HPP
#define SOL_STACK_HPP
#include <sol/trampoline.hpp>
#include <sol/stack_core.hpp>
#include <sol/stack_reference.hpp>
#include <sol/stack_check.hpp>
#include <sol/stack_get.hpp>
#include <sol/stack_check_get.hpp>
#include <sol/stack_push.hpp>
#include <sol/stack_pop.hpp>
#include <sol/stack_field.hpp>
#include <sol/stack_probe.hpp>
#include <sol/assert.hpp>
#include <cstring>
#include <array>
namespace sol {
namespace detail {
using typical_chunk_name_t = char[SOL_ID_SIZE_I_];
using typical_file_chunk_name_t = char[SOL_FILE_ID_SIZE_I_];
inline const std::string& default_chunk_name() {
static const std::string name = "";
return name;
}
template <std::size_t N>
const char* make_chunk_name(const string_view& code, const std::string& chunkname, char (&basechunkname)[N]) {
if (chunkname.empty()) {
auto it = code.cbegin();
auto e = code.cend();
std::size_t i = 0;
static const std::size_t n = N - 4;
for (i = 0; i < n && it != e; ++i, ++it) {
basechunkname[i] = *it;
}
if (it != e) {
for (std::size_t c = 0; c < 3; ++i, ++c) {
basechunkname[i] = '.';
}
}
basechunkname[i] = '\0';
return &basechunkname[0];
}
else {
return chunkname.c_str();
}
}
inline void clear_entries(stack_reference r) {
stack::push(r.lua_state(), lua_nil);
while (lua_next(r.lua_state(), -2)) {
absolute_index key(r.lua_state(), -2);
auto pn = stack::pop_n(r.lua_state(), 1);
stack::set_field<false, true>(r.lua_state(), key, lua_nil, r.stack_index());
}
}
inline void clear_entries(const reference& registry_reference) {
auto pp = stack::push_pop(registry_reference);
stack_reference ref(registry_reference.lua_state(), -1);
clear_entries(ref);
}
} // namespace detail
namespace stack {
namespace stack_detail {
template <typename T>
inline int push_as_upvalues(lua_State* L, T& item) {
typedef std::decay_t<T> TValue;
static const std::size_t itemsize = sizeof(TValue);
static const std::size_t voidsize = sizeof(void*);
static const std::size_t voidsizem1 = voidsize - 1;
static const std::size_t data_t_count = (sizeof(TValue) + voidsizem1) / voidsize;
typedef std::array<void*, data_t_count> data_t;
data_t data { {} };
std::memcpy(&data[0], std::addressof(item), itemsize);
int pushcount = 0;
for (const auto& v : data) {
lua_pushlightuserdata(L, v);
pushcount += 1;
}
return pushcount;
}
template <typename T>
inline std::pair<T, int> get_as_upvalues(lua_State* L, int index = 2) {
static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
data_t voiddata { {} };
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[i] = lua_touserdata(L, upvalue_index(index++));
}
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template <typename T>
inline std::pair<T, int> get_as_upvalues_using_function(lua_State* L, int function_index = -1) {
static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
function_index = lua_absindex(L, function_index);
int index = 0;
data_t voiddata { {} };
for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) {
// first upvalue is nullptr to respect environment shenanigans
// So +2 instead of +1
const char* upvalue_name = lua_getupvalue(L, function_index, index + 2);
if (upvalue_name == nullptr) {
// We should freak out here...
break;
}
voiddata[index] = lua_touserdata(L, -1);
++index;
}
lua_pop(L, index);
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template <bool checked, typename Handler, typename Fx, typename... Args>
static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, Handler&&, record&, Fx&& fx, Args&&... args) {
return std::forward<Fx>(fx)(std::forward<Args>(args)...);
}
template <bool checked, typename Arg, typename... Args, std::size_t I, std::size_t... Is, typename Handler, typename Fx, typename... FxArgs>
static decltype(auto) eval(types<Arg, Args...>, std::index_sequence<I, Is...>, lua_State* L_, int start_index_, Handler&& handler_,
record& tracking_, Fx&& fx_, FxArgs&&... fxargs_) {
#if 0 && SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
// NOTE: THIS IS TERMPORARILY TURNED OFF BECAUSE IT IMPACTS ACTUAL SEMANTICS W.R.T. THINGS LIKE LUAJIT,
// SO IT MUST REMAIN OFF UNTIL WE CAN ESTABLISH SIMILAR BEHAVIOR IN MODES WHERE `checked == false`!
// We can save performance/time by letting errors unwind produced arguments
// rather than checking everything once, and then potentially re-doing work
if constexpr (checked) {
return eval<checked>(types<Args...>(),
std::index_sequence<Is...>(),
L_,
start_index_,
std::forward<Handler>(handler_),
tracking_,
std::forward<Fx>(fx_),
std::forward<FxArgs>(fxargs_)...,
*stack_detail::check_get_arg<Arg>(L_, start_index_ + tracking_.used, handler_, tracking_));
}
else
#endif
{
return eval<checked>(types<Args...>(),
std::index_sequence<Is...>(),
L_,
start_index_,
std::forward<Handler>(handler_),
tracking_,
std::forward<Fx>(fx_),
std::forward<FxArgs>(fxargs_)...,
stack_detail::unchecked_get_arg<Arg>(L_, start_index_ + tracking_.used, tracking_));
}
}
template <bool checkargs = detail::default_safe_function_calls, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs>
inline decltype(auto) call(types<R>, types<Args...> argument_types_, std::index_sequence<I...> argument_indices_, lua_State* L_,
int start_index_, Fx&& fx_, FxArgs&&... args_) {
static_assert(meta::all_v<meta::is_not_move_only<Args>...>,
"One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take "
"a reference and std::move it manually if this was your intention.");
argument_handler<types<R, Args...>> handler {};
record tracking {};
#if SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
if constexpr (checkargs) {
multi_check<Args...>(L_, start_index_, handler);
}
#endif
if constexpr (std::is_void_v<R>) {
eval<checkargs>(
argument_types_, argument_indices_, L_, start_index_, handler, tracking, std::forward<Fx>(fx_), std::forward<FxArgs>(args_)...);
}
else {
return eval<checkargs>(
argument_types_, argument_indices_, L_, start_index_, handler, tracking, std::forward<Fx>(fx_), std::forward<FxArgs>(args_)...);
}
}
template <typename T>
void raw_table_set(lua_State* L, T&& arg, int tableindex = -2) {
int push_count = push(L, std::forward<T>(arg));
SOL_ASSERT(push_count == 1);
std::size_t unique_index = static_cast<std::size_t>(luaL_len(L, tableindex) + 1u);
lua_rawseti(L, tableindex, static_cast<int>(unique_index));
}
} // namespace stack_detail
template <typename T>
int set_ref(lua_State* L, T&& arg, int tableindex = -2) {
int push_count = push(L, std::forward<T>(arg));
SOL_ASSERT(push_count == 1);
return luaL_ref(L, tableindex);
}
template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
using args_indices = std::make_index_sequence<sizeof...(Args)>;
if constexpr (std::is_void_v<R>) {
stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
}
else {
return stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
}
}
template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
if constexpr (std::is_void_v<R>) {
call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
}
else {
return call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
}
}
template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
inline decltype(auto) call_from_top(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
using expected_count_t = meta::count_for_pack<lua_size, Args...>;
if constexpr (std::is_void_v<R>) {
call<check_args>(tr,
ta,
L,
(std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
std::forward<Fx>(fx),
std::forward<FxArgs>(args)...);
}
else {
return call<check_args>(tr,
ta,
L,
(std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
std::forward<Fx>(fx),
std::forward<FxArgs>(args)...);
}
}
template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Ret0, typename... Ret, typename... Args,
typename Fx, typename... FxArgs>
inline int call_into_lua(types<Ret0, Ret...> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
if constexpr (std::is_void_v<Ret0>) {
call<check_args>(tr, ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
if constexpr (clean_stack) {
lua_settop(L, 0);
}
return 0;
}
else {
(void)tr;
decltype(auto) r
= call<check_args>(types<meta::return_type_t<Ret0, Ret...>>(), ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
using R = meta::unqualified_t<decltype(r)>;
using is_stack = meta::any<is_stack_based<R>, std::is_same<R, absolute_index>, std::is_same<R, ref_index>, std::is_same<R, raw_index>>;
if constexpr (clean_stack && !is_stack::value) {
lua_settop(L, 0);
}
return push_reference(L, std::forward<decltype(r)>(r));
}
}
template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Fx, typename... FxArgs>
inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
using traits_type = lua_bind_traits<meta::unqualified_t<Fx>>;
using args_list = typename traits_type::args_list;
using returns_list = typename traits_type::returns_list;
return call_into_lua<check_args, clean_stack>(returns_list(), args_list(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
}
inline call_syntax get_call_syntax(lua_State* L, const string_view& key, int index) {
if (lua_gettop(L) < 1) {
return call_syntax::dot;
}
luaL_getmetatable(L, key.data());
auto pn = pop_n(L, 1);
if (lua_compare(L, -1, index, LUA_OPEQ) != 1) {
return call_syntax::dot;
}
return call_syntax::colon;
}
inline void script(
lua_State* L, lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
detail::typical_chunk_name_t basechunkname = {};
const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
lua_error(L);
}
}
inline void script(
lua_State* L, const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
detail::typical_chunk_name_t basechunkname = {};
const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
lua_error(L);
}
}
inline void script_file(lua_State* L, const std::string& filename, load_mode mode = load_mode::any) {
if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
lua_error(L);
}
}
inline void luajit_exception_handler(lua_State* L, int (*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) {
#if SOL_IS_ON(SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE)
if (L == nullptr) {
return;
}
#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
#endif // make sure stack doesn't overflow
lua_pushlightuserdata(L, (void*)handler);
auto pn = pop_n(L, 1);
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
#else
(void)L;
(void)handler;
#endif
}
inline void luajit_exception_off(lua_State* L) {
#if SOL_IS_ON(SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE)
if (L == nullptr) {
return;
}
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_OFF);
#else
(void)L;
#endif
}
} // namespace stack
} // namespace sol
#endif // SOL_STACK_HPP