/*============================================================================ * Ruby.xs * Inline::Ruby method bindings. * * Here's a quick map of this file: * * XS: * Inline::Ruby::rb_eval ===> evaluates ruby code * Inline::Ruby::rb_call_function \ * Inline::Ruby::rb_call_class_method ====> call_ruby_method() * Inline::Ruby::rb_call_instance_method / * * C: * call_ruby_method * |__ my_error_wrapper * | |__ rb_funcall2 ===> calls Ruby method * | \__ rb_iterate * | |__ my_iter_it * | | \__ rb_funcall2 ===> calls Ruby method * | | * | \__ my_iter_bl ===> calls Perl iterator * | * \__ my_error_trap ===> throws Perl exception *==========================================================================*/ #include "rb2pl.h" #ifdef EXPOSE_PERL # include "perlmodule.h" #endif /*============================================================================ * This macro creates and fills a ruby array from the Perl call stack. It * relies on the Perl dXSARGS() macro having been called before it; XS does * this automatically. It also requires NUM_FIXED_ARGS to be defined. *==========================================================================*/ #define INIT_RUBY_ARGV(name) { \ int i; \ name = rb_ary_new2(items >= NUM_FIXED_ARGS ? items-NUM_FIXED_ARGS : 0); \ for (i = NUM_FIXED_ARGS; i < items; i++) { \ VALUE tmp = pl2rb(ST(i)); \ rb_ary_push(name, tmp); \ } \ } /*============================================================================ * These macros either flatten the return value (converted into Perl * variables) onto the Perl stack, or return it preserved. *==========================================================================*/ #define FLATTEN_RETVAL(name) { \ if (GIMME_V == G_ARRAY && SvROK(name) && \ SvTYPE(SvRV(name)) == SVt_PVAV) \ { \ AV* av = (AV*)SvRV(name); \ int len = av_len(av) + 1; \ int i; \ for (i=0; ilen, RARRAY(args)->ptr); } /*============================================================================ * This is the iterator block invoked whenever the Ruby code calls yield(). It * is passed an optional value each time it's called (opt), which contains the * Perl subroutine ref to forward the call to. The arguments to the block come * in 'res'. *==========================================================================*/ static VALUE my_iter_bl(res, cv) VALUE res; SV* cv; { dSP; SV* args; I32 count; /* how many values returned on Perl stack */ I32 ax; VALUE rb_retval; Printf(("Note: inside my_iter_bl...\n")); Printf(("Note: TYPE(res) == %i\n", TYPE(res))); Printf(("Note: the ruby args are: %s\n", STR2CSTR(rb_inspect(res)))); args = rb2pl(res); /* convert the Ruby args to Perl args */ ENTER; SAVETMPS; PUSHMARK(SP); #ifdef FLATTEN_CALLBACK_ARGS if (SvROK(args) && SvTYPE(SvRV(args)) == SVt_PVAV) { AV* av = (AV*) SvRV(args); int len = av_len(av) + 1; int i; for (i=0; ilen, RARRAY(argv)->ptr); } /* If we get here, there were no exceptions */ Printf(("No exceptions thrown, clearing ERRSV!\n")); sv_setpvf(ERRSV, ""); return retv; } /* This serves to wrap the eval() in a rescue() block */ static VALUE my_eval_string(str) VALUE str; { return rb_eval_string(STR2CSTR(str)); } /*============================================================================ * This is my generic ruby method caller. It will call either global * functions, class methods, or instance methods. There's a simple way to tell * which is which. The first argument is either a string (the name of the * class), an object (the object to call the method on), or undef (indicates a * global function). The second is the name of the method to call. The third * is an optional iterator function. *==========================================================================*/ static SV* call_ruby_method(obj, method, iter, argv) VALUE obj; char* method; SV* iter; VALUE argv; { VALUE wrap_argv; VALUE rb_retval; SV* pl_retval; Printf(("call_ruby_method(%p, '%s', %p)\n", obj, method, iter)); /* If obj is a string, it is the name the class upon which to call the * class method. */ if (TYPE(obj) == T_STRING) { Printf(("call_ruby_method: obj is a class; getting a handle...\n")); obj = rb_const_get(rb_cObject, rb_intern(STR2CSTR(obj))); } /* Because we want to "wrap" the call in a rescue block, we use rb_rescue. * The block we call is yet another wrapper, which just forwards the call * to the real function: */ wrap_argv = rb_ary_new3(4, obj, rb_str_new2(method), (VALUE)iter, argv); rb_retval = rb_rescue2(&my_error_wrapper, wrap_argv, &my_error_trap, Qnil, rb_eException, 0); if (!rb_retval) { croak("Error: rb_funcall2() returned a NULL C pointer"); return &PL_sv_undef; /* not reached */ } #ifdef CHECK_CONTEXT if (GIMME_V == G_VOID) return &PL_sv_undef; #endif return rb2pl(rb_retval); } MODULE = Inline::Ruby PACKAGE = Inline::Ruby PREFIX = my_ BOOT: #ifdef CREATE_RUBY do_rbinit(); #endif PROTOTYPES: DISABLE #============================================================================= # This is called to evaluate ruby code (duh!). It uses rb_rescue to trap any # compile errors raised by the interpreter. If an exception is thrown we # return an undef, and set the global variable "$@". #============================================================================= int config_var(str) char* str CODE: if (strEQ(str, "CHECK_CONTEXT")) #ifdef CHECK_CONTEXT RETVAL = 1; #else RETVAL = 0; #endif else if (strEQ(str, "FLATTEN_ARRAYS")) #ifdef FLATTEN_ARRAYS RETVAL = 1; #else RETVAL = 0; #endif else if (strEQ(str, "FLATTEN_CALLBACK_ARGS")) #ifdef FLATTEN_CALLBACK_ARGS RETVAL = 1; #else RETVAL = 0; #endif else { if (PL_dowarn) warn("Inline::Ruby::config_var: unknown config var '%s'", str); XSRETURN_UNDEF; } OUTPUT: RETVAL #============================================================================= # This is called to evaluate ruby code (duh!). It uses rb_rescue to trap any # compile errors raised by the interpreter. If an exception is thrown we # return an undef, and set the global variable "$@". #============================================================================= void my_rb_eval(str) char* str PREINIT: SV* pl_retval; PPCODE: Printf(("About to evaluate some Ruby code.\n")); pl_retval = rb2pl(rb_rescue2(&my_eval_string, rb_str_new2(str), &my_error_trap, Qnil, rb_eException, 0)); Printf(("Done.\n")); #if defined CHECK_CONTEXT && defined FLATTEN_ARRAYS FLATTEN_RETVAL(pl_retval); #else PRESERVE_RETVAL(pl_retval); #endif #============================================================================= # This sub calls a global ruby method. It takes the name of the method to run, # an optional iterator, and any arguments to the method. #============================================================================= #undef NUM_FIXED_ARGS #define NUM_FIXED_ARGS 1 void my_rb_call_function(FNAME, ...) char* FNAME PREINIT: VALUE argv; SV* pl_retval; PPCODE: Printf(("rb_call_function(\"%s\")\n", FNAME)); INIT_RUBY_ARGV(argv); pl_retval = call_ruby_method(Qnil, FNAME, NULL, argv); #if defined CHECK_CONTEXT && defined FLATTEN_ARRAYS FLATTEN_RETVAL(pl_retval); #else PRESERVE_RETVAL(pl_retval); #endif #============================================================================= # This is called whenever you need to call a class method. It takes the name # of the class, the method name, an iterator block, and any arguments to the # method. If the iterator block is not a reference to a Perl subroutine, it is # not passed to the ruby method. #============================================================================= #undef NUM_FIXED_ARGS #define NUM_FIXED_ARGS 2 void my_rb_call_class_method(KLASS, mname, ...) char* KLASS char* mname PREINIT: VALUE klass; VALUE argv; SV* pl_retval; PPCODE: Printf(("rb_call_class_method('%s', '%s', %p, ...)\n",KLASS,mname)); INIT_RUBY_ARGV(argv); klass = rb_str_new2(KLASS); pl_retval = call_ruby_method(klass, mname, NULL, argv); #if defined CHECK_CONTEXT && defined FLATTEN_ARRAYS FLATTEN_RETVAL(pl_retval); #else PRESERVE_RETVAL(pl_retval); #endif #============================================================================= # This is called whenever you need to call an instance method. It takes the # instance of the class, the method name, an iterator block, and any arguments # to the method. If the iterator block is not a reference to a Perl # subroutine, it is not passed to the ruby method. #============================================================================= #undef NUM_FIXED_ARGS #define NUM_FIXED_ARGS 2 void my_rb_call_instance_method(_inst, mname, ...) SV* _inst char* mname PREINIT: VALUE inst; VALUE argv; SV* pl_retval; SV* iter; PPCODE: Printf(("rb_call_instance_method(%p, '%s', %p, ...)\n", _inst, mname)); if (isa_InlineRubyWrapper(_inst)) { inst = UNWRAP_RUBY_OBJ(_inst); iter = INLINE_MAGIC(_inst)->iter; Printf(("inst (%p) successfully passed the PVMG test\n", inst)); } else { croak("Object is not a wrapped Inline::Ruby::Object object"); XSRETURN_EMPTY; } INIT_RUBY_ARGV(argv); pl_retval = call_ruby_method(inst, mname, iter, argv); #if defined CHECK_CONTEXT && defined FLATTEN_ARRAYS FLATTEN_RETVAL(pl_retval); #else PRESERVE_RETVAL(pl_retval); #endif #============================================================================= # This sub calls a global ruby method. It takes the name of the method to run, # an optional iterator, and any arguments to the method. #============================================================================= SV* my_rb_iter(obj, iter=NULL) SV* obj SV* iter CODE: /* Case 1: obj is an instance method */ if (items == 2 && isa_InlineRubyWrapper(obj)) { RETVAL = rb2pl(pl2rb(obj)); /* deep copy */ INLINE_MAGIC(RETVAL)->iter = iter; SvREFCNT_inc(iter); } /* Case 2: obj is a class name */ else if (items == 2 && SvTYPE(obj) == SVt_PV) { RETVAL = new_InlineRubyWrapper(rb_str_new2(SvPV_nolen(obj)), iter); } else if (items == 1) RETVAL = new_InlineRubyWrapper(Qnil, obj); /* pass sub in obj */ else RETVAL = new_InlineRubyWrapper(Qnil, iter); OUTPUT: RETVAL MODULE = Inline::Ruby PACKAGE = Inline::Ruby::Object void DESTROY(obj) SV* obj CODE: if (isa_InlineRubyWrapper(obj)) { VALUE rb_object = UNWRAP_RUBY_OBJ(obj); /* talk to the ruby garbage collector */ } MODULE = Inline::Ruby PACKAGE = Inline::Ruby::Exception SV* message(obj) SV* obj ALIAS: Inline::Ruby::Exception::inspect = 1 Inline::Ruby::Exception::backtrace = 2 PREINIT: char* method; CODE: switch(ix) { case 0: method = "message"; break; case 1: method = "inspect"; break; case 2: method = "backtrace"; break; default: croak("Internal error in Inline::Ruby::Exception"); XSRETURN_EMPTY; } if (SvROK(obj) && SvTYPE(SvRV(obj)) == SVt_PVMG) { VALUE rb_exception = (VALUE)SvIV(SvRV(obj)); RETVAL = rb2pl(rb_funcall(rb_exception, rb_intern(method), 0)); } else { croak("Not an Inline::Ruby::Exception object"); XSRETURN_EMPTY; } OUTPUT: RETVAL SV* type(obj) SV* obj CODE: if (SvROK(obj) && SvTYPE(SvRV(obj)) == SVt_PVMG) { VALUE rb_exception = (VALUE)SvIV(SvRV(obj)); VALUE klass = rb_funcall(rb_exception, rb_intern("class"), 0); RETVAL = rb2pl(rb_funcall(klass, rb_intern("name"), 0)); } else { croak("Not an Inline::Ruby::Exception object"); XSRETURN_EMPTY; } OUTPUT: RETVAL