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


Фотография

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


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 6

#1 Оффлайн   Fingercomp

Fingercomp
  • Гуру
  • Сообщений: 2 001
  • Уровень сигнала: 169,88%
  • В игре: 1278 час. 43 мин.

Награды

                                               

Отправлено 03 Сентябрь 2016 - 23:48

Начнём сразу с кода. Пихнём в файл ./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 стандартные функции для своих нужд, И просто так отплясаться не получится. Шмактус, однако.


  • 1Ridav, Alex и Totoro это нравится

#2 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 04 Сентябрь 2016 - 06:38

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

---

Нашел эту тему: http://computercraft...tanty/?hl=const


Сообщение отредактировал SergOmarov: 04 Сентябрь 2016 - 06:45


#3 Оффлайн   Fingercomp

Fingercomp
  • Автор темы
  • Гуру
  • Сообщений: 2 001
  • Уровень сигнала: 169,88%
  • В игре: 1278 час. 43 мин.

Награды

                                               

Отправлено 04 Сентябрь 2016 - 10:22

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



#4 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 04 Сентябрь 2016 - 10:42

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



#5 Оффлайн   NEO

NEO
  • Пользователи
  • Сообщений: 1 748
  • Уровень сигнала: 4,84%
  • В игре: 36 час. 25 мин.
  • ГородСолнце

Награды

   3                        

Отправлено 04 Сентябрь 2016 - 12:47

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

:smile3:

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



#6 Оффлайн   Totoro

Totoro
  • Хранители Кода
  • Сообщений: 1 735
  • Уровень сигнала: 0,29%
  • В игре: 2 час. 13 мин.

Награды

                                      

Отправлено 04 Сентябрь 2016 - 13:03

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

 

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


  • SergOmarov это нравится

#7 Оффлайн   SergOmarov

SergOmarov
  • Пользователи
  • Сообщений: 564
  • Уровень сигнала: 0,35%
  • В игре: 2 час. 36 мин.

Награды

     

Отправлено 04 Сентябрь 2016 - 13:19

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

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






Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных