lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I'd like to share with you an issue that I've had when  writing an __index function in the context of a Lua to objective C bridge and the solution that I have implemented to handle it.

By convention in objective C, a property and its associated getter method have the same name (most of the time, but let's keep this simple :) and both can used indifferently.
For example, if an object have a property name "color", you can get it by writing "object.color" or "object:color()" at your convenience (translated to the Lua syntax).

 To make a method / property like this one accessible from Lua, the classic method is to add an __index function the object's metatable who has the following prototype:
metatable.__index = function (object, key)
But from inside this function there is no hint of whether the expected result for the key "color" shall be a function (as in: object:color()) or an other object resulting of the call of the "color" method ((as in: object.color). So the __index function implementation has to make some guess or arbitrary choice to decide what to return.

And there is no simple good-enough heuristic to help here.:
- A method without parameter can not always be considered as a property (e.g. doesn't have the same semantic meaning as object:save());
- forcing the programmer to use a method-only syntax is really painful;
- introspection of the objective C runtime could help here, since properties are supposed to be declared as such to the runtime (and it was the initial method that I implemented). Unfortunately there are lots of inconsistencies in these property declarations (that are invisible for the objective C programmer as they are masked by the compiler) and using the runtime to decide which methods shall be presented as properties and which methods shall be returned as functions turns quickly into a nightmare for the Lua user programmer.

On the other hand, the Lua compiler could have the knowledge of what an object's field is used for: function call or other stuff. And it actually has this knowledge: see function suffixedexp in lparser.c.
But this information is detected too late, after having completed the processing of the field. 
Too bad...

Really too bad ? Actually not quite: when the method syntax is used, Lua generates a dedicated OpCode OP_SELF. When the VM execute this OpCode, it execute a gettable to get the method function and if we can detect from our __index function that it's called in the context of OP_SELF, we have mostly the information we need for our decision without patching Lua. Yes !!!

All we need now is to write the functions to make this detection.
It turns out in my case that I need two boolean informations:
1) is the gettable called from Lua or from C?
2) is it called while executing the OP_SELF OpCode?

And the corresponding functions are (added in lvm.c for convenience).

static OpCode currentOpCode (CallInfo *ci) {
    Instruction inst = *(ci->u.l.savedpc - 1);  /* current instruction */
    return GET_OPCODE(inst);

int luaV_isGetTableCalledFromLua (lua_State *L) {
    CallInfo *ci = L->ci->previous; /* caller ci */
    return ((ci != NULL) && (ttype(ci->func) == LUA_TLCL));

int luaV_isGetTableForLuaMethod (lua_State *L) {
    /* To be called from a TM_INDEX function to determine if the current index metamethod call expects a method as the result */
    int isForMethodCall = 0;
    CallInfo *ci = L->ci->previous; /* caller ci */
    if ((ci != NULL) && (ttype(ci->func) == LUA_TLCL)) {
        /* Caller is a Lua function */
        isForMethodCall = (currentOpCode(ci) == OP_SELF);
    return isForMethodCall;

From the test I ran, all this is working well and it solved my issue in a really satisfactory way for the Lua developer who uses the bridge. 

And as the only change to the Lua code consists in adding a few independent functions there is no risk that it badly impacts the reliability or performance of the Lua implementation.
But it has a slight taste of quick and dirty hacking anyway... 

And it would be much better if some type of hint about the foreseeable usage of a field could be passed as an extra parameter to the index metamethod in a future version of Lua. This would allow the index metamethod to return different (but related) data for a same key depending of the field expected usage. 

What do you think of it?