Pergunta

Eu sou dev chumbo para Bitfighter , e estamos usando Lua como uma linguagem de script para permitir aos jogadores programar seus próprios navios robô personalizados.

Na Lua, você não precisa declarar variáveis, e todas as variáveis ??padrão de escopo global, a menos que declarado de outra forma. Isto leva a alguns problemas. Tome o seguinte trecho, por exemplo:

loc = bot:getLoc()
items = bot:findItems(ShipType)     -- Find a Ship

minDist = 999999
found = false

for indx, item in ipairs(items) do           
   local d = loc:distSquared(item:getLoc())  

   if(d < minDist) then
      closestItem = item
      minDist = d
   end
end

if(closestItem != nil) then 
   firingAngle = getFiringSolution(closestItem) 
end

Neste trecho, se findItems () retorna nenhum candidato, então closestItem vai ainda se referem a tudo o que enviá-lo encontrado pela última vez ao redor, e no meio tempo, aquele navio poderia ter sido morto. Se o navio é morto, ele não existe mais, e getFiringSolution () irá falhar.

Você detectar o problema? Bem, nem meus usuários. É sutil, mas com efeito dramático.

Uma solução seria exigir que todas as variáveis ??ser declarado, e para todas as variáveis ??para o padrão para escopo local. Enquanto que a mudança não tornaria impossível para programadores para se referir a objetos que não existem mais, seria torná-lo mais difícil fazê-lo inadvertidamente.

Existe alguma maneira de dizer Lua para o padrão todos vars de âmbito local, e / ou para exigir que eles sejam declarados? Sei que algumas outras línguas (por exemplo, Perl) tem essa opção disponível.

Obrigado!


Muitas boas respostas aqui, obrigado!

Eu decidi ir com uma versão ligeiramente modificada do módulo da Lua 'rigorosa'. Esta parece ter-me onde eu quero ir, e eu vou cortá-lo um pouco para melhorar as mensagens e torná-los mais adequado para o meu contexto particular.

Foi útil?

Solução

Não há nenhuma opção para definir esse comportamento, mas há um módulo 'rigorosa' fornecido com a instalação padrão, que faz exatamente isso (modificando os meta-tabelas). Uso: require 'rigorosa'

Para obter mais em profundidade informações e outras soluções: http://lua-users.org/wiki / DetectingUndefinedVariables , mas eu recomendo 'rigorosa'.

Outras dicas

Sorta.

Na Lua, globals notionally ao vivo no _G mesa globals (a realidade é um pouco mais complexa, mas do lado da Lua não há nenhuma maneira de dizer AFAIK). Tal como acontece com todas as outras tabelas Lua, é possível anexar um metatable __newindex para _G que controla como as variáveis ??são adicionadas a ele. Que este __newindex manipulador de fazer o que você quer fazer quando alguém cria um mundial:. Lançar um erro, permitir-lo, mas imprime um aviso, etc

Para mexer com _G, é mais simples e limpa para uso setfenv. Veja a documentação .

"local por padrão é errado ". Por favor, veja

http://lua-users.org/wiki/LocalByDefault

http://lua-users.org/wiki/LuaScopingDiscussion

Você precisa usar algum tipo de proteção do meio ambiente global. Existem algumas ferramentas estáticos para fazer isso (não muito maduro), mas a solução mais comum é a proteção de uso de tempo de execução, com base em __index e __newindex em metatable de _G.

plugue

Shameles: esta página também pode ser útil:

http://code.google.com/p/lua-alchemy / wiki / LuaGlobalEnvironmentProtection

Note que, enquanto ele discute Lua embutido em swf, a técnica descrita (ver fontes ) fazer o trabalho para Lua genérico. Nós usamos algo nesse sentido no nosso código de produção no trabalho.

Na verdade, a variável global extra com a referência obsoleto para o navio será suficiente para manter o GC de descartar o objeto. Assim, poderia ser detectado em tempo de execução por perceber que o navio está agora "morto" e recusando-se a fazer qualquer coisa com ele. Ainda não é o navio certo, mas pelo menos você não falhar.

Uma coisa que você pode fazer é manter scripts de usuário em um sandbox , provavelmente, uma caixa de areia por script. Com a manipulação direito de qualquer tabela de ambiente do sandbox ou seu metatable, você pode organizar para descartar todas ou a maioria das variáveis ??globais a partir da sandbox antes (ou logo após) código de chamada do usuário.

Limpando a caixa de areia após as chamadas teria a vantagem de descartar referências extras para coisas que não deve pendurar ao redor. Isso poderia ser feito, mantendo uma lista branca de campos que estão autorizados a permanecer no ambiente, e exclusão de todo o resto.

Por exemplo, os seguintes implementa uma chamada no modo seguro a uma função fornecida pelo utilizador com um meio ambiente que contém somente os nomes de branco-listados atrás de uma tabela zero fresco fornecido para cada chamada.

-- table of globals that will available to user scripts
local user_G = {
        print=_G.print,
        math=_G.math,
        -- ...
    }
-- metatable for user sandbox
local env_mt = { __index=user_G }


-- call the function in a sandbox with an environment in which new global 
-- variables can be created and modified but they will be discarded when the 
-- user code completes.
function doUserCode(user_code, ...)
    local env = setmetatable({}, env_mt) -- create a fresh user environment with RO globals
    setfenv(user_code, env)        -- hang it on the user code
    local results = {pcall(user_code, ...)}
    setfenv(user_code,{})
    return unpack(results)
end

Esta poderia ser estendido para fazer a tabela global somente leitura, empurrando-o por trás de um acesso mais metatable se você queria.

Note-se que uma solução completa sandbox também considerar o que fazer sobre o código de utilizador que acidentalmente (ou maliciosamente) executa um infinito (ou simplesmente muito longa) loop ou outra operação. soluções gerais para isso são um tema ocasional de discussão sobre o Lua lista , mas boas soluções são difíceis.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top