3

对于 MRI 1.9+ 和 Rubinius 实现,Ruby 源代码被编译成字节码,然后由 VM 解释该字节码。当使用解释器从命令行运行 Ruby 脚本时,我想知道这种机制的细节。

  • 解释器是否首先编译脚本中所需的所有相关源文件,然后运行所有内容?还是它会执行一些代码,然后在需要时以一种懒惰的方式编译其他文件?
  • 如果是后者(我怀疑),这个过程是通过文件还是通过代码块完成的?
  • 在什么时候它停止执行字节码并再次运行编译过程?
  • 这个过程与 MRI 和 Rubinius 有什么不同吗?

例如,如果我运行“ruby my_main_script.rb”,它需要 3 个其他 rb 源文件(并且这个文件本身没有任何要求),我想象的可能性是:

  • :解释器解析 my_main_script.rb 和 3 个文件。在解析之后,它将所有 AST 树编译为字节码。然后它继续使用 VM 运行字节码。

  • B : Ruby 解析 my_main_script.rb 并将其编译为字节码。然后它运行字节码。当遇到对另一个文件中的方法的调用时,它首先解析并编译该文件并继续执行。如果是这种情况,我想详细了解一下。

  • C : Ruby 根据一些(对我来说不知道的)标准解析和编译来自 my_main_script.rb 的一些代码,它运行该字节码,然后在需要时解析和编译另一段。这个过程和“当需要时”条件检测方法对我来说是有趣的理解。


2016 年 3 月 30 日更新

我编写了这个小实验脚本来尝试检查 B 是否是正确答案:

class RubyVM
  class InstructionSequence
    class << self
      alias :old_compile_file :compile_file
      def compile_file(code, opt)
        puts "Injecting code..."
        old_compile_file(code, opt)
      end
      alias :old_compile :compile
      def compile(code)
        puts "Injecting code..."
        old_compile(code)
      end
    end
  end
end

require_relative 'say_hi'

'say_hi.rb' 只包含“puts 'hello'”这一行。如果 B 是正确答案,那么输出不应该如下吗?

Injecting code...
hello

它只是输出“你好”......

4

1 回答 1

2

对我来说B是正确的答案。

Ruby 允许我们通过动态加载代码autoload并将字符串作为代码 ( eval) 执行,因此它必须能够随时解析和执行代码。

因此,首先它将您的主程序所需的所有文件转换为 YARV 指令,但如果您使用autoloadeval这些文件/代码将在以后转换。

关于这个过程的一本非常好的书是显微镜下的 Ruby

于 2015-12-25T09:29:44.550 回答