XRuby是什么?它是一个开源编译器。与其它编译器一样,它完成的工作是将一种格式的语言转换成另一种。与大多数编译器不同的是,它是将Ruby的代码(.rb)转换成Java的字节码(.class)。
图1:将Ruby编译为字节码
Ruby编译器?那岂不又是一个Ruby的实现。自从Ruby越来越受到人们的关注,各种Ruby实现也逐渐出现在人们的视野之中。除了Ruby之父松本行弘(Matz)开发的版本(Matz Reference Implementation,简称MRI)之外,JRuby、Rubinius、IronRuby、Ruby.NET、Cardinal等不同Ruby实现的名头也是一天比一天响亮。同为Ruby实现,XRuby有哪些不同之处呢?
XRuby是一个将Ruby代码编译为Java字节码的编译器,这说明
运行在JVM上让XRuby有别于用C完成的MRI,.NET平台上的IronRuby和Ruby.NET和Parrot上的Cardinal,而编译器的特点又让XRuby和JRuby有所差别。
无可否认,经过多年发展,Java已经成为软件开发世界的主流语言之一,选择Java平台还因为:
从程序设计语言的发展来看,一种新兴的程序设计语言都会存在与主流程序设计语言的接口,比如C/C++对汇编语言的接口,比如Java语言对于C/C++ 的接口。通过这样的接口,利用主流语言已有的资源,帮助新语言得到更好的接受。用C开发的MRI让Ruby已经拥有了和C/C++的接口,XRuby为 Ruby提供了一种通往Java世界的选择。拥有Ruby和Java的接口,意味着我们可以将Ruby的开发效率和Java的丰富资源结合起来:在 Ruby的代码中使用Java的类库;将Ruby应用部署到Java EE的服务器上……
为什么要将Ruby应用部署到Java EE服务器上呢?Java EE技术经过多年的发展已经走向成熟,并在业界得到了广泛的应用,Java EE的服务器逐渐稳定,在很多领域中都有了成熟的解决方案。更重要的是,将Ruby应用部署到Java EE服务器上,意味着之前众多企业在Java EE服务器方面的投资将会得到重用,这会极大的减少基础设施建设方面的浪费。
如果我们想将程序设计语言编写的源代码运行起来,通常情况下,我们有两条路可以走:解释和编译,这也是我们让Ruby运行于JVM平台的两种选择。
解释,这是MRI最初的选择。因为有MRI作为参考,所以,这也是一种相对来说比较容易移植的选择。这种做法需要有一种专门的执行引擎。如下图所示,源码经过MRI解析之后,产生了一棵抽象语法树(Abstract Syntax Tree,简称AST)。利用这棵树产生我们需要的结果,这是执行引擎的工作。这种做法的好处就是可以把执行逻辑独立处理,无须为每个平台编写不同的代码,所以,这种做法的可移植性很好,它也就成了不少程序设计语言最初的选择。不过,随之而来的问题就是这会降低执行速度,毕竟,这个软件执行引擎的性能无法与硬件相比。所以,“编译”会让程序拥有更佳的性能。
图2:解释
采用编译的方式,我们可以将代码编译成可以由硬件直接执行的二进制代码。对于JVM这个“虚拟”的机器而言,它的二进制代码就是字节码。因为不同硬件和不同操作系统的二进制格式是不同的,所以,如果希望语言得到广泛应用,那便需要针对不同平台实现平台相关的编译器后端,理论上说,这不是一件不能完成的任务,但实际上,这意味着巨大的工作量。JVM的存在降低了这个难度,因为主流软硬件平台上基本都有自己的JVM实现,所以,代码可以运行在JVM上便意味可以运行在大多数平台上。当然,因为JVM本身是虚拟机,也是一种软件实现,所以,性能上也会有一些损失。不过,随着JVM技术上的不断进步,性能损失越来越小。另外,从Java平台的广泛应用也证明了,这样的损失在实践中是可以接受的。不过,相对于解释的方式有MRI这种成熟实现作为参考,编译显然需要更多的探索。
图3:编译
除了执行性能之外,将Ruby代码编译为Java的字节码还可以为我们带来另外的好处,那就是保护源代码。解释的方式让很多人不放心的一点就在于,产品发布意味着将源代码也发布出去。对于需要保护知识产权的公司和个人而言,这是他们所不愿意看到的。通过编译的方式,我们将源代码转成了字节码,这样,源代码可以得到有效保护。破解字节码理论上也是有可能的,不过,这是另外的故事了。
XRuby从起步开始走的就是编译的道路,而JVM上的另外一种Ruby实现——JRuby则是从解释起步的。相比于解释,编译方式在性能方面有着很大的优势,所以,从解释方式起步的JRuby最终也走上编译的道路。
让我们从一段简单的Ruby代码出发,更具体了解一下XRuby。
class MyClass
def say_hello_three_times
3.times { puts "hello"}
end
end
MyClass.new.say_hello_three_times
(simple.rb)
这里,我们定义了名为MyClass的类,其中有一个say_hello_three_times,它的工作就是输出三次“hello”。即便你并不了解Ruby语言,相信这段代码看上去,也不是那么难以理解,只是语法上不尽相同而已。稍微需要解释一下的就是:
3.times { puts "hello"}
同许多面向对象程序设计语言相比,在Ruby中,面向对象做得更加彻底,所以,即便像“3”这样的数字也是一个对象,所以,我们可以调用它的方法。再有,就是后面括号里的东西。在Ruby中,这是一种语法结构,称为block。从这段代码来说,block中的代码会在执行3.times的过程中调用。当然,这里展现的只是非常简单的用法,事实上,block的存在使得Ruby代码具有了更强的表现力。这不是一篇Ruby语言的介绍,如果你对Ruby语言更感兴趣,那本著名的《Programming Ruby》是个不错的选择。
将这段代码保存在一个名为simple.rb,然后,用XRuby对它进行编译。
java -jar xruby-0.3.1.jar -c simple.rb
编译之后,会得到一个名为simple.jar的文件,运行这个JAR文件,就可以得到运行的结果
java -jar simple.jar
屏幕输出为:
hello
hello
hello
当然,我们也可以像使用其它Ruby实现一样,直接运行这段代码,而无需生成中间的JAR文件。
java -jar xruby-0.3.1.jar simple.rb
我们会得到同样的结果:
hello
hello
hello
作为一个Ruby用户,如果你对XRuby有兴趣,你所需要做的只是把XRuby安装到你的系统,像其它Ruby实现一样去使用,因为从用户的角度而言,XRuby和它们没有区别。如果你对Ruby语言的实现感兴趣,也许你需要了解XRuby的一些内部实现。
图4:XRuby架构图
上图展示了XRuby的架构,主要包括两个部分:编译器和运行时。编译器部分将Ruby代码编译生成字节码,运行时部分则实现了对Ruby语言的支持。下面具体看一下各个部分。
图5:编译器
Ruby代码经过Parser的解析,会形成一棵抽象语法树,Tree Parser处理这棵树,形成Code DOM。Code Generator部分利用Code DOM生成对应的字节码。这里的Code DOM是对XRuby内部对Ruby语言建立的一个模型,通过这个模型进行代码生成。这个模型将XRuby的编译器前端很好的独立出来,这样,在保持前端不变的情况下,我们可以用它为不同的平台去做代码生成,这也XRuby之所以为“X”的一个原因。
图6:运行时
从这张架构图中,我们可以看到,XRuby的运行时包括动态语言的支持、内建库(Builtin)和Java支持几个部分。
Ruby是一种动态语言,而JVM本身是为Java这样的静态语言提供的平台,很难直接在二者之间做直接的映射。动态语言支持的部分就是为了在JVM平台上支持动态语言特性而存在的。也是因为这个部分的存在,我们即便采用编译的方式,依然使用Ruby的动态语言特性,比如,在程序运行期间为类增加或删除方法等等。
我们知道,除了语言本身外,程序库是一种程序设计语言可以成功的另一种强大的推动力。同样,Ruby的优秀,除了它优雅的语法外,与它有强大的内建库密不可分。所以,XRuby当然也少不了对于内建库的支持。
正如前面提到的,XRuby为Ruby提供了一条通往Java的道路,Java支持部分就是完成这样的工作。有了这个部分,你就可以在Ruby脚本使用Java的类库,这样,丰富的Java资源可以在Ruby世界中得到重用。
如果这里的讨论引起你对XRuby更多的兴趣,或许你希望更多的了解XRuby,那最好的方式是参与到XRuby的开发中来。
进入XRuby开发最好的起点莫过于内建库,下面是一个简单的内建库中Array#length实现的例子:
@RubyLevelClass(name="Array")
public class RubyArray … {
…
@RubyLevelMethod(name="length")
public RubyFixnum length() {
return ObjectFactory.createFixnum(this.array_.size());
}
…
}
XRuby提供了一个名为@RubyLevelClass的Annotation,它将Java层次的类绑定到Ruby层次的类上。这里可以看到, RubyArray是一个定义在Java层次的类,通过@RubyLevelClass,它被绑定为Ruby层次的Array这个类上。同样, @RubyLevelMethod完成的是将Java层次的方法绑定到Ruby层次上的工作。在上面的例子中,我们用这个Annotation将Java 的length方法绑定到了Ruby的length方法上。这样,在Ruby代码中调用Array类的length方法最终会调用到RubyArray的 length方法上来。
当然,这是一个极为简单的例子,不过,它至少可以说明,XRuby的开发并不像许多人想象的那么复杂。实际上,大多数XRuby的参与者都是从内建库起步的,随着对XRuby了解的增多,再逐渐的参与到其它部分的开发之中,比如动态语言支持和编译器部分等等。
事实上,XRuby欢迎任何形式的贡献,也鼓励大家进行任何尝试,这也是XRuby不断前进的动力。在这里,大家总会有冒出一些新的想法把事情做得更好。你可以选择不同的方式参与XRuby:
参与XRuby这样一个开源编译器项目会有很多乐趣:
Ruby之父松本行弘经常将乐趣挂在嘴边,因为编程本来就该是一件充满乐趣的事。如果你对XRuby有兴趣,那不妨加入我们,和我们一起体验这份开发的乐趣,共同享用JVM上的Ruby!