[macruby-changes] [1562] MacRuby/branches/experimental

source_changes at macosforge.org source_changes at macosforge.org
Mon May 11 02:37:39 PDT 2009


Revision: 1562
          http://trac.macosforge.org/projects/ruby/changeset/1562
Author:   lsansonetti at apple.com
Date:     2009-05-11 02:37:37 -0700 (Mon, 11 May 2009)
Log Message:
-----------
a new implementation for storing active blocks in the VM which fixes lots of bugs

Modified Paths:
--------------
    MacRuby/branches/experimental/object.c
    MacRuby/branches/experimental/proc.c
    MacRuby/branches/experimental/roxor.cpp
    MacRuby/branches/experimental/roxor.h
    MacRuby/branches/experimental/test_vm/block.rb
    MacRuby/branches/experimental/vm_eval.c

Modified: MacRuby/branches/experimental/object.c
===================================================================
--- MacRuby/branches/experimental/object.c	2009-05-08 23:36:37 UTC (rev 1561)
+++ MacRuby/branches/experimental/object.c	2009-05-11 09:37:37 UTC (rev 1562)
@@ -1699,11 +1699,15 @@
     }
 
     //init_obj = rb_obj_call_init(obj, argc, argv);
+
+    rb_vm_block_t *block = rb_vm_current_block();
     if (argc == 0) {
-	init_obj = rb_vm_call_with_cache(initializeCache, obj, selInitialize, argc, argv);
+	init_obj = rb_vm_call_with_cache2(initializeCache, block, obj,
+		CLASS_OF(obj), selInitialize, argc, argv);
     }
     else {
-	init_obj = rb_vm_call_with_cache(initialize2Cache, obj, selInitialize2, argc, argv);
+	init_obj = rb_vm_call_with_cache2(initialize2Cache, block, obj,
+		CLASS_OF(obj), selInitialize2, argc, argv);
     }
 
     if (init_obj != Qnil) {
@@ -2190,15 +2194,15 @@
 //    m = rb_intern(method);
 //    if (!rb_obj_respond_to(val, m, Qtrue)) {
 
-      SEL sel = sel_registerName(method);
-      if (!rb_vm_respond_to(val, sel, true)) {
-        if (raise) {
+    SEL sel = sel_registerName(method);
+    if (!rb_vm_respond_to(val, sel, true)) {
+	if (raise) {
 	    rb_raise(rb_eTypeError, "can't convert %s into %s",
-		     NIL_P(val) ? "nil" :
-		     val == Qtrue ? "true" :
-		     val == Qfalse ? "false" :
-		     rb_obj_classname(val), 
-		     tname);
+		    NIL_P(val) ? "nil" :
+		    val == Qtrue ? "true" :
+		    val == Qfalse ? "false" :
+		    rb_obj_classname(val), 
+		    tname);
 	}
 	else {
 	    return Qnil;

Modified: MacRuby/branches/experimental/proc.c
===================================================================
--- MacRuby/branches/experimental/proc.c	2009-05-08 23:36:37 UTC (rev 1561)
+++ MacRuby/branches/experimental/proc.c	2009-05-11 09:37:37 UTC (rev 1562)
@@ -295,19 +295,8 @@
 static VALUE
 proc_new(VALUE klass, int is_lambda)
 {
-    rb_vm_block_t *block = rb_vm_current_block();
+    rb_vm_block_t *block = rb_vm_first_block();
     if (block == NULL) {
-	/* If the current block is NULL, let's check if there is a previous
-	 * block. This is to conform to some dark Ruby behaviors, such as:
-	 *
-	 *  def foo; Proc.new; end; foo { p 42 }.call
-	 *
-	 *  def foo(x=Proc.new); x.call; end; foo { p 42 }
-	 */
-	block = rb_vm_previous_block();
-    }
-
-    if (block == NULL) {
 	rb_raise(rb_eArgError,
 		"tried to create Proc object without a block");
     }
@@ -1079,7 +1068,7 @@
 	}
     }
 
-    VALUE result = rb_vm_call_with_cache2(data->cache, data->recv,
+    VALUE result = rb_vm_call_with_cache2(data->cache, NULL, data->recv,
 	    data->oclass, data->sel, argc, argv);
 
     if (safe >= 0) {

Modified: MacRuby/branches/experimental/roxor.cpp
===================================================================
--- MacRuby/branches/experimental/roxor.cpp	2009-05-08 23:36:37 UTC (rev 1561)
+++ MacRuby/branches/experimental/roxor.cpp	2009-05-11 09:37:37 UTC (rev 1562)
@@ -501,6 +501,8 @@
 	std::map<std::string, void *> c_stubs, objc_stubs,
 	    to_rval_convertors, to_ocval_convertors;
 
+	std::vector<rb_vm_block_t *> current_blocks;
+
     public:
 	static RoxorVM *current;
 
@@ -518,10 +520,46 @@
 	std::map<NODE *, rb_vm_block_t *> blocks;
 	std::map<double, struct rb_float_cache *> float_cache;
 	unsigned char method_missing_reason;
-	rb_vm_block_t *current_block;
-	rb_vm_block_t *previous_block;
 	bool parse_in_eval;
 
+	std::string debug_blocks(void);
+
+	bool is_block_current(rb_vm_block_t *b) {
+	    return b == NULL
+		? false
+		: current_blocks.empty()
+		? false
+		: current_blocks.back() == b;
+	}
+
+	void add_current_block(rb_vm_block_t *b) {
+	    current_blocks.push_back(b);
+	}
+
+	void pop_current_block(void) {
+	    assert(!current_blocks.empty());
+	    current_blocks.pop_back();
+	}
+
+	rb_vm_block_t *current_block(void) {
+	    return current_blocks.empty() ? NULL : current_blocks.back();
+	}
+
+	rb_vm_block_t *previous_block(void) {
+	    if (current_blocks.size() > 1) {
+		return current_blocks[current_blocks.size() - 2];
+	    }
+	    return NULL;
+	}
+
+	rb_vm_block_t *first_block(void) {
+	    rb_vm_block_t *b = current_block();
+	    if (b == NULL) {
+		b = previous_block();
+	    }
+	    return b;
+	}
+
 	std::map<VALUE, rb_vm_catch_t *> catch_jmp_bufs;
 	std::vector<jmp_buf *> return_from_block_jmp_bufs;
 
@@ -2596,8 +2634,6 @@
     last_status = Qnil;
     errinfo = Qnil;
 
-    current_block = NULL;
-    previous_block = NULL;
     parse_in_eval = false;
 
 #if ROXOR_VM_DEBUG
@@ -2636,6 +2672,28 @@
     assert(iee != NULL);
 }
 
+
+std::string
+RoxorVM::debug_blocks(void)
+{
+    std::string s;
+    if (current_blocks.empty()) {
+	s.append(" empty");
+    }
+    else {
+	for (std::vector<rb_vm_block_t *>::iterator i =
+		current_blocks.begin();
+		i != current_blocks.end();
+		++i) {
+	    char buf[100];
+	    snprintf(buf, sizeof buf, "%p", *i);
+	    s.append(" ");
+	    s.append(buf);
+	}
+    }
+    return s;
+}
+
 IMP
 RoxorVM::compile(Function *func)
 {
@@ -7544,9 +7602,50 @@
 
 __attribute__((always_inline))
 static VALUE
-__rb_vm_dispatch(struct mcache *cache, VALUE self, Class klass, SEL sel, 
-		 unsigned char opt, int argc, const VALUE *argv)
+__rb_vm_ruby_dispatch(VALUE self, SEL sel, NODE *node, IMP imp,
+		      rb_vm_arity_t &arity, int argc, const VALUE *argv)
 {
+    if ((argc < arity.min) || ((arity.max != -1) && (argc > arity.max))) {
+	rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+		argc, arity.min);
+    }
+
+    assert(node != NULL);
+    const int node_type = nd_type(node);
+
+    if (node_type == NODE_SCOPE && node->nd_body == NULL
+	&& arity.max == arity.min) {
+	// Calling an empty method, let's just return nil!
+	return Qnil;
+    }
+    if (node_type == NODE_FBODY && arity.max != arity.min) {
+	// Calling a function defined with rb_objc_define_method with
+	// a negative arity, which means a different calling convention.
+	if (arity.real == 2) {
+	    return ((VALUE (*)(VALUE, SEL, int, const VALUE *))imp)
+		(self, 0, argc, argv);
+	}
+	else if (arity.real == 1) {
+	    return ((VALUE (*)(VALUE, SEL, ...))imp)
+		(self, 0, rb_ary_new4(argc, argv));
+	}
+	else {
+	    printf("invalid negative arity for C function %d\n",
+		    arity.real);
+	    abort();
+	}
+    }
+
+    return __rb_vm_rcall(self, sel, node, imp, arity,
+	    argc, argv);
+}
+
+__attribute__((always_inline))
+static VALUE
+__rb_vm_dispatch(struct mcache *cache, VALUE self, Class klass, SEL sel,
+		 rb_vm_block_t *block, unsigned char opt, int argc,
+		 const VALUE *argv)
+{
     assert(cache != NULL);
 
     if (klass == NULL) {
@@ -7654,52 +7753,38 @@
 	    goto recache;
 	}
 
-	if ((argc < rcache.arity.min)
-	    || ((rcache.arity.max != -1) && (argc > rcache.arity.max))) {
-	    rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
-		    argc, rcache.arity.min);
-	}
-
 #if ROXOR_VM_DEBUG
-	printf("ruby dispatch %c[<%s %p> %s] (imp=%p, node=%p, cached=%s)\n",
+	printf("ruby dispatch %c[<%s %p> %s] (imp=%p, node=%p, block=%p, cached=%s)\n",
 		class_isMetaClass(klass) ? '+' : '-',
 		class_getName(klass),
 		(void *)self,
 		sel_getName(sel),
 		rcache.imp,
 		rcache.node,
+		block,
 		cached ? "true" : "false");
 #endif
 
-	assert(rcache.node != NULL);
-	const int node_type = nd_type(rcache.node);
+	bool block_already_current = GET_VM()->is_block_current(block);
+	if (!block_already_current) {
+	    GET_VM()->add_current_block(block);
+	}
 
-	if (node_type == NODE_SCOPE && rcache.node->nd_body == NULL
-	    && rcache.arity.max == rcache.arity.min) {
-	    // Calling an empty method, let's just return nil!
-	    return Qnil;
+	VALUE ret = Qnil;
+	try {
+	    ret = __rb_vm_ruby_dispatch(self, sel, rcache.node, rcache.imp,
+		    rcache.arity, argc, argv);
 	}
-	if (node_type == NODE_FBODY
-	    && rcache.arity.max != rcache.arity.min) {
-	    // Calling a function defined with rb_objc_define_method with
-	    // a negative arity, which means a different calling convention.
-	    if (rcache.arity.real == 2) {
-		return ((VALUE (*)(VALUE, SEL, int, const VALUE *))rcache.imp)
-		    (self, 0, argc, argv);
+	catch (...) {
+	    if (!block_already_current) {
+		GET_VM()->pop_current_block();
 	    }
-	    else if (rcache.arity.real == 1) {
-		return ((VALUE (*)(VALUE, SEL, ...))rcache.imp)
-		    (self, 0, rb_ary_new4(argc, argv));
-	    }
-	    else {
-		printf("invalid negative arity for C function %d\n",
-		       rcache.arity.real);
-		abort();
-	    }
+	    throw;
 	}
-
-	return __rb_vm_rcall(self, sel, rcache.node, rcache.imp, rcache.arity,
-			     argc, argv);
+	if (!block_already_current) {
+	    GET_VM()->pop_current_block();
+	}
+	return ret;
     }
     else if (cache->flag == MCACHE_OCALL) {
 	if (ocache.klass != klass) {
@@ -7813,9 +7898,9 @@
 }
 
 extern "C"
-VALUE
+    VALUE
 rb_vm_dispatch(struct mcache *cache, VALUE self, SEL sel, rb_vm_block_t *block, 
-	       unsigned char opt, int argc, ...)
+	unsigned char opt, int argc, ...)
 {
     VALUE argv[MAX_DISPATCH_ARGS];
     if (argc > 0) {
@@ -7825,22 +7910,8 @@
 	va_end(ar);
     }
 
-    const bool swap_blocks = block == NULL || block != GET_VM()->current_block;
+    VALUE retval = __rb_vm_dispatch(cache, self, NULL, sel, block, opt, argc, argv);
 
-    rb_vm_block_t *old_block = NULL;
-    if (swap_blocks) {
-	old_block = GET_VM()->previous_block;
-	GET_VM()->previous_block = GET_VM()->current_block;
-	GET_VM()->current_block = block;
-    }
-
-    VALUE retval = __rb_vm_dispatch(cache, self, NULL, sel, opt, argc, argv);
-
-    if (swap_blocks) {
-	GET_VM()->current_block = GET_VM()->previous_block;
-	GET_VM()->previous_block = old_block;
-    }
-
     if (!GET_VM()->bindings.empty()) {
 	rb_objc_release(GET_VM()->bindings.back());
 	GET_VM()->bindings.pop_back();
@@ -7924,7 +7995,7 @@
 		return obj;
 	}
     }
-    return __rb_vm_dispatch(cache, obj, NULL, selLTLT, 0, 1, &other);
+    return __rb_vm_dispatch(cache, obj, NULL, selLTLT, NULL, 0, 1, &other);
 }
 
 extern "C"
@@ -7940,7 +8011,7 @@
 	extern VALUE rb_ary_aref(VALUE ary, SEL sel, int argc, VALUE *argv);
 	return rb_ary_aref(obj, 0, 1, &other);
     }
-    return __rb_vm_dispatch(cache, obj, NULL, selAREF, 0, 1, &other);
+    return __rb_vm_dispatch(cache, obj, NULL, selAREF, NULL, 0, 1, &other);
 }
 
 extern "C"
@@ -7958,7 +8029,7 @@
     VALUE args[2];
     args[0] = other1;
     args[1] = other2;
-    return __rb_vm_dispatch(cache, obj, NULL, selASET, 0, 2, args);
+    return __rb_vm_dispatch(cache, obj, NULL, selASET, NULL, 0, 2, args);
 }
 
 extern "C"
@@ -7968,6 +8039,7 @@
     if (obj == Qnil) {
 	return NULL;
     }
+
     VALUE proc = rb_check_convert_type(obj, T_DATA, "Proc", "to_proc");
     if (NIL_P(proc)) {
 	rb_raise(rb_eTypeError,
@@ -8207,16 +8279,18 @@
 VALUE
 rb_vm_call(VALUE self, SEL sel, int argc, const VALUE *argv, bool super)
 {
+    struct mcache *cache;
+    unsigned char flg = 0;
     if (super) {
-	struct mcache cache; 
-	cache.flag = 0;
-	return  __rb_vm_dispatch(&cache, self, NULL, sel, 
-				 DISPATCH_SUPER, argc, argv);
+	cache = (struct mcache *)alloca(sizeof(struct mcache));
+	cache->flag = 0;
+	flg = DISPATCH_SUPER;
     }
     else {
-	struct mcache *cache = GET_VM()->method_cache_get(sel, false);
-	return __rb_vm_dispatch(cache, self, NULL, sel, 0, argc, argv);
+	cache = GET_VM()->method_cache_get(sel, false);
     }
+
+    return __rb_vm_dispatch(cache, self, NULL, sel, NULL, flg, argc, argv);
 }
 
 extern "C"
@@ -8224,17 +8298,17 @@
 rb_vm_call_with_cache(void *cache, VALUE self, SEL sel, int argc, 
 		      const VALUE *argv)
 {
-    return __rb_vm_dispatch((struct mcache *)cache, self, NULL, sel, 0,
+    return __rb_vm_dispatch((struct mcache *)cache, self, NULL, sel, NULL, 0,
 	    argc, argv);
 }
 
 extern "C"
 VALUE
-rb_vm_call_with_cache2(void *cache, VALUE self, VALUE klass, SEL sel,
-		       int argc, const VALUE *argv)
+rb_vm_call_with_cache2(void *cache, rb_vm_block_t *block, VALUE self, VALUE klass,
+		       SEL sel, int argc, const VALUE *argv)
 {
     return __rb_vm_dispatch((struct mcache *)cache, self, (Class)klass, sel,
-	    0, argc, argv);
+	    block, 0, argc, argv);
 }
 
 extern "C"
@@ -8249,55 +8323,44 @@
 int
 rb_block_given_p(void)
 {
-    return GET_VM()->current_block != NULL ? Qtrue : Qfalse;
+    return GET_VM()->current_block() != NULL ? Qtrue : Qfalse;
 }
 
-// Should only be used by #block_given?.
+// Should only be used by Proc.new.
 extern "C"
-bool
-rb_vm_block_saved(void)
+rb_vm_block_t *
+rb_vm_first_block(void)
 {
-    return GET_VM()->previous_block != NULL ? Qtrue : Qfalse;
+    return GET_VM()->first_block();
 }
 
+// Should only be used by #block_given?
 extern "C"
-rb_vm_block_t *
-rb_vm_current_block(void)
+bool
+rb_vm_block_saved(void)
 {
-    return GET_VM()->current_block;
+    return GET_VM()->previous_block() != NULL;
 }
 
 extern "C"
 rb_vm_block_t *
-rb_vm_previous_block(void)
+rb_vm_current_block(void)
 {
-    return GET_VM()->previous_block;
+    return GET_VM()->current_block();
 }
 
-extern "C"
-void
-rb_vm_change_current_block(rb_vm_block_t *block)
-{
-    GET_VM()->previous_block = GET_VM()->current_block;
-    GET_VM()->current_block = block;
-}
-
-extern "C"
-void
-rb_vm_restore_current_block(void)
-{
-    GET_VM()->current_block = GET_VM()->previous_block;
-    GET_VM()->previous_block = NULL;
-}
-
 extern "C" VALUE rb_proc_alloc_with_block(VALUE klass, rb_vm_block_t *proc);
 
 extern "C"
 VALUE
 rb_vm_current_block_object(void)
 {
-    if (GET_VM()->current_block != NULL) {
-	return rb_proc_alloc_with_block(rb_cProc, GET_VM()->current_block);
+    rb_vm_block_t *b = GET_VM()->current_block();
+    if (b != NULL) {
+#if ROXOR_VM_DEBUG
+	printf("create Proc object based on block %p\n", b);
+#endif
+	return rb_proc_alloc_with_block(rb_cProc, b);
     }
     return Qnil;
 }
@@ -8416,8 +8479,15 @@
 
     assert(!(b->flags & VM_BLOCK_ACTIVE));
     b->flags |= VM_BLOCK_ACTIVE;
-    VALUE v = __rb_vm_bcall(b->self, (VALUE)b->dvars, b,
-			    b->imp, b->arity, argc, argv);
+    VALUE v = Qnil;
+    try {
+	v = __rb_vm_bcall(b->self, (VALUE)b->dvars, b, b->imp, b->arity,
+		argc, argv);
+    }
+    catch (...) {
+	b->flags &= ~VM_BLOCK_ACTIVE;
+	throw;
+    }
     b->flags &= ~VM_BLOCK_ACTIVE;
 
     return v;
@@ -8433,16 +8503,24 @@
 static inline VALUE
 rb_vm_yield0(int argc, const VALUE *argv)
 {
-    rb_vm_block_t *b = GET_VM()->current_block;
-
+    rb_vm_block_t *b = GET_VM()->current_block();
     if (b == NULL) {
 	rb_raise(rb_eLocalJumpError, "no block given");
     }
 
-    GET_VM()->current_block = GET_VM()->previous_block;
-    VALUE retval = rb_vm_block_eval0(b, argc, argv);
-    GET_VM()->current_block = b;
+    GET_VM()->pop_current_block();
 
+    VALUE retval = Qnil;
+    try {
+	retval = rb_vm_block_eval0(b, argc, argv);
+    }
+    catch (...) {
+	GET_VM()->add_current_block(b);
+	throw;
+    }
+
+    GET_VM()->add_current_block(b);
+
     return retval;
 }
 
@@ -8457,19 +8535,28 @@
 VALUE
 rb_vm_yield_under(VALUE klass, VALUE self, int argc, const VALUE *argv)
 {
-    rb_vm_block_t *b = GET_VM()->current_block;
+    rb_vm_block_t *b = GET_VM()->current_block();
+    GET_VM()->pop_current_block();
 
-    GET_VM()->current_block = NULL;
     VALUE old_self = b->self;
     b->self = self;
     //Class old_class = GET_VM()->current_class;
     //GET_VM()->current_class = (Class)klass;
 
-    VALUE retval = rb_vm_block_eval0(b, argc, argv);
+    VALUE retval = Qnil;
+    try {
+	retval = rb_vm_block_eval0(b, argc, argv);
+    }
+    catch (...) {
+	b->self = old_self;
+	//GET_VM()->current_class = old_class;
+	GET_VM()->add_current_block(b);
+	throw;
+    }
 
     b->self = old_self;
-    GET_VM()->current_block = b;
     //GET_VM()->current_class = old_class;
+    GET_VM()->add_current_block(b);
 
     return retval;
 }

Modified: MacRuby/branches/experimental/roxor.h
===================================================================
--- MacRuby/branches/experimental/roxor.h	2009-05-08 23:36:37 UTC (rev 1561)
+++ MacRuby/branches/experimental/roxor.h	2009-05-11 09:37:37 UTC (rev 1562)
@@ -76,7 +76,7 @@
 void rb_vm_copy_methods(Class from_class, Class to_class);
 VALUE rb_vm_call(VALUE self, SEL sel, int argc, const VALUE *args, bool super);
 VALUE rb_vm_call_with_cache(void *cache, VALUE self, SEL sel, int argc, const VALUE *argv);
-VALUE rb_vm_call_with_cache2(void *cache, VALUE self, VALUE klass, SEL sel, int argc, const VALUE *argv);
+VALUE rb_vm_call_with_cache2(void *cache, rb_vm_block_t *block, VALUE self, VALUE klass, SEL sel, int argc, const VALUE *argv);
 void *rb_vm_get_call_cache(SEL sel);
 VALUE rb_vm_yield(int argc, const VALUE *argv);
 VALUE rb_vm_yield_under(VALUE klass, VALUE self, int argc, const VALUE *argv);
@@ -147,10 +147,8 @@
        rb_vm_block_t *parent_block,
        int dvars_size, ...);
 rb_vm_block_t *rb_vm_current_block(void);
-rb_vm_block_t *rb_vm_previous_block(void);
+rb_vm_block_t *rb_vm_first_block(void);
 bool rb_vm_block_saved(void);
-void rb_vm_change_current_block(rb_vm_block_t *block);
-void rb_vm_restore_current_block(void);
 VALUE rb_vm_block_eval(rb_vm_block_t *block, int argc, const VALUE *argv);
 
 rb_vm_binding_t *rb_vm_current_binding(void);

Modified: MacRuby/branches/experimental/test_vm/block.rb
===================================================================
--- MacRuby/branches/experimental/test_vm/block.rb	2009-05-08 23:36:37 UTC (rev 1561)
+++ MacRuby/branches/experimental/test_vm/block.rb	2009-05-11 09:37:37 UTC (rev 1562)
@@ -7,6 +7,43 @@
 assert ":ok", "def foo; yield; end; foo { p :ok }"
 assert "42",  "def foo; yield 42; end; foo { |x| p x }"
 assert "",    "def foo; end; foo { p :nok }" 
+
+assert ":ok", "def foo; yield; end; send(:foo) { p :ok }"
+assert ":ok", "def foo; yield; end; send(:foo, &proc { p :ok })"
+
+assert ":ok", %{
+  class X; def initialize; yield ;end; end
+  X.new { p :ok }
+}
+
+assert ":ok", %{
+  def foo; raise; end
+  def bar; p :ok unless block_given?; end
+  begin
+    foo { p :nok }
+  rescue
+    bar
+  end
+}
+
+assert ":ok", %{
+  def foo; yield; end
+  begin
+    foo { raise }
+  rescue
+    1.times { p :ok }
+  end
+}
+
+assert ":ok", "def foo(&b); b.call; end; foo { p :ok }"
+assert "42",  "def foo(&b); b.call(42); end; foo { |x| p x }"
+assert ":ok", "def foo(&b); p :ok if b.nil?; end; foo"
+
+assert ":ok", %{
+  class X; def initialize(&b); b.call ;end; end
+  X.new { p :ok }
+}
+
 assert "42", %q{
   def foo; yield 20, 1, 20, 1; end
   foo do |a, b, c, d|
@@ -392,13 +429,19 @@
 
 assert ':ok', %{
   def foo(x); p :ok if block_given?; end
-  def bar; p :ok if block_given?; end
+  def bar; p :nok if block_given?; end
   foo(bar) {}
 }
 
 assert ':ok', %{
+  def foo(x=bar); p :ok if block_given?; end
+  def bar; p :nok if block_given?; end
+  foo {}
+}
+
+assert ':ok', %{
   class X
-    def foo; p :ko if block_given?; self; end
+    def foo; p :nok if block_given?; self; end
     def bar; p :ok if block_given?; self; end
   end
   X.new.foo.bar {}
@@ -413,6 +456,32 @@
 }
 
 assert ':ok', %{
+  class X
+    def initialize(x=Proc.new); x.call; end
+  end
+  X.new { p :ok }
+}
+
+assert '3', %{
+  def foo(a=Proc.new, b=Proc.new, c=Proc.new)
+    a.call; b.call; c.call
+  end
+  i = 0
+  foo { i+=1 }
+  p i
+}
+
+assert ':ok', %{
+  def foo(x=Proc.new); x.call; end
+  def bar; foo; end
+  begin
+    bar { p :nok }
+  rescue ArgumentError
+    p :ok
+  end
+}
+
+assert ':ok', %{
   def a
     yield
   end

Modified: MacRuby/branches/experimental/vm_eval.c
===================================================================
--- MacRuby/branches/experimental/vm_eval.c	2009-05-08 23:36:37 UTC (rev 1561)
+++ MacRuby/branches/experimental/vm_eval.c	2009-05-11 09:37:37 UTC (rev 1562)
@@ -21,7 +21,8 @@
 #include "vm_method.c"
 
 static inline VALUE
-rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, int scope)
+rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, int scope,
+	bool pass_current_block)
 {
     SEL sel;
     if (mid == ID_ALLOCATOR) {
@@ -38,7 +39,11 @@
 	    sel = sel_registerName(midstr);
 	}
     }
-    return rb_vm_call(recv, sel, argc, argv, false);
+
+    void *cache = rb_vm_get_call_cache(sel);
+    rb_vm_block_t *block = pass_current_block ? rb_vm_current_block() : NULL;
+    return rb_vm_call_with_cache2(cache, block, recv, CLASS_OF(recv),
+	    sel, argc, argv);
 }
 
 /*
@@ -89,7 +94,7 @@
     argc = RARRAY_LEN(args);	/* Assigns LONG, but argc is INT */
     argv = ALLOCA_N(VALUE, argc);
     MEMCPY(argv, RARRAY_PTR(args), VALUE, argc);
-    return rb_call(/*CLASS_OF(recv),*/ recv, mid, argc, argv, CALL_FCALL);
+    return rb_call(recv, mid, argc, argv, CALL_FCALL, false);
 }
 
 VALUE
@@ -112,19 +117,19 @@
     else {
 	argv = 0;
     }
-    return rb_call(recv, mid, n, argv, CALL_FCALL);
+    return rb_call(recv, mid, n, argv, CALL_FCALL, false);
 }
 
 VALUE
 rb_funcall2(VALUE recv, ID mid, int argc, const VALUE *argv)
 {
-    return rb_call(recv, mid, argc, argv, CALL_FCALL);
+    return rb_call(recv, mid, argc, argv, CALL_FCALL, false);
 }
 
 VALUE
 rb_funcall3(VALUE recv, ID mid, int argc, const VALUE *argv)
 {
-    return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+    return rb_call(recv, mid, argc, argv, CALL_PUBLIC, false);
 }
 
 static VALUE
@@ -137,7 +142,7 @@
     }
 
     vid = *argv++; argc--;
-    return rb_call(recv, rb_to_id(vid), argc, argv, scope);
+    return rb_call(recv, rb_to_id(vid), argc, argv, scope, true);
 }
 
 /*
@@ -278,13 +283,10 @@
 {
     NODE *node = NEW_IFUNC(bl_proc, data2);
     rb_vm_block_t *b = rb_vm_prepare_block(NULL, node, obj, NULL, NULL, 0, 0);
-    rb_vm_change_current_block(b);
     if (cache == NULL) {
 	cache = rb_vm_get_call_cache(sel);
     }
-    VALUE val = rb_vm_call_with_cache2(cache, obj, 0, sel, argc, argv);
-    rb_vm_restore_current_block();
-    return val;
+    return rb_vm_call_with_cache2(cache, b, obj, 0, sel, argc, argv);
 }
 
 VALUE
@@ -306,7 +308,7 @@
 VALUE
 rb_each(VALUE obj)
 {
-    return rb_call(obj, idEach, 0, 0, CALL_FCALL);
+    return rb_call(obj, idEach, 0, 0, CALL_FCALL, false);
 }
 
 static VALUE
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090511/e55ac9e9/attachment-0001.html>


More information about the macruby-changes mailing list