Самостоятельной Модификации Кода
-
В последнее время я думаю о написании самоизменяющихся программ, я думаю, что это может быть мощным и интересным... Поэтому в настоящее время я ищу язык, который позволит легко изменять собственный код программы..
-
Я читал о C# (как способ обхода) и способности компилировать и выполнять код во время выполнения, но это слишком больно..
- я также думаю о сборке... там легче изменить запущенный код, но он не очень мощный (очень сырой)...
можете ли вы предложить мне мощный язык -или функцию-которая поддерживает изменение кода во время выполнения?.?
Подсказки
Вот что я имею в виду, изменяя код во время выполнения:
Start:
a=10,b=20,c=0;
label1: c=a+b;
....
label1= c=a*b;
goto label1;
И может составлять список инструкций:
code1.add(c=a+b);
code1.add(c=c*(c-1));
code1. execute();
Спасибо!
13 ответов:
Malbolge было бы хорошим местом для начала. Каждая инструкция самоизменяется, и с ней очень весело(*) играть.
(*) отказ от ответственности: на самом деле это не может быть весело.
Я настоятельно рекомендую Lisp. Данные Lisp могут быть прочитаны и исполнены как код. Лисп-код может быть записан в виде данных.
Он считается одним из канонических самомодифицируемых языков.Пример списка (данных):
'(+ 1 2 3)
Или, вызывая данные как код
(eval '(+ 1 2 3))
Запускает функцию+.
Вы также можете войти и редактировать членов списков на лету.
Правка:
Я написал программу, чтобы динамически генерировать программу и оценивать ее на лету, затем доложите мне, как это произошло по сравнению с базовым уровнем (div by 0 был обычным отчетом, ха).
Все ответы до сих пор касаются компиляции reflection / runtime, но в комментариях, которые вы упомянули, вас интересует фактическийсамоизменяющийся код -код, который изменяет себя в памяти.
Нет способа сделать это в C#, Java или даже (переносимо) в C - то есть вы не можете изменить загруженный в память двоичный файл, используя эти языки.
В общем случае, единственный способ сделать это-это сборка, и она сильно зависит от процессора. На самом деле, это очень операционная система кроме того, для защиты от полиморфных вирусов Большинство современных операционных систем (включая Windows XP+, Linux и BSD) принудительно применяют W^X , что означает, что вы должны пройти через некоторые трудности , чтобы написать полиморфные исполняемые файлы в этих операционных системах, для тех, которые позволяют это вообще.
В некоторых интерпретируемых языках может быть возможно, что программа изменит свой собственный исходный код во время выполнения. Perl,
Python(видишь здесь), и все известные мне реализации Javascript этого не допускают.
Могу я предложить Python , хороший динамический язык очень высокого уровня, который включает в себя богатый интроспекционный анализ (и, например, использование
compile
,eval
илиexec
допускает форму самоизменяющегося кода). Очень простой пример, основанный на вашем вопросе:Обратите внимание, что в приведенном выше примере кодаdef label1(a,b,c): c=a+b return c a,b,c=10,20,0 print label1(a,b,c) # prints 30 newdef= \ """ def label1(a,b,c): c=a*b return c """ exec(newdef,globals(),globals()) print label1(a,b,c) # prints 200
c
изменяется только область действия функции.
Лично я нахожу довольно странным, что вы находите сборку более легкой для обработки, чем C#. Я нахожу еще более странным, что вы думаете, что сборка не так мощна: вы не можете получить ничего более мощного, чем сырой машинный язык. Во всяком случае, каждому свое.
C# имеет отличные службы отражения, но если у вас есть отвращение к этому.. Если вы действительно знакомы с C или C++, вы всегда можете написать программу, которая пишет C/C++ и выдает его компилятору. Это было бы только жизнеспособно, если ваш решение не требует быстрого самостоятельного переписывания времени оборота (порядка десятков секунд и более).
Javascript и Python также поддерживают отражение. Если вы хотите изучить новый, интересный язык программирования, который является мощным, но не слишком технически сложным, я бы предложил Python.
Общий шепелявый язык был разработан с учетом такого рода вещей. Вы также можете попробовать Smalltalk , где использование отражения для изменения запущенного кода не является неизвестным.
В обоих этих языках вы, скорее всего, замените всю функцию или весь метод, а не одну строку кода. Методы Smalltalk, как правило, более мелкозернистые, чем функции Lisp, так что это может быть хорошим местом для начала.
Многие языки позволяют вамeval код во время выполнения.
- Лисп
- Perl
- Python
- PHP
- Рубин
- Groovy (через GroovyShell)
В языках высокого уровня, где вы компилируете и выполняете код во время выполнения, это не самоизменяющийся код, а динамическая загрузка класса. Используя принципы наследования, можно заменить фабрику классов и изменить поведение приложения во время выполнения.
Только в языке ассемблера вы действительно имеете истинную самомодификацию, записывая непосредственно в сегмент кода. Но практического применения у него мало. Если вам нравится вызов, напишите самошифрующийся, возможно, полиморфный вирус. Это было бы забавно.
Я написал код класса Python, который позволяет добавлять и удалять новые строки кода к объекту, печатать код и выделять его. Код класса показан в конце.
Пример: если x == 1, то код изменяет свое значение на x = 2, а затем удаляет весь блок с условием, которое проверяло это условие.
#Initialize Variables x = 1 #Create Code code = Code() code + 'global x, code' #Adds a new Code instance code[0] with this line of code => internally code.subcode[0] code + "if x == 1:" #Adds a new Code instance code[1] with this line of code => internally code.subcode[1] code[1] + "x = 2" #Adds a new Code instance 0 under code[1] with this line of code => internally code.subcode[1].subcode[0] code[1] + "del code[1]" #Adds a new Code instance 0 under code[1] with this line of code => internally code.subcode[1].subcode[1]
После создания кода Вы можете распечатать его:
#Prints print "Initial Code:" print code print "x = " + str(x)
Вывод:
Initial Code: global x, code if x == 1: x = 2 del code[1] x = 1
Выполните cade, вызвав объект: code ()
print "Code after execution:" code() #Executes code print code print "x = " + str(x)
Вывод 2:
Как вы можете видеть, код изменил переменную x на значение 2 и удалил весь блок if. Это может быть полезно, чтобы избежать проверки условий после их выполнения. В реальной жизни этот сценарий может быть обработан системой сопрограмм, но этот эксперимент с самоизменяющимся кодом - просто забава.Code after execution: global x, code x = 2
class Code: def __init__(self,line = '',indent = -1): if indent < -1: raise NameError('Invalid {} indent'.format(indent)) self.strindent = '' for i in xrange(indent): self.strindent = ' ' + self.strindent self.strsubindent = ' ' + self.strindent self.line = line self.subcode = [] self.indent = indent def __add__(self,other): if other.__class__ is str: other_code = Code(other,self.indent+1) self.subcode.append(other_code) return self elif other.__class__ is Code: self.subcode.append(other) return self def __sub__(self,other): if other.__class__ is str: for code in self.subcode: if code.line == other: self.subcode.remove(code) return self elif other.__class__ is Code: self.subcode.remove(other) def __repr__(self): rep = self.strindent + self.line + '\n' for code in self.subcode: rep += code.__repr__() return rep def __call__(self): print 'executing code' exec(self.__repr__()) return self.__repr__() def __getitem__(self,key): if key.__class__ is str: for code in self.subcode: if code.line is key: return code elif key.__class__ is int: return self.subcode[key] def __delitem__(self,key): if key.__class__ is str: for i in range(len(self.subcode)): code = self.subcode[i] if code.line is key: del self.subcode[i] elif key.__class__ is int: del self.subcode[key]
Я иногда, хотя Очень редко делаю самоизменяющийся код в Ruby.
Иногда у вас есть метод, в котором вы действительно не знаете, правильно ли инициализированы данные, которые вы используете (например, какой-то ленивый кэш). Таким образом, вы должны проверить в начале вашего метода, правильно ли инициализированы данные, а затем, возможно, инициализировать его. Но на самом деле вам нужно сделать эту инициализацию только один раз, но вы проверяете ее каждый раз.
Итак, иногда я пишу метод, который выполняет инициализацию и затем заменяет себя версией, которая не включает код инициализации.
Но, честно говоря, я не думаю, что это того стоит. На самом деле, мне стыдно признаться, что я никогда не проводил сравнительный анализ, чтобы увидеть, действительно ли это одно условие имеет какое-либо значение. (На современной реализации Ruby с агрессивно оптимизирующим JIT-компилятором, управляемым обратной связью профиля, вероятно, нет.)class Cache def [](key) @backing_store ||= self.expensive_initialization def [](key) @backing_store[key] end @backing_store[key] end end
Обратите внимание, что в зависимости от того, как вы Определите "самоизменяющийся код", это может быть или не быть тем, что вы хотите. Вы заменяете некоторую часть текущей исполняемой программы, так что ...
EDIT: теперь, когда я думаю об этом, эта оптимизация не имеет большого смысла. Дорогостоящая инициализация все равно выполняется только один раз. Единственное, чего избегает модификация, - это условности. Было бы лучше взять пример, где чек сам по себе стоит дорого, но я не могу придумать один.
Тем не менее, я подумал о классном примере самоизменяющегося кода: Maxine JVM. Maxine-это исследовательская виртуальная машина (технически она не может называться "JVM", потому что ее разработчики не запускают тесты совместимости), написанная полностью на Java. Теперь, есть много JVMs, написанных в себе, но Максин-единственная, которую я знаю, которая также работает в себе. Это чрезвычайно мощное средство. Например, JIT-компилятор может JIT-компилировать себя в адаптировать его к типу кода он JIT-компиляцию.Очень похожая вещь происходит в Klein VM, которая является виртуальной машиной для языка самопрограммирования.
В обоих случаях виртуальная машина может оптимизировать и перекомпилироватьсаму себя во время выполнения.
Это можно сделать на языке Maple (язык компьютерной алгебры). В отличие от многих ответов выше, которые используют скомпилированные языки, которые позволяют только создавать и связывать в Новый код во время выполнения, здесь вы можете честно модифицировать код текущей программы. (Ruby и Lisp, как указывают другие респонденты, также позволяют вам это делать; вероятно, Smalltalk тоже).
На самом деле, раньше в Maple было стандартным, что большинство библиотечных функций были небольшими заглушками, которые загружались их "реальное" я с диска по первому вызову, а затем самоизменяется до загруженной версии. Это уже не так, поскольку загрузка библиотеки была виртуализирована.
Как указывали другие: для этого вам нужен интерпретируемый язык с мощными средствами отражения и овеществления.
Я написал автоматический нормализатор / упрощатель для кода Maple, который я начал запускать на всей библиотеке (включая саму библиотеку); и потому, что я не был слишком осторожен во всех моих код, нормализатор изменил сам себя. Я также написалчастичный оценщик (недавно принятый SCP) под названиемMapleMIX - доступный на sourceforge - но не смог полностью применить его к себе (это не было целью дизайна).
В Lua можно "зацепить" существующий код, что позволяет присоединять произвольный код к вызовам функций. Это звучит примерно так:
local oldMyFunction = myFunction myFunction = function(arg) if arg.blah then return oldMyFunction(arg) end else --do whatever end end
Вы также можете просто перепахать функции, что в некотором роде дает вам самоизменяющийся код.