Вопрос об объявлении переменных и области видимости для Lua

StackOverflow https://stackoverflow.com/questions/1014757

  •  06-07-2019
  •  | 
  •  

Вопрос

Я ведущий разработчик для Битфайтер, и мы используем Lua в качестве языка сценариев, чтобы позволить игрокам программировать свои собственные корабли-роботы.

В Lua вам не нужно объявлять переменные, и все переменные по умолчанию имеют глобальную область видимости, если не указано иное.Это приводит к некоторым проблемам.Возьмем, к примеру, следующий фрагмент:

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

В этом фрагменте, если FindItems() не возвращает кандидатов, то closestItem по-прежнему будет ссылаться на любой корабль, который он обнаружил в прошлый раз, и за прошедшее время этот корабль мог погибнуть.Если корабль будет уничтожен, он больше не существует, и getFiringSolution() завершится неудачей.

Вы заметили проблему?Ну, и мои пользователи тоже.Это тонко, но с драматическим эффектом.

Одним из решений было бы потребовать, чтобы все переменные были объявлены и для всех переменных по умолчанию использовалась локальная область видимости.Хотя это изменение не сделало бы невозможным для программистов ссылаться на объекты, которые больше не существуют, было бы сложнее сделать это непреднамеренно.

Есть ли какой-нибудь способ сообщить Lua, чтобы все переменные по умолчанию были в локальной области видимости, и / или потребовать, чтобы они были объявлены?Я знаю некоторые другие языки (напримерPerl) доступна эта опция.

Спасибо!


Здесь много хороших ответов, спасибо!

Я решил использовать слегка измененную версию модуля Lua 'strict'.Кажется, это привело меня туда, куда я хочу, и я немного изменю это, чтобы улучшить сообщения и сделать их более подходящими для моего конкретного контекста.

Это было полезно?

Решение

Нет опции для настройки такого поведения, но есть модуль 'strict', поставляемый со стандартной установкой, который делает именно это (путем изменения мета-таблиц).Использование:требовать "строгого"

Для получения более подробной информации и других решений: http://lua-users.org/wiki/DetectingUndefinedVariables, но я рекомендую "строгий".

Другие советы

Вроде того.

В Lua глобальные переменные условно находятся в таблице globals _G (реальность немного сложнее, но со стороны Lua нет способа определить AFAIK).Как и во всех других таблицах Lua, можно прикрепить __newindex метатабильный для _G это управляет тем, как переменные добавляются к нему.Пусть это __newindex обработчик делайте все, что вы хотите сделать, когда кто-то создает глобальный:выдайте ошибку, разрешите ее, но выведите предупреждение и т.д.

Вмешиваться в _G, это самый простой и чистый в использовании setfenv.Смотрите на Документация.

"Локальным по умолчанию является неправильно".Пожалуйста, посмотрите

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

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

Вам нужно использовать какую-то глобальную защиту окружающей среды.Для этого есть несколько статических инструментов (не слишком зрелых), но наиболее распространенным решением является использование защиты во время выполнения, основанной на __index и __newindex в _Gэто поддается метаморфозам.

Штепсельная вилка Shameles:эта страница также может быть полезна:

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

Обратите внимание, что, хотя в нем обсуждается Lua, встроенный в swf, описанный метод (см. источники) выполняйте работу для универсального Lua.Мы используем что-то подобное в нашем производственном коде на работе.

На самом деле, дополнительной глобальной переменной с устаревшей ссылкой на ship будет достаточно, чтобы не дать GC отбросить объект.Таким образом, это можно было бы обнаружить во время выполнения, заметив, что корабль теперь "мертв", и отказавшись что-либо с ним делать.Это все еще неподходящий корабль, но, по крайней мере, вы не разобьетесь.

Одна вещь, которую вы можете сделать, это сохранить пользовательские скрипты в песочница, вероятно, песочница для каждого скрипта.При правильном обращении либо к таблице среды изолированной среды, либо к ее метатаблице вы можете удалить все или большинство глобальных переменных из изолированной среды до (или сразу после) вызова пользовательского кода.

Очистка песочницы после вызовов имела бы преимущество в отбрасывании дополнительных ссылок на вещи, которые не должны висеть без дела.Это можно было бы сделать, сохранив белый список полей, которым разрешено оставаться в среде, и удалив все остальные.

Например, следующее реализует изолированный вызов функции, предоставляемой пользователем, со средой, содержащей только имена из белого списка, за новой таблицей нуля, предоставляемой для каждого вызова.

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

Это можно было бы расширить, чтобы сделать глобальную таблицу доступной только для чтения, поместив ее обратно за еще один метатаблический доступ, если хотите.

Обратите внимание, что полное решение для изолированной среды также должно учитывать, что делать с пользовательским кодом, который случайно (или злонамеренно) выполняет бесконечный (или просто очень длинный) цикл или другую операцию.Общие решения для этого время от времени становятся темой обсуждения на Список Lua, но хорошие решения даются с трудом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top