閱讀527 返回首頁    go 阿裏雲 go 技術社區[雲棲]


介紹下XRuby項目

XRuby是什麼?它是一個編譯器。與其它編譯器一樣,它完成的工作是將一種格式的語言轉換成另一種。與大多數編譯器不同的是,它是將Ruby的代碼(.rb)轉換成Java的bytecode(.class)。

Xruby是一群中國開發者維護的項目,它的目的如上所述。它的主頁是https://code.google.com/p/xruby/。與JRuby不同,JRuby一開始是想使用java寫ruby解析器,性能上是個大問題,當然現在也走上了編譯這條路。而XRuby是第一個實現這種想法的人。

我翻譯下了《XRuby Hacking Guide》,這篇文章是XRuby的入門指南。

介紹

這篇文章是為了幫助用戶/開發者理解xruby的內部結構而寫的。

如何編譯ruby?

怎麼將ruby編譯成java字節碼呢?首先,你不必成為一名字節碼方麵的專家來考慮這個問題,Java的字節碼是對原生機器語言的較高層次的抽象,非常類似於java源代碼。你可以簡化這個問題為:如何用java表示一段ruby程序?

這兩門語言有很多的相同之處:ruby是一門OO語言,它有類、方法、變量等,java也是如此。這是否意味著我們可以將一個ruby類類比為java類,ruby方法作為java方法?可是,除了這些相同之處外,它們之間有足夠的不同點讓你打消這個主意:首先,ruby是一門動態類型語言,因此一個方法可以接受不同類型的參數,而在java中,參數類型是方法簽名(signature)的一部分。其次,在ruby中,方法可以從一個類中動態地添加和移除;但是目前的JVM並不支持這樣的行為。值的注意的上述的這些問題也許會在將來的JVM版本中解決,請參考Gilad Bracha's work at JSR 292.

第一個辦法是我們自己維護一個類型係統,這正是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

當然,你將看到下麵的輸出:

hello
hello
hello
如果你查看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, nullnull"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), nullnew BLOCK._cls1(), "times");
    }

    
public say_hello_three_times$0()
    {
        
super(0false);
    }
}


//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()
    {
        
super(0false);
    }
}

在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'"的實現。

源碼結構

  • com.xruby.compiler.parser 提供了一個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)。
  • com.xruby.compiler.codedom 定義了描述ruby腳本結構的內部結構(internal structure)。內部結構作為前端和後端的接口,對於xruby是非常重要的。
  • com.xruby.compiler.codegen 實現了編譯器的後端(代碼生成)。後端將前端生成的內部結構轉換為java字節碼。代碼生成是通過ASM實現的,ASM簡化了對字節碼的操作。
  • com.xruby.runtime 實現了xruby運行時(runtime),它維護著運行ruby腳本必需的類型係統, com.xruby.runtime.lang 描述了ruby類型的運行時結構,一些ruby內建標準庫實現在 com.xruby.runtime.builtin.

內建庫

通往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());
    }
}

dot.gif
RubyClass c 
= RubyRuntime.GlobalScope.defineNewClass("Fixnum", RubyRuntime.IntegerClass);
c.defineMethod(
"+"new Fixnum_operator_plus());
dot.gif

XRuby的語法解析器

Xruby的解析器使用 Antlr 作為解析器的生成器。 這是目前相比於c ruby唯一另類的ruby語法。

對於大部分編程語言來說,詞法分析(lexing)和語法解析是兩個不同的步驟:首先詞法分析器將輸入的字符組織成單詞(token),然後解析器將單詞組織成句法單元。但是在ruby(和perl語言)中,詞法分析器和語法解析器是緊緊地耦合在一起的:有時候詞法分析器需要從語法分析器中獲取上下文信息。

疑難解決

作為Xruby的開發者,我們的任何改變都可能導致編譯器出錯並且生成有問題的字節碼。當這種情況發生時,我們可以依賴3樣工具:javap,ASM和你所喜歡的java反編譯器(比如jad

如果生成的class文件格式正確但是運行結果不是預期的,我們可以簡單地使用反編譯工具將字節碼轉換成可讀的java源代碼,以便查找錯誤。

如果你遇到是一個verifier error,大部分的反編譯器都不能正常工作(jad在這種情況也許會crash掉)。我們不得不使用javap來研讀字節碼。多數情況下,JVM class驗證器(verifier)給出的信息沒什麼用處,但是我們可以通過ASM更快地找到錯誤發生點。(see ASM FAQ: Why do I get the [xxx] verifier error?).

文章轉自莊周夢蝶  ,原文發布時間5.17

最後更新:2017-05-17 12:02:09

  上一篇:go  從Oracle遷移到MySQL的各種坑及自救方案
  下一篇:go  對MarshalByRefObject的解釋