Revision: 3372 http://trac.macosforge.org/projects/ruby/changeset/3372 Author: lsansonetti@apple.com Date: 2010-01-29 17:31:55 -0800 (Fri, 29 Jan 2010) Log Message: ----------- now properly popping exceptions from the VM stack when we unwind from a handler, added blocks symbolication Modified Paths: -------------- MacRuby/trunk/compiler.cpp MacRuby/trunk/compiler.h MacRuby/trunk/vm.cpp MacRuby/trunk/vm.h Modified: MacRuby/trunk/compiler.cpp =================================================================== --- MacRuby/trunk/compiler.cpp 2010-01-30 01:26:55 UTC (rev 3371) +++ MacRuby/trunk/compiler.cpp 2010-01-30 01:31:55 UTC (rev 3372) @@ -1951,15 +1951,17 @@ } void -RoxorCompiler::compile_pop_exception(void) +RoxorCompiler::compile_pop_exception(int pos) { if (popExceptionFunc == NULL) { - // void rb_vm_pop_exception(void); + // void rb_vm_pop_exception(int pos); popExceptionFunc = cast<Function>( module->getOrInsertFunction("rb_vm_pop_exception", - VoidTy, NULL)); + VoidTy, Int32Ty, NULL)); } - CallInst::Create(popExceptionFunc, "", bb); + std::vector<Value *> params; + params.push_back(ConstantInt::get(Int32Ty, pos)); + CallInst::Create(popExceptionFunc, params.begin(), params.end(), "", bb); } void @@ -4886,13 +4888,28 @@ bb = handler_bb; assert(n->nd_body != NULL); + + // Compile the rescue handler within another exception + // handler. + BasicBlock *old_rescue_invoke_bb = rescue_invoke_bb; + BasicBlock *new_rescue_invoke_bb = + BasicBlock::Create(context, "rescue", f); + rescue_invoke_bb = new_rescue_invoke_bb; + Value *header_val = compile_node(n->nd_body); handler_bb = bb; BranchInst::Create(merge_bb, bb); - handlers.push_back(std::pair<Value *, BasicBlock *> (header_val, handler_bb)); + // If the handler raised an exception, pop the previous + // one from the VM stack and rethrow. + bb = new_rescue_invoke_bb; + compile_landing_pad_header(); + compile_pop_exception(1); + compile_rethrow_exception(); + rescue_invoke_bb = old_rescue_invoke_bb; + bb = handler_bb = next_handler_bb; n = n->nd_head; @@ -5050,7 +5067,8 @@ current_loop_begin_bb = loopBB; current_loop_body_bb = bodyBB; current_loop_end_bb = afterBB; - current_loop_exit_val = PHINode::Create(RubyObjTy, "loop_exit", afterBB); + current_loop_exit_val = PHINode::Create(RubyObjTy, + "loop_exit", afterBB); current_loop_exit_val->addIncoming(nilVal, exitBB); bb = bodyBB; Modified: MacRuby/trunk/compiler.h =================================================================== --- MacRuby/trunk/compiler.h 2010-01-30 01:26:55 UTC (rev 3371) +++ MacRuby/trunk/compiler.h 2010-01-30 01:31:55 UTC (rev 3372) @@ -321,7 +321,7 @@ void compile_landing_pad_footer(bool pop_exception=true); Value *compile_current_exception(void); void compile_rethrow_exception(void); - void compile_pop_exception(void); + void compile_pop_exception(int pos=0); Value *compile_lvar_slot(ID name); bool compile_lvars(ID *tbl); Value *compile_new_struct(Value *klass, std::vector<Value *> &fields); Modified: MacRuby/trunk/vm.cpp =================================================================== --- MacRuby/trunk/vm.cpp 2010-01-30 01:26:55 UTC (rev 3371) +++ MacRuby/trunk/vm.cpp 2010-01-30 01:31:55 UTC (rev 3372) @@ -271,6 +271,7 @@ extern "C" void *__cxa_allocate_exception(size_t); extern "C" void __cxa_throw(void *, void *, void (*)(void *)); extern "C" void __cxa_rethrow(void); +extern "C" std::type_info *__cxa_current_exception_type(void); RoxorCore::RoxorCore(void) { @@ -553,6 +554,25 @@ #endif } +// Dummy function to be used for debugging (in gdb). +extern "C" +void +rb_symbolicate(void *addr) +{ + void *start = NULL; + char path[1000]; + char name[100]; + unsigned long ln = 0; + if (GET_CORE()->symbolize_call_address(addr, &start, path, sizeof path, + &ln, name, sizeof name)) { + printf("addr %p start %p selector %s location %s:%ld\n", + addr, start, name, path, ln); + } + else { + printf("addr %p unknown\n", addr); + } +} + bool RoxorCore::symbolize_call_address(void *addr, void **startp, char *path, size_t path_len, unsigned long *ln, char *name, size_t name_len) @@ -577,37 +597,40 @@ *startp = start; } - if (name != NULL || path != NULL || ln != NULL) { - std::map<IMP, rb_vm_method_node_t *>::iterator iter = - ruby_imps.find((IMP)start); - if (iter == ruby_imps.end()) { - // TODO symbolize objc selectors - return false; - } - - rb_vm_method_node_t *node = iter->second; + if (f != NULL) { if (ln != NULL) { *ln = 0; - if (f != NULL) { - for (std::vector<RoxorFunction::Line>::iterator iter = - f->lines.begin(); iter != f->lines.end(); ++iter) { - *ln = (*iter).line; - if ((*iter).address <= (uintptr_t)addr) { - break; - } + for (std::vector<RoxorFunction::Line>::iterator iter = + f->lines.begin(); iter != f->lines.end(); ++iter) { + *ln = (*iter).line; + if ((*iter).address <= (uintptr_t)addr) { + break; } } } if (path != NULL) { - if (f != NULL && f->path.size() > 0) { - strncpy(path, f->path.c_str(), path_len); + strncpy(path, f->path.c_str(), path_len); + } + if (name != NULL) { + std::map<IMP, rb_vm_method_node_t *>::iterator iter = + ruby_imps.find((IMP)start); + if (iter == ruby_imps.end()) { + strncpy(name, "block", name_len); } else { - strncpy(path, "core", path_len); + strncpy(name, sel_getName(iter->second->sel), name_len); } } + } + else { + if (ln != NULL) { + *ln = 0; + } + if (path != NULL) { + strncpy(path, "core", path_len); + } if (name != NULL) { - strncpy(name, sel_getName(node->sel), name_len); + name[0] = '\0'; } } @@ -3426,6 +3449,61 @@ #endif } +void +RoxorVM::push_current_exception(VALUE exc) +{ + assert(!NIL_P(exc)); + rb_objc_retain((void *)exc); + current_exceptions.push_back(exc); +//printf("PUSH %p %s\n", (void *)exc, RSTRING_PTR(rb_inspect(exc))); +} + +class RoxorThreadRaiseException { +}; + +static inline bool +current_exception_is_return_from_block(void) +{ + const std::type_info *exc_type = __cxa_current_exception_type(); + return exc_type != NULL + && *exc_type == typeid(RoxorReturnFromBlockException *); +} + +static inline bool +current_exception_is_catch_throw(void) +{ + const std::type_info *exc_type = __cxa_current_exception_type(); + return exc_type != NULL + && *exc_type == typeid(RoxorCatchThrowException *); +} + +static inline bool +current_exception_is_thread_raise(void) +{ + const std::type_info *exc_type = __cxa_current_exception_type(); + return exc_type != NULL + && *exc_type == typeid(RoxorThreadRaiseException *); +} + +void +RoxorVM::pop_current_exception(int pos) +{ + if (current_exception_is_return_from_block() + || current_exception_is_catch_throw() + || current_exception_is_thread_raise()) { + return; + } + + assert((size_t)pos < current_exceptions.size()); + + std::vector<VALUE>::iterator iter = current_exceptions.end() - (pos + 1); + VALUE exc = *iter; + current_exceptions.erase(iter); + + rb_objc_release((void *)exc); +//printf("POP (%d) %p %s\n", pos, (void *)exc, RSTRING_PTR(rb_inspect(exc))); +} + #if !__LP64__ extern "C" void @@ -3465,13 +3543,14 @@ extern "C" VALUE rb_rescue2(VALUE (*b_proc) (ANYARGS), VALUE data1, - VALUE (*r_proc) (ANYARGS), VALUE data2, ...) + VALUE (*r_proc) (ANYARGS), VALUE data2, ...) { try { return (*b_proc)(data1); } catch (...) { - VALUE exc = rb_vm_current_exception(); + RoxorVM *vm = GET_VM(); + VALUE exc = vm->current_exception(); if (exc != Qnil) { va_list ar; VALUE eclass; @@ -3487,8 +3566,9 @@ va_end(ar); if (handled) { + vm->pop_current_exception(); if (r_proc != NULL) { - return (*r_proc)(data2); + return (*r_proc)(data2, exc); } return Qnil; } @@ -3604,16 +3684,6 @@ return vm->pop_broken_with(); } -extern "C" std::type_info *__cxa_current_exception_type(void); - -static inline bool -current_exception_is_return_from_block(void) -{ - const std::type_info *exc_type = __cxa_current_exception_type(); - return exc_type != NULL - && *exc_type == typeid(RoxorReturnFromBlockException *); -} - extern "C" VALUE rb_vm_check_return_from_block_exc(RoxorReturnFromBlockException **pexc, int id) @@ -3652,8 +3722,11 @@ char name[100]; unsigned long ln = 0; + path[0] = name[0] = '\0'; + if (GET_CORE()->symbolize_call_address(callstack[i], NULL, - path, sizeof path, &ln, name, sizeof name)) { + path, sizeof path, &ln, name, sizeof name) + && name[0] != '\0') { char entry[PATH_MAX]; if (ln == 0) { snprintf(entry, sizeof entry, "%s:in `%s'", @@ -3709,7 +3782,7 @@ extern "C" void -rb_vm_pop_exception(void) +rb_vm_pop_exception(int pos) { GET_VM()->pop_current_exception(); } @@ -4435,7 +4508,7 @@ // Killing a thread is implemented using a non-catchable (from Ruby) // exception, which allows us to call the ensure blocks before dying, // which is unfortunately covered in the Ruby specifications. - rb_vm_rethrow(); + throw new RoxorThreadRaiseException(); } static void Modified: MacRuby/trunk/vm.h =================================================================== --- MacRuby/trunk/vm.h 2010-01-30 01:26:55 UTC (rev 3371) +++ MacRuby/trunk/vm.h 2010-01-30 01:31:55 UTC (rev 3372) @@ -847,13 +847,20 @@ METHOD_MISSING_SUPER } rb_vm_method_missing_reason_t; -// Custome C++ exception for catch/throw blocks +// Custom C++ exception class for catch/throw blocks. class RoxorCatchThrowException { - public: - VALUE throw_symbol; - VALUE throw_value; + public: + VALUE throw_symbol; + VALUE throw_value; }; +// Custom C++ exception class used to implement "return-from-block". +class RoxorReturnFromBlockException { + public: + VALUE val; + int id; +}; + // 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. @@ -1011,20 +1018,9 @@ ? Qnil : current_exceptions.back(); } - void push_current_exception(VALUE exc) { - assert(!NIL_P(exc)); - rb_objc_retain((void *)exc); - current_exceptions.push_back(exc); - } + void push_current_exception(VALUE exc); + void pop_current_exception(int pos=0); - VALUE pop_current_exception(void) { - assert(!current_exceptions.empty()); - VALUE exc = current_exceptions.back(); - rb_objc_release((void *)exc); - current_exceptions.pop_back(); - return exc; - } - VALUE *get_binding_lvar(ID name, bool create); VALUE ruby_catch(VALUE tag); @@ -1048,13 +1044,6 @@ #define GET_VM() (RoxorVM::current()) #define GET_THREAD() (GetThreadPtr(GET_VM()->get_thread())) -// Custom C++ exception class used to implement "return-from-block". -class RoxorReturnFromBlockException { - public: - VALUE val; - int id; -}; - #endif /* __cplusplus */ #endif /* __VM_H_ */