const char *lsr_objecttostring(lua_State *L, int index) { index = lua_absindex(L, index); if (lua_isstring(L, index)) { return lua_tostring(L, index); } if (lua_isnumber(L, index)) { static char nbuffer[1024]; snprintf(nbuffer, 1024, "%f", lua_tonumber(L, 1)); return nbuffer; } if (lua_isboolean(L, index)) { return lua_toboolean(L, index) ? "true" : "false"; } if (lua_isfunction(L, index) || lua_iscfunction(L, index)) { lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, 1); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 2); // anonymous function return "system.Function"; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_pop(L, 2); return methodBase->getStringSignature().c_str(); } if (lua_isnil(L, index)) { return "null"; } lualoom_getmember(L, index, "toString"); lua_call(L, 0, 1); const char *sreturn = lua_tostring(L, -1); lua_pop(L, 1); return sreturn; }
/* * Static methods use the lsr_method closure which is generated at class initialization time * Instance methods are bound to a generated lsr_method closure the first time the method is referenced (at runtime) * lsr_method is a thin wrapper which handles catching native calls for profiling, default arguments, etc */ int lsr_method(lua_State *L) { int nargs = lua_gettop(L); MethodBase *method = (MethodBase *)lua_topointer(L, lua_upvalueindex(1)); bool staticCall = method->isStatic(); if (staticCall) { lua_pushvalue(L, lua_upvalueindex(2)); lua_insert(L, 1); // method } else { lua_pushvalue(L, lua_upvalueindex(2)); lua_insert(L, 1); // this (can be a loomscript table or luabridge userdata lua_pushvalue(L, lua_upvalueindex(3)); lua_insert(L, 1); // method } int dargs = nargs; int fidx = method->getFirstDefaultParm(); int varArgIdx = method->getVarArgIndex(); int varArgVectorIdx = lua_gettop(L); // don't consider the varargs in when // checking whether we need to insert default arguments if ((fidx != -1) && (varArgIdx != -1)) { dargs--; } if (dargs < method->getNumParameters()) { // TODO: Report line numbers LOOM-603 // if we have var args and not enough parameters, VM will insert null for ...args value // otherwise, we have a compiler error if (varArgIdx < 0) { lmAssert(fidx >= 0, "Method '%s::%s' called with too few arguments.", method->getDeclaringType()->getFullName().c_str(), method->getStringSignature().c_str()); } bool inserted = false; for (int i = dargs; i < method->getNumParameters(); i++) { if (i == varArgIdx) { break; } lua_pushvalue(L, lua_upvalueindex(i - fidx + (staticCall ? 3 : 4))); inserted = true; nargs++; } // if we inserted *and* have var args, we // need to shift the var args to the end if (inserted && (varArgIdx != -1)) { lua_pushvalue(L, varArgVectorIdx); lua_remove(L, varArgVectorIdx); } } LSLuaState *ls = LSLuaState::getLuaState(L); // error handling lua_getglobal(L, "__ls_traceback"); lua_insert(L, 1); if (lua_pcall(L, nargs + (staticCall ? 0 : 1), LUA_MULTRET, 1)) { ls->triggerRuntimeError("Error calling %s:%s", method->getDeclaringType()->getFullName().c_str(), method->getName()); } // get rid of the traceback lua_remove(L, 1); int nreturn = lua_gettop(L); if (nreturn == 1) { if (method->isNative() && method->isMethod() && lua_isuserdata(L, -1)) { lualoom_pushnative_userdata(L, ((MethodInfo *)method)->getReturnType(), -1); } } return nreturn; }
static int _call(lua_State *L) { // position 1 is the function // position 2 is the thisObject // position 3 is the varargs // look in global method lookup for methodbase lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, 1); lua_rawget(L, -2); MethodBase *methodBase = NULL; int varArgs = -1; if (!lua_isnil(L, -1)) { methodBase = (MethodBase *)lua_topointer(L, -1); varArgs = methodBase->getVarArgIndex(); lua_pop(L, 2); } else { // we better be a local function with an upvalue at index 1 describing the parameter index of varargs const char *upvalue = lua_getupvalue(L, 1, 1); lmAssert(upvalue, "Internal Error: funcinfo not at upvalue 1"); #ifdef LOOM_DEBUG lmAssert(!strncmp(upvalue, "__ls_funcinfo_arginfo", 21), "Internal Error: funcinfo not __ls_funcinfo_arginfo"); #endif lmAssert(lua_isnumber(L, -1), "Internal Error: __ls_funcinfo_arginfo not a number"); // vararg count is packed into lower 16 bits, with 0xFFFF stored for no-varargs unsigned int mask = ((( unsigned int) lua_tonumber(L, -1)) & 0x0000FFFF); varArgs = mask == 0xFFFF ? -1 : mask; lua_pop(L, 3); } // check for static call if (lua_isnil(L, 2)) { // remove the this object (which should be null for static call/apply) lua_remove(L, 2); } else { // otherwise, we better be an instance method int top = lua_gettop(L); if (!methodBase) { lua_pushstring(L, "MethodBase is missing from function table in Function.call(this, ...)"); lua_error(L); } lua_pushnumber(L, methodBase->getOrdinal()); lua_gettable(L, 2); if (lua_isnil(L, -1)) { char error[512]; snprintf(error, 512, "Unable to resolve instance method %s for Function.call(this, ...)", methodBase->getStringSignature().c_str()); lua_pushstring(L, error); lua_error(L); } lua_replace(L, 1); lua_settop(L, top); lua_remove(L, 2); } int nargs = 0; if (!lua_isnil(L, 2)) { // we have a varargs array of values // get the length int vlength = lsr_vector_get_length(L, 2); // retrieve the interval vector store lua_rawgeti(L, 2, LSINDEXVECTOR); int vindex = lua_gettop(L); // loop through the values and unwind for (int i = 0; i < vlength; i++) { // if we hit the varArgs index, the rest wants to be a vector if (i == varArgs) { // we're at the var args argument and have some left if (i) { // shift and store new length for (int j = 0; j < vlength - i; j++) { lua_rawgeti(L, vindex, j + varArgs); lua_rawseti(L, vindex, j); } lsr_vector_set_length(L, 2, vlength - varArgs); } // reuse the varargs vector in the call, as an arg lua_pushvalue(L, 2); nargs++; // outta here break; } else { // unwind and keep going lua_rawgeti(L, vindex, i); nargs++; } } // remove vector table lua_remove(L, vindex); // ... and varargs lua_remove(L, 2); } else { // no args, so remove the null lua_remove(L, 2); } // and call lua_call(L, nargs, 1); return 1; }