Есть ли простой способ получить все глобальные переменные, определенные в файле кода Lua? - PullRequest
2 голосов
/ 28 марта 2019

Всем известно, что переменные в Lua, если они не определены явно как "локальные", будут глобальными. Это иногда вызывает проблемы, такие как переопределение библиотечных функций или неожиданное предоставление значения для другой глобальной переменной с тем же именем. Так что будет очень полезно, если есть способ найти все глобальные переменные, которые определены в одном файле кода Lua.

Однако мне не удалось найти какой-либо ключ к решению этой, казалось бы, довольно популярной проблемы. Лучший ответ, который я могу получить в Интернете, - это использовать _G для печати всех глобальных переменных в среде, что не очень помогает. В настоящее время я пишу Lua в Intellij Idea с помощью Emmylua, мощного инструмента, который может отображать глобальные переменные в особом стиле и легко может отследить глобальную переменную до ее определения; но когда код становится достаточно длинным, это тоже мало поможет.

Итак, я просто хочу получить список глобальных переменных, определенных в данном файле кода Lua. Либо с помощью инструмента, либо с замечательной функцией. Если это может облегчить задачу, мы можем предположить, что файл кода является модулем. Если он может в дальнейшем распечатать определения местоположения для этих глобальных переменных, это даже лучше. Кто-нибудь может мне помочь?

Ответы [ 2 ]

0 голосов
/ 01 апреля 2019

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

Lua предоставляет компилятор luac, который принимает аргумент -l для списка.

В Lua 5.1 есть код операции SETGLOBAL. В столбце указывается номер строки оператора, а в комментарии указывается имя глобального.

В версии 5.2 и более поздних есть код операции SETTABUP. Столбец указывает номер строки оператора, а комментарий - имя таблицы и ключ. «Глобалы» находятся в таблице, на которую ссылается значение _ENV.

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

Кстати, во многих модульных системах скрипт модуля не устанавливает глобальные переменные.

0 голосов
/ 28 марта 2019

У Lua нет способа узнать, когда или где была введена глобальная переменная.

В особом, что значение является функцией, debug.getinfo может помочь, сообщив вам, где функцияопределяется (что часто, но не всегда одно и то же место, где функция делается глобальной).

Вы можете получить необходимую информацию во время введения глобальной функции.Это может быть сделано путем установки метатаблицы с помощью метода __newindex в глобальной таблице.Этот метод будет вызываться, когда вводится новый глобал (но не тогда, когда существующий глобал переопределяется).В этом методе вы можете выяснить, откуда пришел абонент, набрав debug.getinfo.Также будьте осторожны, если любой другой ваш код пытается использовать метатаблицу в глобальной среде, вы должны хорошо с ней поиграть.(Может иметь только одну метатаблицу.)

Вы также можете избежать использования глобальной таблицы.Один из промежуточных способов сделать это - переопределить окружение.В Lua 5.2 и Lua 5.3 это делается путем объявления локальной таблицы с именем _ENV - вместо этого все обращения к глобальной таблице будут иметь доступ к этой таблице.(На самом деле, глобальные доступы всегда используют _ENV, а _ENV - это _G по умолчанию.) Вы можете сделать это главным образом невидимым, предоставив метатаблице _ENV, которая перенаправляет доступ к _G (или любой другой среде).Разница здесь в том, что __newindex будет по-прежнему вызываться, даже если в _G существует привязка, поэтому этот метод может обнаруживать переопределения.

Использование _ENV, хотя по своей природе локально для области (например, для каждогофайл должен его переопределить).Такой крюк может быть установлен и во всем миреЕсли вы загружаете свои модули вручную с помощью функции load (маловероятно), вы можете просто указать пользовательский _ENV в качестве аргумента.Если вы используете require, можно получить удержание загруженного файла перед его выполнением путем переопределения (или исправления обезьяны) искателя Lua в package.searchers[2].Это встроенная функция, которую require вызывает, чтобы найти файл в вашей файловой системе и затем загрузить его.Возвращаемым значением является загруженная функция, которая затем запускается require.Таким образом, после загрузки, но до возвращения обратно к require, вы можете использовать debug.setupvalue для переопределения значения по умолчанию _ENV (если есть).

Пример кода (только слегка проверенный):

local global_info = {}

local default_searcher2 = package.searchers[2]

package.searchers[2] = function(...)
  local result = default_searcher2(...)
  local parent_environment = _G
  local my_env = setmetatable({}, {
    __index = parent_environment,
    __newindex = function(self, k, v)
      local new_info = debug.getinfo(2)

      -- keeping rich data like this could be a memory leak
      -- if some globals are assigned repeatedly, but that
      -- may still be okay in a debugging scenario
      local history = global_info[k]
      if history == nil then
        history = {}
        global_info[k] = history
      end
      table.insert(history, {info = new_info, value = v})

      parent_environment[k] = v
    end,
  })
  if type(result) == "function" then
    debug.setupvalue(result, 1, my_env)
  end
  return result
end

function gethistory(name)
  local history = global_info[name]
  if history == nil then
    print('"' .. name .. '" has never been defined...')
  else
    print('History for "' .. name .. '":')
    for _, record in ipairs(history) do
      print(record.info.short_src .. ": " .. record.info.currentline)
    end
  end
end

Обратите внимание, что ловушка здесь будет применяться только к файлам, требуемым после запуска этого кода, и в основном применяется только к файлам Lua (не C-библиотекам), которые включаются через встроенный require,Он не устанавливает метатаблицы в глобальной среде, поэтому не конфликтует, но его можно обойти, если файлы обращаются к _G напрямую (или, например, настраивают доступ к _G вместо _ENV в своих собственных _ENV таблицах).Такие вещи также могут быть учтены, но это может быть кроличья нора в зависимости от того, насколько «невидимым» должен быть этот патч.

В Lua 5.1 вместо _ENV у вас есть setfenv, чтоЯ полагаю, что может быть использован аналогичный эффект.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...