Attempts in C range with the ugly but obvious (repeating the source code as a string, and essentially printing the string twice) to the downright obfuscated.
Things are different in dynamic languages, of course, and there's a reason why this problem was never a RubyQuiz (UPDATE: it has! with a necessary extra 'piping' condition): any ruby program can be turned into a quine with the following code:
if __FILE__ == $0
puts File.open(__FILE__, 'r') { |f| f.read }
end
But perhaps something more can be done? Perhaps the source code and the bytecode can be displayed.
It turns out that the RubyVM object in YARV-based Ruby interpreters makes this possible:
#!/usr/bin/env ruby
def no_op
$some_variable ||= 1024
end
if __FILE__ == $0
buf = File.open(__FILE__, 'r') { |f| f.read }
code = RubyVM::InstructionSequence.compile(buf)
puts buf
puts code.disasm
end
Running this under ruby-1.9.2-head produces the following output:
bash$ rvm ruby-1.9.2-head do ruby quine.rb
#!/usr/bin/env ruby
def no_op
$some_variable ||= 1024
end
if __FILE__ == $0
buf = File.open(__FILE__, 'r') { |f| f.read }
code = RubyVM::InstructionSequence.compile(buf)
puts buf
puts code.disasm
end
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
== catch table
| catch type: break st: 0029 ed: 0046 sp: 0000 cont: 0046
|------------------------------------------------------------------------
local table (size: 3, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 3] buf [ 2] code
0000 trace 1 ( 3)
0002 putspecialobject 1
0004 putspecialobject 2
0006 putobject :no_op
0008 putiseq no_op
0010 send :"core#define_method", 3, nil, 0, <ic:0>
0016 pop
0017 trace 1 ( 7)
0019 putstring "<compiled>"
0021 getglobal $0
0023 opt_eq <ic:9>
0025 branchunless 100
0027 trace 1 ( 8)
0029 getinlinecache 36, <ic:2>
0032 getconstant :File
0034 setinlinecache <ic:2>
0036 putstring "<compiled>"
0038 putstring "r"
0040 send :open, 2, block in <compiled>, 0, <ic:3>
0046 setlocal buf
0048 trace 1 ( 9)
0050 getinlinecache 59, <ic:4>
0053 getconstant :RubyVM
0055 getconstant :InstructionSequence
0057 setinlinecache <ic:4>
0059 getlocal buf
0061 send :compile, 1, nil, 0, <ic:5>
0067 setlocal code
0069 trace 1 ( 10)
0071 putnil
0072 getlocal buf
0074 send :puts, 1, nil, 8, <ic:6>
0080 pop
0081 trace 1 ( 11)
0083 putnil
0084 getlocal code
0086 send :disasm, 0, nil, 0, <ic:7>
0092 send :puts, 1, nil, 8, <ic:8>
0098 leave ( 7)
0099 pop
0100 putnil ( 11)
0101 leave
== disasm: <RubyVM::InstructionSequence:no_op@<compiled>>===============
0000 trace 8 ( 3)
0002 trace 1 ( 4)
0004 putnil
0005 defined 7, :$some_variable, false
0009 branchunless 17
0011 getglobal $some_variable
0013 dup
0014 branchif 22
0016 pop
0017 putobject 1024
0019 dup
0020 setglobal $some_variable
0022 trace 16 ( 5)
0024 leave ( 4)
== disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
== catch table
| catch type: redo st: 0000 ed: 0011 sp: 0000 cont: 0000
| catch type: next st: 0000 ed: 0011 sp: 0000 cont: 0011
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] f<Arg>
0000 trace 1 ( 8)
0002 getdynamic f, 0
0005 send :read, 0, nil, 0, <ic:0>
0011 leave
No comments:
Post a Comment