Ruby 1.9.0のバイトコードをいじり倒す(その1)

昨日の日記を書いたら、おとなり日記に入っていたsshiさんの日記(http://d.hatena.ne.jp/sshi/20071226)でVM::InstructionSequenceを使う話が出ていました。VM::InstructionSequenceは消え行く運命で、使っている人も少ないみたいですが、私は大好きなので、うれしくて今まで掴んだノウハウをここに書きたいと思います。私が勝手に考えたものですので、将来の変更に弱いとかセキュリティ上のリスクがあるとか色々問題があると思いますが、ご了承ください。

笹田さん著の「YARV Maniacs」(http://jp.rubyist.net/magazine/?0006-YarvManiacs)は全部(特に第8回)読んでおくといいです。

現状のRubyの実装ではVM::InstructionSequenceはちゃんと動きません。とりあえずこんな感じかなというパッチです。実はこれだけではまだ不完全ですが、追いきれていないです。詳しくは後で書きます。続きは前の日記なので注意してください

Index: iseq.c
===================================================================
--- iseq.c	(revision 14778)
+++ iseq.c	(working copy)
@@ -354,7 +354,7 @@
 
     name        = CHECK_STRING(rb_ary_entry(data, 5));
     filename    = CHECK_STRING(rb_ary_entry(data, 6));
-    line        = CHECK_ARRAY(rb_ary_entry(data, 7));
+    line        = CHECK_INTEGER(rb_ary_entry(data, 7));
 
     type        = CHECK_SYMBOL(rb_ary_entry(data, 8));
     locals      = CHECK_ARRAY(rb_ary_entry(data, 9));
@@ -1054,6 +1054,7 @@
 	}
 	else {
 	    rb_ary_push(args, INT2FIX(iseq->argc));
+	    rb_ary_push(args, INT2FIX(iseq->arg_opts));
 	    rb_ary_push(args, arg_opt_labels);
 	    rb_ary_push(args, INT2FIX(iseq->arg_post_len));
 	    rb_ary_push(args, INT2FIX(iseq->arg_post_start));
@@ -1195,6 +1196,7 @@
     rb_ary_push(val, misc);
     rb_ary_push(val, iseq->name);
     rb_ary_push(val, iseq->filename);
+    rb_ary_push(val, INT2FIX(0));
     rb_ary_push(val, type);
     rb_ary_push(val, locals);
     rb_ary_push(val, args);
@@ -1285,7 +1287,7 @@
     rb_define_method(rb_cISeq, "eval", iseq_eval, 0);
 
     /* disable this feature because there is no verifier. */
-    /* rb_define_singleton_method(rb_cISeq, "load", iseq_s_load, -1); */
+     rb_define_singleton_method(rb_cISeq, "load", iseq_s_load, -1);
 
     rb_define_singleton_method(rb_cISeq, "compile", iseq_s_compile, -1);
     rb_define_singleton_method(rb_cISeq, "new", iseq_s_compile, -1);
Index: compile.c
===================================================================
--- compile.c	(revision 14778)
+++ compile.c	(working copy)
@@ -4852,7 +4852,7 @@
 	opt = 1;
     }
 
-    iseq->local_table_size = opt + RARRAY_LEN(locals);
+    iseq->local_table_size = RARRAY_LEN(locals);
     iseq->local_table = tbl = (ID *)ALLOC_N(ID *, iseq->local_table_size);
     iseq->local_size = opt + iseq->local_table_size;
 
@@ -4867,14 +4867,14 @@
 	iseq->arg_simple = 1;
     }
     else {
-	int i = 0;
-	VALUE argc = CHECK_INTEGER(rb_ary_entry(args, i++));
-	VALUE arg_opts = CHECK_INTEGER(rb_ary_entry(args, i++));
-	VALUE arg_opt_labels = CHECK_ARRAY(rb_ary_entry(args, i++));
-	VALUE arg_post_len = CHECK_INTEGER(rb_ary_entry(args, i++));
-	VALUE arg_post_start = CHECK_INTEGER(rb_ary_entry(args, i++));
-	VALUE arg_rest = CHECK_INTEGER(rb_ary_entry(args, i++));
-	VALUE arg_block = CHECK_INTEGER(rb_ary_entry(args, i++));
+        int i;
+	VALUE argc = CHECK_INTEGER(rb_ary_entry(args, 0));
+	VALUE arg_opts = CHECK_INTEGER(rb_ary_entry(args, 1));
+	VALUE arg_opt_labels = CHECK_ARRAY(rb_ary_entry(args, 2));
+	VALUE arg_post_len = CHECK_INTEGER(rb_ary_entry(args, 3));
+	VALUE arg_post_start = CHECK_INTEGER(rb_ary_entry(args, 4));
+	VALUE arg_rest = CHECK_INTEGER(rb_ary_entry(args, 5));
+	VALUE arg_block = CHECK_INTEGER(rb_ary_entry(args, 6));
 
 	iseq->argc = FIX2INT(argc);
 	iseq->arg_opts = FIX2INT(arg_opts);