lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> > I examined the code of lbci and used it to make my own static
analysis
> > routine. The requirements for getting all of the line numbers and
> > matching them to names of functions are non-trivial, but it works!
>
> Could you share your code if it only uses the lbci api?
>
> > The only thing I don't like about this is that it's tied to the
private
> > interface of Lua, ie. it is version-dependent and needs access to
the
> > complete Lua source in order to build. But things could be worse.
>
> Well, I can promise to keep lbci updated if there's enough interest.

I can share the code, but it's very task-specific and it needs some
extraction from its environment. It doesn't actually use the lbci API;
it just uses the techniques demonstrated in it.

My goal is to create a mapping of valid lines in a source file to a
structure of meta-information (currently only containing a pointer to a
function name). I can then query this map for the nearest valid line of
source to any arbitrary line supplied by the user by calling
lower_bound().



// Header-materials
#include <map>

struct LineMeta
{
	const char	*functionName;
};

struct SourceMeta
{
	typedef std::map<int, LineMeta> LineMap;

	LineMap	lineMapping;
};

extern SourceMeta GetSourceMeta(lua_State *L, int stack);



// Implementation details

extern "C"
{
	#include "lua.h"

	#include "ldebug.h"
	#include "lobject.h"
	#include "lopcodes.h"
}

typedef std::map<int, const char *> FunctionNameMap;

static void RecurseOnPrototype(Proto *p, SourceMeta::LineMap *m,
FunctionNameMap *fnm)
{
	int		lastLine = 0;

	// Iterate over the opcodes/set of valid lines
	for (int i = 0; i < p->sizelineinfo; i++)
	{
		OpCode	o = GET_OPCODE(p->code[i]);
		int		thisLine = p->lineinfo[i];

		if (o == OP_SETGLOBAL)
		{
			// Store that this line possibly indicates the
start of a function definition
			(*fnm)[thisLine] =
getstr(tsvalue(&p->k[GETARG_Bx(p->code[i])]));
		}

		if (lastLine == thisLine)
		{
			// We've visited this line already in a previous
opcode
			continue;
		}

		// This is a new, valid line of source so create an
entry for it.
		// Check to see if the linedefined field matches up to a
global name we've stored off.
		LineMeta				lm;
		FunctionNameMap::iterator	itr =
fnm->find(p->linedefined);

		lm.functionName = (itr == fnm->end()) ? NULL :
itr->second;
		m->insert(make_pair(thisLine, lm));
		lastLine = thisLine;	// This is the new most-recently
visited line
	}

	// Process any sub-functions in this definition
	for (int i = 0; i < p->sizep; i++)
	{
		RecurseOnPrototype(p->p[i], m, fnm);
	}
}

SourceMeta GetSourceMeta(lua_State *L, int stack)
{
	Proto			*p = ((Closure *)lua_topointer(L,
stack))->l.p;
	FunctionNameMap	fnm;
	SourceMeta		result;

	RecurseOnPrototype(p, &result.lineMapping, &fnm);

	return result;
}



If you call GetSourceMeta() with your Lua code loaded onto the stack
(eg. by calling luaL_loadbuffer) then it returns the complete mapping of
valid lines to function names.

Dan.