cast_off 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.gsub(/\n[ ]*--step-[0-9]+[ ]*/, '')
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 30 # 30 is a argument of 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 30 # 30 is a argument of 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
- CastOff.development(false)
135
- CastOff.use_base_configuration(true)
136
- # step 3
137
- CastOff.load()
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 if step
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.7"
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 = '>= 1.9.2'
30
+ spec.required_ruby_version = '= 1.9.3'
28
31
  end
29
32
 
@@ -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 filename, need_binding;
1353
- VALUE argv[6];
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] = filename;
1381
- argv[1] = INT2FIX(line);
1382
- argv[2] = ID2SYM(id);
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
- /* rb_proc_call_with_block(proc, 6, argv, Qnil); */
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[3] = (self && srcfile) ? rb_binding_new() : Qnil;
1391
- need_binding = rb_proc_call_with_block(proc, 5, argv, Qnil);
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));