From: Brent Roman Date: 2007-10-12T02:25:37+09:00 Subject: [PATCH] adds __file__ and __line__ methods to Methods and Procs This is a multi-part message in MIME format. --------------050108000001020103020908 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Please consider the attached patch to the proc.c supplied of Ruby 1.9.0 ChangeLog: Mon Oct 11 16:45:19 2007 Brent Roman * added __file__ and __line__ methods to Method and Proc classes [ruby-core:872] This patch adds instance methods __file__ and __line__ to the built-in classes Method and Proc. These methods behave analogously to the reserved words __FILE__ and __LINE__ to retrieve the Ruby source filename and line number of the start of the receiver. The reserved words __FILE__ and __LINE__ cannot be used to extract the source location of a previously parsed block of ruby code. Please see: [ruby-talk]:21555 [ruby-core]:872 I've written a ruby class called SourceRef as an example of what's made possible with this core patch. If you've ever found yourself searching over dozens of directories to find where a particular Ruby definition, you should appreciate the simplicity of typing: irb> require 'sourceref' irb> require 'strange' irb> view StrangeClass/:wierdMethod And having wierdMethod's source text pop up in your favorite text editor! You can download sourceref.rb (about 10kB of Ruby) at: ftp://ftp.mbari.org/pub/brent/sourceref.rb The __file__ and __line__ methods in core enable this sort of introspection on methods and procs. They merely allow Ruby code to access the information that the Ruby parser and YARV already collect about every method they compile. So, there's no additional memory overhead for this. Note that, sometime between v1.6.8 and v1.9.0, the Proc#to_s method was enhanced to output the file and line number where procs were defined. However, there still appears to be no analog for Methods. Is this intentional? I suppose if Method#inspect were enhanced in the same way as Proc#to_s has been, I could get this functionality without these __line__ and __file__ methods. Please let me know if that would be the preferred approach. I'd be happy to submit an alternative patch if there was some indication that it would be accepted into the mainline. - brent --------------050108000001020103020908 Content-Type: text/x-patch; name="ruby19-srcref.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="ruby19-srcref.patch" --- /tmp/pro_1.1.2.1_4.c 2007-10-11 09:39:49.000000000 -0700 +++ proc.c 2007-10-10 18:03:08.000000000 -0700 @@ -3,7 +3,7 @@ proc.c - Proc, Binding, Env $Author: brent $ - $Date: 2007/10/09 23:16:55 $ + $Date: 2007/10/11 01:03:08 $ created at: Wed Jan 17 12:13:14 2007 Copyright (C) 2004-2007 Koichi Sasada @@ -470,6 +470,53 @@ return FIX2INT(proc_arity(proc)); } + +static rb_iseq_t * +get_proc_iseq(VALUE self) +{ + rb_proc_t *proc; + rb_iseq_t *iseq; + GetProcPtr(self, proc); + iseq = proc->block.iseq; + if (RUBY_VM_NORMAL_ISEQ_P(iseq)) + return iseq; + rb_raise(rb_eTypeError, "(Native?) Proc has no Ruby source"); +} + + +/* + * call-seq: + * prc.__file__ => String + * + * returns the ruby source filename containing this proc + * raises TypeError if this proc was not defined in ruby (i.e. native) + */ + +static VALUE +proc_filename(VALUE self) +{ + return get_proc_iseq(self)->filename; +} + + +/* + * call-seq: + * prc.__line__ => Fixnum + * + * returns the starting ruby source lineno of this proc or nil if none known + * raises TypeError if this proc was not defined in ruby (i.e. native) + */ + +static VALUE +proc_line_no(VALUE self) +{ + rb_iseq_t *iseq = get_proc_iseq(self); + if (!iseq->insn_info_table) + return Qnil; + return INT2FIX(iseq->insn_info_table[0].line_no); +} + + /* * call-seq: * prc == other_proc => true or false @@ -1256,6 +1303,57 @@ return rb_mod_method_arity(CLASS_OF(obj), id); } + + +static rb_iseq_t * +get_method_iseq(VALUE method) +{ + struct METHOD *data; + NODE *body; + Data_Get_Struct(method, struct METHOD, data); + body=data->body; + if (nd_type(body) == RUBY_VM_METHOD_NODE) { + rb_iseq_t *iseq; + GetISeqPtr((VALUE)body->nd_body, iseq); + if (RUBY_VM_NORMAL_ISEQ_P(iseq)) + return iseq; + } + rb_raise(rb_eTypeError, "(Native?) Method has no Ruby source"); +} + +/* + * call-seq: + * meth.__file__ => String + * + * returns the ruby source filename containing this method's definition + * raises TypeError if this method was not defined in ruby (i.e. native) + */ + +static VALUE +method_filename(VALUE method) +{ + return get_method_iseq(method)->filename; +} + + +/* + * call-seq: + * meth.__line__ => Fixnum + * + * returns the starting ruby source lineno of this method of nil if unknown + * raises TypeError if this method was not defined in ruby (i.e. native) + */ + +static VALUE +method_line_no(VALUE method) +{ + rb_iseq_t *iseq = get_method_iseq(method); + if (!iseq->insn_info_table) + return Qnil; + return INT2FIX(iseq->insn_info_table[0].line_no); +} + + /* * call-seq: * meth.to_s => string @@ -1450,6 +1548,8 @@ rb_define_method(rb_cProc, "hash", proc_hash, 0); rb_define_method(rb_cProc, "to_s", proc_to_s, 0); rb_define_method(rb_cProc, "lambda?", proc_lambda_p, 0); + rb_define_method(rb_cProc, "__file__", proc_filename, 0); + rb_define_method(rb_cProc, "__line__", proc_line_no, 0); /* Exceptions */ rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError); @@ -1484,6 +1584,8 @@ rb_define_method(rb_cMethod, "owner", method_owner, 0); rb_define_method(rb_cMethod, "unbind", method_unbind, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); + rb_define_method(rb_cMethod, "__file__", method_filename, 0); + rb_define_method(rb_cMethod, "__line__", method_line_no, 0); /* UnboundMethod */ rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject); @@ -1499,6 +1601,8 @@ rb_define_method(rb_cUnboundMethod, "name", method_name, 0); rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0); rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); + rb_define_method(rb_cUnboundMethod, "__file__", method_filename, 0); + rb_define_method(rb_cUnboundMethod, "__line__", method_line_no, 0); /* Module#*_method */ rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1); --------------050108000001020103020908--