Catch without a tag argument

matzruby e$B$K$"$k!"0z?t$J$7$Ge(B catch e$B$,8F$Y$k5!G=$Oe(B 1.9
e$B$K$OF~e(B
e$B$i$J$$$N$G$7$g$&$+!#e(B

% ./ruby -ve ‘p catch {|t| throw t, 1 }’
ruby 1.9.0 (2007-04-20 patchlevel 0) [i686-linux] - matz
1

% ./ruby -ve ‘p catch {|t| throw t, 1 }’
ruby 1.9.0 (2007-08-22 patchlevel 0) [i686-linux]
-e:1:in catch': wrong number of arguments(0 for 1) (ArgumentError) from -e:1:in

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:31615] Re: catch without a tag argument”
on Thu, 23 Aug 2007 08:03:19 +0900, Nobuyoshi N.
[email protected] writes:

|At Thu, 23 Aug 2007 00:34:12 +0900,
|Tanaka A. wrote in [ruby-dev:31609]:
|> matzruby e$B$K$"$k!"0z?t$J$7$Ge(B catch e$B$,8F$Y$k5!G=$Oe(B 1.9 e$B$K$OF~e(B
|> e$B$i$J$$$N$G$7$g$&$+!#e(B
|
|e$B$$$m$$$m$HJQ$o$C$F$k$h$&$G$=$N$^$^$G$OF0$-$^$;$s$G$7$?!#e(B

e$BF~$l$J$$M}M3$O$J$$$N$G$9$,!“$=$N$^$^J|CV$7$F$^$7$?!#$J$+$@$5e(B
e$B$s!”%3%_%C%H$7$F$/$@$5$$!#e(B

e$B$J$+$@$G$9!#e(B

At Thu, 23 Aug 2007 00:34:12 +0900,
Tanaka A. wrote in [ruby-dev:31609]:

matzruby e$B$K$"$k!"0z?t$J$7$Ge(B catch e$B$,8F$Y$k5!G=$Oe(B 1.9 e$B$K$OF~e(B
e$B$i$J$$$N$G$7$g$&$+!#e(B

e$B$$$m$$$m$HJQ$o$C$F$k$h$&$G$=$N$^$^$G$OF0$-$^$;$s$G$7$?!#e(B

Index: eval_jump.ci

— eval_jump.ci (revision 13128)
+++ eval_jump.ci (working copy)
@@ -6,5 +6,6 @@
#include “eval_intern.h”

-NORETURN(static VALUE rb_f_throw _((int, VALUE *)));
+NORETURN(static VALUE rb_f_throw(int, VALUE *));
+int rb_vm_catch_p(VALUE dst, VALUE tag);

/*
@@ -24,25 +25,8 @@ rb_f_throw(int argc, VALUE *argv)
{
VALUE tag, value;

  • rb_thread_t *th = GET_THREAD();

  • struct rb_vm_tag *tt = th->tag;

    rb_scan_args(argc, argv, “11”, &tag, &value);

  • tag = ID2SYM(rb_to_id(tag));

  • while (tt) {

  • if (tt->tag == tag) {

  •  tt->retval = value;
    
  •  break;
    
  • }

  • tt = tt->prev;

  • }

  • if (!tt) {

  • rb_name_error(SYM2ID(tag), “uncaught throw `%s’”,

  •      rb_id2name(SYM2ID(tag)));
    
  • }

  • rb_trap_restore_mask();

  • th->errinfo = tag;

  • JUMP_TAG(TAG_THROW);

  • rb_throw_obj(tag, value);
    #ifndef GNUC
    return Qnil; /* not reached */
    @@ -53,14 +37,11 @@ void
    rb_throw(const char *tag, VALUE val)
    {
  • VALUE argv[2];
  • argv[0] = ID2SYM(rb_intern(tag));
  • argv[1] = val;
  • rb_f_throw(2, argv);
  • rb_throw_obj(ID2SYM(rb_intern(tag)), val);
    }

/*

  • call-seq:
    • catch(symbol) {| | block }  > obj
      
    • catch(symbol) { block }  => obj
      
    • catch() {|tag| block }   => obj
      
    • +catch+ executes its block. If a +throw+ is
      @@ -92,41 +73,22 @@ rb_throw(const char *tag, VALUE val)

static VALUE
-rb_f_catch(VALUE dmy, VALUE tag)
+rb_f_catch(int argc, VALUE *argv)
{

  • int state;
  • VALUE val = Qnil; /* OK */
  • rb_thread_t *th = GET_THREAD();
  • tag = ID2SYM(rb_to_id(tag));
  • PUSH_TAG();
  • VALUE tag;
  • th->tag->tag = tag;
  • if ((state = EXEC_TAG()) == 0) {
  • val = rb_yield_0(1, &tag);
  • }
  • else if (state == TAG_THROW && th->errinfo == tag) {
  • val = th->tag->retval;
  • th->errinfo = Qnil;
  • state = 0;
  • rb_scan_args(argc, argv, “01”, &tag);
  • if (argc == 0) {
  • tag = Qundef;
    }
  • POP_TAG();
  • if (state)
  • JUMP_TAG(state);
  • return val;
    -}

-static VALUE
-catch_i(VALUE tag)
-{

  • return rb_funcall(Qnil, rb_intern(“catch”), 1, tag);
  • return rb_catch_obj(tag, rb_yield, Qnil);
    }

VALUE
-rb_catch(const char *tag, VALUE (*func)(), VALUE data)
+rb_catch(const char *tag, VALUE (*func)(ANYARGS), VALUE data)
{

  • return rb_iterate((VALUE (*)_((VALUE)))catch_i,
    ID2SYM(rb_intern(tag)),
  •      func, data);
    
  • if (!tag) {
  • return rb_catch_obj(Qundef, func, data);
  • }
  • return rb_catch_obj(ID2SYM(rb_intern(tag)), func, data);
    }

@@ -277,5 +239,5 @@ void
Init_jump(void)
{

  • rb_define_global_function(“catch”, rb_f_catch, 1);
  • rb_define_global_function(“catch”, rb_f_catch, -1);
    rb_define_global_function(“throw”, rb_f_throw, -1);
    rb_define_global_function(“at_exit”, rb_f_at_exit, 0);
    Index: vm.c
    ===================================================================
    — vm.c (revision 13128)
    +++ vm.c (working copy)
    @@ -982,4 +995,76 @@ rb_iter_break()
    }

+static inline int
+catching_p(VALUE dst, VALUE tag)
+{

  • if (dst == tag) return Qtrue;
  • if (SPECIAL_CONST_P(dst)) return Qfalse;
  • if (BUILTIN_TYPE(dst) != T_NODE) return Qfalse;
  • if (GET_THROWOBJ_STATE(dst) != TAG_THROW) return Qfalse;
  • if (GET_THROWOBJ_VAL(dst) != tag) return Qfalse;
  • return Qtrue;
    +}

+void
+rb_throw_obj(VALUE tag, VALUE value)
+{

  • rb_thread_t *th = GET_THREAD();
  • struct rb_vm_tag *tt = th->tag;
  • while (tt) {
  • if (catching_p(tt->tag, tag)) {
  •  tag = tt->tag;
    
  •  tt->retval = value;
    
  •  break;
    
  • }
  • tt = tt->prev;
  • }
  • if (!tt) {
  • VALUE argv[2];
  • argv[0] = rb_sprintf(“uncaught throw `%s’”,
    RSTRING_PTR(rb_obj_as_string(tag)));
  • argv[1] = tag;
  • rb_exc_raise(rb_class_new_instance(2, argv, rb_eNameError));
  • }
  • rb_trap_restore_mask();
  • th->errinfo = tag;
  • TH_JUMP_TAG(th, TAG_THROW);
    +#ifndef GNUC
  • return Qnil; /* not reached */
    +#endif
    +}

+VALUE
+rb_catch_obj(VALUE tag, VALUE (*func)(ANYARGS), VALUE data)
+{

  • int state;
  • VALUE val = Qnil; /* OK */
  • VALUE dest = tag;
  • rb_thread_t *th = GET_THREAD();
  • if (tag == Qundef) {
  • tag = rb_obj_alloc(rb_cObject);
  • dest = (VALUE)NEW_THROW_OBJECT(tag,
    GC_GUARDED_PTR_REF(*th->cfp->dfp),
  •           TAG_THROW);
    
  • }
  • TH_PUSH_TAG(th);
  • th->tag->tag = dest;
  • if ((state = EXEC_TAG()) == 0) {
  • val = (*func)(tag, data);
  • }
  • else if (state == TAG_THROW && th->errinfo == dest) {
  • val = th->tag->retval;
  • th->errinfo = Qnil;
  • state = 0;
  • }
  • TH_POP_TAG();
  • if (state)
  • JUMP_TAG(state);
  • return val;
    +}

/* optimization: redefine management */

Index: include/ruby/ruby.h

— include/ruby/ruby.h (revision 13128)
+++ include/ruby/ruby.h (working copy)
@@ -723,5 +723,7 @@ VALUE rb_rescue2(VALUE()(ANYARGS),VALUE
VALUE rb_ensure(VALUE(
)(ANYARGS),VALUE,VALUE()(ANYARGS),VALUE);
VALUE rb_catch(const char
,VALUE()(ANYARGS),VALUE);
+VALUE rb_catch_obj(VALUE,VALUE(
)(ANYARGS),VALUE);
NORETURN(void rb_throw(const char*,VALUE));
+NORETURN(void rb_throw_obj(VALUE,VALUE));

VALUE rb_require(const char*);