[前][次][番号順一覧][スレッド一覧]

ruby-changes:23750

From: ko1 <ko1@a...>
Date: Sat, 26 May 2012 13:49:34 +0900 (JST)
Subject: [ruby-changes:23750] ko1:r35801 (trunk): * vm.c (RubyVM::FrameInfo): add a class to access each frame

ko1	2012-05-26 13:49:23 +0900 (Sat, 26 May 2012)

  New Revision: 35801

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=35801

  Log:
    * vm.c (RubyVM::FrameInfo): add a class to access each frame
      information.  You don't need to parse strings from caller().
      FrameInfo has the following methods:
      FrameInfo#name: method name, class name, etc with decorations.
      FrameInfo#basename: name without decorations.
      FrameInfo#line_no: line number.
      FrameInfo#filename: file name.
      FrameInfo#filepath: full filepath.
      FrameInfo#iseq: iseq if it is iseq frame (defined by ruby script)
      FrameInfo#to_s: return caller() method style string.
      RubyVM::FrameInfoFrameInfo.caller(n, lev) returns array of
      FrameInfo objects.  The name "RubyVM::FrameInfoFrameInfo.caller"
      is long and ambiguous (same as caller() method), we need to change
      the name before Ruby 2.0 release.
      Good names or comments are welcome.
    * test/ruby/test_backtrace.rb: add a test for above change.

  Modified files:
    trunk/ChangeLog
    trunk/test/ruby/test_backtrace.rb
    trunk/vm.c
    trunk/vm_eval.c

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 35800)
+++ ChangeLog	(revision 35801)
@@ -1,3 +1,23 @@
+Sat May 26 13:40:29 2012  Koichi Sasada  <ko1@a...>
+
+	* vm.c (RubyVM::FrameInfo): add a class to access each frame
+	  information.  You don't need to parse strings from caller().
+	  FrameInfo has the following methods:
+	    FrameInfo#name: method name, class name, etc with decorations.
+	    FrameInfo#basename: name without decorations.
+	    FrameInfo#line_no: line number.
+	    FrameInfo#filename: file name.
+	    FrameInfo#filepath: full filepath.
+	    FrameInfo#iseq: iseq if it is iseq frame (defined by ruby script)
+	    FrameInfo#to_s: return caller() method style string.
+	  RubyVM::FrameInfoFrameInfo.caller(n, lev) returns array of
+	  FrameInfo objects.  The name "RubyVM::FrameInfoFrameInfo.caller"
+	  is long and ambiguous (same as caller() method), we need to change
+	  the name before Ruby 2.0 release.
+	  Good names or comments are welcome.
+
+	* test/ruby/test_backtrace.rb: add a test for above change.
+
 Sat May 26 12:18:09 2012  Koichi Sasada  <ko1@a...>
 
 	* vm.c (frame_info_to_str): add `break'.
Index: vm_eval.c
===================================================================
--- vm_eval.c	(revision 35800)
+++ vm_eval.c	(revision 35801)
@@ -21,6 +21,7 @@
 static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary);
 
 static VALUE vm_backtrace_str_ary(rb_thread_t *th, size_t lev, size_t n);
+static VALUE vm_backtrace_frame_ary(rb_thread_t *th, size_t lev, size_t n);
 static void vm_backtrace_print(FILE *fp);
 
 typedef enum call_type {
@@ -1636,6 +1637,36 @@
     return vm_backtrace_str_ary(GET_THREAD(), lev+1, n);
 }
 
+static VALUE
+rb_f_caller_frame_info(int argc, VALUE *argv)
+{
+    VALUE level, vn;
+    int lev, n;
+
+    rb_scan_args(argc, argv, "02", &level, &vn);
+
+    lev = NIL_P(level) ? 1 : NUM2INT(level);
+
+    if (NIL_P(vn)) {
+	n = 0;
+    }
+    else {
+	n = NUM2INT(vn);
+	if (n == 0) {
+	    return rb_ary_new();
+	}
+    }
+
+    if (lev < 0) {
+	rb_raise(rb_eArgError, "negative level (%d)", lev);
+    }
+    if (n < 0) {
+	rb_raise(rb_eArgError, "negative n (%d)", n);
+    }
+
+    return vm_backtrace_frame_ary(GET_THREAD(), lev+1, n);
+}
+
 void
 rb_backtrace(void)
 {
@@ -1806,4 +1837,3 @@
 
     rb_define_global_function("caller", rb_f_caller, -1);
 }
-
Index: vm.c
===================================================================
--- vm.c	(revision 35800)
+++ vm.c	(revision 35801)
@@ -36,10 +36,8 @@
 VALUE rb_cThread;
 VALUE rb_cEnv;
 VALUE rb_mRubyVMFrozenCore;
-VALUE rb_cBacktrace;
-#if 0
-VALUE rb_cFrameInfo;
-#endif
+static VALUE rb_cBacktrace;
+static VALUE rb_cFrameInfo;
 
 VALUE ruby_vm_const_missing_count = 0;
 char ruby_vm_redefined_flag[BOP_LAST_];
@@ -761,11 +759,6 @@
 	FRAME_INFO_TYPE_IFUNC,
     } type;
 
-    enum FRAME_INFO_STORAGE {
-	FRAME_INFO_STORAGE_BACKTRACE,
-	FRAME_INFO_STORAGE_VALUE,
-    } storage;
-
     union {
 	struct {
 	    const rb_iseq_t *iseq;
@@ -781,42 +774,64 @@
     } body;
 } rb_frame_info_t;
 
+struct valued_frame_info {
+    rb_frame_info_t *fi;
+    VALUE btobj;
+};
+
 static void
 frame_info_mark(void *ptr)
 {
     if (ptr) {
-	rb_frame_info_t *fi = (rb_frame_info_t *)ptr;
+	struct valued_frame_info *vfi = (struct valued_frame_info *)ptr;
+	rb_gc_mark(vfi->btobj);
+    }
+}
 
-	switch (fi->type) {
-	  case FRAME_INFO_TYPE_ISEQ:
-	  case FRAME_INFO_TYPE_ISEQ_CALCED:
-	    rb_gc_mark(fi->body.iseq.iseq->self);
-	    break;
-	  case FRAME_INFO_TYPE_CFUNC:
-	    if (fi->body.cfunc.prev_fi && fi->body.cfunc.prev_fi->storage == FRAME_INFO_STORAGE_VALUE) {
-		/* TODO: rb_gc_mark(fi->body.cfunc.prev_fi->self) */
-	    }
-	    break;
-	  case FRAME_INFO_TYPE_IFUNC:
-	  default:
-	    break;
-	}
+static void
+frame_info_mark_entry(rb_frame_info_t *fi)
+{
+    switch (fi->type) {
+      case FRAME_INFO_TYPE_ISEQ:
+      case FRAME_INFO_TYPE_ISEQ_CALCED:
+	rb_gc_mark(fi->body.iseq.iseq->self);
+	break;
+      case FRAME_INFO_TYPE_CFUNC:
+      case FRAME_INFO_TYPE_IFUNC:
+      default:
+	break;
     }
 }
 
-static VALUE
-frame_info_format(VALUE file, int line_no, VALUE name)
+static void
+frame_info_free(void *ptr)
 {
-    if (line_no != 0) {
-	return rb_enc_sprintf(rb_enc_compatible(file, name), "%s:%d:in `%s'",
-			      RSTRING_PTR(file), line_no, RSTRING_PTR(name));
+    if (ptr) {
+	rb_frame_info_t *fi = (rb_frame_info_t *)ptr;
+	ruby_xfree(fi);
     }
-    else {
-	return rb_enc_sprintf(rb_enc_compatible(file, name), "%s:in `%s'",
-			      RSTRING_PTR(file), RSTRING_PTR(name));
-    }
 }
 
+static size_t
+frame_info_memsize(const void *ptr)
+{
+    /* rb_frame_info_t *fi = (rb_frame_info_t *)ptr; */
+    return sizeof(rb_frame_info_t);
+}
+
+static const rb_data_type_t frame_info_data_type = {
+    "frame_info",
+    {frame_info_mark, frame_info_free, frame_info_memsize,},
+};
+
+static inline rb_frame_info_t *
+frame_info_ptr(VALUE fiobj)
+{
+    struct valued_frame_info *vfi;
+    GetCoreDataFromValue(fiobj, struct valued_frame_info, vfi);
+    return vfi->fi;
+}
+
 static int
 frame_info_line_no(rb_frame_info_t *fi)
 {
@@ -827,13 +842,143 @@
       case FRAME_INFO_TYPE_ISEQ_CALCED:
 	return fi->body.iseq.line_no.line_no;
       case FRAME_INFO_TYPE_CFUNC:
+	if (fi->body.cfunc.prev_fi) {
+	    return frame_info_line_no(fi->body.cfunc.prev_fi);
+	}
+	return 0;
+      default:
+	rb_bug("frame_info_line_no: unreachable");
+    }
+}
+
+static VALUE
+frame_info_line_no_m(VALUE self)
+{
+    return INT2FIX(frame_info_line_no(frame_info_ptr(self)));
+}
+
+static VALUE
+frame_info_name(rb_frame_info_t *fi)
+{
+    switch (fi->type) {
+      case FRAME_INFO_TYPE_ISEQ: 
+      case FRAME_INFO_TYPE_ISEQ_CALCED:
+	return fi->body.iseq.iseq->location.name;
+      case FRAME_INFO_TYPE_CFUNC:
+	return rb_id2str(fi->body.cfunc.mid);
       case FRAME_INFO_TYPE_IFUNC:
-	break;
+      default:
+	rb_bug("frame_info_name: unreachable");
     }
-    return 0;
 }
 
 static VALUE
+frame_info_name_m(VALUE self)
+{
+    return frame_info_name(frame_info_ptr(self));
+}
+
+static VALUE
+frame_info_basename(rb_frame_info_t *fi)
+{
+    switch (fi->type) {
+      case FRAME_INFO_TYPE_ISEQ: 
+      case FRAME_INFO_TYPE_ISEQ_CALCED:
+	return fi->body.iseq.iseq->location.basename;
+      case FRAME_INFO_TYPE_CFUNC:
+	return rb_sym_to_s(ID2SYM(fi->body.cfunc.mid));
+      case FRAME_INFO_TYPE_IFUNC:
+      default:
+	rb_bug("frame_info_basename: unreachable");
+   }
+}
+
+static VALUE
+frame_info_basename_m(VALUE self)
+{
+    return frame_info_basename(frame_info_ptr(self));
+}
+
+static VALUE
+frame_info_filename(rb_frame_info_t *fi)
+{
+    switch (fi->type) {
+      case FRAME_INFO_TYPE_ISEQ: 
+      case FRAME_INFO_TYPE_ISEQ_CALCED:
+	return fi->body.iseq.iseq->location.filename;
+      case FRAME_INFO_TYPE_CFUNC:
+	if (fi->body.cfunc.prev_fi) {
+	    return frame_info_filename(fi->body.cfunc.prev_fi);
+	}
+	return Qnil;
+      case FRAME_INFO_TYPE_IFUNC:
+      default:
+	rb_bug("frame_info_filename: unreachable");
+    }
+}
+
+static VALUE
+frame_info_filename_m(VALUE self)
+{
+    return frame_info_filename(frame_info_ptr(self));
+}
+
+static VALUE
+frame_info_filepath(rb_frame_info_t *fi)
+{
+    switch (fi->type) {
+      case FRAME_INFO_TYPE_ISEQ: 
+      case FRAME_INFO_TYPE_ISEQ_CALCED:
+	return fi->body.iseq.iseq->location.filepath;
+      case FRAME_INFO_TYPE_CFUNC:
+	if (fi->body.cfunc.prev_fi) {
+	    return frame_info_filepath(fi->body.cfunc.prev_fi);
+	}
+	return Qnil;
+      case FRAME_INFO_TYPE_IFUNC:
+      default:
+	rb_bug("frame_info_filepath: unreachable");
+    }
+}
+
+static VALUE
+frame_info_filepath_m(VALUE self)
+{
+    return frame_info_filepath(frame_info_ptr(self));
+}
+
+static VALUE
+frame_info_iseq(rb_frame_info_t *fi)
+{
+    switch (fi->type) {
+      case FRAME_INFO_TYPE_ISEQ:
+      case FRAME_INFO_TYPE_ISEQ_CALCED:
+	return fi->body.iseq.iseq->self;
+      default:
+	return Qnil;
+    }
+}
+
+static VALUE
+frame_info_iseq_m(VALUE self)
+{
+    return frame_info_iseq(frame_info_ptr(self));
+}
+
+static VALUE
+frame_info_format(VALUE file, int line_no, VALUE name)
+{
+    if (line_no != 0) {
+	return rb_enc_sprintf(rb_enc_compatible(file, name), "%s:%d:in `%s'",
+			      RSTRING_PTR(file), line_no, RSTRING_PTR(name));
+    }
+    else {
+	return rb_enc_sprintf(rb_enc_compatible(file, name), "%s:in `%s'",
+			      RSTRING_PTR(file), RSTRING_PTR(name));
+    }
+}
+
+static VALUE
 frame_info_to_str(rb_frame_info_t *fi)
 {
     VALUE file, name;
@@ -872,6 +1017,12 @@
     return frame_info_format(file, line_no, name);
 }
 
+static VALUE
+frame_info_to_str_m(VALUE self)
+{
+    return frame_info_to_str(frame_info_ptr(self));
+}
+
 typedef struct rb_backtrace_struct {
     rb_frame_info_t *backtrace;
     rb_frame_info_t *backtrace_base;
@@ -887,7 +1038,7 @@
 	size_t i, s = bt->backtrace_size;
 
 	for (i=0; i<s; i++) {
-	    frame_info_mark(&bt->backtrace[i]);
+	    frame_info_mark_entry(&bt->backtrace[i]);
 	    rb_gc_mark(bt->strary);
 	}
     }
@@ -1005,7 +1156,6 @@
     struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr;
     rb_frame_info_t *fi = &arg->bt->backtrace[arg->bt->backtrace_size++];
     fi->type = FRAME_INFO_TYPE_ISEQ;
-    fi->storage = FRAME_INFO_STORAGE_BACKTRACE;
     fi->body.iseq.iseq = iseq;
     fi->body.iseq.line_no.pc = pc;
     arg->prev_fi = fi;
@@ -1017,7 +1167,6 @@
     struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr;
     rb_frame_info_t *fi = &arg->bt->backtrace[arg->bt->backtrace_size++];
     fi->type = FRAME_INFO_TYPE_CFUNC;
-    fi->storage = FRAME_INFO_STORAGE_BACKTRACE;
     fi->body.cfunc.mid = mid;
     fi->body.cfunc.prev_fi = arg->prev_fi;
 }
@@ -1044,7 +1193,7 @@
 }
 
 static VALUE
-backtreace_collect(rb_backtrace_t *bt, int lev, int n, VALUE (*func)(rb_frame_info_t *))
+backtreace_collect(rb_backtrace_t *bt, int lev, int n, VALUE (*func)(rb_frame_info_t *, void *arg), void *arg)
 {
     VALUE btary;
     int i;
@@ -1053,12 +1202,18 @@
 
     for (i=0; i+lev<(int)bt->backtrace_size && i<n; i++) {
 	rb_frame_info_t *fi = &bt->backtrace[bt->backtrace_size - 1 - (lev+i)];
-	rb_ary_push(btary, func(fi));
+	rb_ary_push(btary, func(fi, arg));
     }
 
     return btary;
 }
 
+static VALUE
+frame_info_to_str_dmyarg(rb_frame_info_t *fi, void *dmy)
+{
+    return frame_info_to_str(fi);
+}
+
 VALUE
 rb_backtrace_to_str_ary(VALUE self)
 {
@@ -1069,7 +1224,7 @@
 	return bt->strary;
     }
     else {
-	bt->strary = backtreace_collect(bt, 0, bt->backtrace_size, frame_info_to_str);
+	bt->strary = backtreace_collect(bt, 0, bt->backtrace_size, frame_info_to_str_dmyarg, 0);
 	return bt->strary;
     }
 }
@@ -1080,8 +1235,6 @@
     rb_backtrace_t *bt;
     size_t size;
     GetCoreDataFromValue(self, rb_backtrace_t, bt);
-    /* fprintf(stderr, "btsize: %d, lev: %d, n: %d\n", (int)bt->backtrace_size, lev, n); */
-
     size = bt->backtrace_size;
 
     if (n == 0) {
@@ -1091,22 +1244,39 @@
 	return Qnil;
     }
 
-    return backtreace_collect(bt, lev, n, frame_info_to_str);
+    return backtreace_collect(bt, lev, n, frame_info_to_str_dmyarg, 0);
 }
 
-#if 0
 static VALUE
-rb_backtrace_to_frame_ary(VALUE self)
+frame_info_create(rb_frame_info_t *srcfi, void *btobj)
 {
-    return backtreace_collect(self, frame_info_create);
+    VALUE obj;
+    struct valued_frame_info *vfi;
+    obj = TypedData_Make_Struct(rb_cFrameInfo, struct valued_frame_info, &frame_info_data_type, vfi);
+
+    vfi->fi = srcfi;
+    vfi->btobj = (VALUE)btobj;
+
+    return obj;
 }
 
 static VALUE
-backtrace_frame_ary(rb_thread_t *th, size_t lev, size_t n)
+backtrace_to_frame_ary(VALUE self, size_t lev, size_t n)
 {
-    return rb_backtrace_to_frame_ary(backtrace_create(th, lev, n));
+    rb_backtrace_t *bt;
+    size_t size;
+    GetCoreDataFromValue(self, rb_backtrace_t, bt);
+    size = bt->backtrace_size;
+
+    if (n == 0) {
+	n = size;
+    }
+    if (lev > size) {
+	return Qnil;
+    }
+
+    return backtreace_collect(bt, lev, n, frame_info_create, (void *)self);
 }
-#endif
 
 static VALUE
 backtrace_dump_data(VALUE self)
@@ -1130,6 +1300,12 @@
     return backtrace_to_str_ary2(backtrace_object(th), lev, n);
 }
 
+static VALUE
+vm_backtrace_frame_ary(rb_thread_t *th, size_t lev, size_t n)
+{
+    return backtrace_to_frame_ary(backtrace_object(th), lev, n);
+}
+
 /* old style backtrace directly */
 
 struct oldbt_arg {
@@ -2587,6 +2763,19 @@
     rb_undef_method(CLASS_OF(rb_cBacktrace), "new");
     rb_marshal_define_compat(rb_cBacktrace, rb_cArray, backtrace_dump_data, backtrace_load_data);
 
+    /* ::RubyVM::FrameInfo */
+    rb_cFrameInfo = rb_define_class_under(rb_cRubyVM, "FrameInfo", rb_cObject);
+    rb_undef_alloc_func(rb_cFrameInfo);
+    rb_undef_method(CLASS_OF(rb_cFrameInfo), "new");
+    rb_define_method(rb_cFrameInfo, "line_no", frame_info_line_no_m, 0);
+    rb_define_method(rb_cFrameInfo, "name", frame_info_name_m, 0);
+    rb_define_method(rb_cFrameInfo, "basename", frame_info_basename_m, 0);
+    rb_define_method(rb_cFrameInfo, "filename", frame_info_filename_m, 0);
+    rb_define_method(rb_cFrameInfo, "filepath", frame_info_filepath_m, 0);
+    rb_define_method(rb_cFrameInfo, "iseq", frame_info_iseq_m, 0);
+    rb_define_method(rb_cFrameInfo, "to_s", frame_info_to_str_m, 0);
+    rb_define_singleton_method(rb_cFrameInfo, "caller", rb_f_caller_frame_info, -1);
+
     /* ::RubyVM::USAGE_ANALYSIS_* */
     rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN", rb_hash_new());
     rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_REGS", rb_hash_new());
Index: test/ruby/test_backtrace.rb
===================================================================
--- test/ruby/test_backtrace.rb	(revision 35800)
+++ test/ruby/test_backtrace.rb	(revision 35801)
@@ -84,4 +84,14 @@
     }
     rec[m]
   end
+
+  def test_caller_frame_info
+    fis = RubyVM::FrameInfo.caller(0); cs = caller(0)
+    assert_equal(cs.size, fis.size)
+    fis.zip(cs).each{|fi, s|
+      assert_match(/#{fi.name}/, s)
+      assert_match(/#{fi.filename}/, s)
+      assert_match(/#{fi.line_no}/, s)
+    }
+  end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]