xruby是一群中国开发者维护的项目,它的目的如上所述。它的主页是http://code.google.com/p/xruby/。与jruby不同,jruby一开始是想使用java写ruby解析器,性能上是个大问题,当然现在也走上了编译这条路。而xruby是第一个实现这种想法的人。
我翻译下了《xruby hacking guide》,这篇文章是xruby的入门指南。
这篇文章是为了帮助用户/开发者理解xruby的内部结构而写的。
怎么将ruby编译成java字节码呢?首先,你不必成为一名字节码方面的专家来考虑这个问题,java的字节码是对原生机器语言的较高层次的抽象,非常类似于java源代码。你可以简化这个问题为:如何用java表示一段ruby程序?
第一个办法是我们自己维护一个类型系统,这正是xruby目前采用的办法(ruby.net好像也是如此)。从jvm的角度看,一个ruby类只是一个object,这个object中包含着代表方法等的其他object。我们将在后面更多讨论这点。
另一个办法是动态地编译(ruby)源代码,在运行时获得类型信息,将源代码编译成高效的代码(字节码?)是可能的。(一些方法由于duct typeing的特性将被编译成好几个版本)
我们将比较这两个办法,
通过一个例子来了解xruby:
def say_hello_three_times
3.times {puts 'hello'}
end
say_hello_three_times
将上面的代码存为test.rb,使用xruby编译(下载的xruby解压后运行build.bat或者build.sh生成xruby-0.1.3.jar):
java -jar xruby-0.1.3.jar -c test.rb
可以看到生成了一个test.jar文件,执行下面的命令来运行这个程序:
java -jar test.jar
当然,你将看到下面的输出:
如果你查看test.jar文件,你将看到以下3个class文件:
test/block$1.class
test/say_hello_three_times$0.class
test/main.class
这些class文件等价于下面这段java程序:
//test/main.class
public class main
implements rubyprogram
{
public main()
{
}
public static void main(string args[])
rubyruntime.init(args);
(new main()).run();
rubyruntime.fini();
public rubyvalue run()
rubyruntime.objectclass.definemethod("say_hello_three_times", new say_hello_three_times._cls0());
return rubyruntime.callmethod(objectfactory.toplevelselfvalue, null, null, "say_hello_three_times");
}
//say_hello_three_times$0.class
class say_hello_three_times$0 extends rubymethod
protected rubyvalue run(rubyvalue rubyvalue, rubyarray arrayvalue, rubyblock rubyblock)
return rubyruntime.callpublicmethod(objectfactory.createfixnum(3), null, new block._cls1(), "times");
public say_hello_three_times$0()
super(0, false);
//test/block$1.class
class block$1 extends rubyblock
protected rubyvalue run(rubyvalue rubyvalue, rubyarray arrayvalue)
rubyarray arrayvalue1 = new rubyarray(1);
arrayvalue1.add(objectfactory.createstring("hello"));
return rubyruntime.callmethod(rubyvalue, arrayvalue1, null, "puts");
public block$1()
在main类中:首先在"object"类中定义了一个私有的方法"say_hello_three_times",然后通过no parameter, no block和一个top level "self"作为接收者的方式调用这个方法。
"say_hello_three_times$0"类表示say_hello_three_times方法的实现(参考command模式)。在代码中,我们可以看到fixnum"3"(接收者)调用了"timer"这个方法,仍然没有parameter,但是有一个block被传进去(方法)。
block$1类则表示传进"3.times"方法中的block,代码中是"puts 'hello'"的实现。
<b>com.xruby.compiler.parser</b> 提供了一个compiler前端(parser and tree parser)。 parser转换ruby脚本成ast (abstract syntax tree),然后 tree parser将ast转换为内部结构(internal structure)。
编译器前端使用 antlr 作为语法分析器的生成器.实践证明,将这个前端分为两部分可以带来好处:parser 和 tree parser;其中 parser 解析脚本,而tree parser生成内部结构(internal structure)。
<b>com.xruby.compiler.codedom</b> 定义了描述ruby脚本结构的内部结构(internal structure)。内部结构作为前端和后端的接口,对于xruby是非常重要的。
<b>com.xruby.compiler.codegen</b> 实现了编译器的后端(代码生成)。后端将前端生成的内部结构转换为java字节码。代码生成是通过asm实现的,asm简化了对字节码的操作。
<b>com.xruby.runtime</b> 实现了xruby运行时(runtime),它维护着运行ruby脚本必需的类型系统, <b>com.xruby.runtime.lang</b> 描述了ruby类型的运行时结构,一些ruby内建标准库实现在 <b>com.xruby.runtime.builtin</b>.
通往xuby hacking之路最简便的办法就是学习 'com.xruby.runtime.builtin'包的源代码。
下面是来自fixnum::+方法实现的代码片段:
class fixnum_operator_plus extends rubymethod {
public fixnum_operator_plus() {
super(1);
protected rubyvalue run(rubyvalue receiver, rubyarray args, rubyblock block) {
rubyfixnum value1 = (rubyfixnum)receiver.getvalue();
rubyfixnum value2 = (rubyfixnum)args.get(0).getvalue();
return objectfactory.createfixnum(value1.intvalue() + value2.intvalue());
rubyclass c = rubyruntime.globalscope.definenewclass("fixnum", rubyruntime.integerclass);
c.definemethod("+", new fixnum_operator_plus());
对于大部分编程语言来说,词法分析(lexing)和语法解析是两个不同的步骤:首先词法分析器将输入的字符组织成单词(token),然后解析器将单词组织成句法单元。但是在ruby(和perl语言)中,词法分析器和语法解析器是紧紧地耦合在一起的:有时候词法分析器需要从语法分析器中获取上下文信息。
如果生成的class文件格式正确但是运行结果不是预期的,我们可以简单地使用反编译工具将字节码转换成可读的java源代码,以便查找错误。
文章转自庄周梦蝶 ,原文发布时间5.17