【问题标题】:Programming language for self-modifying code?用于自修改代码的编程语言?
【发布时间】:2011-03-04 16:53:12
【问题描述】:
  • 我最近正在考虑写自修改程序,我认为它可能是强大而有趣的。所以我目前正在寻找一种可以轻松修改程序自己代码的语言。
  • 我了解了 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();

【问题讨论】:

  • 您想在运行时编译/执行代码,还是想在加载到内存时修改可执行文件本身:这是两件非常不同的事情。 (通常“自修改”表示第二个)
  • @BlueRaja - Danny Pflughoeft:我对第二个感兴趣。第一个是指第二个是不可能或不可用的......
  • 我过去经常考虑自修改代码。在我了解了函数指针和面向对象的语言之后,我从未发现它有太多用处。您对此有什么实际用途吗?
  • @aaaa bbbb:我想学习这样一门语言来发现它的实际用途!
  • Redcode (vyznev.net/corewar/guide.html) ,许多最优秀的战士都在自我修改......

标签: programming-languages dynamic-languages self-modifying


【解决方案1】:

Malbolge 将是一个很好的起点。每条指令都是自我修改的,玩起来很有趣(*)。

(*) 免责声明:实际上可能并不好玩。

【讨论】:

  • 一个人花了几年时间写出hello world的编程语言……哦等等,hello world程序是由计算机生成的。玩得开心。
【解决方案2】:

我强烈推荐 Lisp。 Lisp 数据可以作为代码读取和执行。 Lisp 代码可以写成数据。

它被认为是规范的自修改语言之一。

示例列表(数据):

'(+ 1 2 3) 

或者,将数据调用为代码

(eval '(+ 1 2 3)) 

运行 + 函数。

您还可以随时进入并编辑列表的成员。

编辑:

我编写了一个程序来动态生成一个程序并动态评估它,然后向我报告它与基线相比的效果(div by 0 是通常的报告,哈哈)。

【讨论】:

    【解决方案3】:

    到目前为止,每个答案都是关于反射/运行时编译,但在您提到的 cmets 中,您对实际的 self-modifying code 感兴趣 - 代码在内存中自行修改。

    在 C#、Java 甚至(可移植的)C 中都无法做到这一点 - 也就是说,您无法使用这些语言修改加载的内存中二进制文件。

    通常,执行此操作的唯一方法是使用汇编,并且它高度依赖于处理器。事实上,它也高度依赖于操作系统:为了防止 polymorphic viruses,大多数现代操作系统(包括 Windows XP+、Linux 和 BSD) 强制执行 W^X,这意味着您必须 @ 987654324@ 在那些操作系统中编写多态可执行文件,对于那些完全允许它的操作系统。

    在某些解释性语言中,程序可能会在运行时修改自己的源代码。 Perl、Python (参见here,以及我所知道的每个 Javascript 实现都不允许这样做。

    【讨论】:

    • 你说得很好。根据您的看法,C#/其他 .NET 风格/Java 使用字节码而不是正确的程序集,但问题仍然存在。
    • 我应该补充一点,如果你要这样做,稍微迂回和hacky的方式迭代地向编译器发布源代码,运行二进制文件,然后让该二进制文件依次修改源代码将它重新发布给编译器......即使读写周转时间很差,也会(有效地)对程序代码进行读写访问。
    • 虽然我同意这种观点的要点,但我建议直接改变生成/运行的字节码的可能性(例如在 Python 中:geofft.mit.edu/blog/sipb/73)(从概念上讲,它与对装配做同样的事情)。
    【解决方案4】:

    就我个人而言,我觉得很奇怪你发现汇编比 C# 更容易处理。我发现您认为汇编没有那么强大更奇怪:您无法获得比原始机器语言更强大的功能。无论如何,各有各的。

    C# 具有出色的反射服务,但如果您对此有反感。如果您对 C 或 C++ 非常熟悉,您总是可以编写一个编写 C/C++ 的程序并将其发布给编译器。这只有在您的解决方案不需要快速的自重写周转时间(大约几十秒或更长时间)时才可行。

    Javascript 和 Python 也都支持反射。如果您正在考虑学习一种新的、有趣的、功能强大但对技术要求不高的编程语言,我建议您使用 Python。

    【讨论】:

    • 我看不出有什么奇怪的地方。汇编非常简单,而 C# 庞大而复杂——我是作为一个已经编写 C# 几年的人这么说的!至于power,我认为在编程中它通常是指“表达能力”或“抽象构建能力”(显然“power”的字面意思在这里没有用!),而汇编语言在这方面特别差。跨度>
    • @Reinderien:我的意思是在更改现有代码方面,汇编比 C# 更容易......另外在可读性和可写性方面,汇编功能不太强大......
    • 我同意 C# 庞大而复杂。话虽如此,比较在 C# 和汇编中打开文件所需的代码行数。对于现代编程任务,汇编根本不是一种选择。对于需要大量优化的子子任务,也许。至于权力,我本质上是指你被允许直接操纵的东西的数量。然而,这并不总是有用的。这就像得到一堆碳并被告知要建造一条狗。即时修改汇编可能会变得非常棘手-您实际上必须充当编译器。您需要完成所有寻址。
    • @Ken @Reinderien @Paul:汇编比 C# 难。 期间。像有效 C#一样难吗?也许不吧。但是,有效的汇编肯定比有效的 C# 更难。您必须掌握您的架构,考虑增加的圈复杂度、缓存、流水线、中断,并可能在需求发生变化时完全重新设计整个算法,以使其尽可能高效(保存寄存器、对齐一些数据、修复一些 ILP 等),如果在硬件级别:控制 CPU(MSR、TLB、页表、I/O、控制寄存器等)。
    • @Ken:如果您不使用它的全部前沿(或编写编译器),为什么还需要使用汇编?如果你不把它推到极限,编译器就会谋杀你。
    【解决方案5】:

    我可以推荐Python,这是一种很好的非常高级的动态语言,其中包含丰富的内省(例如,使用compileevalexec 允许一种形式的自修改代码)。基于您的问题的一个非常简单的示例:

    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 仅在函数范围内进行了更改。

    【讨论】:

      【解决方案6】:

      Common Lisp 的设计考虑了这种事情。你也可以试试Smalltalk,这里使用反射来修改运行代码并不陌生。

      在这两种语言中,您可能会替换整个函数或整个方法,而不是一行代码。 Smalltalk 方法往往比 Lisp 函数更细粒度,因此这可能是一个很好的起点。

      【讨论】:

        【解决方案7】:

        许多语言允许您在运行时eval 代码。

        • Lisp
        • Perl
        • Python
        • PHP
        • 红宝石
        • Groovy(通过 GroovyShell)

        【讨论】:

          【解决方案8】:

          在运行时编译和执行代码的高级语言中,它并不是真正的自修改代码,而是动态类加载。使用继承原则,您可以替换类 Factory 并在运行时更改应用程序行为。

          只有在汇编语言中你才真正拥有真正的自我修改,通过直接写入代码段。但它的实际用途很少。如果你喜欢挑战,写一个自加密的,也许是多态的病毒。那会很有趣。

          【讨论】:

            【解决方案9】:

            我有时,虽然非常很少在 Ruby 中进行自我修改代码。

            有时您有一种方法,您并不真正知道您正在使用的数据(例如一些惰性缓存)是否已正确初始化。因此,您必须在方法开始时检查数据是否已正确初始化,然后可能会对其进行初始化。但是你真的只需要做一次初始化,但你每次都检查它。

            所以,有时我会编写一个方法来进行初始化,然后将其自身替换为不包含初始化代码的版本。

            class Cache
              def [](key)
                @backing_store ||= self.expensive_initialization
            
                def [](key)
                  @backing_store[key]
                end
            
                @backing_store[key]
              end
            end
            

            但老实说,我认为这不值得。事实上,我很尴尬地承认,我从未真正进行过基准测试,看看那个 one 条件是否真的有什么不同。 (在具有积极优化配置文件反馈驱动的 JIT 编译器的现代 Ruby 实现中,可能不会。)

            请注意,根据您定义“自修改代码”的方式,这可能是也可能不是您想要的。您正在替换当前正在执行的程序的某些部分,所以……

            编辑:现在我想一想,优化没有多大意义。无论如何,昂贵的初始化只执行一次。修改避免的唯一事情是条件。最好举个例子,支票本身很贵,但我想不出一个。

            但是,我想到了一个很酷的自修改代码示例:Maxine JVM。 Maxine 是一个完全用 Java 编写的研究 VM(实际上允许称它为“JVM”,因为它的开发人员不运行兼容性测试套件)。现在,有很多 JVM 是自己编写的,但 Maxine 是我所知道的唯一一个自己也运行的。这是非常强大的。例如,JIT 编译器可以对其自身进行 JIT 编译,以使其适应其正在 JIT 编译的代码类型。

            类似的事情发生在Klein VM,它是自编程语言的虚拟机。

            在这两种情况下,VM 都可以在运行时优化和重新编译自身

            【讨论】:

              【解决方案10】:

              我编写了 Python 类 Code,它使您能够向对象添加和删除新的代码行,打印代码并执行它。类代码显示在最后。

              示例:如果 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:

              Code after execution:
              
              global x, code
              
              x = 2
              

              如您所见,代码将变量 x 更改为值 2 并删除了整个 if 块。这对于避免在满足条件后检查条件可能很有用。在现实生活中,这种case-scenario可以通过协程系统来处理,但是这种自我修改的代码实验只是为了好玩。

              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]
              

              【讨论】:

                【解决方案11】:

                您可以在 Maple(计算机代数语言)中执行此操作。与上面那些使用编译语言的许多答案不同,这些语言只允许您在运行时创建和链接 new 代码,在这里您可以诚实地修改当前正在运行的程序的代码。 (正如其他回答者所指出的,Ruby 和 Lisp 也允许您这样做;可能 Smalltalk 也是如此)。

                实际上,过去在 Maple 中的标准是大多数库函数都是小型存根,它们会在第一次调用时从磁盘加载它们的“真实”自我,然后自我修改到加载的版本。这不再是这种情况,因为库加载已被虚拟化。

                正如其他人所指出的:您需要一种具有强大反射和具体化功能的解释性语言来实现这一目标。

                我已经为 Maple 代码编写了一个自动规范化器/简化器,我开始在整个库(包括它自己)上运行它;而且因为我在所有代码中都不太小心,所以规范器确实修改了自己。我还写了一个名为 MapleMIX - available on sourceforgePartial Evaluator(最近被 SCP 接受)——但不能完全将其应用于自身(这不是设计目标)。

                【讨论】:

                  【解决方案12】:

                  你看过 Java 吗? Java 6 有一个compiler API,因此您可以编写代码并在 Java VM 中编译它。

                  【讨论】:

                    【解决方案13】:

                    Lua 中,您可以“挂钩”现有代码,这允许您将任意代码附加到函数调用中。它是这样的:

                    local oldMyFunction = myFunction
                    myFunction = function(arg)
                        if arg.blah then return oldMyFunction(arg) end
                        else
                            --do whatever
                        end
                    end
                    

                    您也可以简单地遍历函数,这可以让您自行修改代码。

                    【讨论】:

                      【解决方案14】:

                      Dlang 的 LLVM 实现包含 @dynamicCompile 和 @dynamicCompileConst 函数属性,允许您在编译时根据本机主机的指令集进行编译,并通过重新编译在运行时更改编译时常量。

                      https://forum.dlang.org/thread/bskpxhrqyfkvaqzoospx@forum.dlang.org

                      【讨论】:

                        猜你喜欢
                        • 2015-09-26
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-04-22
                        • 1970-01-01
                        • 2014-02-11
                        • 1970-01-01
                        • 2012-05-06
                        • 1970-01-01
                        相关资源
                        最近更新 更多