cast_off 0.3.7 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +81 -513
- data/README.ja +177 -0
- data/bin/cast_off +12 -37
- data/cast_off.gemspec +6 -3
- data/ext/cast_off/cast_off.c.rb +186 -116
- data/ext/cast_off/generated_c_include/inline_api.h +0 -165
- data/ext/cast_off/generated_c_include/unbox_api.h.rb +1 -1
- data/lib/cast_off.rb +1 -0
- data/lib/cast_off/compile.rb +150 -208
- data/lib/cast_off/compile/code_manager.rb +0 -49
- data/lib/cast_off/compile/configuration.rb +8 -8
- data/lib/cast_off/compile/hook_invocation.rb +1 -0
- data/lib/cast_off/compile/ir/call_ir.rb +21 -13
- data/lib/cast_off/compile/translator.rb +6 -7
- data/lib/cast_off/suggestion.rb +1 -1
- metadata +10 -12
- data/README.en +0 -228
data/README.ja
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
* はじめに
|
2
|
+
本ツール CastOff は Ruby1.9.3 用のコンパイラです。
|
3
|
+
CastOff は、Ruby のメソッドを C 拡張形式のメソッドへと変換します。
|
4
|
+
C 拡張に変換することで、Ruby の仮想マシンのオーバヘッドを削減し、
|
5
|
+
対象となるメソッドを高速化することができます。
|
6
|
+
本 README では、CastOff の基本機能について紹介します。
|
7
|
+
ご質問、ご要望等があれば、shiba@rvm.jp もしくは
|
8
|
+
http://github.com/soba1104/CastOff/issues まで、ご連絡ください。
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
* ライセンス
|
13
|
+
Ruby 処理系のライセンスに従います。
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
* インストール方法
|
18
|
+
$gem install cast_off
|
19
|
+
|
20
|
+
CastOff は Ruby 処理系に一切手をくわえることなく、Ruby 処理系の拡張ライブラリとして実装しています。
|
21
|
+
このため、gem コマンド経由で容易にインストールすることができます。
|
22
|
+
ただし、脱最適化などの処理が処理系の実装依存になっているため、Ruby 1.9.3 のみの対応となっています。
|
23
|
+
Ruby1.9.3 以外のバージョンの Ruby 処理系では動作しないため、ご注意ください。
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
* CastOff の利用における注意点
|
28
|
+
CastOff は現在アルファ版です。重要な場面では CastOff を利用しないでください。
|
29
|
+
|
30
|
+
** 利用すると非互換になる機能
|
31
|
+
現状、CastOff を用いた場合、Ruby 処理系との非互換性が生じる可能性があります。
|
32
|
+
ここでは、現時点で把握できている、Ruby 処理系との非互換が生じる機能、
|
33
|
+
特にコンパイルエラーを出すことが出来ない機能について列挙します。
|
34
|
+
ここで列挙した機能を使用するプログラムやメソッドに対しては、CastOff を利用するべきではありません。
|
35
|
+
ここに列挙した機能以外で非互換になる点を発見された方は、
|
36
|
+
お手数ですが、shiba@rvm.jp もしくは http://github.com/soba1104/CastOff/issues まで、ご報告をお願い申し上げます。
|
37
|
+
|
38
|
+
-定数の再定義
|
39
|
+
CastOff は、コンパイルしたコードの読み込み時、もしくは最初に実行したときに定数解決を行います。
|
40
|
+
性能上の理由により、CastOff は定数解決後は常に同じ値を使いまわすため、定数の再定義に対応することができません。
|
41
|
+
定数の上書きを行うようなプログラムでは、CastOff を利用しないでください。
|
42
|
+
定数の上書きのフック、もしくは RubyVM が管理する世代番号のチェックが高速にできるようになった場合、
|
43
|
+
この非互換性は解消する予定です。
|
44
|
+
|
45
|
+
-継続(Continuation)
|
46
|
+
Ruby の継続(Continuation) を用いた場合、実行コンテキストの保存に失敗することが確認できています。
|
47
|
+
継続を使用するプログラムでは、CastOff を利用しないでください。
|
48
|
+
|
49
|
+
-メソッドや Proc のパラメータ取得
|
50
|
+
コンパイル済みメソッドや Proc に対する情報を取得しようとした場合、
|
51
|
+
コンパイル前と異なる挙動を示すメソッドがいくつかあります。
|
52
|
+
例えば、Method#arity や Proc#arity などの、メソッドや Proc の引数に
|
53
|
+
関する情報を取得しようとした場合、コンパイル前とコンパイル後では異なる値になります。
|
54
|
+
|
55
|
+
|
56
|
+
** 起動時間
|
57
|
+
CastOff は Ruby で実装しているので、CastOff を用いると、CastOff の読み込みというオーバヘッドが発生します。
|
58
|
+
このため、実行時間が短いプログラムに対してCastOff を利用すると、逆に遅くなる可能性があります。
|
59
|
+
|
60
|
+
また、CastOff を用いた場合、通常の require などの処理に加えて、CastOff がコンパイルしたメソッドの数だけ、コンパイル済みコードの読み込み、
|
61
|
+
および、メソッド上書きのフックなどの処理(以降、初期化処理と呼ぶ)が走ります。
|
62
|
+
閾値の設定次第では数多くのメソッドがコンパイルされるため、初期化処理に時間がかかります。
|
63
|
+
|
64
|
+
|
65
|
+
** コンパイル時間
|
66
|
+
CastOff は Ruby で実装しており、実装コストの兼ね合いから、コンパイル時間には配慮していません。
|
67
|
+
このため、CastOff によるコンパイルは非常に時間がかかります。
|
68
|
+
起動時間に関する注意でも述べたように、CastOff を利用した場合、
|
69
|
+
閾値の設定次第では数多くのメソッドがコンパイルされます。
|
70
|
+
閾値を低く設定しすぎると、コンパイルに非常に時間がかかるという点に、ご注意ください。
|
71
|
+
|
72
|
+
|
73
|
+
* CastOff の利用方法
|
74
|
+
CastOff が提供するコマンドラインツール cast_off では、
|
75
|
+
引数に高速化したいプログラムを指定することで、容易にコンパイルを行うことができます。
|
76
|
+
詳しくは下で解説しますが、CastOff の利用は、次のようなコマンドを繰り返し実行することで行います。
|
77
|
+
|
78
|
+
-----------------------------------------------------
|
79
|
+
$cast_off コンパイル対象のスクリプト コンパイル対象に与える引数の例
|
80
|
+
-----------------------------------------------------
|
81
|
+
|
82
|
+
例えば、次のようなスクリプトの実行を高速化したい場合、
|
83
|
+
|
84
|
+
$ruby foo.rb bar
|
85
|
+
|
86
|
+
次のように実行することで、foo.rb と、使用するライブラリをコンパイルすることができます。
|
87
|
+
(この例での bar は、foo.rb に対する引数です)
|
88
|
+
|
89
|
+
$cast_off foo.rb bar
|
90
|
+
|
91
|
+
CastOff は、コンパイルを次の2つの手順で行います。
|
92
|
+
1:コンパイル対象のメソッドを決定
|
93
|
+
2:コンパイル対象のプロファイル情報を収集し、コンパイル
|
94
|
+
この2つのステップそれぞれで対象プログラムを実行します。
|
95
|
+
このため、上の例の場合、foo.rb を、コンパイルのために2度実行します。
|
96
|
+
実行の回数は、今後、1-2の手順をまとめることで、削減する予定です。
|
97
|
+
|
98
|
+
コンパイルした後は、--run オプションを用いることで、コンパイルしたコードを読み込んで動作させることができます。
|
99
|
+
上の例の場合、次のようなコマンドを実行することで、コンパイル済みコードを用いて foo.rb を実行することができます。
|
100
|
+
|
101
|
+
$cast_off --run foo.rb bar
|
102
|
+
|
103
|
+
また、CastOff の利用で注意する必要があるのは、コンパイル時とは異なる引数を用いて実行する場合の挙動です。
|
104
|
+
上の例では、コンパイル時に bar という引数を与えています。
|
105
|
+
CastOff は、bar を引数として foo.rb を実行したときのプロファイル情報を基にコンパイルを行うため、
|
106
|
+
異なる引数を与えた場合、クラス情報の不整合を起こし、例外、もしくは脱最適化を発生させる可能性があります。
|
107
|
+
|
108
|
+
例えば、foo.rb が下の例のようなプログラムだった場合、bar という引数がわたってきた場合は String オブジェクトが、
|
109
|
+
baz という引数がわたってきた場合は Symbol オブジェクトが、sample メソッドに渡されます。
|
110
|
+
|
111
|
+
-----------------------------------------------------
|
112
|
+
# 例) foo.rb
|
113
|
+
def sample(o)
|
114
|
+
puts o.inspect
|
115
|
+
end
|
116
|
+
case ARGV.shift
|
117
|
+
when 'bar'
|
118
|
+
sample('bar')
|
119
|
+
when 'baz'
|
120
|
+
sample(:baz)
|
121
|
+
end
|
122
|
+
-----------------------------------------------------
|
123
|
+
|
124
|
+
ここで、cast_off foo.rb bar として foo.rb をコンパイルすると、CastOff は、プロファイル情報から、
|
125
|
+
sample メソッドに渡されるオブジェクトは String オブジェクトのみであるという判断を下します。
|
126
|
+
そして、sample メソッドに渡されるオブジェクトが String オブジェクトであるという前提に基づいたコードを生成します。
|
127
|
+
このため、foo.rb に baz という引数を渡した場合、sample メソッドに対し、想定していなかった Symbol オブジェクトが
|
128
|
+
渡ってきてしまい、コンパイルの前提条件が崩れてしまいます。
|
129
|
+
CastOff は、コンパイルの前提条件が崩れたことを検出したときに、例外の発生、もしくは脱最適化を行います。
|
130
|
+
例外を発生させた場合は実行を継続させることができず、脱最適化を行った場合はパフォーマンスに対するペナルティが発生してしまいます。
|
131
|
+
|
132
|
+
このような場合には、
|
133
|
+
|
134
|
+
$cast_off foo.rb bar
|
135
|
+
|
136
|
+
とした次に、
|
137
|
+
|
138
|
+
$cast_off foo.rb baz
|
139
|
+
|
140
|
+
としてください。
|
141
|
+
このようにすることで、引数を bar とした場合、引数を baz とした場合の両方のプロファイル情報を用いてコンパイルを行うことができます。
|
142
|
+
プロファイル情報やコンパイル結果を削除したい場合は、--clear という引数を与えて実行してください。
|
143
|
+
|
144
|
+
|
145
|
+
***コマンドライン引数
|
146
|
+
cast_off [options] [programfile] [arguments]
|
147
|
+
|
148
|
+
|
149
|
+
***オプション一覧
|
150
|
+
--run
|
151
|
+
コンパイル済みコードを用いて、対象プログラム[programfile]を実行します。
|
152
|
+
|
153
|
+
--clear
|
154
|
+
プロファイル結果やコンパイル結果を削除します。
|
155
|
+
name オプションによって foo と名づけたコンパイル結果を削除する場合、次のコマンドを使用してください。
|
156
|
+
$cast_off --clear --name=foo
|
157
|
+
|
158
|
+
--threshold=COUNT
|
159
|
+
COUNT 回以上実行されたメソッドをコンパイルするよう、閾値を設定します。
|
160
|
+
COUNT のデフォルト値は100です。
|
161
|
+
|
162
|
+
--name=NAME
|
163
|
+
コンパイル結果に NAME という名前をつけます。
|
164
|
+
ここでつけた名前は、コンパイル済みコードが既に存在するかどうかの確認に使用します。
|
165
|
+
コンパイル済みコードが見つかり、コンパイル対象やコンパイルやプロファイル情報
|
166
|
+
に変更が無かった場合、CastOff はコンパイル済みコードを再利用します。
|
167
|
+
name オプションを使用しなかった場合、CastOff は File.basename([programfile]) の結果を名前として使用します。
|
168
|
+
|
169
|
+
--verbose
|
170
|
+
コンパイルの進捗とコンパイル結果に関する内部情報を表示します。
|
171
|
+
|
172
|
+
-h, --help
|
173
|
+
ヘルプメッセージを表示します。
|
174
|
+
|
175
|
+
--version
|
176
|
+
CastOff のバージョンを表示します。
|
177
|
+
|
data/bin/cast_off
CHANGED
@@ -36,13 +36,11 @@ opt.on('--clear', <<-EOS.strip) {|v| clear = true }
|
|
36
36
|
you should execute following command.
|
37
37
|
$cast_off --clear --name=foo
|
38
38
|
EOS
|
39
|
-
opt.on('--step-1') {|v| step = '1' }
|
40
|
-
opt.on('--step-2') {|v| step = '2' }
|
41
39
|
opt.on('--verbose', <<-EOS.strip) {|v| verbose = true }
|
42
40
|
Show compilation progress and internal information.
|
43
41
|
EOS
|
44
42
|
opt.on_tail('-h', "--help", "Show this help.") do
|
45
|
-
message = opt.to_s
|
43
|
+
message = opt.to_s
|
46
44
|
message.concat(<<-EOS)
|
47
45
|
|
48
46
|
Example:
|
@@ -55,10 +53,13 @@ opt.on_tail('-h', "--help", "Show this help.") do
|
|
55
53
|
------------------------------------------
|
56
54
|
|
57
55
|
1 Profile and compile fib.rb:
|
58
|
-
$cast_off fib.rb
|
56
|
+
$cast_off fib.rb 10 # 10 is a argument of fib.rb.
|
57
|
+
# Because profile execution is very slow,
|
58
|
+
# argument of fib.rb is 10 to finish profile execution faster.
|
59
59
|
|
60
60
|
2 Run fib.rb with compiled codes:
|
61
|
-
$cast_off --run fib.rb
|
61
|
+
$cast_off --run fib.rb 35 # 35 is a argument of fib.rb.
|
62
|
+
# Compiled fib is used in this execution, so fib.rb runs faster.
|
62
63
|
|
63
64
|
Report bugs to <http://github.com/soba1104/CastOff/issues>.
|
64
65
|
EOS
|
@@ -113,44 +114,18 @@ CastOff.verbose(verbose)
|
|
113
114
|
case step
|
114
115
|
when nil
|
115
116
|
STDERR.puts("-------------------------------- compilation start: threshold = #{threshold}, name = #{name}, profiling = ruby #{args} --------------------------------")
|
116
|
-
2.times do |i|
|
117
|
-
system("#{this} --step-#{i + 1} --threshold=#{threshold} --name=#{name} #{verbose ? '--verbose' : ''} #{args}")
|
118
|
-
unless $? == 0
|
119
|
-
STDERR.puts("Failed to execute 'ruby #{args}'")
|
120
|
-
exit
|
121
|
-
end
|
122
|
-
end
|
123
|
-
when '1'
|
124
|
-
STDERR.puts("-------------------------------- step 1 --------------------------------") if verbose
|
125
117
|
CastOff.development(true)
|
126
118
|
CastOff.use_base_configuration(false)
|
127
119
|
CastOff.autocompile()
|
128
|
-
when '2'
|
129
|
-
STDERR.puts("-------------------------------- step 2 --------------------------------") if verbose
|
130
|
-
CastOff.development(true)
|
131
|
-
CastOff.use_base_configuration(false)
|
132
|
-
CastOff.autoload()
|
133
120
|
at_exit{
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
121
|
+
at_exit{
|
122
|
+
CastOff.development(false)
|
123
|
+
CastOff.use_base_configuration(true)
|
124
|
+
CastOff.re_compile_all()
|
125
|
+
}
|
138
126
|
}
|
139
127
|
fin = Object.new
|
140
128
|
ObjectSpace.define_finalizer(fin){
|
141
|
-
=begin
|
142
|
-
msg = <<-EOS
|
143
|
-
|
144
|
-
----------------------------------------------------------------------------------------
|
145
|
-
Compilation finished successfully.
|
146
|
-
Please add following lines to top of #{script}.
|
147
|
-
|
148
|
-
require 'cast_off'
|
149
|
-
#{configuration.chomp}
|
150
|
-
CastOff.autoload()
|
151
|
-
----------------------------------------------------------------------------------------
|
152
|
-
EOS
|
153
|
-
=end
|
154
129
|
msg = <<-EOS
|
155
130
|
----------------------------------------------------------------------------------------
|
156
131
|
Compilation finished successfully.
|
@@ -168,5 +143,5 @@ else
|
|
168
143
|
end
|
169
144
|
|
170
145
|
$0 = script
|
171
|
-
load script
|
146
|
+
load script
|
172
147
|
|
data/cast_off.gemspec
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "cast_off"
|
3
|
-
spec.version = "0.
|
3
|
+
spec.version = "0.4.0"
|
4
4
|
spec.platform = Gem::Platform::RUBY
|
5
5
|
spec.summary = "Compiler for Ruby1.9.3"
|
6
6
|
spec.description = <<-EOS
|
7
7
|
CastOff is a compiler for Ruby1.9.3.
|
8
8
|
Command line tool cast_off is available after installation.
|
9
9
|
See 'cast_off --help' for more information.
|
10
|
+
Currently, CastOff supports Ruby1.9.3 only.
|
11
|
+
So, if you attempt to use CastOff, please install CastOff under Ruby1.9.3 runtime.
|
12
|
+
|
10
13
|
EOS
|
11
14
|
spec.files = Dir['{lib/**/*,ext/**/*,bin/**/*,doc/**/*}'] + %w[
|
12
15
|
cast_off.gemspec
|
13
|
-
README.en
|
14
16
|
README
|
17
|
+
README.ja
|
15
18
|
]
|
16
19
|
spec.bindir = 'bin'
|
17
20
|
spec.executables << 'cast_off'
|
@@ -24,6 +27,6 @@ See 'cast_off --help' for more information.
|
|
24
27
|
spec.email = 'shiba@rvm.jp'
|
25
28
|
spec.homepage = 'http://github.com/soba1104/CastOff'
|
26
29
|
#spec.rubyforge_project = 'cast_off'
|
27
|
-
spec.required_ruby_version = '
|
30
|
+
spec.required_ruby_version = '= 1.9.3'
|
28
31
|
end
|
29
32
|
|
data/ext/cast_off/cast_off.c.rb
CHANGED
@@ -698,8 +698,10 @@ static VALUE cast_off_class_wrapper_to_s(VALUE self)
|
|
698
698
|
return rb_class_path(obj);
|
699
699
|
} else if (__klass == rb_cModule) {
|
700
700
|
return rb_mod_name(obj);
|
701
|
-
} else {
|
701
|
+
} else if (obj == oMain) {
|
702
702
|
return rb_funcall(obj, rb_intern("to_s"), 0);
|
703
|
+
} else if (RCLASS_M_TBL(klass)->num_entries == 0) {
|
704
|
+
return rb_class_path(__klass);
|
703
705
|
}
|
704
706
|
}
|
705
707
|
rb_raise(rb_eCastOffCompileError, "CastOff can't handle un-marshalable object");
|
@@ -1245,112 +1247,11 @@ static VALUE cast_off_should_not_be_reached(VALUE self)
|
|
1245
1247
|
rb_bug("should not be reached");
|
1246
1248
|
}
|
1247
1249
|
|
1248
|
-
static void cast_off_class_definition_end_handler(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
1249
|
-
{
|
1250
|
-
/* self = target class */
|
1251
|
-
/* id = klass = 0 */
|
1252
|
-
|
1253
|
-
static VALUE args = Qnil;
|
1254
|
-
|
1255
|
-
if (args == Qnil) {
|
1256
|
-
args = rb_ary_new();
|
1257
|
-
rb_gc_register_mark_object(args);
|
1258
|
-
}
|
1259
|
-
|
1260
|
-
rb_proc_call(proc, args);
|
1261
|
-
}
|
1262
|
-
|
1263
|
-
static VALUE cast_off_hook_class_definition_end(VALUE self, VALUE proc)
|
1264
|
-
{
|
1265
|
-
static int hook = 0;
|
1266
|
-
|
1267
|
-
if (proc == Qnil) {
|
1268
|
-
if (!hook) {
|
1269
|
-
return Qfalse;
|
1270
|
-
}
|
1271
|
-
hook = 0;
|
1272
|
-
rb_remove_event_hook(&cast_off_class_definition_end_handler);
|
1273
|
-
return Qtrue;
|
1274
|
-
}
|
1275
|
-
|
1276
|
-
if (hook) {
|
1277
|
-
return Qfalse;
|
1278
|
-
}
|
1279
|
-
|
1280
|
-
if (rb_class_of(proc) != rb_cProc) {
|
1281
|
-
rb_bug("cast_off_hook_class_definition_end: should not be reached(0)");
|
1282
|
-
}
|
1283
|
-
|
1284
|
-
rb_add_event_hook(&cast_off_class_definition_end_handler, RUBY_EVENT_END, proc);
|
1285
|
-
hook = 1;
|
1286
|
-
|
1287
|
-
return Qtrue;
|
1288
|
-
}
|
1289
|
-
|
1290
|
-
#define IN_HEAP_P(th, ptr) \
|
1291
|
-
(!((th)->stack < (ptr) && (ptr) < ((th)->stack + (th)->stack_size)))
|
1292
|
-
|
1293
|
-
static VALUE caller_info(rb_thread_t *th)
|
1294
|
-
{
|
1295
|
-
rb_control_frame_t *cfp;
|
1296
|
-
VALUE *lfp = NULL;
|
1297
|
-
VALUE recv, name;
|
1298
|
-
VALUE klass;
|
1299
|
-
ID mid;
|
1300
|
-
rb_method_entry_t *me;
|
1301
|
-
|
1302
|
-
cfp = th->cfp; /* this method frame */
|
1303
|
-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
1304
|
-
while (1) {
|
1305
|
-
if ((VALUE *) cfp >= th->stack + th->stack_size) {
|
1306
|
-
if (lfp && IN_HEAP_P(th, lfp)) {
|
1307
|
-
return Qnil;
|
1308
|
-
}
|
1309
|
-
rb_bug("caller_info: should not be reached (0) %p", cfp->lfp);
|
1310
|
-
}
|
1311
|
-
if (lfp && cfp->dfp != lfp) {
|
1312
|
-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
1313
|
-
continue;
|
1314
|
-
}
|
1315
|
-
lfp = NULL;
|
1316
|
-
switch(VM_FRAME_TYPE(cfp)) {
|
1317
|
-
case VM_FRAME_MAGIC_METHOD:
|
1318
|
-
recv = cfp->self;
|
1319
|
-
name = cfp->iseq->name;
|
1320
|
-
klass = rb_class_of(recv);
|
1321
|
-
mid = rb_intern(RSTRING_PTR(name));
|
1322
|
-
me = search_method(klass, mid);
|
1323
|
-
if (!me) {
|
1324
|
-
rb_bug("caller_info: should not be reached (1)");
|
1325
|
-
}
|
1326
|
-
klass = me->klass;
|
1327
|
-
return rb_ary_new3(2, klass, ID2SYM(mid));
|
1328
|
-
case VM_FRAME_MAGIC_BLOCK:
|
1329
|
-
case VM_FRAME_MAGIC_PROC:
|
1330
|
-
case VM_FRAME_MAGIC_LAMBDA:
|
1331
|
-
lfp = cfp->lfp;
|
1332
|
-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
1333
|
-
continue;
|
1334
|
-
case VM_FRAME_MAGIC_FINISH:
|
1335
|
-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
1336
|
-
continue;
|
1337
|
-
case VM_FRAME_MAGIC_CLASS:
|
1338
|
-
case VM_FRAME_MAGIC_TOP:
|
1339
|
-
case VM_FRAME_MAGIC_CFUNC:
|
1340
|
-
case VM_FRAME_MAGIC_IFUNC:
|
1341
|
-
case VM_FRAME_MAGIC_EVAL:
|
1342
|
-
return Qnil;
|
1343
|
-
default:
|
1344
|
-
rb_bug("caller_info: should not be reached (2)");
|
1345
|
-
}
|
1346
|
-
}
|
1347
|
-
}
|
1348
|
-
|
1349
1250
|
static void cast_off_method_invocation_handler(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
1350
1251
|
{
|
1351
1252
|
const char *srcfile;
|
1352
|
-
VALUE
|
1353
|
-
VALUE argv[
|
1253
|
+
VALUE need_binding;
|
1254
|
+
VALUE argv[3];
|
1354
1255
|
int line;
|
1355
1256
|
|
1356
1257
|
if (klass == 0) {
|
@@ -1372,23 +1273,18 @@ static void cast_off_method_invocation_handler(rb_event_flag_t event, VALUE proc
|
|
1372
1273
|
|
1373
1274
|
srcfile = rb_sourcefile();
|
1374
1275
|
if (!srcfile) return;
|
1375
|
-
filename = rb_str_new2(srcfile);
|
1376
1276
|
|
1377
1277
|
line = rb_sourceline();
|
1378
1278
|
if (line < 0) return;
|
1379
1279
|
|
1380
|
-
argv[0] =
|
1381
|
-
argv[1] =
|
1382
|
-
argv[2] =
|
1383
|
-
argv[3] = Qnil;
|
1384
|
-
argv[4] = klass;
|
1385
|
-
/* argv[5] = caller_info(th); */
|
1280
|
+
argv[0] = klass;
|
1281
|
+
argv[1] = ID2SYM(id);
|
1282
|
+
argv[2] = Qnil;
|
1386
1283
|
|
1387
|
-
|
1388
|
-
need_binding = rb_proc_call_with_block(proc, 5, argv, Qnil);
|
1284
|
+
need_binding = rb_proc_call_with_block(proc, 3, argv, Qnil);
|
1389
1285
|
if (RTEST(need_binding)) {
|
1390
|
-
argv[
|
1391
|
-
need_binding = rb_proc_call_with_block(proc,
|
1286
|
+
argv[2] = (self && srcfile) ? rb_binding_new() : Qnil;
|
1287
|
+
need_binding = rb_proc_call_with_block(proc, 3, argv, Qnil);
|
1392
1288
|
}
|
1393
1289
|
if (RTEST(need_binding)) {
|
1394
1290
|
rb_bug("cast_off_method_invocation_handler: should not be reached");
|
@@ -1509,6 +1405,175 @@ VALUE cast_off_same_method_p(VALUE dummy, VALUE obj, VALUE mid)
|
|
1509
1405
|
}
|
1510
1406
|
}
|
1511
1407
|
|
1408
|
+
static VALUE cast_off_override_target_of_current_method(VALUE dummy, VALUE obj)
|
1409
|
+
{
|
1410
|
+
VALUE klass = rb_class_of(obj);
|
1411
|
+
VALUE thval = rb_thread_current();
|
1412
|
+
rb_thread_t *th = DATA_PTR(thval);
|
1413
|
+
rb_control_frame_t *cfp;
|
1414
|
+
const rb_method_entry_t *me0, *me1;
|
1415
|
+
ID mid;
|
1416
|
+
|
1417
|
+
cfp = th->cfp; /* this method frame */
|
1418
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); /* target frame */
|
1419
|
+
if (!cfp->me) {
|
1420
|
+
rb_bug("cast_off_target_of_current_method: should not be reached(0)");
|
1421
|
+
}
|
1422
|
+
if (cfp->me->def->type != VM_METHOD_TYPE_BMETHOD) {
|
1423
|
+
rb_bug("cast_off_target_of_current_method: should not be reached(1)");
|
1424
|
+
}
|
1425
|
+
me0 = cfp->me;
|
1426
|
+
|
1427
|
+
mid = cfp->me->called_id;
|
1428
|
+
if (!mid) {
|
1429
|
+
rb_bug("cast_off_target_of_current_method: should not be reached(2)");
|
1430
|
+
}
|
1431
|
+
|
1432
|
+
while(1) {
|
1433
|
+
if (!klass) return Qnil;
|
1434
|
+
me1 = search_method(klass, mid);
|
1435
|
+
if (!me1) return Qnil; /* maybe method object */
|
1436
|
+
if (rb_method_definition_eq(me0->def, me1->def)) {
|
1437
|
+
VALUE target = me1->klass;
|
1438
|
+
if (rb_obj_class(target) != rb_cClass && rb_obj_class(target) != rb_cModule) {
|
1439
|
+
rb_bug("cast_off_target_of_current_method: should not be reached(3)");
|
1440
|
+
}
|
1441
|
+
return target;
|
1442
|
+
}
|
1443
|
+
klass = RCLASS_SUPER(klass);
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
rb_bug("cast_off_target_of_current_method: should not be reached(4)");
|
1447
|
+
}
|
1448
|
+
|
1449
|
+
static VALUE cast_off_singleton_class_p(VALUE dummy, VALUE klass)
|
1450
|
+
{
|
1451
|
+
if (rb_obj_class(klass) != rb_cClass && rb_obj_class(klass) != rb_cModule) {
|
1452
|
+
rb_bug("cast_off_singleton_class_p: should not be reached");
|
1453
|
+
}
|
1454
|
+
return FL_TEST(klass, FL_SINGLETON) ? Qtrue : Qfalse;
|
1455
|
+
}
|
1456
|
+
|
1457
|
+
static VALUE cast_off_current_bmethod_proc(VALUE dummy)
|
1458
|
+
{
|
1459
|
+
VALUE thval = rb_thread_current();
|
1460
|
+
rb_thread_t *th = DATA_PTR(thval);
|
1461
|
+
rb_control_frame_t *cfp;
|
1462
|
+
|
1463
|
+
cfp = th->cfp; /* this method frame */
|
1464
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); /* target frame */
|
1465
|
+
if (!cfp->me) {
|
1466
|
+
rb_bug("cast_off_create_current_bmethod_proc: should not be reached(0)");
|
1467
|
+
}
|
1468
|
+
if (cfp->me->def->type != VM_METHOD_TYPE_BMETHOD) {
|
1469
|
+
rb_bug("cast_off_create_current_bmethod_proc: should not be reached(1)");
|
1470
|
+
}
|
1471
|
+
if (rb_obj_class(cfp->me->def->body.proc) != rb_cProc) {
|
1472
|
+
rb_bug("cast_off_create_current_bmethod_proc: should not be reached(2)");
|
1473
|
+
}
|
1474
|
+
return cfp->me->def->body.proc;
|
1475
|
+
}
|
1476
|
+
|
1477
|
+
/* copy of ruby source */
|
1478
|
+
struct METHOD {
|
1479
|
+
VALUE recv;
|
1480
|
+
VALUE rclass;
|
1481
|
+
ID id;
|
1482
|
+
%if RUBY_VERSION == "1.9.3"
|
1483
|
+
rb_method_entry_t *me;
|
1484
|
+
struct unlinked_method_entry_list_entry *ume;
|
1485
|
+
%else
|
1486
|
+
rb_method_entry_t me;
|
1487
|
+
%end
|
1488
|
+
};
|
1489
|
+
|
1490
|
+
static VALUE cast_off_instance_bmethod_proc(VALUE dummy, VALUE bmethod)
|
1491
|
+
{
|
1492
|
+
struct METHOD *method = DATA_PTR(bmethod);
|
1493
|
+
%if RUBY_VERSION == "1.9.3"
|
1494
|
+
rb_method_entry_t *me = method->me;
|
1495
|
+
%else
|
1496
|
+
rb_method_entry_t *me = &method->me;
|
1497
|
+
%end
|
1498
|
+
|
1499
|
+
if (me->def->type != VM_METHOD_TYPE_BMETHOD) {
|
1500
|
+
rb_bug("cast_off_instance_bmethod_proc: should not be reached(0)");
|
1501
|
+
}
|
1502
|
+
if (rb_obj_class(me->def->body.proc) != rb_cProc) {
|
1503
|
+
rb_bug("cast_off_instance_bmethod_proc: should not be reached(1)");
|
1504
|
+
}
|
1505
|
+
return me->def->body.proc;
|
1506
|
+
}
|
1507
|
+
|
1508
|
+
static VALUE cast_off_rewrite_method_object(VALUE dummy, VALUE dst, VALUE src, VALUE proc)
|
1509
|
+
{
|
1510
|
+
rb_method_entry_t *dst_me, *src_me;
|
1511
|
+
struct METHOD *dst_mptr, *src_mptr;
|
1512
|
+
|
1513
|
+
if (rb_obj_class(dst) != rb_cMethod ||
|
1514
|
+
rb_obj_class(src) != rb_cUnboundMethod ||
|
1515
|
+
rb_obj_class(proc) != rb_cProc) {
|
1516
|
+
rb_bug("cast_off_rewrite_method_object: should not be reached(0)");
|
1517
|
+
}
|
1518
|
+
|
1519
|
+
dst_mptr = DATA_PTR(dst);
|
1520
|
+
%if RUBY_VERSION == "1.9.3"
|
1521
|
+
dst_me = dst_mptr->me;
|
1522
|
+
%else
|
1523
|
+
dst_me = &dst_mptr->me;
|
1524
|
+
%end
|
1525
|
+
if (dst_me->def->type != VM_METHOD_TYPE_BMETHOD) {
|
1526
|
+
return Qfalse;
|
1527
|
+
}
|
1528
|
+
if (dst_me->def->body.proc != proc) {
|
1529
|
+
return Qfalse;
|
1530
|
+
}
|
1531
|
+
|
1532
|
+
src_mptr = DATA_PTR(src);
|
1533
|
+
%if RUBY_VERSION == "1.9.3"
|
1534
|
+
src_me = src_mptr->me;
|
1535
|
+
%else
|
1536
|
+
src_me = &src_mptr->me;
|
1537
|
+
%end
|
1538
|
+
|
1539
|
+
%if RUBY_VERSION == "1.9.3"
|
1540
|
+
{
|
1541
|
+
/* copy of bm_free: proc.c */
|
1542
|
+
VALUE thval = rb_thread_current();
|
1543
|
+
rb_thread_t *th = DATA_PTR(thval);
|
1544
|
+
rb_vm_t *vm = th->vm;
|
1545
|
+
struct unlinked_method_entry_list_entry *ume = dst_mptr->ume;
|
1546
|
+
|
1547
|
+
ume->me = dst_me;
|
1548
|
+
ume->next = vm->unlinked_method_entry_list;
|
1549
|
+
vm->unlinked_method_entry_list = ume;
|
1550
|
+
dst_mptr->ume = ALLOC(struct unlinked_method_entry_list_entry);
|
1551
|
+
}
|
1552
|
+
|
1553
|
+
/* copy of mnew: proc.c */
|
1554
|
+
dst_mptr->me = ALLOC(rb_method_entry_t);
|
1555
|
+
dst_me = dst_mptr->me;
|
1556
|
+
%end
|
1557
|
+
*dst_me = *src_me;
|
1558
|
+
dst_me->def->alias_count++;
|
1559
|
+
|
1560
|
+
return Qtrue;
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
static VALUE cast_off_rewrite_rclass_of_unbound_method_object(VALUE dummy, VALUE mobj, VALUE rclass)
|
1564
|
+
{
|
1565
|
+
struct METHOD *mptr;
|
1566
|
+
|
1567
|
+
if (rb_obj_class(mobj) != rb_cUnboundMethod || rb_obj_class(rclass) != rb_cClass) {
|
1568
|
+
rb_bug("cast_off_rewrite_rclass_of_unbound_method_object(0)");
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
mptr = DATA_PTR(mobj);
|
1572
|
+
mptr->rclass = rclass;
|
1573
|
+
|
1574
|
+
return Qnil;
|
1575
|
+
}
|
1576
|
+
|
1512
1577
|
/* for deoptimization */
|
1513
1578
|
rb_iseq_t *cast_off_Fixnum_times_iseq;
|
1514
1579
|
rb_iseq_t *cast_off_Array_each_iseq;
|
@@ -1541,9 +1606,14 @@ void Init_cast_off(void)
|
|
1541
1606
|
rb_define_method(rb_mCastOffCompiler, "load_compiled_file", cast_off_load_compiled_file, 1);
|
1542
1607
|
rb_define_method(rb_mCastOffCompiler, "get_caller", cast_off_get_caller, 0);
|
1543
1608
|
rb_define_method(rb_mCastOffCompiler, "hook_method_invocation", cast_off_hook_method_invocation, 1);
|
1544
|
-
rb_define_method(rb_mCastOffCompiler, "hook_class_definition_end", cast_off_hook_class_definition_end, 1);
|
1545
1609
|
rb_define_method(rb_mCastOffCompiler, "current_method_id", cast_off_current_method_id, 0);
|
1546
1610
|
rb_define_method(rb_mCastOffCompiler, "same_method?", cast_off_same_method_p, 2);
|
1611
|
+
rb_define_method(rb_mCastOffCompiler, "singleton_class?", cast_off_singleton_class_p, 1);
|
1612
|
+
rb_define_method(rb_mCastOffCompiler, "override_target_of_current_method", cast_off_override_target_of_current_method, 1);
|
1613
|
+
rb_define_method(rb_mCastOffCompiler, "current_bmethod_proc", cast_off_current_bmethod_proc, 0);
|
1614
|
+
rb_define_method(rb_mCastOffCompiler, "instance_bmethod_proc", cast_off_instance_bmethod_proc, 1);
|
1615
|
+
rb_define_method(rb_mCastOffCompiler, "rewrite_method_object", cast_off_rewrite_method_object, 3);
|
1616
|
+
rb_define_method(rb_mCastOffCompiler, "rewrite_rclass_of_unbound_method_object", cast_off_rewrite_rclass_of_unbound_method_object, 2);
|
1547
1617
|
rb_define_method(rb_mCastOffCompilerInstruction, "get_child_iseq", cast_off_get_child_iseq, 2);
|
1548
1618
|
rb_define_const(rb_mCastOffCompilerInstruction, "ROBJECT_EMBED_LEN_MAX", LONG2FIX(ROBJECT_EMBED_LEN_MAX));
|
1549
1619
|
rb_define_const(rb_mCastOffCompilerInstruction, "THROW_TAG_RETURN", LONG2FIX(TAG_RETURN));
|