自修改代码的编程语言?

35
  • 我最近在考虑编写自修改程序,我认为这可能很强大和有趣。因此,我目前正在寻找一种可以轻松修改程序代码的语言。
  • 我了解到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();

7
你想在运行时编译/执行代码,还是想在可执行文件加载到内存时修改它本身:这两件事情非常不同。(通常,“自修改”指的是第二种情况。) - BlueRaja - Danny Pflughoeft
2
@BlueRaja - Danny Pflughoeft:我对第二个感兴趣。如果第二个不可能或不可用,那么第一个就是意味着的。 - Betamoo
我曾经经常思考自修改代码。但在了解了函数指针和面向对象语言后,我发现它并没有太多用处。你有任何实际应用吗? - aaaa bbbb
@Paul Nathan:抱歉,我会修复它。 - Betamoo
3
在Redcode(http://vyznev.net/corewar/guide.html)中,许多最优秀的战士都是自我修改的... - dmckee --- ex-moderator kitten
显示剩余3条评论
14个回答

49
Malbolge是一个不错的开始点,它涉及到it技术。每个指令都是自修改的,玩起来很有趣。但请注意,实际上可能并不好玩。

24
编程语言中,一个人要花几年时间才能编写出“Hello World”程序……哦等等,其实是由计算机生成的。好好享受一下吧。 - Warty

24

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

它被认为是经典的自修改语言之一。

示例列表(数据):

'(+ 1 2 3) 
或者,将数据视为代码。
(eval '(+ 1 2 3)) 

运行 + 函数。

您还可以即时编辑列表成员。

编辑:

我编写了一个程序来动态生成程序并即时评估它,然后向我报告与基准的差异(通常是 div by 0),哈哈。


16
到目前为止,所有的答案都是关于反射/运行时编译的,但在评论中您提到您对实际的 自修改代码 感兴趣——即在内存中修改自身的代码。
在 C#、Java,甚至(可移植地)在 C 中都无法做到这一点-也就是说,您不能使用这些语言修改已加载到内存中的二进制文件。
通常,唯一的方法是使用汇编语言,而且高度依赖于处理器。事实上,它还高度依赖于操作系统:为了防止 多态病毒,大多数现代操作系统 (包括 Windows XP+,Linux 和 BSD) 实施 W^X,这意味着您必须 费些力气 才能在这些操作系统中编写多态可执行文件,对于那些允许的操作系统来说。
在一些解释型语言中,可能存在让程序在运行时修改自身源代码的可能性。Perl、Python (见 此处 和我所知道的每个 JavaScript 实现都不允许这样做。

你说得很对。从不同的角度来看,C# / 其他 .NET 变体 / Java 使用字节码而不是真正的汇编语言,但问题依然存在。 - Reinderien
1
我应该补充一下,如果您采用迂回和hacky的方式,即通过迭代地向编译器发出源代码、运行二进制文件,然后让该二进制文件修改源代码以重新发出给编译器... 那么这将(有效地)具有对程序代码的读写访问权限,尽管读写周转时间很长。 - Reinderien
1
尽管我同意这种观点的主旨,但我建议在Python中可能会有直接更改生成/运行字节码的可能性(例如:http://geofft.mit.edu/blog/sipb/73),这在概念上与使用汇编语言做同样的事情并没有太大区别。 - ChristopheD

10

我可以建议使用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只在函数作用域内被更改。


10

就我个人而言,我发现你认为汇编比 C# 更容易掌握相当奇怪。我更觉得奇怪的是你认为汇编不如 C# 强大:在原始机器语言上你无法获得更高的运行效率。不管怎样,每个人都有自己的喜好。

C# 拥有出色的反射服务,但如果你对此感到厌恶... 如果你真的熟悉 C 或 C++,你可以编写一个程序生成 C/C++ 代码并将其提交给编译器。这只有在您的解决方案不需要快速自我重写时才可行(所需时间为几十秒或更长)。

Javascript 和 Python 也都支持反射。如果你正在考虑学习一种新的、有趣的编程语言,既强大又不需要过多的技术要求,我建议你学习 Python。


6
我在这里看不出什么奇怪的。汇编语言是最简单的语言之一,而C#则非常庞大和复杂——我自己写了几年C#也这样认为!至于“power”(能力),我认为在编程中它通常指的是“表达能力”或“抽象构建能力”(显然,“power”的字面意思在这里无用!),而汇编语言在这方面特别差。 - Ken
我认为汇编比C#或Java容易得多。它非常直接和简单。 - Paul Nathan
3
我同意C#非常庞大而复杂。话虽如此,比较一下在C#和汇编中打开一个文件所需的代码行数。对于现代编程任务来说,汇编根本不是一个选择。对于需要进行重度优化的子任务,也许可以考虑使用汇编。至于强大程度,我基本上指的是您可以直接操纵的事物数量。然而,这并不总是有帮助的;这就像给你一堆碳,让你建一个狗一样。在汇编中进行实时修改可能非常棘手 - 你必须充当编译器。你需要处理所有的寻址问题。 - Reinderien
3
@Ken @Reinderien @Paul:汇编比C#难。没话说。和_C#的高效写法_一样难吗?也许不是。但是,_高效的_汇编绝对比高效的C#更难。你必须掌握架构,考虑到增加的圆形复杂度、缓存、流水线、中断,可能甚至需要完全重新设计整个算法以使其尽可能地高效(保存寄存器、对齐某些数据、修复一些指令级并行性问题等)。如果在硬件层次上:控制CPU(MSRs、TLB、页表、I/O、控制寄存器等)。 - L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
1
@Ken:如果你没有使用汇编的全部功能(或者写一个编译器),为什么你需要使用它呢?如果你没有将其推到极限,编译器会让你很痛苦的。 - L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
显示剩余5条评论

10

Common Lisp是为此类事情而设计的。您还可以尝试Smalltalk,在那里使用反射来修改运行代码并不罕见。

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


7

许多编程语言允许您在运行时评估代码。

  • Lisp
  • Perl
  • Python
  • PHP
  • Ruby
  • Groovy(通过GroovyShell)

4
在高级语言中,您可以在运行时编译和执行代码,其实并不是真正的自修改代码,而是动态类加载。使用继承原则,您可以替换一个类工厂并在运行时更改应用程序行为。
只有在汇编语言中,您才能真正拥有真正的自我修改,通过直接写入代码段。但是它的实际用途很少。如果您喜欢挑战,请编写一个自加密、多态病毒。那将是有趣的事情。

3
我有时会在 Ruby 中使用自修改代码,但非常少见。
有些方法可能存在数据初始化问题(如懒加载缓存),因此需要在方法开头检查数据是否已经正确初始化,如果未初始化则进行初始化操作。但实际上只需要进行一次初始化,而每次都进行检查效率较低。
因此,有时我会编写一个方法来完成初始化,并用不含初始化代码的版本替换它本身。
class Cache
  def [](key)
    @backing_store ||= self.expensive_initialization

    def [](key)
      @backing_store[key]
    end

    @backing_store[key]
  end
end

但是,说实话,我不认为这值得。事实上,我很尴尬地承认我从来没有真正进行过基准测试,看看这一个条件是否真的有任何区别。(在现代 Ruby 实现中,具有积极优化的配置文件反馈驱动的 JIT 编译器可能不需要。)
请注意,根据您如何定义“自修改代码”,这可能或可能不是您想要的。您正在替换当前执行程序的某个部分,因此...
编辑:现在我想起来了,那种优化并没有多大意义。昂贵的初始化只会被执行一次。修改所避免的唯一事情就是条件。最好举一个检查本身很昂贵的例子,但我想不出来。
然而,我想到了一个很酷的自修改代码的例子:Maxine JVM。Maxine是一个研究VM(技术上实际上不允许称其为“JVM”,因为其开发人员不运行兼容性测试套件),完全用Java编写。现在,有很多JVM是自己编写的,但是Maxine是我知道的唯一一个也在自己内部运行的。这非常强大。例如,JIT编译器可以JIT编译自身以适应它正在JIT编译的代码类型。 Klein VM中发生了非常类似的事情,这是Self编程语言的VM。
在两种情况下,VM可以在运行时优化和重新编译自身

2
我编写了一个Python类Code,它能够让您向对象添加和删除新的代码行,打印代码并执行它。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

通过调用对象来执行代码: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块。这可能有助于避免在满足条件后进行条件检查。在现实生活中,这种情况可以通过协程系统来处理,但这种自修改代码实验只是为了好玩。

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]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接