JRuby/コンパイル処理を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*はじめに [#weec78da]
スクリプト解析処理まで進んだのでいよいよJavaバイトコード...
*org.jruby.Ruby [#z56c5253]
**runNormallyメソッド [#ua202c8a]
スクリプトの解析が終わると解析結果のNodeを引数にrunNormal...
**tryCompileメソッド [#hcccbf4c]
tryCompileメソッドはいくつかありますが順に追っていくとコ...
JRubyのRuby互換バージョンが1.9の場合はASTCompiler19クラス...
*org.jruby.compiler.ASTCompiler [#t8e1884f]
**compileRootメソッド [#v0b7ba19]
compileRootメソッドに移ります。まずStandardASMCompilerイ...
compileRootメソッドでは以下の処理が行われています。
+開始処理(ScriptCompiler.startScriptメソッド)
+トップレベルのコードをメソッドとしてコンパイル
++BodyCompilerインターフェースの取得(実際に返されるのはo...
++RootNodeの子要素に対してcompileメソッドを適用
+終了処理(ScriptCompiler.endScriptメソッド)
**compileメソッド [#jbcb30fb]
compileメソッドは巨大なswitch文です。Nodeの種類に応じてco...
*org.ruby.compiler.impl.StandardASMCompiler [#a57779c0]
**startScriptメソッド [#j9370ddd]
クラスの生成には[[ASM>http://asm.ow2.org/]]が利用されてい...
***余談:staticインポート [#s169074e]
ところでstartScriptメソッドを見ているとpとかsigというメソ...
**startFileMethodメソッド [#t0a7e94f]
このメソッドではスクリプトのトップレベルに書かれているコ...
**endScriptメソッド [#m2323e46]
このメソッドでは引数により生成するクラスにloadメソッドとm...
*コンパイルしてみる [#a148822c]
それでは[[スクリプト解析を読む>JRuby/スクリプト解析を読む...
**MethodBodyCompiler.beginMethodメソッド [#i8e085dd]
先ほど説明したstartFileMethodメソッド中でMethodBodyCompil...
#code(Java){{
public class MethodBodyCompiler extends RootScopedBodyCom...
public void beginMethod(CompilerCallback args, Static...
method.start();
variableCompiler.beginMethod(args, scope);
// visit a label to start scoping for local vars ...
method.label(scopeStart);
}
}
}}
variableCompilerはスーパークラスであるBaseBodyCompilerの...
#code(Java){{
public abstract class BaseBodyCompiler implements BodyCom...
public BaseBodyCompiler(StandardASMCompiler scriptCom...
this.script = scriptCompiler;
this.scope = scope;
this.inspector = inspector;
this.methodName = methodName;
this.rubyName = rubyName;
this.argParamCount = getActualArgsCount(scope);
method = new SkinnyMethodAdapter(script.getClassV...
createVariableCompiler();
invocationCompiler = OptoFactory.newInvocationCom...
this.scopeIndex = scopeIndex;
}
}
}}
委譲されてMethodBodyCompilerに戻ってきます。
#code(Java){{
public class MethodBodyCompiler extends RootScopedBodyCom...
protected void createVariableCompiler() {
if (inspector == null) {
variableCompiler = new HeapBasedVariableCompi...
} else if (inspector.hasClosure() || inspector.ha...
variableCompiler = new HeapBasedVariableCompi...
} else {
variableCompiler = new StackBasedVariableComp...
}
}
}
public abstract class BaseBodyCompiler implements BodyCom...
protected int getFirstTempIndex() {
// さわだ追記:ARGS_INDEX = 3, argParamCount = 1,...
return StandardASMCompiler.ARGS_INDEX + argParamC...
}
}
}}
トップレベルではクロージャ(ブロック)もscope awareなメソ...
で、StackBasedVariableCompiler.beginMethodメソッド。
#code(Java){{
public class StackBasedVariableCompiler extends AbstractV...
public StackBasedVariableCompiler(
BaseBodyCompiler methodCompiler,
SkinnyMethodAdapter method,
StaticScope scope,
boolean specificArity,
int argsIndex,
int firstTempIndex) {
super(methodCompiler, method, scope, specificArit...
this.baseVariableIndex = firstTempIndex;
}
public void beginMethod(CompilerCallback argsCallback...
// fill in all vars with nil so compiler is happy...
if (scope.getNumberOfVariables() > 0) {
// if we don't have opt args, start after arg...
// this is for crap like def foo(a = (b = tru...
// FIXME: only starting after required args, ...
// and rest args conflicts with compileRoot u...
if (scope.getRequiredArgs() < scope.getNumber...
int start = scope.getRequiredArgs();
methodCompiler.loadNil();
for (int i = start; i < scope.getNumberOf...
if (i + 1 < scope.getNumberOfVariable...
assignLocalVariable(i, false);
}
}
// temp locals must start after last real local
tempVariableIndex += scope.getNumberOfVariabl...
}
if (argsCallback != null) {
argsCallback.call(methodCompiler);
}
}
public void assignLocalVariable(int index, boolean ex...
if (expr) {
method.dup();
}
method.astore(baseVariableIndex + index);
}
}
}}
あちこち飛びますがloadNilメソッドは以下の通りです。
#code(Java){{
public class MethodBodyCompiler extends RootScopedBodyCom...
public void loadNil() {
loadThreadContext();
if (Options.COMPILE_INVOKEDYNAMIC.load()) {
method.invokedynamic("nil", sig(IRubyObject.c...
} else {
method.getfield(p(ThreadContext.class), "nil"...
}
}
public void loadThreadContext() {
method.aload(StandardASMCompiler.THREADCONTEXT_IN...
}
}
}}
何をしているかというと、ローカル変数にnilを代入するという...
ALOAD 1 // ThreadContextロード
GETFIELD org/jruby/runtime/ThreadContext.nil : Lorg/jrub...
DUP // 取得したnilを複製
ASTORE 9 // ローカル変数nにnilを設定
ASTORE 10 // ローカル変数piにnilを設定
**ASTCompiler.compileClassメソッド [#y557aa83]
さてローカル変数の設定ができたのでNodeのコンパイルに取り...
defineClassメソッドはけっこう長いですがやっていることは以...
+JRuby的にRubyクラスを定義(RubyModule.defineOrGetClassUn...
+クラス定義文に対応するメソッドの呼び出し(先頭で定義して...
+クラス定義文に対応するメソッドのコンパイル(ClassBodyCom...
**ASTCompiler.compileDefnメソッド [#ofe264c0]
クラス定義本体のNodeをたどっていくとDefnNodeに行き当たり...
defineNewMethodメソッドでは以下のことが行われています。
+JRuby的にメソッドの登録(RuntimeHelpers.defの呼び出しま...
+メソッド定義のコンパイル(BodyCompiler取得しているところ...
**ASTCompiler19.compileArgsメソッド [#w59faf6e]
メソッド定義用のBodyCompilerを生成する際にVariableCompile...
ASTCompiler19.compileArgsメソッドはcompileMethodArgsメソ...
Rubyには引数の種類が複数あるので引数設定処理は単純ではあ...
def bar
2
end
def foo(a, b = 1, c = bar) # 実はデフォルト値としてメソ...
end
このfooメソッドの引数設定処理は以下のようになります。
// 引数aの設定
ALOAD 3
ICONST_0
ALOAD 1
GETFIELD org/jruby/runtime/ThreadContext.nil : Lorg/jru...
INVOKESTATIC org/jruby/javasupport/util/RuntimeHelpers....
ASTORE 9
// 引数bの設定
// 2つ目の引数が渡されているかのチェック
ALOAD 3
ICONST_1
ICONST_0
INVOKESTATIC org/jruby/javasupport/util/RuntimeHelpers....
DUP
// 2つ目の引数が渡されてない場合はデフォルト値設定処...
IFNULL L1
// 渡されている場合はそれを設定
ASTORE 10
// 引数cの設定
// 3つ目の引数が渡されているかのチェック
ALOAD 3
ICONST_2
ICONST_0
INVOKESTATIC org/jruby/javasupport/util/RuntimeHelpers....
DUP
// 2つ目の引数が渡されてない場合はデフォルト値設定処...
IFNULL L2
// 渡されている場合はそれを設定
ASTORE 11
// 引数が3つ渡されているのでデフォルト値設定処理をス...
GOTO L3
// 引数bのデフォルト値設定処理
L1
FRAME FULL [opt org/jruby/runtime/ThreadContext org/jrub...
ALOAD 1
GETFIELD org/jruby/runtime/ThreadContext.runtime : Lorg...
INVOKESTATIC org/jruby/RubyFixnum.one (Lorg/jruby/Ruby;...
ASTORE 10
// 引数cのデフォルト値設定処理
L2
FRAME SAME1 org/jruby/runtime/builtin/IRubyObject
ALOAD 0
INVOKEVIRTUAL opt.getCallSite0 ()Lorg/jruby/runtime/Cal...
ALOAD 1
ALOAD 2
ALOAD 2
INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jru...
ASTORE 11
今回対象としているスクリプトではメソッドの引数はシンプル...
ALOAD 3 // 1つ目の引数ロード
ALOAD 5 // DynamicScopeロード
SWAP
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.setValueZer...
**ASTCompiler.compileCallメソッド [#d4068719]
メソッド内に入って1つ目にあるのはLocalAsgnNodeですがまあ...
invokeDynamicメソッドではレシーバのコンパイル、引数のコン...
**ASTCompiler19.compileIterメソッド [#e293e94d]
IterNodeに対応するcompileIterメソッドもASTCompiler19クラ...
createNewClosure19メソッドではBlockBodyクラスとブロックを...
**ASTCompiler.compileIfメソッド [#rf971c6d]
ブロックに入るとDAsgnNodeがあります。もっともそのブロック...
IfNodeを処理するcompileIfではいくつか最適化が行われていま...
通常はBodyCompiler.performBooleanBranch2メソッドに処理が...
条件部で生成されるコードがちょっとわかりにくいので解説し...
condition=CallOneArgNode
receiverNode=CallOneArgNode
receiverNode=CallOneArgNode
receiverNode=DVarNode
name='x'
location=0
name='*'
arg1=DVarNode
name='x'
location=0
name='+'
arg1=CallOneArgNode
receiverNode=CallOneArgNode
receiverNode=DVarNode
name='y'
location=1
name='*'
arg1=DVarNode
name='y'
location=1
name='<='
arg1=FixNumNode
value=1
で、生成されるコードはこんな感じです。
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite3
ALOAD 1
ALOAD 2
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite4
ALOAD 1
ALOAD 2
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite5
ALOAD 1
ALOAD 2
ALOAD 9
ALOAD 9
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite6
ALOAD 1
ALOAD 2
ALOAD 10
ALOAD 10
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
LDC 1
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
何回もALOADされててわかりにくいですがStandardInvocationCo...
+CallSiteのキャッシュ(ALOAD 0とINVOKEVIRTUAL montecarlo.g...
+ThreadContextのロード(ALOAD 1)
+selfのロード(ALOAD 2)
+引数の処理
+Rubyメソッドの呼び出し(INVOKEVIRTUAL CallSite.call
となっています。それを考えると、
ALOAD 0 ----------+
INVOKEVIRTUAL montecarlo.getCallSite3 |
ALOAD 1 |
ALOAD 2 |
ALOAD 0 --------+ |
INVOKEVIRTUAL montecarlo.getCallSite4 | |
ALOAD 1 | |
ALOAD 2 | |
ALOAD 0 -+ | |
INVOKEVIRTUAL montecarlo.getCallSite5 | | |
ALOAD 1 | | |
ALOAD 2 |x * x | |
ALOAD 9 | | |
ALOAD 9 | | |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call -+ |x...
ALOAD 0 -+ | |
INVOKEVIRTUAL montecarlo.getCallSite6 | | ...
ALOAD 1 | | |
ALOAD 2 |y * y | |
ALOAD 10 | | |
ALOAD 10 | | |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call -+ | |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call --------+ |
LDC 1 |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call ----------+
という入れ子関係になっていることがわかります。ちなみに、...
**ASTCompiler.compileLocalAsgnメソッド、compileLocalVarメ...
さて次にthen節のコンパイル処理を見ていきましょう。「count...
thenBody=LocalAsgnNode
name='count'
location=1
depth=1
valueNode=CallOneArgNode
receiverNode=LocalVarNode
name='count'
location=1
depth=1
name='+'
arg1=FixNumNode
value=1
先ほどはLocalAsgnNodeは面白くないと飛ばしましたがそれは通...
StackBasedVariableCompilerのassignLocalVariableメソッドは...
#code(Java){{
public void assignLocalVariable(int index, int depth, Co...
if (depth == 0) {
assignLocalVariable(index, value, expr);
} else {
assignHeapLocal(value, depth, index, expr);
}
}
}}
assignHeapLocalメソッドはAbstractVariableCompilerクラスに...
#code(Java){{
protected void assignHeapLocal(CompilerCallback value, in...
switch (index) {
case 0:
unwrapParentScopes(depth);
value.call(methodCompiler);
method.invokevirtual(p(DynamicScope.class), "setV...
break;
...
}
...
}
protected void unwrapParentScopes(int depth) {
// unwrap scopes to appropriate depth
method.aload(methodCompiler.getDynamicScopeIndex());
while (depth > 0) {
method.invokevirtual(p(DynamicScope.class), "getN...
depth--;
}
}
}}
というわけでdepth分スコープをさかのぼる処理をした後、その...
ALOAD 5 ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.getNextCapt...
ALOAD 0 ...
INVOKEVIRTUAL montecarlo.getCallSite7 ...
ALOAD 1 ...
ALOAD 2 ...
ALOAD 5 ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.getNextCapt...
ALOAD 1 ...
GETFIELD org/jruby/runtime/ThreadContext.nil ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.getValueOne...
LDC 1 ...
INVOKEVIRTUAL org/jruby/runtime/CallSite.call ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.setValueOne...
ここまで読めば後は大体わかるでしょう。
*再度org.jruby.Ruby [#u7d69444]
**runScriptメソッド [#i1b27b12]
これでNodeからJavaバイトコードへの変換が終了しました。Rub...
*おわりに [#pc77250f]
今回はスクリプト解析で作成したNodeをJavaバイトコードに変...
ということがわかるようになりました。
さて、ここまででバイトコードに変換され後は実行するだけな...
終了行:
#contents
*はじめに [#weec78da]
スクリプト解析処理まで進んだのでいよいよJavaバイトコード...
*org.jruby.Ruby [#z56c5253]
**runNormallyメソッド [#ua202c8a]
スクリプトの解析が終わると解析結果のNodeを引数にrunNormal...
**tryCompileメソッド [#hcccbf4c]
tryCompileメソッドはいくつかありますが順に追っていくとコ...
JRubyのRuby互換バージョンが1.9の場合はASTCompiler19クラス...
*org.jruby.compiler.ASTCompiler [#t8e1884f]
**compileRootメソッド [#v0b7ba19]
compileRootメソッドに移ります。まずStandardASMCompilerイ...
compileRootメソッドでは以下の処理が行われています。
+開始処理(ScriptCompiler.startScriptメソッド)
+トップレベルのコードをメソッドとしてコンパイル
++BodyCompilerインターフェースの取得(実際に返されるのはo...
++RootNodeの子要素に対してcompileメソッドを適用
+終了処理(ScriptCompiler.endScriptメソッド)
**compileメソッド [#jbcb30fb]
compileメソッドは巨大なswitch文です。Nodeの種類に応じてco...
*org.ruby.compiler.impl.StandardASMCompiler [#a57779c0]
**startScriptメソッド [#j9370ddd]
クラスの生成には[[ASM>http://asm.ow2.org/]]が利用されてい...
***余談:staticインポート [#s169074e]
ところでstartScriptメソッドを見ているとpとかsigというメソ...
**startFileMethodメソッド [#t0a7e94f]
このメソッドではスクリプトのトップレベルに書かれているコ...
**endScriptメソッド [#m2323e46]
このメソッドでは引数により生成するクラスにloadメソッドとm...
*コンパイルしてみる [#a148822c]
それでは[[スクリプト解析を読む>JRuby/スクリプト解析を読む...
**MethodBodyCompiler.beginMethodメソッド [#i8e085dd]
先ほど説明したstartFileMethodメソッド中でMethodBodyCompil...
#code(Java){{
public class MethodBodyCompiler extends RootScopedBodyCom...
public void beginMethod(CompilerCallback args, Static...
method.start();
variableCompiler.beginMethod(args, scope);
// visit a label to start scoping for local vars ...
method.label(scopeStart);
}
}
}}
variableCompilerはスーパークラスであるBaseBodyCompilerの...
#code(Java){{
public abstract class BaseBodyCompiler implements BodyCom...
public BaseBodyCompiler(StandardASMCompiler scriptCom...
this.script = scriptCompiler;
this.scope = scope;
this.inspector = inspector;
this.methodName = methodName;
this.rubyName = rubyName;
this.argParamCount = getActualArgsCount(scope);
method = new SkinnyMethodAdapter(script.getClassV...
createVariableCompiler();
invocationCompiler = OptoFactory.newInvocationCom...
this.scopeIndex = scopeIndex;
}
}
}}
委譲されてMethodBodyCompilerに戻ってきます。
#code(Java){{
public class MethodBodyCompiler extends RootScopedBodyCom...
protected void createVariableCompiler() {
if (inspector == null) {
variableCompiler = new HeapBasedVariableCompi...
} else if (inspector.hasClosure() || inspector.ha...
variableCompiler = new HeapBasedVariableCompi...
} else {
variableCompiler = new StackBasedVariableComp...
}
}
}
public abstract class BaseBodyCompiler implements BodyCom...
protected int getFirstTempIndex() {
// さわだ追記:ARGS_INDEX = 3, argParamCount = 1,...
return StandardASMCompiler.ARGS_INDEX + argParamC...
}
}
}}
トップレベルではクロージャ(ブロック)もscope awareなメソ...
で、StackBasedVariableCompiler.beginMethodメソッド。
#code(Java){{
public class StackBasedVariableCompiler extends AbstractV...
public StackBasedVariableCompiler(
BaseBodyCompiler methodCompiler,
SkinnyMethodAdapter method,
StaticScope scope,
boolean specificArity,
int argsIndex,
int firstTempIndex) {
super(methodCompiler, method, scope, specificArit...
this.baseVariableIndex = firstTempIndex;
}
public void beginMethod(CompilerCallback argsCallback...
// fill in all vars with nil so compiler is happy...
if (scope.getNumberOfVariables() > 0) {
// if we don't have opt args, start after arg...
// this is for crap like def foo(a = (b = tru...
// FIXME: only starting after required args, ...
// and rest args conflicts with compileRoot u...
if (scope.getRequiredArgs() < scope.getNumber...
int start = scope.getRequiredArgs();
methodCompiler.loadNil();
for (int i = start; i < scope.getNumberOf...
if (i + 1 < scope.getNumberOfVariable...
assignLocalVariable(i, false);
}
}
// temp locals must start after last real local
tempVariableIndex += scope.getNumberOfVariabl...
}
if (argsCallback != null) {
argsCallback.call(methodCompiler);
}
}
public void assignLocalVariable(int index, boolean ex...
if (expr) {
method.dup();
}
method.astore(baseVariableIndex + index);
}
}
}}
あちこち飛びますがloadNilメソッドは以下の通りです。
#code(Java){{
public class MethodBodyCompiler extends RootScopedBodyCom...
public void loadNil() {
loadThreadContext();
if (Options.COMPILE_INVOKEDYNAMIC.load()) {
method.invokedynamic("nil", sig(IRubyObject.c...
} else {
method.getfield(p(ThreadContext.class), "nil"...
}
}
public void loadThreadContext() {
method.aload(StandardASMCompiler.THREADCONTEXT_IN...
}
}
}}
何をしているかというと、ローカル変数にnilを代入するという...
ALOAD 1 // ThreadContextロード
GETFIELD org/jruby/runtime/ThreadContext.nil : Lorg/jrub...
DUP // 取得したnilを複製
ASTORE 9 // ローカル変数nにnilを設定
ASTORE 10 // ローカル変数piにnilを設定
**ASTCompiler.compileClassメソッド [#y557aa83]
さてローカル変数の設定ができたのでNodeのコンパイルに取り...
defineClassメソッドはけっこう長いですがやっていることは以...
+JRuby的にRubyクラスを定義(RubyModule.defineOrGetClassUn...
+クラス定義文に対応するメソッドの呼び出し(先頭で定義して...
+クラス定義文に対応するメソッドのコンパイル(ClassBodyCom...
**ASTCompiler.compileDefnメソッド [#ofe264c0]
クラス定義本体のNodeをたどっていくとDefnNodeに行き当たり...
defineNewMethodメソッドでは以下のことが行われています。
+JRuby的にメソッドの登録(RuntimeHelpers.defの呼び出しま...
+メソッド定義のコンパイル(BodyCompiler取得しているところ...
**ASTCompiler19.compileArgsメソッド [#w59faf6e]
メソッド定義用のBodyCompilerを生成する際にVariableCompile...
ASTCompiler19.compileArgsメソッドはcompileMethodArgsメソ...
Rubyには引数の種類が複数あるので引数設定処理は単純ではあ...
def bar
2
end
def foo(a, b = 1, c = bar) # 実はデフォルト値としてメソ...
end
このfooメソッドの引数設定処理は以下のようになります。
// 引数aの設定
ALOAD 3
ICONST_0
ALOAD 1
GETFIELD org/jruby/runtime/ThreadContext.nil : Lorg/jru...
INVOKESTATIC org/jruby/javasupport/util/RuntimeHelpers....
ASTORE 9
// 引数bの設定
// 2つ目の引数が渡されているかのチェック
ALOAD 3
ICONST_1
ICONST_0
INVOKESTATIC org/jruby/javasupport/util/RuntimeHelpers....
DUP
// 2つ目の引数が渡されてない場合はデフォルト値設定処...
IFNULL L1
// 渡されている場合はそれを設定
ASTORE 10
// 引数cの設定
// 3つ目の引数が渡されているかのチェック
ALOAD 3
ICONST_2
ICONST_0
INVOKESTATIC org/jruby/javasupport/util/RuntimeHelpers....
DUP
// 2つ目の引数が渡されてない場合はデフォルト値設定処...
IFNULL L2
// 渡されている場合はそれを設定
ASTORE 11
// 引数が3つ渡されているのでデフォルト値設定処理をス...
GOTO L3
// 引数bのデフォルト値設定処理
L1
FRAME FULL [opt org/jruby/runtime/ThreadContext org/jrub...
ALOAD 1
GETFIELD org/jruby/runtime/ThreadContext.runtime : Lorg...
INVOKESTATIC org/jruby/RubyFixnum.one (Lorg/jruby/Ruby;...
ASTORE 10
// 引数cのデフォルト値設定処理
L2
FRAME SAME1 org/jruby/runtime/builtin/IRubyObject
ALOAD 0
INVOKEVIRTUAL opt.getCallSite0 ()Lorg/jruby/runtime/Cal...
ALOAD 1
ALOAD 2
ALOAD 2
INVOKEVIRTUAL org/jruby/runtime/CallSite.call (Lorg/jru...
ASTORE 11
今回対象としているスクリプトではメソッドの引数はシンプル...
ALOAD 3 // 1つ目の引数ロード
ALOAD 5 // DynamicScopeロード
SWAP
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.setValueZer...
**ASTCompiler.compileCallメソッド [#d4068719]
メソッド内に入って1つ目にあるのはLocalAsgnNodeですがまあ...
invokeDynamicメソッドではレシーバのコンパイル、引数のコン...
**ASTCompiler19.compileIterメソッド [#e293e94d]
IterNodeに対応するcompileIterメソッドもASTCompiler19クラ...
createNewClosure19メソッドではBlockBodyクラスとブロックを...
**ASTCompiler.compileIfメソッド [#rf971c6d]
ブロックに入るとDAsgnNodeがあります。もっともそのブロック...
IfNodeを処理するcompileIfではいくつか最適化が行われていま...
通常はBodyCompiler.performBooleanBranch2メソッドに処理が...
条件部で生成されるコードがちょっとわかりにくいので解説し...
condition=CallOneArgNode
receiverNode=CallOneArgNode
receiverNode=CallOneArgNode
receiverNode=DVarNode
name='x'
location=0
name='*'
arg1=DVarNode
name='x'
location=0
name='+'
arg1=CallOneArgNode
receiverNode=CallOneArgNode
receiverNode=DVarNode
name='y'
location=1
name='*'
arg1=DVarNode
name='y'
location=1
name='<='
arg1=FixNumNode
value=1
で、生成されるコードはこんな感じです。
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite3
ALOAD 1
ALOAD 2
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite4
ALOAD 1
ALOAD 2
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite5
ALOAD 1
ALOAD 2
ALOAD 9
ALOAD 9
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
ALOAD 0
INVOKEVIRTUAL montecarlo.getCallSite6
ALOAD 1
ALOAD 2
ALOAD 10
ALOAD 10
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
LDC 1
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
何回もALOADされててわかりにくいですがStandardInvocationCo...
+CallSiteのキャッシュ(ALOAD 0とINVOKEVIRTUAL montecarlo.g...
+ThreadContextのロード(ALOAD 1)
+selfのロード(ALOAD 2)
+引数の処理
+Rubyメソッドの呼び出し(INVOKEVIRTUAL CallSite.call
となっています。それを考えると、
ALOAD 0 ----------+
INVOKEVIRTUAL montecarlo.getCallSite3 |
ALOAD 1 |
ALOAD 2 |
ALOAD 0 --------+ |
INVOKEVIRTUAL montecarlo.getCallSite4 | |
ALOAD 1 | |
ALOAD 2 | |
ALOAD 0 -+ | |
INVOKEVIRTUAL montecarlo.getCallSite5 | | |
ALOAD 1 | | |
ALOAD 2 |x * x | |
ALOAD 9 | | |
ALOAD 9 | | |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call -+ |x...
ALOAD 0 -+ | |
INVOKEVIRTUAL montecarlo.getCallSite6 | | ...
ALOAD 1 | | |
ALOAD 2 |y * y | |
ALOAD 10 | | |
ALOAD 10 | | |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call -+ | |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call --------+ |
LDC 1 |
INVOKEVIRTUAL org/jruby/runtime/CallSite.call ----------+
という入れ子関係になっていることがわかります。ちなみに、...
**ASTCompiler.compileLocalAsgnメソッド、compileLocalVarメ...
さて次にthen節のコンパイル処理を見ていきましょう。「count...
thenBody=LocalAsgnNode
name='count'
location=1
depth=1
valueNode=CallOneArgNode
receiverNode=LocalVarNode
name='count'
location=1
depth=1
name='+'
arg1=FixNumNode
value=1
先ほどはLocalAsgnNodeは面白くないと飛ばしましたがそれは通...
StackBasedVariableCompilerのassignLocalVariableメソッドは...
#code(Java){{
public void assignLocalVariable(int index, int depth, Co...
if (depth == 0) {
assignLocalVariable(index, value, expr);
} else {
assignHeapLocal(value, depth, index, expr);
}
}
}}
assignHeapLocalメソッドはAbstractVariableCompilerクラスに...
#code(Java){{
protected void assignHeapLocal(CompilerCallback value, in...
switch (index) {
case 0:
unwrapParentScopes(depth);
value.call(methodCompiler);
method.invokevirtual(p(DynamicScope.class), "setV...
break;
...
}
...
}
protected void unwrapParentScopes(int depth) {
// unwrap scopes to appropriate depth
method.aload(methodCompiler.getDynamicScopeIndex());
while (depth > 0) {
method.invokevirtual(p(DynamicScope.class), "getN...
depth--;
}
}
}}
というわけでdepth分スコープをさかのぼる処理をした後、その...
ALOAD 5 ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.getNextCapt...
ALOAD 0 ...
INVOKEVIRTUAL montecarlo.getCallSite7 ...
ALOAD 1 ...
ALOAD 2 ...
ALOAD 5 ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.getNextCapt...
ALOAD 1 ...
GETFIELD org/jruby/runtime/ThreadContext.nil ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.getValueOne...
LDC 1 ...
INVOKEVIRTUAL org/jruby/runtime/CallSite.call ...
INVOKEVIRTUAL org/jruby/runtime/DynamicScope.setValueOne...
ここまで読めば後は大体わかるでしょう。
*再度org.jruby.Ruby [#u7d69444]
**runScriptメソッド [#i1b27b12]
これでNodeからJavaバイトコードへの変換が終了しました。Rub...
*おわりに [#pc77250f]
今回はスクリプト解析で作成したNodeをJavaバイトコードに変...
ということがわかるようになりました。
さて、ここまででバイトコードに変換され後は実行するだけな...
ページ名: