Rhino Code Reading #0x03
入社二週間目にして社内のネットワーク環境全域に障害を起した岩永ですごめんなさい。
Rhinoのコードリーディング 0x03回目です。
前回はRhinoを用いて、"1+1"というソースコードを読み込み、2を出力するプログラムを作成しました。
今回は文字列を評価してその結果を返す関数「evaluateString」が、内部で行なっていることを疑似コードを用いて解説したいと思います。
私も今回のアリエル育成プログラムで初めて学ぶ分野なため、情報として不正確な内容が含まれているかもしれません。
もし何かお気付きの際は情け容赦ある突っ込みをお待ちしております。
それでは初めます。
public class Context
{
public final Object evaluateString(Scriptable scope, String source,
String sourceName, int lineno,
Object securityDomain)
{
// (1) CompilerEnvironsクラスの初期化
CompilerEnvirons compilerEnv = new CompilerEnvirons();
compilerEnv.initFromContext(this);
// (2) ソースコードの解析
Parser p = new Parser(compilerEnv);
AstRoot ast = p.parse(sourceString, sourceName, lineno);
// (3) 中間コードへの変換
IRFactory irf = new IRFactory(compilerEnv);
ScriptNode tree = irf.transformTree(ast);
// (4) Javaバイトコードの生成
Evaluator compiler = new Codegen();
Object bytecode = compiler.compile(compilerEnv, tree);
// (5) スクリプトオブジェクトの生成・実行
Script = compiler.createScriptObject(bytecode, securityDomain);
return script.exec(this, scope);
}
}
上記がevaluateStringの疑似コードです。
ソースコードを(1)〜(5)までの5項目に分け、順番に解説していきたいと思います。
(1) CompilerEnvironsクラスの初期化
CompilerEnvironsクラスのインスタンスを生成・初期化を行ないます。
CompilerEnvironsクラスはコンパイルの環境情報を保持するためのクラスであり、
保持する項目としては、「JavaScriptのバージョン」・「コンパイル時の最適化レベル」・「警告をエラーとして扱うか」などが上げられます。
(2) ソースコードの解析
ソースコードの解析を行ない抽象構文木(Abstract Syntax Tree、AST)と呼ばれる、「プログラムの構造を表現するための木構造のデータ」を作成します。
Parser.parseが戻り値として返すAstRootクラスは、抽象構文木のルート要素となるクラスです。
Parser.parseの内部では、字句解析(Lexical Analysis)や構文解析(Syntactic Analysis)と呼ばれる処理が行なわれます。
字句解析・構文解析とはそれぞれ、以下のような処理のことを指します。
- 字句解析 - 文字列(ソースコード)を「value(名前)」・「11(リテラル)」・「*(演算子)」などの基本要素(トークン)に分解する処理
- 構文解析 - トークン列から構文木を作成する処理
(3) 中間コードへの変換
(2)で作成した抽象構文木を、中間コードもしくは中間表現(Intermediate Representation)と呼ばれるとソースコードとJavaバイトコードの中間にあたるデータに変換します。
中間コードは抽象構文木と同様の木構造のデータであり、JavadocではIR Tree(Intermediate Representation Tree)と表現がされています。
この抽象構文木から中間コードへの変換の過程には簡単な最適化も行なわれ、"1+1"のような式は数値リテラルの"2"に変換されます。
(4) Javaバイトコードの作成
JVM上で動作させる為のJavaバイトコードを作成します。
Codegen(コンパイラ)クラスのcompileメソッドは、中間コードからJavaバイトコードに翻訳(コンパイル)するメソッドです。
Evaluator型はコンパイラとインタプリンタを抽象化したインターフェイスで、Codegenクラスで実装しています。
疑似コードではCodegenクラスのインスタンスを直接作成していますが、
本来はcreateCompilerという、Evaluator型のオブジェクトを取得するFactory Methodを呼び出します。
(5) Scriptの作成・実行
Scriptオブジェクトを作成、実行します。
Scriptは、スクリプトをJava上から実行するためのexecメソッドを持つインターフェイスです。
Codegen.createScriptObjectはJavaバイトコードを入力にScript型のオブジェクトを返しますが、
実際の型は、CalssLoaderを使用して新たに定義された、Scriptインターフェイスを実装する独自クラスです。
作成されたScriptの実行結果は、evaluateStringの戻り値として呼び出し元へと返されます。
まとめ
evaluateStringを一通り追って、一連の実装流れと、バックグラウンドとなる概念の触りを学習しました。
evaluateStringには「ソースコードの解析・コンパイル・実行」とソースコードをプログラムとして動作させるの過程が詰め込まれています。
その為、evaluateStringを理解することはコンパイラやインタプリタが行なっていることの理解へと繋がり、プログラムに対する理解を深める良い教材になりそうです。
次回に何を書くかは今の所未定です。
- Category(s)
- rhino
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/iwanaga/rhino-code-reading03/tbping