Question

Je suis responsable de développement pour Bitfighter . Nous utilisons Lua comme langage de script pour permettre aux joueurs de programmer leurs propres vaisseaux robotisés.

Dans Lua, il n'est pas nécessaire de déclarer les variables. Toutes les variables par défaut ont une portée globale, sauf indication contraire. Cela conduit à des problèmes. Prenez l'extrait suivant, par exemple:

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

Dans cet extrait, si findItems () ne renvoie aucun candidat, mostItem fera toujours référence au navire trouvé la dernière fois et, dans l'intervalle, ce navire aurait pu être tué. Si le navire est tué, il n'existe plus et getFiringSolution () échouera.

Avez-vous repéré le problème? Eh bien, mes utilisateurs non plus. C'est subtil, mais avec un effet dramatique.

Une solution consisterait à exiger que toutes les variables soient déclarées et que toutes les variables soient définies par défaut sur la portée locale. Ce changement n'empêchera pas les programmeurs de se référer à des objets qui n'existent plus, mais il leur sera plus difficile de le faire par inadvertance.

Existe-t-il un moyen de dire à Lua de mettre tous les vars par défaut en portée locale et / ou d’exiger qu’ils soient déclarés? Je connais d'autres langues (Perl, par exemple) proposant cette option.

Merci!

Beaucoup de bonnes réponses ici, merci!

J'ai décidé de choisir une version légèrement modifiée du module Lua 'strict'. Cela semble me mener là où je veux aller, et je vais le bidouiller un peu pour améliorer les messages et les rendre plus appropriés à mon contexte particulier.

Était-ce utile?

La solution

Il n'y a pas d'option pour définir ce comportement, mais il existe un module 'strict' fourni avec l'installation standard, qui fait exactement cela (en modifiant les méta-tables). Usage: nécessite 'strict'

Pour plus d'informations et d'autres solutions plus détaillées: http://lua-users.org/wiki / DetectingUndefinedVariables , mais je recommande 'strict'.

Autres conseils

Sorta.

À Lua, les globaux résident théoriquement dans la table de globals _G (la réalité est un peu plus complexe, mais du côté de Lua, il n’ya aucun moyen de le savoir). Comme avec toutes les autres tables Lua, il est possible d'associer un __ newindex à _G qui contrôle la manière dont les variables y sont ajoutées. Laissez ce gestionnaire __ newindex faire ce que vous voulez quand quelqu'un crée un global: émet une erreur, l'autorise mais affiche un avertissement, etc.

Pour manipuler _G , il est plus simple et plus simple d'utiliser setfenv . Voir la documentation .

"Local par défaut est incorrect ". S'il vous plaît voir

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

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

Vous devez utiliser une sorte de protection de l'environnement mondial. Il existe certains outils statiques pour le faire (pas trop évolués), mais la solution la plus courante consiste à utiliser une protection à l'exécution, basée sur __ index et __ newindex dans _G est métatable.

Shameles plug: cette page peut également être utile:

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

Notez que, bien qu’il traite de Lua intégré à swf, la technique décrite (voir sources ) fonctionne pour le générique Lua. Nous utilisons quelque chose dans ce sens dans notre code de production au travail.

En fait, la variable globale supplémentaire avec la référence obsolète au navire sera suffisante pour empêcher le CPG d’écarter l’objet. Donc, il pourrait être détecté au moment de l'exécution en remarquant que le navire est maintenant "mort". et refusant de faire quoi que ce soit avec elle. Ce n'est toujours pas le bon navire, mais au moins vous ne vous écrasez pas.

Vous pouvez notamment conserver les scripts utilisateur dans un bac à sable , probablement un bac à sable par script. Avec la bonne manipulation de la table d'environnement du sandbox ou de son métatable, vous pouvez supprimer toutes les variables globales ou la plupart des variables globales du sandbox avant (ou juste après) l'appel du code de l'utilisateur.

Nettoyer le bac à sable après les appels aurait l’avantage de supprimer des références supplémentaires à des choses qui ne devraient pas traîner. Cela peut être fait en conservant une liste blanche des champs autorisés à rester dans l'environnement et en supprimant tout le reste.

Par exemple, ce qui suit implémente un appel sandbox dans une fonction fournie par l'utilisateur avec un environnement contenant uniquement les noms en liste blanche derrière une nouvelle table de travail fournie pour chaque appel.

-- 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

Cela pourrait être étendu pour rendre la table globale en lecture seule en la repoussant derrière un autre accès métatable si vous le souhaitez.

Notez qu'une solution complète de bac à sable considérerait également ce qu'il faut faire avec un code utilisateur qui exécute accidentellement (ou malicieusement) une boucle infinie (ou simplement très longue) ou une autre opération. Les solutions générales à cet égard sont un sujet de discussion occasionnel sur la liste Lua , mais de bonnes solutions sont difficiles.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top