aboutsummaryrefslogtreecommitdiff
path: root/src/FbTk/Luamm.cc
diff options
context:
space:
mode:
authorPavel Labath <pavelo@centrum.sk>2011-05-19 09:09:37 (GMT)
committerPavel Labath <pavelo@centrum.sk>2011-11-01 09:52:45 (GMT)
commit800a1a761d57919bb3556d5ce48380b6328b14ef (patch)
treedfaa8858136956852bb1a1a3fb77eb14437b9e7c /src/FbTk/Luamm.cc
parent650ccad88ed769132b9a260464fe66b2d31eaab6 (diff)
downloadfluxbox_paul-800a1a761d57919bb3556d5ce48380b6328b14ef.zip
fluxbox_paul-800a1a761d57919bb3556d5ce48380b6328b14ef.tar.bz2
C++ binding for lua
copied from conky (http://conky.sf.net) and relicensed. Since I am the person who wrote it in the first place there should not be a problem with licence conversion.
Diffstat (limited to 'src/FbTk/Luamm.cc')
-rw-r--r--src/FbTk/Luamm.cc468
1 files changed, 468 insertions, 0 deletions
diff --git a/src/FbTk/Luamm.cc b/src/FbTk/Luamm.cc
new file mode 100644
index 0000000..a4a6eca
--- /dev/null
+++ b/src/FbTk/Luamm.cc
@@ -0,0 +1,468 @@
1// luamm: C++ binding for lua
2// Copyright (C) 2010 - 2011 Pavel Labath
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22
23#include <config.h>
24
25#include "Luamm.hh"
26
27namespace lua {
28 namespace {
29 // keys for storing values in lua registry
30 const char cpp_exception_metatable[] = "lua::cpp_exception_metatable";
31 const char cpp_function_metatable [] = "lua::cpp_function_metatable";
32 const char lua_exception_namespace[] = "lua::lua_exception_namespace";
33 const char this_cpp_object [] = "lua::this_cpp_object";
34
35 // converts C++ exceptions to strings, so lua can do something with them
36 int exception_to_string(lua_State *l)
37 {
38 std::exception_ptr *ptr = static_cast<std::exception_ptr *>(lua_touserdata(l, -1));
39 assert(ptr);
40 try {
41 std::rethrow_exception(*ptr);
42 }
43 catch(std::exception &e) {
44 lua_pushstring(l, e.what());
45 }
46 catch(...) {
47 lua_pushstring(l, ptr->__cxa_exception_type()->name());
48 }
49 return 1;
50 }
51
52 int absindex(lua_State *l, int index) throw()
53 { return index<0 && -index<=lua_gettop(l) ? lua_gettop(l)+1+index : index; }
54
55 // Just like getfield(), only without calling metamethods (or throwing random exceptions)
56 inline void rawgetfield(lua_State *l, int index, const char *k) throw(std::bad_alloc)
57 {
58 index = absindex(l, index);
59 if(not lua_checkstack(l, 1))
60 throw std::bad_alloc();
61
62 lua_pushstring(l, k);
63 lua_rawget(l, index);
64 }
65
66 // Just like setfield(), only without calling metamethods (or throwing random exceptions)
67 inline void rawsetfield(lua_State *l, int index, const char *k) throw(std::bad_alloc)
68 {
69 index = absindex(l, index);
70 if(not lua_checkstack(l, 2))
71 throw std::bad_alloc();
72
73 lua_pushstring(l, k);
74 lua_insert(l, -2);
75 lua_rawset(l, index);
76 }
77
78 int closure_trampoline(lua_State *l)
79 {
80 lua_checkstack(l, 2);
81 rawgetfield(l, REGISTRYINDEX, this_cpp_object);
82 assert(lua_islightuserdata(l, -1));
83 state *L = static_cast<state *>( lua_touserdata(l, -1) );
84 lua_pop(l, 1);
85
86 try {
87 cpp_function *fn = static_cast<cpp_function *>( L->touserdata(lua_upvalueindex(1)) );
88 assert(fn);
89 return (*fn)(L);
90 }
91 catch(lua::exception &e) {
92 // rethrow lua errors as such
93 e.push_lua_error(L);
94 }
95 catch(...) {
96 // C++ exceptions (pointers to them, actually) are stored as lua userdata and
97 // then thrown
98 L->createuserdata<std::exception_ptr>(std::current_exception());
99 L->rawgetfield(REGISTRYINDEX, cpp_exception_metatable);
100 L->setmetatable(-2);
101 }
102
103 // lua_error does longjmp(), so destructors for objects in this function will not be
104 // called
105 return lua_error(l);
106 }
107
108 /*
109 * This function is called when lua encounters an error outside of any protected
110 * environment
111 * Throwing the exception through lua code appears to work, even if it was compiled
112 * without -fexceptions. If it turns out, it fails in some conditions, it could be
113 * replaced with some longjmp() magic. But that shouldn't be necessary, as this function
114 * will not be called under normal conditions (we execute everything in protected mode).
115 */
116 int panic_throw(lua_State *l)
117 {
118 if(not lua_checkstack(l, 1))
119 throw std::bad_alloc();
120
121 rawgetfield(l, REGISTRYINDEX, this_cpp_object);
122 assert(lua_islightuserdata(l, -1));
123 state *L = static_cast<state *>( lua_touserdata(l, -1) );
124 lua_pop(l, 1);
125
126 throw lua::exception(L);
127 }
128
129 // protected mode wrappers for various lua functions
130 int safe_concat_trampoline(lua_State *l)
131 {
132 lua_concat(l, lua_gettop(l));
133 return 1;
134 }
135
136 template<int (*compare)(lua_State *, int, int)>
137 int safe_compare_trampoline(lua_State *l)
138 {
139 int r = compare(l, 1, 2);
140 lua_pop(l, 2);
141 lua_pushinteger(l, r);
142 return 1;
143 }
144
145 int safe_gc_trampoline(lua_State *l)
146 {
147 int what = lua_tointeger(l, -2);
148 int data = lua_tointeger(l, -1);
149 lua_pop(l, 2);
150 lua_pushinteger(l, lua_gc(l, what, data));
151 return 1;
152 }
153
154 template<void (*misc)(lua_State *, int), int nresults>
155 int safe_misc_trampoline(lua_State *l)
156 {
157 misc(l, 1);
158 return nresults;
159 }
160
161 int safe_next_trampoline(lua_State *l)
162 {
163 int r = lua_next(l, 1);
164 lua_checkstack(l, 1);
165 lua_pushinteger(l, r);
166 return r ? 3 : 1;
167 }
168
169 }
170
171 std::string exception::get_error_msg(state *L)
172 {
173 static const std::string default_msg("Unknown lua exception");
174
175 try {
176 return L->tostring(-1);
177 }
178 catch(not_string_error &e) {
179 return default_msg;
180 }
181 }
182
183 exception::exception(state *l)
184 : std::runtime_error(get_error_msg(l)), L(l)
185 {
186 L->checkstack(1);
187
188 L->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
189 L->insert(-2);
190 key = L->ref(-2);
191 L->pop(1);
192 }
193
194 exception::~exception() throw()
195 {
196 if(not L)
197 return;
198 L->checkstack(1);
199
200 L->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
201 L->unref(-1, key);
202 L->pop();
203 }
204
205 void exception::push_lua_error(state *l)
206 {
207 if(l != L)
208 throw std::runtime_error("Cannot transfer exceptions between different lua contexts");
209 l->checkstack(2);
210
211 l->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
212 l->rawgeti(-1, key);
213 l->replace(-2);
214 }
215
216 state::state()
217 {
218 if(lua_State *l = luaL_newstate())
219 cobj.reset(l, &lua_close);
220 else {
221 // docs say this can happen only in case of a memory allocation error
222 throw std::bad_alloc();
223 }
224
225 // set our panic function
226 lua_atpanic(cobj.get(), panic_throw);
227
228 checkstack(2);
229
230 // store a pointer to ourselves
231 pushlightuserdata(this);
232 rawsetfield(REGISTRYINDEX, this_cpp_object);
233
234 // a metatable for C++ exceptions travelling through lua code
235 newmetatable(cpp_exception_metatable);
236 lua_pushcfunction(cobj.get(), &exception_to_string);
237 rawsetfield(-2, "__tostring");
238 pushboolean(false);
239 rawsetfield(-2, "__metatable");
240 pushdestructor<std::exception_ptr>();
241 rawsetfield(-2, "__gc");
242 pop();
243
244 // a metatable for C++ functions callable from lua code
245 newmetatable(cpp_function_metatable);
246 pushboolean(false);
247 rawsetfield(-2, "__metatable");
248 pushdestructor<cpp_function>();
249 rawsetfield(-2, "__gc");
250 pop();
251
252 // while they're travelling through C++ code, lua exceptions will reside here
253 newtable();
254 rawsetfield(REGISTRYINDEX, lua_exception_namespace);
255
256 luaL_openlibs(cobj.get());
257 }
258
259 void state::call(int nargs, int nresults, int errfunc)
260 {
261 int r = lua_pcall(cobj.get(), nargs, nresults, errfunc);
262 if(r == 0)
263 return;
264
265 if(r == LUA_ERRMEM) {
266 // memory allocation error, cross your fingers
267 throw std::bad_alloc();
268 }
269
270 checkstack(3);
271 rawgetfield(REGISTRYINDEX, cpp_exception_metatable);
272 if(getmetatable(-2)) {
273 if(rawequal(-1, -2)) {
274 // it's a C++ exception, rethrow it
275 std::exception_ptr *ptr = static_cast<std::exception_ptr *>(touserdata(-3));
276 assert(ptr);
277
278 /*
279 * we create a copy, so we can pop the object without fearing the exception will
280 * be collected by lua's GC
281 */
282 std::exception_ptr t(*ptr); ptr = NULL;
283 pop(3);
284 std::rethrow_exception(t);
285 }
286 pop(2);
287 }
288 // it's a lua exception, wrap it
289 if(r == LUA_ERRERR)
290 throw lua::errfunc_error(this);
291 else
292 throw lua::exception(this);
293 }
294
295 void state::checkstack(int extra) throw(std::bad_alloc)
296 {
297 if(not lua_checkstack(cobj.get(), extra))
298 throw std::bad_alloc();
299 }
300
301 void state::concat(int n)
302 {
303 assert(n>=0);
304 checkstack(1);
305 lua_pushcfunction(cobj.get(), safe_concat_trampoline);
306 insert(-n-1);
307 call(n, 1, 0);
308 }
309
310 bool state::equal(int index1, int index2)
311 {
312 // avoid pcall overhead in trivial cases
313 if( rawequal(index1, index2) )
314 return true;
315
316 return safe_compare(&safe_compare_trampoline<lua_equal>, index1, index2);
317 }
318
319 int state::gc(int what, int data)
320 {
321 checkstack(3);
322 lua_pushcfunction(cobj.get(), safe_gc_trampoline);
323 pushinteger(what);
324 pushinteger(data);
325 call(2, 1, 0);
326 assert(isnumber(-1));
327 int r = tointeger(-1);
328 pop();
329 return r;
330 }
331
332 void state::getfield(int index, const char *k)
333 {
334 checkstack(1);
335 index = absindex(index);
336 pushstring(k);
337 gettable(index);
338 }
339
340 void state::gettable(int index)
341 {
342 checkstack(2);
343 pushvalue(index);
344 insert(-2);
345 lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_gettable, 1>));
346 insert(-3);
347 call(2, 1, 0);
348 }
349
350 bool state::lessthan(int index1, int index2)
351 {
352 return safe_compare(&safe_compare_trampoline<&lua_lessthan>, index1, index2);
353 }
354
355 void state::loadfile(const char *filename)
356 throw(lua::syntax_error, lua::file_error, std::bad_alloc)
357 {
358 switch(luaL_loadfile(cobj.get(), filename)) {
359 case 0:
360 return;
361 case LUA_ERRSYNTAX:
362 throw lua::syntax_error(this);
363 case LUA_ERRFILE:
364 throw lua::file_error(this);
365 case LUA_ERRMEM:
366 throw std::bad_alloc();
367 default:
368 assert(0);
369 }
370 }
371
372 void state::loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc)
373 {
374 switch(luaL_loadstring(cobj.get(), s)) {
375 case 0:
376 return;
377 case LUA_ERRSYNTAX:
378 throw lua::syntax_error(this);
379 case LUA_ERRMEM:
380 throw std::bad_alloc();
381 default:
382 assert(0);
383 }
384 }
385
386 bool state::next(int index)
387 {
388 checkstack(2);
389 pushvalue(index);
390 insert(-2);
391 lua_pushcfunction(cobj.get(), &safe_next_trampoline);
392 insert(-3);
393
394 call(2, MULTRET, 0);
395
396 assert(isnumber(-1));
397 int r = tointeger(-1);
398 pop();
399 return r;
400 }
401
402 void state::pushclosure(const cpp_function &fn, int n)
403 {
404 checkstack(2);
405
406 createuserdata<cpp_function>(fn);
407 rawgetfield(REGISTRYINDEX, cpp_function_metatable);
408 setmetatable(-2);
409
410 insert(-n-1);
411 lua_pushcclosure(cobj.get(), &closure_trampoline, n+1);
412 }
413
414 void state::rawgetfield(int index, const char *k) throw(std::bad_alloc)
415 { lua::rawgetfield(cobj.get(), index, k); }
416
417 void state::rawsetfield(int index, const char *k) throw(std::bad_alloc)
418 { lua::rawsetfield(cobj.get(), index, k); }
419
420 bool state::safe_compare(lua_CFunction trampoline, int index1, int index2)
421 {
422 // if one of the indexes is invalid, return false
423 if(isnone(index1) || isnone(index2))
424 return false;
425
426 // convert relative indexes into absolute
427 index1 = absindex(index1);
428 index2 = absindex(index2);
429
430 checkstack(3);
431 lua_pushcfunction(cobj.get(), trampoline);
432 pushvalue(index1);
433 pushvalue(index2);
434 call(2, 1, 0);
435 assert(isnumber(-1));
436 int r = tointeger(-1);
437 pop();
438 return r;
439 }
440
441 void state::setfield(int index, const char *k)
442 {
443 checkstack(1);
444 index = absindex(index);
445 pushstring(k);
446 insert(-2);
447 settable(index);
448 }
449
450 void state::settable(int index)
451 {
452 checkstack(2);
453 pushvalue(index);
454 insert(-3);
455 lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_settable, 0>));
456 insert(-4);
457 call(3, 0, 0);
458 }
459
460 std::string state::tostring(int index) throw(lua::not_string_error)
461 {
462 size_t len;
463 const char *str = lua_tolstring(cobj.get(), index, &len);
464 if(not str)
465 throw not_string_error();
466 return std::string(str, len);
467 }
468}