Ruby脚本解释流程

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Ruby提供了ripper这个工具,我们可利用此工具分析ruby代码,方便定位性能问题。 以下内容都是以如下代码为例: x > 100 ? 'foo' : 'bar' Ruby在执行程序前会将代码转化为更加结构化的语句 第一步:将代码切分为一个一个的词条 ripper的tokenize的方法可以将代

Ruby脚本解释流程


Ruby提供了ripper这个工具,我们可利用此工具分析ruby代码,方便定位性能问题。

以下内容都是以如下代码为例:


x > 100 ? 'foo' : 'bar'


Ruby在执行程序前会将代码转化为更加结构化的语句


第一步:将代码切分为一个一个的词条


ripper的tokenize的方法可以将代码转化为词条数组


require 'ripper'
require 'pp'
p Ripper.tokenize("x > 1 ? 'foo' : 'bar'")


以上程序执行结果如下:


["x", " ", ">", " ", "1", " ", "?", " ", "'", "foo", "'", " ", ":", " ", "'", "bar", "'"]


大家可以看到代码只是被分割成一个个词条,这里还不会去判断语法正确性


第二步:词法解析


第一步完成后,接下来就是词法解析,这一步会对每一个词条加上一些附加信息

require 'ripper' require 'pp'
pp Ripper.lex("x > 100 ? 'foo' : 'bar'")

以上程序执行结果如下:


[[[1, 0], :on_ident, "x"], 
[[1, 1], :on_sp, " "],
[[1, 2], :on_op, ">"], 
[[1, 3], :on_sp, " "],
[[1, 4], :on_int, "100"],
[[1, 7], :on_sp, " "],
[[1, 8], :on_op, "?"],
[[1, 9], :on_sp, " "],
[[1, 10], :on_tstring_beg, "'"],
[[1, 11], :on_tstring_content, "foo"],
[[1, 14], :on_tstring_end, "'"],
[[1, 15], :on_sp, " "],
[[1, 16], :on_op, ":"],
[[1, 17], :on_sp, " "],
[[1, 18], :on_tstring_beg, "'"],
[[1, 19], :on_tstring_content, "bar"],
[[1, 22], :on_tstring_end, "'"]]


大家可以看到,词条解析这一步中,ruby会为每一个词条打上标签,如表示变量的on_ident、 表示操作符的on_op等等,至于每一行的数组,其实是表示词条所在词条数组的位置,这里词 条数组中每一个元素都是一个字符,“100”有3个字符‘1’、‘0’、‘0’所以[1,4]~[1.6]都是用来存放 100的,bar、foo同理。


第三步:转化为抽象语法树


经过第二步,代码已经解析为可操作的词条了,接下来ruby会将这些词条转化为抽象语法树(AST),这里我们使用sexp方式来模拟


require 'ripper'
require 'pp'
pp Ripper.sexp("x > 100 ? 'foo' : 'bar'")


以上代码输出如下:


[:program,


[[:ifop,


[:binary, [:vcall, [:@ident, "x", [1, 0]]], :>, [:@int, "100", [1, 4]]], [:string_literal,


[:string_content, [:@tstring_content, "foo", [1, 11]]]], [:string_literal, [:string_content,


[:@tstring_content, "bar", [1, 19]]]]]]]


这部分我们对照的源码去看,也是很容易理解的。


第四步:编译字节码


在ruby2.0以后,代码被解析成AST后,还会在编译成字节码,在编译过程中会进行语法检查, 语法错误则编译不通过,编译完成后才能执行,在这里我们使用 RubyVM::InstructionSequence来窥探下其中的奥秘


puts RubyVM::InstructionSequence.compile("x > 100 ? 'foo' : 'bar'").disassemble

程序输出如下:


== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace 1 ( 1)
0002 putself
0003 opt_send_simple <callinfo!mid:x, argc:0, FCALL|VCALL|ARGS_SKIP>
0005 putobject 100
0007 opt_gt <callinfo!mid:>, argc:1, ARGS_SKIP>
0009 branchunless 15
0011 putstring "foo"
0013 leave
0014 pop
0015 putstring "bar"
0017 leave


这段输出可以这样理解


  1. 将self入栈


  1. 调用self的x方法


  1. 将100入栈


  1. 进行x>100的比较


  1. 如果为false,跳转至0015,返回‘bar’


  1. 如果为true 返回‘foo’


第五步:执行编译后的字节码

相关文章
|
测试技术 C++ Ruby
C++程序中嵌入Ruby脚本系统
Ruby,一种为简单快捷面向对象编程(面向对象程序设计)而创的脚本语言,由日本人松本行弘(まつもとゆきひろ,英译:Yukihiro Matsumoto,外号matz)开发,遵守GPL协议和Ruby License。
1591 0
|
Ruby
(转)分享一个技巧,利用批处理调用ruby脚本(可能你为路径苦恼)
#关闭命令显示 @echo off #提示信息 echo Now,listing the controller,please not shutdown the DOS File! #切换到当前路径,.
599 0
|
测试技术 数据安全/隐私保护 Ruby
自己动手破解rar密码-ruby脚本实现
破解密码,上策是社工的方式,下策则是暴力破解了。这里使用暴力破解,字典文件放在txt中;通过调用winrar的相关参数进行操作。经过测试,当字典中密码为100个时,破解耗时8秒(个人配置:i5-3210 2.
956 0
|
3月前
|
Ruby
|
3月前
|
JSON 数据格式 Ruby
|
3月前
|
调度 Ruby