Перейти к содержимому
Fingercomp

Защита от изменений в Lua

Рекомендуемые сообщения

Начнём сразу с кода. Пихнём в файл ./lib.lua

 

local a = "Hi"
local b = "World"
return {
  a = a,
  b = b
}

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

Итак, этот небольшой файлик будет возвращает таблицу с двумя ключами. Подключим его (./test.lua):

 

local test = require("test")
print(test.a, test.b)

Выведет это "Hi      World" и успокоится. Вроде всё нормально.

Допишем в этот файл ещё немного кода:

 

test.a = "Goodbye"
b = "Cruel World"
test2 = require("lib")
print(test2.a, test2.b)

Выведет это, однако, Goodbye Cruel World. Упс.

Если в таблице будет реализация бомбёжки ракетами по союзникам (а как же), изменять что-то не хотелось бы совсем. Вот об неизменяемости речь и пойдёт.

 

Заметим, что в таблице либы ключам значение присваивается не напрямую, а через переменную. Локальную. Доступа к ним (проигнорим debug) ни у кого другого нет. С другой стороны, у Луа есть метатаблицы с метаметодом __index. Воспользуемся этим, прямолинейно:

 

local a = "Hi"
local b = "World"
return setmetatable({}, {
  __index = function(self, k)
    if k == "a" then
      return a
    elseif k == "b" then
      return b
    end
  end
})

 

Код ./test.lua можно даже не менять. Результат всё равно будет тот же. Ибо __index вызывается только для несуществующих значений основной таблицы. Когда мы присвоим какое-то значение, вызываться не будет уже метаметод. Ага, значит, давайте запретим заменять таблицу. При tbl.keyname = value вызывается метаметод __newindex. Окей:

 

local a = "Hi"
local b = "World"
return setmetatable({}, {
  __index = function(self, k)
    if k == "a" then
      return a
    else
      return b
    end
  end,
  __newindex = function(self, k, v)
    print("No way!")
  end
})

Теперь, если мы запустим код тот же, получим что-то вроде этого:

 

Hi      World
No way!
No way!
Hi      World

Воооот, вроде бы цель достигнута. Как бы не так. Заменим файл ./test.lua этим:

 

local test = require("test")
print(test.a, test.b)
rawset(test, "a", "Goodbye")
rawset(test, "b", "Cruel World")
test2 = require("lib")
print(test2.a, test2.b)

Угадайте, что получится. Конечно же, большой и длинный "ууупс".

 

Hi      World
Goodbye Cruel World

Более того, теперь эти значения сидят в обычной таблице и больше не вызывают __newindex. Вот печаль-то.

 

Но тем не менее, именно такой код мы и оставим. Для решения проблемы нам потребуется переопределить rawget и rawset, добавив проверку на наши любимые таблицы и не давая ничего изменить. Получится это сделать только для систем с доступом к контролю над _G.

 

Есть ещё вариант докопаться до сути: getmetatable и setmetatable. Их, как можно догадаться, тоже требуется переопределить. Туда же debug.getmetatable и debug.setmetatable.

 

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

 

Как видно, Луа всеми силами пытается забрать право на забирание динамичности структур. Позволяя стандартно переопределять внутри VM стандартные функции для своих нужд, И просто так отплясаться не получится. Шмактус, однако.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Велосипед, кроме идей переопределить rawget и rawset, getmetatable и setmetatable: уже видел на форуме реализацию функции const для создания неизменяемых переменных по тому же принципу

---

Нашел эту тему: http://computercraft.ru/topic/1645-oc-konstanty/?hl=const

Изменено пользователем SergOmarov

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Одно дело — увидеть минифицированный код бещ комментариев или пояснений, а другое — получить само объяснение механизма, и в нужном разделе. Разные вещи, нет?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

:smile3:

Комментарии не просто так придуманы. Они и есть документация.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Комментарии не просто так придуманы. Они и есть документация.

 

Разные подходы есть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

Комментарии не просто так придуманы. Они и есть документация.

Документация в виде комментов нужна только возле определений полей(и многие ide юзают это для быстрой справки). Зачем их писать после каждого действия?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...