“Ruby Under a Microscope”的版本间的差异

来自Dennis的知识库
跳转到: 导航搜索
第23行: 第23行:
 
* Ripper.sexp 输出 parse 结果,也可以使用命令行 ruby --dump parsetree xxxx.rb 得到。前者是 Ripper 的 AST 展示格式,后者是实际内部的 c 语言 node 节点信息。
 
* Ripper.sexp 输出 parse 结果,也可以使用命令行 ruby --dump parsetree xxxx.rb 得到。前者是 Ripper 的 AST 展示格式,后者是实际内部的 c 语言 node 节点信息。
 
* Ruby 使用手写的 tokenizer ,以及 bison 写的 parser —— [https://github.com/ruby/ruby/blob/510f0ec86912e31babaadf1f66bf2a82351c1359/parse.y parse.y] ,bison生成的解释器是 [https://en.wikipedia.org/wiki/LALR_parser LALR Parser]。
 
* Ruby 使用手写的 tokenizer ,以及 bison 写的 parser —— [https://github.com/ruby/ruby/blob/510f0ec86912e31babaadf1f66bf2a82351c1359/parse.y parse.y] ,bison生成的解释器是 [https://en.wikipedia.org/wiki/LALR_parser LALR Parser]。
 +
 +
== 编译 ==
 +
 +
* Ruby 1.8 没有编译器, Ruby 1.9 之后引入了 YARV( yet another ruby vm) 中间指令。但是 Ruby 并没有独立的编译器,而是在运行时动态编译成字节码,并交给 VM 解释执行。很可惜, Ruby 还是没有 JIT,将字节码编译成本地机器码。但是从测试来看, 1.9 之后的性能,已经远比 1.8 高(简单测试是 4.25 倍左右), 1.8 还是原始的解释执行 AST 的方式。
 +
* 编译的过程本质是遍历 AST ,然后生成 YARV 字节码的过程,具体参考  https://github.com/ruby/ruby/blob/trunk/compile.c 中的 iseq_compile_each 函数,一个大的 switch 派发。
 +
* NODE_SCOPE 表示开始一个新的作用域,作用域绑定着一个本地表 local table,类似 JVM 里的局部变量区,参数和局部变量的信息会放在这里。
 +
* 查看 YARV 字节码:
 +
 +
<pre>
 +
<code>
 +
code = <<END
 +
10.times do |n|
 +
  puts n
 +
end
 +
END
 +
 +
puts RubyVM::InstructionSequence.compile(code).disasm
 +
</code>
 +
</pre>
 +
 +
输出
 +
<pre>
 +
== disasm: #<ISeq:<compiled>@<compiled>>================================
 +
== catch table
 +
| catch type: break  st: 0002 ed: 0008 sp: 0000 cont: 0008
 +
|------------------------------------------------------------------------
 +
0000 trace            1                                              (  1)
 +
0002 putobject        10
 +
0004 send            <callinfo!mid:times, argc:0>, <callcache>, block in <compiled>
 +
0008 leave
 +
== disasm: #<ISeq:block in <compiled>@<compiled>>=======================
 +
== catch table
 +
| catch type: redo  st: 0002 ed: 0010 sp: 0000 cont: 0002
 +
| catch type: next  st: 0002 ed: 0010 sp: 0000 cont: 0010
 +
|------------------------------------------------------------------------
 +
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
 +
[ 2] n<Arg>
 +
0000 trace            256                                            (  1)
 +
0002 trace            1                                              (  2)
 +
0004 putself
 +
0005 getlocal_OP__WC__0 2
 +
0007 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
 +
0010 trace            512                                            (  3)
 +
0012 leave                                                            (  2)
 +
</pre>
 +
 +
其中的 local table 就是本地表,<code><callinfo!mid:times, argc:0>, <callcache>, block in <compiled></code> 这里表示为 10.times 传递了一个 Block,它的指令在下面。

2016年12月10日 (六) 15:12的版本


分词与语法解析

  • 使用 Ripper 输出 lex 结果。
<code>
require 'ripper'
require 'pp'
#ripper is not parser, it can't find error.
code = <<STR
10.times do |n|
  puts n
end
STR

puts code
pp Ripper.lex(code)
</code>
  • Ripper.sexp 输出 parse 结果,也可以使用命令行 ruby --dump parsetree xxxx.rb 得到。前者是 Ripper 的 AST 展示格式,后者是实际内部的 c 语言 node 节点信息。
  • Ruby 使用手写的 tokenizer ,以及 bison 写的 parser —— parse.y ,bison生成的解释器是 LALR Parser

编译

  • Ruby 1.8 没有编译器, Ruby 1.9 之后引入了 YARV( yet another ruby vm) 中间指令。但是 Ruby 并没有独立的编译器,而是在运行时动态编译成字节码,并交给 VM 解释执行。很可惜, Ruby 还是没有 JIT,将字节码编译成本地机器码。但是从测试来看, 1.9 之后的性能,已经远比 1.8 高(简单测试是 4.25 倍左右), 1.8 还是原始的解释执行 AST 的方式。
  • 编译的过程本质是遍历 AST ,然后生成 YARV 字节码的过程,具体参考 https://github.com/ruby/ruby/blob/trunk/compile.c 中的 iseq_compile_each 函数,一个大的 switch 派发。
  • NODE_SCOPE 表示开始一个新的作用域,作用域绑定着一个本地表 local table,类似 JVM 里的局部变量区,参数和局部变量的信息会放在这里。
  • 查看 YARV 字节码:
<code>
code = <<END
10.times do |n|
  puts n
end
END

puts RubyVM::InstructionSequence.compile(code).disasm
</code>

输出

== disasm: #<ISeq:<compiled>@<compiled>>================================
== catch table
| catch type: break  st: 0002 ed: 0008 sp: 0000 cont: 0008
|------------------------------------------------------------------------
0000 trace            1                                               (   1)
0002 putobject        10
0004 send             <callinfo!mid:times, argc:0>, <callcache>, block in <compiled>
0008 leave
== disasm: #<ISeq:block in <compiled>@<compiled>>=======================
== catch table
| catch type: redo   st: 0002 ed: 0010 sp: 0000 cont: 0002
| catch type: next   st: 0002 ed: 0010 sp: 0000 cont: 0010
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] n<Arg>
0000 trace            256                                             (   1)
0002 trace            1                                               (   2)
0004 putself
0005 getlocal_OP__WC__0 2
0007 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0010 trace            512                                             (   3)
0012 leave                                                            (   2)

其中的 local table 就是本地表,<callinfo!mid:times, argc:0>, <callcache>, block in <compiled> 这里表示为 10.times 传递了一个 Block,它的指令在下面。

个人工具
名字空间

变换
操作
导航
工具箱