#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef PERL_UNUSED_DECL # undef PERL_UNUSED_DECL #endif #include "ppport.h" #include "const-c.inc" #include #include #include SV *UNDEF, *LuaNil, NIL; AV *INLINE_RETURN; void push_ary (lua_State *, AV*); void push_hash (lua_State *, HV*); void push_val (lua_State *, SV*); void push_func (lua_State *, CV*); SV* bool_ref (lua_State *, int); SV* table_ref (lua_State *, int); SV* func_ref (lua_State *L); SV* user_data (lua_State *L); SV* luaval_to_perl (lua_State *, int, int*); static lua_State *INTERPRETER = NULL; int is_lua_nil (SV* val) { if (sv_isobject(val) && SvIV(SvRV(val)) == (IV)&PL_sv_undef && strEQ(HvNAME(SvSTASH(SvRV(val))), "Inline::Lua::Nil")) return 1; return 0; } /* Non-destructively translate a a number to a string. * lua_tostring() can't be used as it turns the value * on the stack into a string. */ char * num2string (lua_Number n, I32 *klen) { char s[32]; char *str; STRLEN len; sprintf(s, LUA_NUMBER_FMT, n); len = *klen = strlen(s)+1; New(0, str, len, char); Copy(s, str, len, char); return str; } /* The C-closure responsible for calling Perl functions * that were passed to Lua functions by reference. * The codereference is passed as lightuserdata and * always resides at lua_upvalueindex(1) */ int trigger_cv (lua_State *L) { dSP; register int i; int dopop; int nargs = lua_gettop(L); int nresults; CV *cv = (CV*)lua_touserdata(L, lua_upvalueindex(1)); ENTER; SAVETMPS; PUSHMARK(SP); for (i = 1; i <= nargs; i++) { SV *sv = luaval_to_perl(L, i, &dopop); XPUSHs(sv_2mortal(sv)); } PUTBACK; nresults = call_sv((SV*)cv, G_ARRAY); SPAGAIN; /* again the reversed order of values * in the Lua stack bites, so we * cannot use POPs here */ for (i = 0; i < nresults; i++) { int offset = nresults - i - 1; SV *val = *(sp - offset); SvREFCNT_inc(val); push_val(L, val); } /* pop all in one go */ sp -= nresults; PUTBACK; FREETMPS; LEAVE; return nresults; } /* The callback used by lua_dump to serialize the * bytecode */ int dumper (lua_State *L, const void *p, size_t size, void *f) { fwrite(p, size, 1, (FILE*)f); return 0; } /* push a Perl array onto the Lua stack */ void push_ary (lua_State *L, AV *av) { register int i; lua_newtable(L); for (i = 0; i <= av_len(av); i++) { SV **ptr = av_fetch(av, i, FALSE); lua_pushnumber(L, (lua_Number)i+1); if (ptr) push_val(L, *ptr); else lua_pushnil(L); lua_settable(L, -3); } } /* push a Perl hash onto the Lua stack */ void push_hash (lua_State *L, HV *hv) { register HE* he; lua_newtable(L); hv_iterinit(hv); while (he = hv_iternext(hv)) { I32 len; char *key; key = hv_iterkey(he, &len); lua_pushlstring(L, key, len); push_val(L, hv_iterval(hv, he)); lua_settable(L, -3); } } /* push a Perl function reference onto the Lua stack */ void push_func (lua_State *L, CV *cv) { lua_pushlightuserdata(L, cv); lua_pushcclosure(L, trigger_cv, 1); } /* turn the Perl glob-reference into a FILE* and push it * along with the appropriate metatable onto the Lua stack */ void push_io (lua_State *L, PerlIO *pio) { FILE **fp = (FILE**)lua_newuserdata(L, sizeof(FILE*)); *fp = PerlIO_exportFILE(pio, NULL); luaL_getmetatable(L, "FILE*"); lua_setmetatable(L, -2); } /* push a generic reference into the Lua stack: * calls one of push_(ary|hash|func|io) */ void push_ref (lua_State *L, SV *val) { switch (SvTYPE(SvRV(val))) { case SVt_PVAV: push_ary(L, (AV*)SvRV(val)); return; case SVt_PVHV: push_hash(L, (HV*)SvRV(val)); return; case SVt_PVCV: push_func(L, (CV*)SvRV(val)); return; case SVt_PVGV: push_io(L, IoIFP(sv_2io(SvRV(val)))); return; default: if (sv_derived_from(val, "Inline::Lua::Boolean")) { lua_pushboolean(L, !!SvIV(SvRV(val))); return; } else { croak("Attempt to pass unsupported reference type (%s) to Lua", sv_reftype(SvRV(val), 0)); } } } /* push a Perl value onto the Lua stack: * does the right thing for any Perl type * handled by Inline::Lua */ void push_val (lua_State *L, SV *val) { if (is_lua_nil(val)) { lua_pushnil(L); return; } if (!val || val == &PL_sv_undef || !SvOK(val)) { if (!UNDEF || UNDEF == &PL_sv_undef || !SvOK(UNDEF)) lua_pushnil(L); else /* otherwise we can safely call push_val again * because Inline::Lua::_undef is defined */ push_val(L, UNDEF); return; } switch (SvTYPE(val)) { case SVt_RV: push_ref(L, val); return; case SVt_IV: lua_pushnumber(L, (lua_Number)SvIV(val)); return; case SVt_NV: lua_pushnumber(L, (lua_Number)SvNV(val)); return; case SVt_PV: case SVt_PVIV: case SVt_PVNV: case SVt_PVMG: { STRLEN n_a; char *cval = SvPV(val, n_a); lua_pushlstring(L, cval, n_a); return; } } } /* Turns a Lua type into a Perl type and returns it. * 'dopop' is set to 1 if the caller has to do a lua_pop. * The only case where this does not happen is if the value * is a LUA_TFUNCTION (luaL_ref() already pops it off). */ SV* luaval_to_perl (lua_State *L, int idx, int *dopop) { *dopop = 1; switch (lua_type(L, idx)) { case LUA_TNIL: return &PL_sv_undef; case LUA_TBOOLEAN: return bool_ref(L, lua_toboolean(L, idx)); case LUA_TNUMBER: return newSVnv(lua_tonumber(L, idx)); case LUA_TSTRING: return newSVpvn(lua_tostring(L, idx), lua_strlen(L, idx)); case LUA_TTABLE: return table_ref(L, lua_gettop(L)); case LUA_TFUNCTION: *dopop = 0; return func_ref(L); default: abort(); } } /* Handles the return values of a complete Lua script * upon compilation. Return values are converted into * Perl types, unshifted into INLINE_RETURN and popped * off the Lua stack */ AV* lua_main_return (lua_State *L, int idx, int num) { register int i; int nargs = idx - num + 1; for (i = 0; i < nargs; i++) { int top = idx-i; av_unshift(INLINE_RETURN, 1); switch (lua_type(L, top)) { case LUA_TNIL: av_store(INLINE_RETURN, 0, &PL_sv_undef); case LUA_TBOOLEAN: av_store(INLINE_RETURN, 0, bool_ref(L, lua_toboolean(L, top))); break; case LUA_TNUMBER: av_store(INLINE_RETURN, 0, newSVnv(lua_tonumber(L, top))); break; case LUA_TSTRING: av_store(INLINE_RETURN, 0, newSVpvn(lua_tostring(L, top), lua_strlen(L, top))); break; case LUA_TTABLE: av_store(INLINE_RETURN, 0, table_ref(L, top)); break; case LUA_TFUNCTION: av_store(INLINE_RETURN, 0, func_ref(L)); break; case LUA_TUSERDATA: av_store(INLINE_RETURN, 0, user_data(L)); break; default: croak("Attempt to return unsupported Lua type (%s)", lua_typename(L, lua_type(L, idx))); } } return INLINE_RETURN; } /* Lua tables are both an array and a hash but this can't be known in advance. * Initially it is assumed that the Lua table can be turned into a plain Perl * array. However, once a stringy key is found the strategy has to be switched * and the array populated so far is converted into a hash */ HV * ary_to_hash (AV *ary) { register int i; int len = av_len(ary); HV *hv = newHV(); SV *key = newSViv(0); for (i = 0; i <= len; i++) { if (!av_exists(ary, i)) continue; sv_setiv(key, i+1); /* +1 because Lua tables start at 1 */ hv_store_ent(hv, key, *av_fetch(ary, i, FALSE), 0); } SvREFCNT_dec(key); return hv; } /* Adds a key/value pair from a Lua table to 'any'. * 'any' is a pointer to either an AV* or HV*. When it was * an array and the current key is a string, 'isary' is set * to false and the array transformed into a hash */ int add_pair (lua_State *L, SV **any, int *isary) { #define KEY -2 #define VAL -1 int dopop; if (*isary && lua_type(L, KEY) != LUA_TNUMBER) { HV *tbl = ary_to_hash((AV*)*any); *isary = 0; *any = (SV*)tbl; } if (*isary) { int idx = lua_tonumber(L, KEY); SV *val = luaval_to_perl(L, VAL, &dopop); SvREFCNT_inc(val); if (!av_store((AV*)*any, idx-1, val)) SvREFCNT_dec(val); } else { const char *key; I32 klen; SV *val; int free = 0; switch (lua_type(L, KEY)) { case LUA_TNUMBER: { lua_Number n = lua_tonumber(L, KEY); key = (const char*)num2string(n, &klen); free = 1; break; } case LUA_TSTRING: key = lua_tostring(L, KEY); klen = lua_strlen(L, KEY); break; default: croak("Illegal type (%s) in table subscript", lua_typename(L, lua_type(L, KEY))); } val = luaval_to_perl(L, VAL, &dopop); SvREFCNT_inc(val); if (!hv_store((HV*)*any, key, klen, val, 0)) SvREFCNT_dec(val); if (free) Safefree(key); } return dopop; } /* Return our Inline::Lua::Boolean datatype. * * TODO: Try to do this only once (or twice), and return the same TRUE or * FALSE reference subsequently. */ SV* bool_ref (lua_State *L, int b) { if (b) { return eval_pv("Inline::Lua::Boolean::TRUE", 1); } else { return eval_pv("Inline::Lua::Boolean::FALSE", 1); } } /* The Lua table being in the stack at 'idx' is turned into a * Perl AV _or_ HV (depending on whether the lua table has a stringy * key in it and a reference to that is returned */ SV* table_ref (lua_State *L, int idx) { int isary = 1; /* initially we always assume it's an array */ AV *tbl = newAV(); lua_pushnil(L); while (lua_next(L, idx) != 0) { if (add_pair(L, (SV**)&tbl, &isary)) lua_pop(L, 1); } return newRV_noinc((SV*)tbl); } /* When a Lua function returns a function to perl, a reference * to this function is put into LUA_REGISTRY. Here we call * 'create_func_ref' which returns a Perl closure which does * sub { $lua->call( $func, -1, @_ ) } * Calling this closure would then trigger the Lua function. */ SV* func_ref (lua_State *L) { dSP; SV *lua = sv_newmortal(); SV *func = newSViv(luaL_ref(L, LUA_REGISTRYINDEX)); SV *funcref; sv_setref_pv(lua, "Inline::Lua", (void*)L); ENTER; PUSHMARK(SP); XPUSHs(lua); /* $lua */ XPUSHs(sv_2mortal(func)); /* $func */ PUTBACK; call_pv("Inline::Lua::create_func_ref", G_SCALAR); SPAGAIN; funcref = POPs; SvREFCNT_inc(funcref); PUTBACK; LEAVE; return funcref; } /* Handles userdata variables. * Those could be filehandles, for instance */ SV* user_data (lua_State *L) { FILE **f = luaL_checkudata(L, 1, "FILE*"); if (!f) croak("Attempt to return unsupported Lua type (userdata)"); if (*f) { PerlIO *pio = PerlIO_importFILE(*f, NULL); GV *gv = newGVgen("Inline::Lua"); if (do_open(gv, "+<&", 3, FALSE, 0, 0, pio)) { SV *sv = NEWSV(0,0); sv_setsv(sv, sv_bless(newRV((SV*)gv), gv_stashpv("Inline::Lua", 1))); return sv; } else return &PL_sv_undef; } else croak("Attempt to return closed filehandle"); } MODULE = Inline::Lua PACKAGE = Inline::Lua BOOT: { LuaNil = get_sv("Inline::Lua::Nil", 1); sv_setref_pv(LuaNil, "Inline::Lua::Nil", (void*)&PL_sv_undef); SvREADONLY_on(LuaNil); INLINE_RETURN = newAV(); } INCLUDE: const-xs.inc void register_undef (CLASS, undef) SV *CLASS; SV *undef; CODE: { UNDEF = undef; SvREFCNT_inc(undef); } lua_State * interpreter (CLASS, ...) char *CLASS; CODE: { char *from_file = NULL; STRLEN n_a; if (items > 1) from_file = SvPV(ST(1), n_a); if (!INTERPRETER) { RETVAL = INTERPRETER = lua_open(); if (INTERPRETER) { #if LUA_VERSION_NUM >= 501 luaL_openlibs(INTERPRETER); #else luaopen_base(INTERPRETER); luaopen_table(INTERPRETER); luaopen_io(INTERPRETER); luaopen_string(INTERPRETER); luaopen_debug(INTERPRETER); luaopen_loadlib(INTERPRETER); #endif } } else RETVAL = INTERPRETER; } OUTPUT: RETVAL void destroy (lua) lua_State *lua; CODE: { lua_close(lua); } void compile (lua, code, file, dump) lua_State *lua; SV *code; char *file; I32 dump; CODE: { STRLEN len; char *codestr = SvPV(code, len); int i = 1; int status; status = luaL_loadbuffer(lua, codestr, len, "_INLINED_LUA"); if (dump && status == 0) { FILE *f = fopen(file, "w"); if (f) { lua_dump(lua, dumper, (void*)f); fclose(f); } else croak("Error outputting bytecode to %s: %s\n", file, strerror(errno)); XSRETURN_YES; } switch (status) { case 0: { int nargs = lua_gettop(lua); if ((lua_pcall(lua, 0, LUA_MULTRET, 0)) == 0) { if (lua_gettop(lua) - nargs >= 0) INLINE_RETURN = lua_main_return(lua, lua_gettop(lua), nargs); lua_pop(lua, lua_gettop(lua)); XSRETURN_YES; } else croak("error: %s", lua_tostring(lua, -1)); break; } case LUA_ERRSYNTAX: croak("Couldn't compile inline code"); } } void call (lua, func, nargs, ...) lua_State *lua; SV *func; int nargs; PPCODE: { char *name; int ref; int i = 0, j, status; int actual_args = 0; if (SvPOK(func)) { STRLEN n_a; name = SvPV(func, n_a); lua_getglobal(lua, name); } else { /* function reference */ lua_rawgeti(lua, LUA_REGISTRYINDEX, SvIV(func)); } /* push arguments */ for (i = 0; i < items-3; i++, nargs--, actual_args++) { push_val(lua, ST(i+3)); } /* if less arguments were passed than mentioned in the * lua function prototype, pad with 'nil' */ if (nargs >= 0) for (i = nargs; i > 0; nargs--, actual_args++, i--) push_val(lua, NULL); status = lua_pcall(lua, actual_args, LUA_MULTRET, 0); if (status != 0) croak("error: %s\n", lua_tostring(lua, -1)); /* return args to caller: * lua functions appear to push their return values in reverse order */ nargs = lua_gettop(lua); EXTEND(SP, nargs); j = 1; while (i = lua_gettop(lua)) { switch(lua_type(lua, i)) { case LUA_TNIL: ST(nargs - j++) = &PL_sv_undef; break; case LUA_TNUMBER: ST(nargs - j++) = sv_2mortal(newSVnv(lua_tonumber(lua, i))); break; case LUA_TBOOLEAN: ST(nargs - j++) = sv_2mortal(bool_ref(lua, lua_toboolean(lua, i))); break; case LUA_TSTRING: { STRLEN len = lua_strlen(lua, i); ST(nargs - j++) = sv_2mortal(newSVpvn(lua_tostring(lua, i), len)); } break; case LUA_TTABLE: ST(nargs - j++) = sv_2mortal(table_ref(lua, i)); break; case LUA_TFUNCTION: { ST(nargs - j++) = sv_2mortal(func_ref(lua)); goto no_pop; } case LUA_TUSERDATA: ST(nargs - j++) = sv_2mortal(user_data(lua)); break; default: croak("Attempt to return unsupported Lua type (%s)", lua_typename(lua, lua_type(lua, i))); } lua_pop(lua, 1); no_pop: continue; } XSRETURN(j-1); } void main_returns (CLASS) char *CLASS; PPCODE: { register int i; int len = av_len(INLINE_RETURN) + 1; EXTEND(SP, len); for (i = 0; i < len; i++) { SV **ptr = av_fetch(INLINE_RETURN, i, FALSE); if (ptr) PUSHs(*ptr); else PUSHs(&PL_sv_undef); } XSRETURN(len); }