#include "rb_Global.h" #include "rb_Kernel.h" // Internals from ruby that aren't included in the ruby lib #include "RubySourceSupport.h" #include "eval_intern.h" /*********** * Global * ***********/ void Init_senderGlobal() { rb_define_global_function( "__sender__", rb_RPRuby_Sender___sender__, 0 ); rb_define_global_function( "__caller__", rb_RPRuby_Sender___caller__, 0 ); } /*************************************************************************************************************************************************************** **************************************************************************************************************************************************************** Ruby Global Methods **************************************************************************************************************************************************************** ***************************************************************************************************************************************************************/ /*************** * __sender__ * ***************/ /* * call-seq: * __sender__ -> object * * Return object sending message to receiver. */ VALUE rb_RPRuby_Sender___sender__() { // we want 3 levels of backtrace: // 1: current call to __method__ (__method__ in context) // 2: the frame we want, unless it is :new (call to context: __sender__) // 3: the frame we want in the case #2 is :new VALUE rb_backtrace_limit = INT2FIX( 3 ); VALUE rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 1, & rb_backtrace_limit, rb_mKernel ); int c_backtrace_index = 1; VALUE rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index ); VALUE rb_caller = rb_hash_aref( rb_backtrace_frame_hash, ID2SYM( rb_intern( "method" ) ) ); // if we get :initialize as our caller and our __method__ is :initialize, we need to go up the chain // until our caller is no longer :initialize or :new while ( rb_caller == ID2SYM( rb_intern( "initialize" ) ) ) { c_backtrace_index++; rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index ); rb_caller = rb_hash_aref( rb_backtrace_frame_hash, ID2SYM( rb_intern( "method" ) ) ); // we have one parent past our current method; if that is also :initialize, get the whole backtrace if ( c_backtrace_index == 2 ) { rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 0, NULL, rb_mKernel ); } } // if we get "new" as our caller we need to get the next level, as we are in :initialize // and want to know what called :new // // since we might have had to go up the chain from :initialize through parents before getting here we // want to use a separate if statement if ( rb_caller == ID2SYM( rb_intern( "new" ) ) ) { c_backtrace_index++; rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index ); } VALUE rb_sender = rb_hash_aref( rb_backtrace_frame_hash, ID2SYM( rb_intern( "object" ) ) ); // assuming we have a previous frame, return its rb_self (our current receiver's sender) return rb_sender; } /*************** * __caller__ * ***************/ /* * call-seq: * __caller__ -> object * * Return method sending message to receiver. */ VALUE rb_RPRuby_Sender___caller__() { // we want 3 levels of backtrace: // 1: current call to __method__ (__method__ in context) // 2: the frame we want, unless it is :new (call to context: __sender__) // 3: the frame we want in the case #2 is :new VALUE rb_backtrace_limit = INT2FIX( 3 ); VALUE rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 1, & rb_backtrace_limit, rb_mKernel ); int c_backtrace_index = 1; VALUE rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index ); VALUE rb_caller = rb_hash_aref( rb_backtrace_frame_hash, ID2SYM( rb_intern( "method" ) ) ); // we want to compare our caller as we go up the chain to our first caller to deal with super methods // we have a symbol so no cloning is necessary (or appropriate) VALUE rb_first_caller = rb_hash_aref( rb_ary_entry( rb_backtrace_array, 0 ), ID2SYM( rb_intern( "method" ) ) ); // if we get :initialize as our caller and our __method__ is :initialize, we need to go up the chain // until our caller is no longer :initialize or :new while ( rb_caller == rb_first_caller ) { c_backtrace_index++; rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index ); rb_caller = rb_hash_aref( rb_backtrace_frame_hash, ID2SYM( rb_intern( "method" ) ) ); // we have one parent past our current method; if that is also :initialize, get the whole backtrace if ( c_backtrace_index == 2 ) { rb_backtrace_array = rb_RPRuby_Sender_Kernel_backtrace( 0, NULL, rb_mKernel ); } } // if we get "new" as our caller we need to get the next level, as we are in :initialize // and want to know what called :new // // since we might have had to go up the chain from :initialize through parents before getting here we // want to use a separate if statement if ( rb_caller == ID2SYM( rb_intern( "new" ) ) ) { c_backtrace_index++; rb_backtrace_frame_hash = rb_ary_entry( rb_backtrace_array, c_backtrace_index ); rb_caller = rb_hash_aref( rb_backtrace_frame_hash, ID2SYM( rb_intern( "method" ) ) ); } // assuming we have a previous frame, return its rb_self (our current receiver's sender) return rb_caller; }