[macruby-changes] [3034] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Nov 19 23:47:31 PST 2009


Revision: 3034
          http://trac.macosforge.org/projects/ruby/changeset/3034
Author:   martinlagardette at apple.com
Date:     2009-11-19 23:47:28 -0800 (Thu, 19 Nov 2009)
Log Message:
-----------

 - ruby catch/throw statements now use C++ exceptions instead of setjmp/longjmp

Modified Paths:
--------------
    MacRuby/trunk/vm.cpp
    MacRuby/trunk/vm.h

Modified: MacRuby/trunk/vm.cpp
===================================================================
--- MacRuby/trunk/vm.cpp	2009-11-19 07:36:31 UTC (rev 3033)
+++ MacRuby/trunk/vm.cpp	2009-11-20 07:47:28 UTC (rev 3034)
@@ -45,6 +45,8 @@
 #include "dtrace.h"
 
 #include <objc/objc-exception.h>
+#include <cxxabi.h>
+using namespace __cxxabiv1;
 
 #include <execinfo.h>
 #include <dlfcn.h>
@@ -332,6 +334,7 @@
     parse_in_eval = false;
     has_ensure = false;
     return_from_block = -1;
+    throw_exc = NULL;
 }
 
 RoxorVM::~RoxorVM(void)
@@ -3148,7 +3151,7 @@
 	objc_exception_throw(ocexc);
     }
     else {
-	__cxa_rethrow();	
+	__cxa_rethrow();
     }
 }
 #endif
@@ -3836,38 +3839,50 @@
 VALUE
 RoxorVM::ruby_catch(VALUE tag)
 {
-    std::map<VALUE, rb_vm_catch_t *>::iterator iter =
-	catch_jmp_bufs.find(tag);
-    rb_vm_catch_t *s = NULL;
-    if (iter == catch_jmp_bufs.end()) {
-	s = (rb_vm_catch_t *)malloc(sizeof(rb_vm_catch_t));
-	s->throw_value = Qnil;
-	s->nested = 1;
-	catch_jmp_bufs[tag] = s;
+    std::map<VALUE, int*>::iterator iter = catch_nesting.find(tag);
+
+    int *nested_ptr = NULL;
+    if (iter == catch_nesting.end()) {
+	nested_ptr = (int *)malloc(sizeof(int));
+	*nested_ptr = 1;
+	catch_nesting[tag] = nested_ptr;
 	GC_RETAIN(tag);
     }
     else {
-	s = iter->second;
-	s->nested++;
+	nested_ptr = iter->second;
+	(*nested_ptr)++;
     }
 
-    VALUE retval;
-    if (setjmp(s->buf) == 0) {
+    VALUE retval = Qundef;
+    try {
 	retval = rb_vm_yield(1, &tag);
     }
-    else {
-	retval = s->throw_value;
-	rb_objc_release((void *)retval);
-	s->throw_value = Qnil;
+    catch (...) {
+	// Cannot catch "RoxorCatchThrowException *exc", otherwise the program
+	// will crash when trying to interpret ruby exceptions.
+	// So we catch ... instead, and retrieve the exception from the VM.
+	std::type_info *t = __cxa_current_exception_type();
+	if (t != NULL && *t == typeid(RoxorCatchThrowException *)) {
+	    RoxorCatchThrowException *exc = GET_VM()->get_throw_exc();
+	    if (exc != NULL && exc->throw_symbol == tag) {
+		retval = exc->throw_value;
+		rb_objc_release((void *)retval);
+		delete exc;
+		GET_VM()->set_throw_exc(NULL);
+	    }
+	}
+	if (retval == Qundef) {
+	    throw;
+	}
     }
 
-    iter = catch_jmp_bufs.find(tag);
-    assert(iter != catch_jmp_bufs.end());
-    s->nested--;
-    if (s->nested == 0) {
-	s = iter->second;
-	free(s);
-	catch_jmp_bufs.erase(iter);
+    iter = catch_nesting.find(tag);
+    assert(iter != catch_nesting.end());
+    (*nested_ptr)--;
+    if (*nested_ptr == 0) {
+	nested_ptr = iter->second;
+	free(nested_ptr);
+	catch_nesting.erase(iter);
 	GC_RELEASE(tag);
     }
 
@@ -3884,20 +3899,24 @@
 VALUE
 RoxorVM::ruby_throw(VALUE tag, VALUE value)
 {
-    std::map<VALUE, rb_vm_catch_t *>::iterator iter =
-	catch_jmp_bufs.find(tag);
-    if (iter == catch_jmp_bufs.end()) {
+    std::map<VALUE, int*>::iterator iter = catch_nesting.find(tag);
+
+    if (iter == catch_nesting.end()) {
         VALUE desc = rb_inspect(tag);
         rb_raise(rb_eArgError, "uncaught throw %s", RSTRING_PTR(desc));
     }
-    rb_vm_catch_t *s = iter->second;
 
     rb_objc_retain((void *)value);
-    s->throw_value = value;
 
-    longjmp(s->buf, 1);
+    RoxorCatchThrowException *exc = new RoxorCatchThrowException;
+    exc->throw_symbol = tag;
+    exc->throw_value = value;
+    // There is no way - yet - to retrieve the exception from the ABI
+    // So instead, we store the exception in the VM
+    GET_VM()->set_throw_exc(exc);
+    throw exc;
 
-    return Qnil; // never reached
+    return Qnil; // Never reached;
 }
 
 extern "C"

Modified: MacRuby/trunk/vm.h
===================================================================
--- MacRuby/trunk/vm.h	2009-11-19 07:36:31 UTC (rev 3033)
+++ MacRuby/trunk/vm.h	2009-11-20 07:47:28 UTC (rev 3034)
@@ -817,6 +817,13 @@
     METHOD_MISSING_SUPER
 } rb_vm_method_missing_reason_t;
 
+// Custome C++ exception for catch/throw blocks
+class RoxorCatchThrowException {
+ public:
+    VALUE throw_symbol;
+    VALUE throw_value;
+};
+
 // The VM class is instantiated per thread. There is always at least one
 // instance. The VM class is purely thread-safe and concurrent, it does not
 // acquire any lock, except when it calls the Core.
@@ -854,7 +861,7 @@
 	std::vector<rb_vm_block_t *> current_blocks;
 	std::vector<VALUE> current_exceptions;
 	std::vector<rb_vm_binding_t *> bindings;
-	std::map<VALUE, rb_vm_catch_t *> catch_jmp_bufs;
+	std::map<VALUE, int *> catch_nesting;
 	std::vector<VALUE> recursive_objects;
 
 	VALUE thread;
@@ -870,6 +877,8 @@
 	bool has_ensure;
 	int return_from_block;
 
+	RoxorCatchThrowException *throw_exc;
+
     public:
 	RoxorVM(void);
 	RoxorVM(const RoxorVM &vm);
@@ -887,6 +896,7 @@
 	ACCESSOR(parse_in_eval, bool);
 	ACCESSOR(has_ensure, bool);
 	ACCESSOR(return_from_block, int);
+	ACCESSOR(throw_exc, RoxorCatchThrowException *);
 
 	std::string debug_blocks(void);
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20091119/d4e8db25/attachment.html>


More information about the macruby-changes mailing list