Estou lançando meu próprio depurador LUA para um projeto C++ & LUA e acabei de encontrar um pequeno obstáculo. Estou tentando seguir a implementação do MobDebug para definir relógios de expressão, que depois de remover todos os recursos, essencialmente se resume a concatenar a expressão que você deseja assistir dentro de uma return(...)
declaração e executá-la. A linha exata é:
local func, res = mobdebug.loadstring("return(" .. exp .. ")")
Isso funciona perfeitamente, desde que você esteja apenas depurando um único script e a maioria das suas variáveis esteja apenas no ambiente global, mas, para meus próprios propósitos, estou tentando restringir/reduzir o escopo da avaliação dessas return(...)
expressões para determinadas tabelas e seus campos.
Mais especificamente, estou trabalhando com algumas "classes" LUA usando o setmetatable / __index
padrão e, dado acesso à tabela (vamos chamá-la de SomeClass
), gostaria de poder avaliar expressões como
self.mSomeVariable + self.mSomeOtherVariable - self:someOperation(...)
onde self
está se referindo SomeClass
(ou seja, existe SomeClass:someOperation
, etc).
Estou realmente sem saber como abordar isso. No começo, tentei o seguinte, e definitivamente não funcionou.
> SomeClass = {}
> SomeClass.DebugEval = function(self, chunk_str) return load("return(" .. chunk_str .. ")")() end
> SomeClass.DebugEval(SomeClass, "print(1)") // 1 nil
> SomeClass.DebugEval(SomeClass, "print(SomeClass)") // table: 0000000000CBB5C0 nil
> SomeClass.DebugEval(SomeClass, "print(self)") // nil nil
O fato de que eu não posso nem referenciar a "classe" self
passando-a diretamente para o argumento da função de encapsulamento me faz suspeitar que isso pode ser um upvalue
problema. Ou seja, load(...)
está criando um fechamento para a execução desse pedaço que não tem como alcançar o self
argumento... mas por quê? Está literalmente lá???
Em todo caso, meu backend depurador já está em C++, então pensei "sem problemas, vou apenas definir manualmente o upvalue
. Novamente, tentei fazer o seguinte usando lua_setupvalue
, mas isso também não funcionou. Estou recebendo um erro de tempo de execução no pcall
.
luaL_dostring(L, "SomeClass = {}"); // just for convenience; I could've done this manually
luaL_loadstring(L, "print(1)"); // [ ... , loadstring_closure ]
lua_getglobal(L, "SomeClass"); // [ ... , loadstring_closure, reference table]
lua_setupvalue(L, -2, 1); // [ ... , loadstring_closure] this returns '_ENV'
lua_pcall(L, 0, 0, 0); // getting LUA_ERRRUN
O que estou esquecendo aqui? Talvez eu esteja abordando isso de uma forma totalmente incorreta? Meu objetivo final é simplesmente ser capaz de executar esses relógios de depurador, mas restrito a certas instâncias de classe, para que eu possa habilitar meus relógios em uma base por instância e inspecioná-los. E sim, eu absolutamente preciso lançar meu próprio depurador. É uma longa história. Toda e qualquer ajuda é apreciada.
A documentação
load
elua_load
as funções no manual do Lua mencionam:Em resumo, o bloco carregado tem apenas um upvalue, que é
_ENV
.Desde Lua 5.2,
SomeClass = {}
é equivalente a_ENV.SomeClass = {}
, eprint(self)
é equivalente aprint(_ENV.self)
. ComoSomeClass
é definido no escopo global, o pedaço carregado também pode ver essa entrada na_ENV
tabela, mas não há lugar que definirá automaticamente_ENV.self
.Você pode esperar que a função funcione assim:
Na verdade, funcionará assim:
Para a função "expect" , o compilador lua adicionará
self
um upvalue para você, mas para a função "fact" , ele não fará nada.E seu código C++ foi completamente substituído
_ENV
porSomeClass
, o que é ir longe demais.