static int _length(lua_State *L) { lmAssert(lua_isfunction(L, 1) || lua_iscfunction(L, 1), "Non-function in Function._length"); // first look in the global method lookup to see if we have a method base to go off lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, 1); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_pushnumber(L, methodBase->getNumParameters()); return 1; } // we don't, so we better be a local function with an upvalue at index 1 describing the number of parameters 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"); // number of args stored in upper 16 bits, so shift and return lua_pushnumber(L, ((unsigned int) lua_tonumber(L, -1)) >> 16); return 1; }
/* * 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 lsr_instanceindex(lua_State *L) { // we hit the instance index metamethod when we can't find a value // in the instance's table, this is a native or a bound method lua_rawgeti(L, 1, LSINDEXTYPE); Type *type = (Type *)lua_topointer(L, -1); lua_pop(L, 1); lmAssert(type, "Missing type on instance index"); if (lua_isnumber(L, 2)) { int ordinal = (int)lua_tonumber(L, 2); MemberInfo *mi = type->getMemberInfoByOrdinal(ordinal); lmAssert(mi, "Unable to find ordinal %s %i", type->getFullName().c_str(), ordinal); if (mi->isStatic()) { lua_rawgeti(L, 1, LSINDEXCLASS); lua_replace(L, 1); lua_gettable(L, 1); return 1; } // we need to look in the class, the result will be cached to instance MethodBase *method = NULL; if (mi->isMethod()) { method = (MethodBase *)mi; } if (method) { lua_rawgeti(L, 1, LSINDEXCLASS); assert(!lua_isnil(L, -1)); int clsIdx = lua_gettop(L); if (!method->isStatic()) { if (method->isFastCall()) { // get the fast call structure pointer lua_pushlightuserdata(L, method->getFastCall()); // get the the "this" if (lua_type(L, 1) == LUA_TTABLE) { lua_rawgeti(L, 1, LSINDEXNATIVE); } else { lua_pushvalue(L, 1); } // unwrap this Detail::UserdataPtr *p1 = (Detail::UserdataPtr *)lua_topointer(L, -1); lua_pushlightuserdata(L, p1->getPointer()); lua_replace(L, -2); lua_pushnumber(L, ordinal); lua_gettable(L, clsIdx); } else { lua_pushlightuserdata(L, method); lua_pushvalue(L, 1); lua_pushnumber(L, ordinal); lua_gettable(L, clsIdx); } assert(!lua_isnil(L, -1)); int nup = 3; int fd = method->getFirstDefaultParm(); if (fd != -1) { utString dname; if (method->isConstructor()) { dname = "__ls_constructor"; } else { dname = method->getName(); } dname += "__default_args"; lua_getfield(L, clsIdx, dname.c_str()); int dargs = lua_gettop(L); for (int i = fd; i < method->getNumParameters(); i++) { lua_pushnumber(L, i); lua_gettable(L, dargs); nup++; } lua_remove(L, dargs); } // check for fast path if (method->isFastCall()) { if (method->getNumParameters()) { // setter lua_pushcclosure(L, lsr_method_fastcall_set, nup); } else { // getter lua_pushcclosure(L, lsr_method_fastcall_get, nup); } } else { // bind the instance method lua_pushcclosure(L, lsr_method, nup); } // store to method lookup, this is worth it // as only happens once per instance method bind // and can now lookup MethodBase on any function // in one table lookup lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_pushlightuserdata(L, method); lua_rawset(L, -3); lua_pop(L, 1); // cache to instance lua_pushvalue(L, -1); lua_rawseti(L, 1, ordinal); return 1; } else { assert(0); } } FieldInfo *field = NULL; if (mi->isField()) { field = (FieldInfo *)mi; } if (field && field->isNative()) { // for primitive fields, we could generate // a direct lookup in the LSINDEXNATIVE table // in bytecode gen, however this only saves for // native vars and not properties // (which should be using fast path anyway and complicates bytecode) // get the native userdata lua_rawgeti(L, 1, LSINDEXNATIVE); lua_pushnumber(L, field->getOrdinal()); lua_gettable(L, -2); // if we are native we need to wrap if (lua_isuserdata(L, -1)) { lualoom_pushnative_userdata(L, field->getType(), -1); } return 1; } // if we get here the value actually is null lua_pushnil(L); return 1; } // if we hit here, this should be an interface access where we have to // look up by string (and cache as these are only ever instance methods) const char *name = lua_tostring(L, 2); const char *pname = name; MemberInfo *mi = NULL; if (!strncmp(name, "__pget_", 7)) { pname = &name[7]; mi = type->findMember(pname, true); lmAssert(mi && mi->isProperty(), "Could not find property getter for '%s' on type '%s'", pname, type->getFullName().c_str()); mi = ((PropertyInfo *)mi)->getGetMethod(); lmAssert(mi, "Found NULL property getter for '%s' on type '%s'", pname, type->getFullName().c_str()); } else if (!strncmp(name, "__pset_", 7)) { pname = &name[7]; mi = type->findMember(pname, true); lmAssert(mi && mi->isProperty(), "Could not find property setter for '%s' on type '%s'", pname, type->getFullName().c_str()); mi = ((PropertyInfo *)mi)->getSetMethod(); lmAssert(mi, "Found NULL property setter for '%s' on type '%s'", pname, type->getFullName().c_str()); } else { mi = type->findMember(name, true); lmAssert(mi, "Unable to find member '%s' via string on instance of type %s", name, type->getFullName().c_str()); assert(mi); assert(mi->isMethod()); assert(mi->getOrdinal()); } lua_pushnumber(L, mi->getOrdinal()); lua_gettable(L, 1); lua_pushstring(L, name); lua_pushvalue(L, -2); lua_rawset(L, 1); return 1; }