예제 #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
    // 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;
    }
예제 #3
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;
}
예제 #4
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;
}