예제 #1
0
static void _getCurrentStack(lua_State *L, utStack<stackinfo>& stack)
{
    int       top = lua_gettop(L);
    lua_Debug lstack;
    int       stackFrame = 0;

    MethodBase *lastMethod = NULL;

    while (true)
    {
        // if we get a null result here, we are out of stack
        if (!lua_getstack(L, stackFrame++, &lstack))
        {
            lua_settop(L, top);
            return;
        }

        // something bad in denmark
        if (!lua_getinfo(L, "fSl", &lstack))
        {
            lua_settop(L, top);
            return;
        }

        bool cfunc = false;
        if (lua_iscfunction(L, -1))
        {
            cfunc = true;
        }

        lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP);
        lua_pushvalue(L, -2);
        lua_rawget(L, -2);

        if (lua_isnil(L, -1))
        {
            lua_settop(L, top);
            continue;
        }

        MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1);

        lua_settop(L, top);

        // we only want the root call, not the pcall wrapper
        if (cfunc && (lastMethod == methodBase))
        {
            continue;
        }

        lastMethod = methodBase;

        stackinfo si;
        si.methodBase = methodBase;
        si.source     = methodBase->isNative() ? "[NATIVE]" : lstack.source;
        si.linenumber = lstack.currentline == -1 ? 0 : lstack.currentline;

        stack.push(si);
    }
}
예제 #2
0
    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;
    }
예제 #3
0
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;
}
예제 #4
0
    static int create(lua_State *L)
    {
        if (lua_iscfunction(L, 1))
        {
            // get the method
            lua_getupvalue(L, 1, 1);
            MethodBase *method = (MethodBase *)lua_topointer(L, -1);
            lua_pop(L, 1);

            // set the _initMethod on the coroutine
            // instance, as methods require a setup resume
            // call (they start in suspended state)

            lua_rawgeti(L, 2, LSINDEXTYPE);
            Type *type = (Type *)lua_topointer(L, -1);
            lua_pop(L, 1);

            assert(type);

            lua_pushboolean(L, 1);
            lualoom_setmember(L, 2, "_initMethod");

            // if we're not static, we also need to store off the instance
            if (!method->isStatic())
            {
                lua_getupvalue(L, 1, 2);
                lualoom_setmember(L, 2, "_this");
            }

            int methodIdx = method->isStatic() ? 2 : 3;

            lua_getglobal(L, "coroutine");
            lua_getfield(L, -1, "create");
            lua_getupvalue(L, 1, methodIdx);
            lua_call(L, 1, LUA_MULTRET);

            return 1;
        }

        if (lua_isfunction(L, 1))
        {
            lua_getglobal(L, "coroutine");
            lua_getfield(L, -1, "create");
            lua_pushvalue(L, 1);
            lua_call(L, 1, LUA_MULTRET);

            lmAssert(lua_isthread(L, -1), "non thread");

            return 1;
        }

        lua_pushnil(L);
        return 1;
    }
예제 #5
0
Expression *TypeCompiler::visit(CallExpression *call)
{
    MethodBase *methodBase = call->methodBase;

    call->function->visitExpression(this);

    // check whether we're calling a methodbase
    if (methodBase)
    {
        lmAssert(methodBase->isMethod(), "Non-method called");

        MethodInfo *method = (MethodInfo *)methodBase;
        generateCall(&call->function->e, call->arguments, method);

        call->e = call->function->e;
    }
    else
    {
        lmAssert(call->function->type, "Untyped call");

        // if we're calling a delegate we need to load up the call method
        if (call->function->type->isDelegate())
        {
            MethodInfo *method = (MethodInfo *)call->function->type->findMember("call");
            lmAssert(method, "delegate with no call method");

            ExpDesc right;
            BC::initExpDesc(&right, VKNUM, 0);
            right.u.nval = method->getOrdinal();

            BC::expToNextReg(cs->fs, &call->function->e);
            BC::expToNextReg(cs->fs, &right);
            BC::expToVal(cs->fs, &right);
            BC::indexed(cs->fs, &call->function->e, &right);

            generateCall(&call->function->e, call->arguments, NULL);
            call->e = call->function->e;
        }
        else
        {
            // we're directly calling a local, instance (bound), or static method of type Function
            generateCall(&call->function->e, call->arguments, NULL);
            call->e = call->function->e;
        }
    }

    return call;
}
예제 #6
0
// Main lua VM debug hook
void LSProfiler::profileHook(lua_State *L, lua_Debug *ar)
{
    // Only process calls from the main thread for now
#ifdef LOOM_ENABLE_JIT
    lua_State *mainL = mainthread(G(L));
#else
    lua_State *mainL = L->l_G->mainthread;
#endif
    if (mainL != L)
        return;

    MethodBase *methodBase = NULL;

    if (ar->event == LUA_HOOKCALL)
    {
        methodBase = getTopMethod(L);

        if (methodBase)
        {
            methodStack.push(methodBase);
            enterMethod(methodBase->getFullMemberName());
        }
    }

    if (ar->event == LUA_HOOKRET)
    {
        methodBase = getTopMethod(L);

        if (methodBase)
        {
            if (methodStack.size())
            {
                methodStack.pop();
                leaveMethod(methodBase->getFullMemberName());
            }
        }
    }
}
예제 #7
0
PropertyInfo *PropertyInfoReader::deserializePropertyInfo(Type   *declaringType,
                                                          json_t *json)
{
    PropertyInfo *pi = new PropertyInfo();

    MemberInfoReader::deserialize(pi, json);

    // handle attr
    json_t *marray = json_object_get(json, "propertyattributes");

    for (size_t i = 0; i < json_array_size(marray); i++)
    {
        utString modifier = json_string_value(json_array_get(marray, i));

        if (modifier == "static")
        {
            pi->attr.isStatic = true;
        }
        else if (modifier == "public")
        {
            pi->attr.isPublic = true;
        }
        else if (modifier == "private")
        {
            pi->attr.isPrivate = true;
        }
        else if (modifier == "protected")
        {
            pi->attr.isProtected = true;
        }
        else if (modifier == "native")
        {
            pi->attr.isNative = true;
        }
    }

    utString stype = json_string_value(json_object_get(json, "type"));
    if (stype.size() > 0)
    {
        // a shortcut?
        pi->type =
            declaringType->getModule()->getAssembly()->getLuaState()->getType(
                stype.c_str());
        assert(pi->type);
    }

    json_t *getter = json_object_get(json, "getter");
    json_t *setter = json_object_get(json, "setter");

    if (getter)
    {
        MethodBase *m = NULL;
        m = MethodReader::deserializeMethodInfo(declaringType, getter);
        assert(m->isMethod());
        pi->getter = (MethodInfo *)m;

        m->setPropertyInfo(pi);
    }

    if (setter)
    {
        MethodBase *m = NULL;
        m = MethodReader::deserializeMethodInfo(declaringType, setter);
        assert(m->isMethod());
        pi->setter = (MethodInfo *)m;

        m->setPropertyInfo(pi);
    }

    json_t *ttypes = json_object_get(json, "templatetypes");
    if (ttypes && json_is_object(ttypes))
    {
        TemplateInfo *info = MemberInfoReader::readTemplateTypeInfo(ttypes);
        assert(info);
        info->resolveTypes(Assembly::getAssembly(declaringType)->getLuaState());
        pi->setTemplateInfo(info);
    }


    return pi;
}
예제 #8
0
    // retrieve a Vector of callstack info, or nil in the case of an invalid stack
    static int getCallStack(lua_State *L, EventType eventType)
    {
        int top = lua_gettop(L);

        lua_Debug ar;

        int            nstack = 0;
        _CallStackInfo cstack[MAX_CALLSTACK];

        // look for method infos
        for (int i = 0; i < MAX_CALLSTACK; i++)
        {
            // if we get a null result here, we are out of stack
            if (!lua_getstack(L, i, &ar))
            {
                break;
            }

            // get function, source, and line
            if (!lua_getinfo(L, "fSl", &ar))
            {
                continue;
            }

            // if we're a cfunction we're not interested in debugging it
            // as we're a LoomScript debugger!
            if (lua_iscfunction(L, -1))
            {
                lua_pop(L, 1);

                // if we are at the top of the stack and we are not an assert event
                // return a null stack as this isn't an interesting stack to the
                // debugger
                if (!i && (eventType != ASSERT_EVENT))
                {
                    lua_pushnil(L);
                    return 1;
                }
                continue;
            }

            // lookup the stack frame's function in the global
            // function table
            lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP);
            lua_pushvalue(L, -2);
            lua_rawget(L, -2);

            // if we aren't found in the global functions
            if (lua_isnil(L, -1))
            {
                // we are at the top of the stack, so this is an invalid stack
                if (!i)
                {
                    break;
                }

                lua_pop(L, 2);
                continue;
            }

            // get the MethodBase
            MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1);

            // if we're native and at the top of the stack, invalid stack
            if (!i && methodBase->isNative())
            {
                break;
            }

            // if we should filter, we need to stop here
            if (filterMethodBase(methodBase))
            {
                break;
            }

            // store out the stack info to our stack array
            _CallStackInfo *cs = &cstack[nstack++];

            cs->method = methodBase;

            if (!methodBase->isNative())
            {
                cs->source = ar.source;
                cs->line   = ar.currentline;
            }
            else
            {
                cs->source = "[Native]";
                cs->line   = 0;
            }

            // early our for line events when we have no breakpoints
            // and haven't hit a Debug.debug()
            if (eventType == LINE_EVENT)
            {
                // check breakpoints
                if (!i && !stepping)
                {
                    // we only stop on a breakpoint
                    if (!hasBreakpoint(cs->source, cs->line) && !debugBreak)
                    {
                        lastSourceEvent[0] = 0;
                        lua_pushnil(L);
                        return 1;
                    }
                }
            }

            lua_pop(L, 3);

            // pop the function, methodbase, and methodlookup
        }

        lua_settop(L, top); // reset stack

        // did we get anything, if not bail early
        if (!nstack)
        {
            lua_pushnil(L);
            return 1;
        }


        // check if the event is from the same line/source as last event
        _CallStackInfo *cs = &cstack[0];

        if (eventType == LINE_EVENT)
        {
            // we have a match, so bail
            if (!strcmp(lastSourceEvent, cs->source) && (lastLineEvent == cs->line))
            {
                lua_pushnil(L);
                return 1;
            }

            // cache for next line event
            lmAssert(strlen(cs->source) < 2000, "source file > 2000 charaters");
            strcpy(lastSourceEvent, cs->source);
            lastLineEvent = cs->line;
        }
        else
        {
            // clear cached event
            lastSourceEvent[0] = 0;
            lastLineEvent      = -1;
        }

        // We return a Vector.<CallStackInfo>

        Type *vectorType = LSLuaState::getLuaState(L)->getType("system.Vector");
        Type *csiType    = LSLuaState::getLuaState(L)->getType(
            "system.CallStackInfo");

        int sourceOrdinal = csiType->getMemberOrdinal("source");
        int lineOrdinal   = csiType->getMemberOrdinal("line");
        int methodOrdinal = csiType->getMemberOrdinal("method");


        // create the vector instance
        lsr_createinstance(L, vectorType);
        lua_rawgeti(L, -1, LSINDEXVECTOR);

        // now we need to loop through and setup our CallStackInfos and get them into our return vector
        for (UTsize i = 0; i < (UTsize)nstack; i++)
        {
            cs = &cstack[i];

            lua_pushnumber(L, i);

            // note that system.CallStackInfo is not a native class, so we just fiddle
            // it with indexes
            lsr_createinstance(L, csiType);

            int csiIdx = lua_gettop(L);

            lua_pushnumber(L, sourceOrdinal);
            lua_pushstring(L, cs->source);
            lua_rawset(L, csiIdx);

            lua_pushnumber(L, lineOrdinal);
            lua_pushnumber(L, cs->line);
            lua_rawset(L, csiIdx);

            lua_pushnumber(L, methodOrdinal);

            if (cs->method->isMethod())
            {
                lualoom_pushnative<MethodInfo>(L, (MethodInfo *)cs->method);
            }
            else if (cs->method->isConstructor())
            {
                lualoom_pushnative<ConstructorInfo>(L, (ConstructorInfo *)cs->method);
            }
            else
            {
                lmAssert(0, "Attempting to debug non-method %s:%s",
                         cs->method->getDeclaringType()->getFullName().c_str(), cs->method->getName());
            }

            assert(lua_istable(L, -1));

            lua_rawset(L, csiIdx);

            lua_rawset(L, -3);
        }

        lua_pop(L, 1);
        // pop the vector table

        lsr_vector_set_length(L, -1, nstack);

        lmAssert(lua_gettop(L) - top == 1, "getCallStack - stack wasn't properly cleaned up");

        return 1;
    }
예제 #9
0
/*
 * 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;
}
예제 #10
0
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;
}
예제 #11
0
void LSProfiler::getCurrentStack(lua_State *L, utStack<MethodBase *>& stack)
{
    int       top = lua_gettop(L);
    lua_Debug lstack;
    int       stackFrame = 0;

    MethodBase *lastMethod = NULL;

    while (true)
    {
        // if we get a null result here, we are out of stack
        if (!lua_getstack(L, stackFrame++, &lstack))
        {
            lua_settop(L, top);
            return;
        }

        // something bad in denmark
        if (!lua_getinfo(L, "f", &lstack))
        {
            lua_settop(L, top);
            return;
        }

        bool cfunc = false;
        if (lua_iscfunction(L, -1))
        {
            cfunc = true;
        }

        lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP);
        lua_pushvalue(L, -2);
        lua_rawget(L, -2);

        if (lua_isnil(L, -1))
        {
            lua_settop(L, top);
            continue;
        }

        MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1);

        lua_settop(L, top);

#ifdef LOOM_ENABLE_JIT
        // We do not receive line return hooks for native calls under JIT :(
        // So, don't add to initial stack
        if (cfunc)
        {
            continue;
        }
#endif


        if (shouldFilterFunction(methodBase->getFullMemberName()))
        {
            continue;
        }

        // we only want the root call, not the pcall wrapper
        if (cfunc && (lastMethod == methodBase))
        {
            continue;
        }

        lastMethod = methodBase;

        stack.push(methodBase);
    }
}
예제 #12
0
void LSProfiler::dumpAllocations(lua_State *L)
{
    lua_gc(L, LUA_GCCOLLECT, 0);

#ifdef LOOM_ENABLE_JIT
    lmLog(gProfilerLogGroup, "Please note: Profiling under JIT does not include native function calls.");
    lmLog(gProfilerLogGroup, "switch to the interpreted VM in order to gather native method allocation");
#endif

    utList<LSProfilerTypeAllocation *> allocs;
    utHashTable<utPointerHashKey, MethodAllocation> methodAggr;

    // Prepare for object allocation dump
    for (UTsize i = 0; i < allocations.size(); i++)
    {
        LSProfilerTypeAllocation* alloc = allocations.at(i);
        allocs.push_back(alloc);
    }

    lmLog(gProfilerLogGroup, "");
    lmLog(gProfilerLogGroup, "Object Allocation Dump");
    lmLog(gProfilerLogGroup, "----------------------");

    while (allocs.size())
    {
        int best = -1;
        LSProfilerTypeAllocation *bestType = NULL;

        for (UTsize i = 0; i < allocs.size(); i++)
        {
            LSProfilerTypeAllocation *alloc = allocs.at(i);

            if (alloc->alive > best)
            {
                best     = alloc->alive;
                bestType = alloc;
            }
        }

        lmAssert(bestType, "couldn't get bestType");

        allocs.erase(bestType);

        lmLog(gProfilerLogGroup, "Alive: %i (%i KiB), Total: %i (%i KiB), Type: %s", bestType->alive, bestType->memoryCurrent/1024, bestType->total, bestType->memoryTotal/1024, bestType->type->getFullName().c_str());

        if (bestType->anonTotal)
        {
            lmLog(gProfilerLogGroup, "    Alive: %i, Total %i (Anonymous)", bestType->anonTotal, bestType->anonCurrent);
        }

        for (UTsize j = 0; j < bestType->methodCurrent.size(); j++)
        {
            MethodBase *methodBase = (MethodBase *)bestType->methodCurrent.keyAt(j).key();
            lmAssert(methodBase == (MethodBase *)bestType->methodTotal.keyAt(j).key(), "mismatched methodbase");

            int *value1 = bestType->methodCurrent.get(methodBase);
            int *value2 = bestType->methodTotal.get(methodBase);

            lmAssert(value1 && value2, "bad pointer on allocation");

            lmLog(gProfilerLogGroup, "     Alive %i, Total %i (%s)", (*value1), (*value2), methodBase->getFullMemberName());
        }
    }

    // Aggregate method allocations
    for (UTsize i = 0; i < allocations.size(); i++)
    {
        LSProfilerTypeAllocation* alloc = allocations.at(i);
        utHashTable<utPointerHashKey, int> &methodCurrent = alloc->methodCurrent;
        utHashTable<utPointerHashKey, int> &methodTotal = alloc->methodTotal;
        for (UTsize j = 0; j < methodCurrent.size(); j++) {
            utPointerHashKey key = methodCurrent.keyAt(j);
            int *current = methodCurrent.get(key);
            lmAssert(current != NULL, "Internal hash table error");
            int *total = methodTotal.get(key);
            lmAssert(total != NULL, "Internal total hash table inconsistency error");
            MethodAllocation *aggr = methodAggr.get(key);
            if (aggr == NULL)
            {
                // First seen method, insert initial values
                MethodAllocation ma;
                ma.currentCount = *current;
                ma.totalCount = *total;
                ma.currentBytes = alloc->memoryCurrent;
                ma.totalBytes = alloc->memoryTotal;
                ma.allocations.push_back(alloc);
                methodAggr.insert(key, ma);
            }
            else
            {
                // Method already seen, update tracked values
                aggr->currentCount += *current;
                aggr->totalCount += *total;
                aggr->currentBytes += alloc->memoryCurrent;
                aggr->totalBytes += alloc->memoryTotal;
                aggr->allocations.push_back(alloc);
            }
        }
    }

    utArray<int> sortByTotal;
    sortByTotal.resize(methodAggr.size());
    for (UTsize i = 0; i < sortByTotal.size(); i++) sortByTotal[i] = i;

    sortMethods = &methodAggr;
    SDL_qsort((void*)sortByTotal.ptr(), sortByTotal.size(), sizeof(int), sortMethodsByTotalCount);
    sortMethods = NULL;

    lmLog(gProfilerLogGroup, "");
    lmLog(gProfilerLogGroup, "Aggregated Method Allocation");
    lmLog(gProfilerLogGroup, "----------------------------");

    for (UTsize i = 0; i < methodAggr.size(); i++)
    {
        utPointerHashKey key = methodAggr.keyAt(sortByTotal[i]);
        MethodBase *methodBase = (MethodBase *)key.key();
        lmAssert(methodBase != NULL, "Internal aggregated method base missing");
        MethodAllocation &ma = *methodAggr.get(key);
        
        lmLog(gProfilerLogGroup, "Total: %i (%i KiB), Alive: %i (%i KiB), Method: %s", ma.totalCount, ma.totalBytes/1024, ma.currentCount, ma.currentBytes/1024, methodBase->getFullMemberName());
        
        //SDL_qsort((void*)ma.allocations.ptr(), ma.allocations.size(), sizeof(LSProfilerTypeAllocation*), sortAllocsByTotal);

        for (UTsize j = 0; j < ma.allocations.size(); j++)
        {
            LSProfilerTypeAllocation* alloc = ma.allocations.at(j);
            int &methodCurrent = *alloc->methodCurrent.get(key);
            int &methodTotal = *alloc->methodTotal.get(key);
            lmLog(gProfilerLogGroup, "     Total: %i / %i (%i kiB), Alive: %i / %i (%i KiB), Type: %s", methodTotal, alloc->total, alloc->memoryTotal / 1024, methodCurrent, alloc->alive, alloc->memoryCurrent / 1024, alloc->type->getFullName().c_str());
        }
    }

}
예제 #13
0
MethodBase *LSProfiler::getTopMethod(lua_State *L)
{
    int       top = lua_gettop(L);
    lua_Debug stack;

    // if we get a null result here, we are out of stack
    if (!lua_getstack(L, 0, &stack))
    {
        lua_settop(L, top);
        return NULL;
    }

    if (!lua_getinfo(L, "f", &stack))
    {
        lua_settop(L, top);
        return NULL;
    }

    bool iscfunc = false;
    if (lua_iscfunction(L, -1))
    {
        iscfunc = true;
    }


#ifdef LOOM_ENABLE_JIT
    // We do not receive line return hooks for native calls under JIT :(
    // So, if we want to profile native methods, we need to be under interpreted VM
    if (iscfunc)
    {
        lua_settop(L, top);
        return NULL;
    }
#endif


    lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP);
    lua_pushvalue(L, -2);
    lua_rawget(L, -2);

    if (lua_isnil(L, -1))
    {
        lua_settop(L, top);
        return NULL;
    }

    MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1);

    lua_settop(L, top);

    if (iscfunc && !methodBase->isNative())
    {
        return NULL;
    }

    if (shouldFilterFunction(methodBase->getFullMemberName()))
    {
        return NULL;
    }

    return methodBase;
}
예제 #14
0
    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;
    }