Ruby脚本解释流程
2022/5/10 11:00:43
本文主要是介绍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
这段输出可以这样理解
- 将self入栈
- 调用self的x方法
- 将100入栈
- 进行x>100的比较
- 如果为false,跳转至0015,返回‘bar’
- 如果为true 返回‘foo’
第五步:执行编译后的字节码
这篇关于Ruby脚本解释流程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-13TiDB + ES:转转业财系统亿级数据存储优化实践
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?
- 2024-05-09企业src漏洞挖掘-有意思的命令执行