Lua Callbacks
Posted: Wed Dec 09, 2009 8:13 pm
Currently the functions which set callbacks like tempTimer, tempTrigger and similar take a string as callback function and compile it each time:
If you have a lot of timers running (for example in a speed walk script) this would be quite inefficient.
It would be more efficient and powerful if you could pass a function directly which only has to be compiled once. This would also enable using Lua closures. The Lua code would then look like this:
Syntax errors get handled a lot sooner so debugging is easier. Another advantage would be that the code block would keep the current environment, while loadstring always uses the global environment. This would break out of any sandboxes.
In order to pass the function block through the C code you can use the Lua registry (http://www.lua.org/manual/5.1/manual.html#3.5).
I am not yet familiar with the Mudlet code and I do not know when I will have the time to explore it, so I cannot give a complete patch here, but still I try to give some code that should be relatively easy to integrate. My code snippets would make the code backwards compatible, it could still handle strings. (Though I would advise to remove this backward compatibility at a later point to make sandboxes possible).
My code snippets assume that there is an object associated with the callback. Its pointer is used as key value for the registry. As an alternative you could create some unique string.
When setting the callback:
When calling the callback function:
Last but not least, the clean up:
A similar solution could be used for the label callbacks. (Only the backward compatibility part would need to be changed). An advantage would be that anonymous functions can be used as callback and closures would be possible.
Code: Select all
tempTimer (0.3, [[ echo ("It is time") ]])
It would be more efficient and powerful if you could pass a function directly which only has to be compiled once. This would also enable using Lua closures. The Lua code would then look like this:
Code: Select all
tempTimer (0.3, function () echo ("It is time") end)
-- or we could use a named function:
tempTimer (0.3, f)
In order to pass the function block through the C code you can use the Lua registry (http://www.lua.org/manual/5.1/manual.html#3.5).
I am not yet familiar with the Mudlet code and I do not know when I will have the time to explore it, so I cannot give a complete patch here, but still I try to give some code that should be relatively easy to integrate. My code snippets would make the code backwards compatible, it could still handle strings. (Though I would advise to remove this backward compatibility at a later point to make sandboxes possible).
My code snippets assume that there is an object associated with the callback. Its pointer is used as key value for the registry. As an alternative you could create some unique string.
Code: Select all
CObjectWithCallback *pObject;
Code: Select all
// save the argument #1 in the registry using a pointer to the object
// associated with the callback as key
lua_pushlightuserdata (L, pObject);
lua_pushvalue (L, 1);
lua_settable (L, LUA_REGISTRYINDEX);
Code: Select all
lua_pushlightuserdata (L, pObject);
lua_gettable (L, LUA_REGISTRYINDEX);
if (lua_isfunction (L, -1)) {
// here we should push any arguments, if the callback function is to be supplied with arguments
lua_call (L, 0, 0);
} else if (lua_isstring (L, -1)) {
// for backward compatibility, handle string callbacks although I would recommend to declare this
// as deprecated
char *s; size_t len;
s = lua_tolstring (L, -1, &len);
if (luaL_loadstring (L, s) != 0) {
// handle the error
} else {
// push arguments, if there are any
lua_call (L, 0, 0);
}
} else {
// we should have checked the type of the argument when setting the callback, so
// the program should never reach this
luaL_error (L, "wrong type for callback function");
}
Code: Select all
// remove the function from the registry so the GC can deal with it
lua_pushlightuserdata (L, pObject);
lua_pushnil (L);
lua_settable (L, LUA_REGISTRYINDEX);