VALUE capi_thread_create(VALUE (*func)(ANYARGS), void* arg, const char* name, const char* file) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); NativeMethod* nm = NativeMethod::create(state, String::create(state, file), G(thread), state->symbol(name), (void*)func, Fixnum::from(1), 0); Pointer* ptr = Pointer::create(state, arg); Thread* thr = Thread::create(env->state(), G(thread), run_function); thr->locals_store(state, state->symbol("function"), nm); thr->locals_store(state, state->symbol("argument"), ptr); VALUE thr_handle = env->get_handle(thr); thr->fork(state); GCTokenImpl gct; // We do a lock and unlock here so we wait until the started // thread is actually ready. This is to prevent we GC stuff on // the stack in the C-API caller that might be stuffed in the void* argument thr->hard_lock(state, gct, env->current_call_frame()); thr->hard_unlock(state, gct, env->current_call_frame()); return thr_handle; }
int rb_thread_select(int max, fd_set* read, fd_set* write, fd_set* except, struct timeval *timeval) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); int ret = 0; { GlobalLock::UnlockGuard guard(env); ret = select(max, read, write, except, timeval); } // Ok, now check if there were async events that happened while // we were waiting on select... if(!env->state()->check_async(env->current_call_frame())) { // Ok, there was an exception raised by an async event. We need // to unwind through the caller back the entrance to the native // method. // Only handle true exceptions being raised, eat all other requests // for now. if(env->state()->thread_state()->raise_reason() == cException) { capi::capi_raise_backend(env->state()->thread_state()->current_exception()); } else { env->state()->thread_state()->clear(); } } return ret; }
VALUE rb_apply(VALUE recv, ID mid, VALUE args) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); env->flush_cached_data(); Array* ary = capi::c_as<Array>(env->get_object(args)); Object* obj = env->get_object(recv); // Unlock, we're leaving extension code. LEAVE_CAPI(env->state()); Object* ret = obj->send(env->state(), env->current_call_frame(), reinterpret_cast<Symbol*>(mid), ary, cNil); // We need to get the handle for the return value before getting // the GEL so that ret isn't accidentally GCd while we wait. VALUE ret_handle = 0; if(ret) ret_handle = env->get_handle(ret); // Re-entering extension code ENTER_CAPI(env->state()); env->update_cached_data(); // An exception occurred if(!ret) env->current_ep()->return_to(env); return ret_handle; }
ID rb_frame_last_func() { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); CallFrame* rcf = env->current_call_frame()->previous->top_ruby_frame(); return env->get_handle(rcf->name()); }
/** * Common implementation for rb_funcall* */ VALUE capi_funcall_backend(const char* file, int line, VALUE receiver, ID method_name, size_t arg_count, VALUE* arg_array) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); env->flush_cached_data(); Array* args = Array::create(env->state(), arg_count); for(size_t i = 0; i < arg_count; i++) { args->set(env->state(), i, env->get_object(arg_array[i])); } Object* blk = RBX_Qnil; if(VALUE blk_handle = env->outgoing_block()) { blk = env->get_object(blk_handle); env->set_outgoing_block(0); } Object* recv = env->get_object(receiver); Object* ret = recv->send(env->state(), env->current_call_frame(), reinterpret_cast<Symbol*>(method_name), args, blk); env->update_cached_data(); // An exception occurred if(!ret) env->current_ep()->return_to(env); return env->get_handle(ret); }
int rb_thread_select(int max, fd_set* read, fd_set* write, fd_set* except, struct timeval *input_tv) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); int ret = 0; struct timeval tv; struct timeval absolute_tv; struct timeval* tvp = NULL; if(input_tv) { // We make a new timeval rather than using input_tv because we modify it. tv = *input_tv; tvp = &tv; gettimeofday(&absolute_tv, NULL); timeradd(&absolute_tv, &tv, &absolute_tv); } for(;;) { env->state()->shared().leave_capi(env->state()); { GCIndependent guard(env); ret = select(max, read, write, except, tvp); } bool ok = env->state()->check_async(env->current_call_frame()); env->state()->shared().enter_capi(env->state()); if(!ok) { // Ok, there was an exception raised by an async event. We need // to unwind through the caller back the entrance to the native // method. // Only handle true exceptions being raised, eat all other requests // for now. if(env->state()->thread_state()->raise_reason() == cException) { capi::capi_raise_backend(env->state()->thread_state()->current_exception()); } else { env->state()->thread_state()->clear(); } } if(ret < 0 && errno == EINTR) { if(input_tv) { struct timeval cur_tv; gettimeofday(&cur_tv, NULL); timersub(&absolute_tv, &cur_tv, &tv); } } else { break; } } return ret; }
void capi_raise_backend(Exception* exception) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); exception->locations(env->state(), Location::from_call_stack(env->state(), env->current_call_frame())); env->state()->raise_exception(exception); env->current_ep()->return_to(env); }
ID rb_frame_this_func() { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); CallFrame* rcf = env->current_call_frame(); Symbol* name = rcf->name(); if(name->nil_p()) return rb_intern("<nil>"); return env->get_handle(name); }
/** @note Shares code with rb_define_class_under, change there too. --rue */ VALUE rb_define_module_under(VALUE parent_handle, const char* name) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Module* parent = c_as<Module>(env->get_object(parent_handle)); Symbol* constant = env->state()->symbol(name); Module* module = rubinius::Helpers::open_module(env->state(), env->current_call_frame(), parent, constant); return env->get_handle(module); }
/** @note Shares code with rb_define_module_under, change there too. --rue */ VALUE rb_define_class_under(VALUE parent_handle, const char* name, VALUE superclass_handle) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Module* parent = c_as<Module>(env->get_object(parent_handle)); Class* superclass = c_as<Class>(env->get_object(superclass_handle)); Symbol* constant = env->state()->symbol(name); bool created = false; Class* cls = rubinius::Helpers::open_class(env->state(), env->current_call_frame(), parent, superclass, constant, &created); return env->get_handle_global(cls); }
Object* NativeMethod::executor_implementation(STATE, CallFrame* call_frame, Dispatch& msg, Arguments& args) { NativeMethod* nm = as<NativeMethod>(msg.method); int arity = nm->arity()->to_int(); if(arity >= 0 && (size_t)arity != args.total()) { Exception* exc = Exception::make_argument_error( state, arity, args.total(), msg.name); exc->locations(state, System::vm_backtrace(state, Fixnum::from(1), call_frame)); state->thread_state()->raise_exception(exc); return NULL; } NativeMethodEnvironment* env = native_method_environment.get(); NativeMethodFrame nmf(env->current_native_frame()); CallFrame* saved_frame = env->current_call_frame(); Object* saved_block = env->block(); env->set_current_call_frame(call_frame); env->set_current_native_frame(&nmf); env->set_current_block(args.block()); Object* ret; ExceptionPoint ep(env); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ret = NULL; } else { #ifdef RBX_PROFILER if(unlikely(state->shared.profiling())) { profiler::MethodEntry method(state, msg, args); ret = nm->call(state, env, args); } else { ret = nm->call(state, env, args); } #else ret = nm->call(state, env, args); #endif } env->set_current_block(saved_block); env->set_current_call_frame(saved_frame); env->set_current_native_frame(nmf.previous()); ep.pop(env); return ret; }
void* rb_thread_call_with_gvl(void* (*func)(void*), void* data) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); GCTokenImpl gct; State* state = env->state(); ENTER_CAPI(state); state->gc_dependent(gct, state->vm()->saved_call_frame()); void* ret = (*func)(data); env->state()->gc_independent(gct, env->current_call_frame()); LEAVE_CAPI(env->state()); return ret; }
VALUE rb_apply(VALUE recv, ID mid, VALUE args) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); env->flush_cached_data(); Array* ary = capi::c_as<Array>(env->get_object(args)); Object* obj = env->get_object(recv); Object* ret = obj->send(env->state(), env->current_call_frame(), reinterpret_cast<Symbol*>(mid), ary, RBX_Qnil); env->update_cached_data(); // An exception occurred if(!ret) env->current_ep()->return_to(env); return env->get_handle(ret); }
VALUE rb_gv_get(const char* name) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); long len; len = strlen(name); if((len == 1 && name[0] == '~') || (len == 2 && name[0] == '$' && name[1] == '~')) { return env->get_handle(Regexp::last_match_result(env->state(), Fixnum::from(0), Fixnum::from(0), env->current_call_frame())); } VALUE Globals = rb_const_get(rb_mRubinius, rb_intern("Globals")); return rb_funcall(Globals, rb_intern("[]"), 1, env->get_handle(prefixed_by(env->state(), '$', rb_intern(name)))); }
VALUE capi_fast_call(VALUE receiver, ID method_name, int arg_count, ...) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); va_list varargs; va_start(varargs, arg_count); Object* args[arg_count]; for(int i = 0; i < arg_count; i++) { args[i] = env->get_object(va_arg(varargs, VALUE)); } va_end(varargs); // Unlock, we're leaving extension code. LEAVE_CAPI(env->state()); Object* recv = env->get_object(receiver); Symbol* method = (Symbol*)method_name; Object* ret = cNil; // Run in block so we properly deconstruct objects allocated // on the stack if we do a longjmp because of an exception. { LookupData lookup(recv, recv->lookup_begin(env->state()), env->state()->globals().sym_private.get()); Arguments args_o(method, recv, cNil, arg_count, args); Dispatch dis(method); ret = dis.send(env->state(), env->current_call_frame(), lookup, args_o); } // We need to get the handle for the return value before getting // the GEL so that ret isn't accidentally GCd while we wait. VALUE ret_handle = 0; if(ret) ret_handle = env->get_handle(ret); // Re-entering extension code ENTER_CAPI(env->state()); // An exception occurred if(!ret) env->current_ep()->return_to(env); return ret_handle; }
void capi_define_method(const char* file, VALUE target, const char* name, CApiGenericFunction fptr, int arity, CApiMethodKind kind) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); Symbol* method_name = state->symbol(name); Module* module = NULL; if(kind == cCApiSingletonMethod) { module = c_as<Module>(env->get_object(target)->singleton_class(env->state())); } else { module = c_as<Module>(env->get_object(target)); } NativeMethod* method = NULL; method = NativeMethod::create(state, String::create(state, file), module, method_name, (void*)fptr, Fixnum::from(arity), env->current_native_frame()->capi_lock_index()); Symbol* visibility; switch(kind) { case cCApiPrivateMethod: visibility = G(sym_private); break; case cCApiProtectedMethod: visibility = G(sym_protected); break; default: /* Also catches singletons for now. @todo Verify OK. --rue */ visibility = G(sym_public); break; } module->add_method(state, method_name, method, visibility); System::vm_reset_method_cache(env->state(), module, method_name, env->current_call_frame()); }
/** * Common implementation for rb_funcall* */ VALUE capi_funcall_backend(const char* file, int line, VALUE receiver, ID method_name, size_t arg_count, VALUE* arg_array) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); env->flush_cached_data(); Array* args = Array::create(env->state(), arg_count); for(size_t i = 0; i < arg_count; i++) { args->set(env->state(), i, env->get_object(arg_array[i])); } Object* blk = cNil; if(VALUE blk_handle = env->outgoing_block()) { blk = env->get_object(blk_handle); env->set_outgoing_block(0); } Object* recv = env->get_object(receiver); // Unlock, we're leaving extension code. LEAVE_CAPI(env->state()); Object* ret = recv->send(env->state(), env->current_call_frame(), reinterpret_cast<Symbol*>(method_name), args, blk); // We need to get the handle for the return value before getting // the GEL so that ret isn't accidentally GCd while we wait. VALUE ret_handle = 0; if(ret) ret_handle = env->get_handle(ret); // Re-entering extension code ENTER_CAPI(env->state()); env->update_cached_data(); // An exception occurred if(!ret) env->current_ep()->return_to(env); return ret_handle; }
/** @note Shares code with rb_define_class_under, change there too. --rue */ VALUE rb_define_module_under(VALUE parent_handle, const char* name) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Module* parent = c_as<Module>(env->get_object(parent_handle)); Symbol* constant = env->state()->symbol(name); LEAVE_CAPI(env->state()); Module* module = rubinius::Helpers::open_module(env->state(), env->current_call_frame(), parent, constant); // The call above could have triggered an Autoload resolve, which may // raise an exception, so we have to check the value returned. if(!module) env->current_ep()->return_to(env); // Grab the module handle before grabbing the lock // so the Module isn't accidentally GC'ed. VALUE module_handle = env->get_handle(module); ENTER_CAPI(env->state()); return module_handle; }
/** @note Shares code with rb_define_module_under, change there too. --rue */ VALUE rb_define_class_under(VALUE outer, const char* name, VALUE super) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Module* module = c_as<Module>(env->get_object(outer)); Class* superclass = c_as<Class>(env->get_object(super ? super : rb_cObject)); Symbol* constant = env->state()->symbol(name); bool created = false; LEAVE_CAPI(env->state()); Class* opened_class = NULL; // Run in a block so OnStack is properly deallocated before we // might do a longjmp because of an exception. { GCTokenImpl gct; OnStack<3> os(env->state(), module, superclass, constant); opened_class = rubinius::Helpers::open_class(env->state(), gct, env->current_call_frame(), module, superclass, constant, &created); } // The call above could have triggered an Autoload resolve, which may // raise an exception, so we have to check the value returned. if(!opened_class) env->current_ep()->return_to(env); // We need to grab the handle before entering back into C-API // code. The problem otherwise can be that the GC runs and // the opened_class is GC'ed. VALUE klass = env->get_handle(opened_class); ENTER_CAPI(env->state()); if(super) rb_funcall(super, rb_intern("inherited"), 1, klass); return klass; }
// THAR BE EVEN MORE DRAGONS. // // When venturing through the valleys of the unmanaged, our hero must // remain vigilant and disciplined! If she should ever find a VALUE for // a reference in her travels: Look away! For these VALUEs are pure // death! Our hero must steel herself and continue on her quest, returning // as soon as possible to the castle of the managed. void* rb_thread_call_without_gvl2(void *(*func)(void *data), void* data1, rb_unblock_function_t ubf, void* ubf_data) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); void* ret = NULL; if(!state->check_async(env->current_call_frame())) { return ret; } if(ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) { state->vm()->interrupt_with_signal(); } else { state->vm()->wait_on_custom_function(ubf, ubf_data); } LEAVE_CAPI(env->state()); { GCIndependent guard(env); ret = (*func)(data1); } ENTER_CAPI(env->state()); state->vm()->clear_waiter(); return ret; }
Object* NativeMethod::executor_implementation(STATE, CallFrame* call_frame, Dispatch& msg, Arguments& args) { NativeMethod* nm = as<NativeMethod>(msg.method); int arity = nm->arity()->to_int(); if(arity >= 0 && (size_t)arity != args.total()) { Exception* exc = Exception::make_argument_error( state, arity, args.total(), msg.name); exc->locations(state, Location::from_call_stack(state, call_frame)); state->thread_state()->raise_exception(exc); return NULL; } NativeMethodEnvironment* env = native_method_environment.get(); // Optionally get the handles back to the proper state. if(state->shared.config.capi_global_flush) { capi::Handles* handles = state->shared.cached_handles(); if(handles->size() > 0) { for(capi::Handles::Iterator i(*handles); i.more(); i.advance()) { i->update(env); } } } // Register the CallFrame, because we might GC below this. state->set_call_frame(call_frame); NativeMethodFrame nmf(env->current_native_frame()); CallFrame* saved_frame = env->current_call_frame(); env->set_current_call_frame(call_frame); env->set_current_native_frame(&nmf); // Be sure to do this after installing nmf as the current // native frame. nmf.setup( env->get_handle(args.recv()), env->get_handle(args.block()), env->get_handle(msg.method), env->get_handle(msg.module)); Object* ret; ExceptionPoint ep(env); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ret = NULL; } else { #ifdef RBX_PROFILER if(unlikely(state->tooling())) { tooling::MethodEntry method(state, msg, args); ret = ArgumentHandler::invoke(state, nm, env, args); } else { ret = ArgumentHandler::invoke(state, nm, env, args); } #else ret = ArgumentHandler::invoke(state, nm, env, args); #endif } env->set_current_call_frame(saved_frame); env->set_current_native_frame(nmf.previous()); ep.pop(env); // Handle any signals that occurred while the native method // was running. if(!state->check_async(call_frame)) return NULL; return ret; }
Object* NativeMethod::executor_implementation(STATE, CallFrame* previous, Executable* exec, Module* mod, Arguments& args) { NativeMethod* nm = as<NativeMethod>(exec); int arity = nm->arity()->to_int(); if(arity >= 0 && (size_t)arity != args.total()) { Exception* exc = Exception::make_argument_error( state, arity, args.total(), args.name()); exc->locations(state, Location::from_call_stack(state, previous)); state->raise_exception(exc); return NULL; } NativeMethodEnvironment* env = native_method_environment.get(); // Optionally get the handles back to the proper state. if(state->shared().config.capi_global_flush) { std::list<capi::Handle*>* handles = env->state()->memory()->cached_capi_handles(); for(std::list<capi::Handle*>::iterator i = handles->begin(); i != handles->end(); ++i) { (*i)->update(env); } } NativeMethodFrame nmf(env->current_native_frame()); CallFrame* call_frame = ALLOCA_CALLFRAME(0); call_frame->previous = previous; call_frame->constant_scope_ = 0; call_frame->dispatch_data = (void*)&nmf; call_frame->compiled_code = 0; call_frame->flags = CallFrame::cNativeMethod; call_frame->optional_jit_data = 0; call_frame->top_scope_ = 0; call_frame->scope = 0; call_frame->arguments = &args; CallFrame* saved_frame = env->current_call_frame(); env->set_current_call_frame(call_frame); env->set_current_native_frame(&nmf); // Register the CallFrame, because we might GC below this. state->set_call_frame(call_frame); // Be sure to do this after installing nmf as the current // native frame. nmf.setup( env->get_handle(args.recv()), env->get_handle(args.block()), env->get_handle(exec), env->get_handle(mod)); // We've got things setup (they can be GC'd properly), so we need to // wait before entering the extension code. ENTER_CAPI(state); Object* ret; ExceptionPoint ep(env); #ifdef RBX_PROFILER // This is organized like this so that we don't jump past the destructor of // MethodEntry. It's duplicated, but it's much easier to understand than // trying to de-dup it. OnStack<2> os(state, exec, mod); if(unlikely(state->vm()->tooling())) { tooling::MethodEntry method(state, exec, mod, args); RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, mod, args.name(), call_frame); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ret = NULL; } else { ret = ArgumentHandler::invoke(state, nm, env, args); } RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, mod, args.name(), call_frame); } else { RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, mod, args.name(), call_frame); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ret = NULL; } else { ret = ArgumentHandler::invoke(state, nm, env, args); } RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, mod, args.name(), call_frame); } #else RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, mod, args.name(), call_frame); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ret = NULL; } else { ret = ArgumentHandler::invoke(state, nm, env, args); } RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, mod, args.name(), call_frame); #endif env->set_current_call_frame(saved_frame); env->set_current_native_frame(nmf.previous()); ep.pop(env); LEAVE_CAPI(state); OnStack<1> os_ret(state, ret); // Handle any signals that occurred while the native method // was running. if(!state->check_async(call_frame)) return NULL; return ret; }
ID rb_frame_last_func() { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); return reinterpret_cast<ID>(env->current_call_frame()->name()); }
ID rb_frame_last_func() { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); return env->get_handle(env->current_call_frame()->name()); }
Object* NativeMethod::executor_implementation(STATE, CallFrame* call_frame, Executable* exec, Module* mod, Arguments& args) { NativeMethod* nm = as<NativeMethod>(exec); int arity = nm->arity()->to_int(); if(arity >= 0 && (size_t)arity != args.total()) { Exception* exc = Exception::make_argument_error( state, arity, args.total(), args.name()); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); return NULL; } NativeMethodEnvironment* env = native_method_environment.get(); // Optionally get the handles back to the proper state. if(state->shared().config.capi_global_flush) { capi::Handles* handles = state->shared().cached_handles(); if(handles->size() > 0) { for(capi::Handles::Iterator i(*handles); i.more(); i.advance()) { i->update(env); } } } // Register the CallFrame, because we might GC below this. state->set_call_frame(call_frame); NativeMethodFrame nmf(env->current_native_frame()); CallFrame cf; cf.previous = call_frame; cf.cm = 0; cf.scope = 0; cf.dispatch_data = (void*)&nmf; cf.flags = CallFrame::cNativeMethod; CallFrame* saved_frame = env->current_call_frame(); env->set_current_call_frame(&cf); env->set_current_native_frame(&nmf); // Be sure to do this after installing nmf as the current // native frame. nmf.setup( env->get_handle(args.recv()), env->get_handle(args.block()), env->get_handle(exec), env->get_handle(mod)); // We've got things setup (they can be GC'd properly), so we need to // wait before entering the extension code. ENTER_CAPI(state); Object* ret; ExceptionPoint ep(env); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ret = NULL; } else { #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { tooling::MethodEntry method(state, exec, mod, args); ret = ArgumentHandler::invoke(state, nm, env, args); } else { ret = ArgumentHandler::invoke(state, nm, env, args); } #else ret = ArgumentHandler::invoke(state, nm, env, args); #endif } env->set_current_call_frame(saved_frame); env->set_current_native_frame(nmf.previous()); ep.pop(env); LEAVE_CAPI(state); // Handle any signals that occurred while the native method // was running. if(!state->check_async(call_frame)) return NULL; return ret; }
Object* run_function(STATE) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Thread* self = state->vm()->thread.get(); NativeMethod* nm = capi::c_as<NativeMethod>(self->locals_aref(state, state->symbol("function"))); Pointer* ptr = capi::c_as<Pointer>(self->locals_aref(state, state->symbol("argument"))); self->locals_remove(state, state->symbol("function")); self->locals_remove(state, state->symbol("argument")); GCTokenImpl gct; NativeMethodFrame nmf(env, 0, nm); CallFrame cf; cf.previous = 0; cf.constant_scope_ = 0; cf.dispatch_data = (void*)&nmf; cf.compiled_code = 0; cf.flags = CallFrame::cNativeMethod; cf.optional_jit_data = 0; cf.top_scope_ = 0; cf.scope = 0; cf.arguments = 0; CallFrame* saved_frame = env->current_call_frame(); env->set_current_call_frame(&cf); env->set_current_native_frame(&nmf); nmf.setup( env->get_handle(self), env->get_handle(cNil), env->get_handle(nm), env->get_handle(nm->module())); { OnStack<3> os(state, self, nm, ptr); self->hard_unlock(state, gct, &cf); } ENTER_CAPI(state); Object* ret = NULL; ExceptionPoint ep(env); PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { // Setup exception in thread so it's raised when joining // Reload self because it might have been moved self = state->vm()->thread.get(); CallFrame* call_frame = env->current_call_frame(); { OnStack<1> os(state, self); self->hard_lock(state, gct, call_frame, false); Exception* exc = capi::c_as<Exception>(self->current_exception(state)); self->exception(state, exc); self->alive(state, cFalse); self->hard_unlock(state, gct, call_frame); } return NULL; } else { ret = env->get_object(nm->func()(ptr->pointer)); } LEAVE_CAPI(state); env->set_current_call_frame(saved_frame); env->set_current_native_frame(nmf.previous()); ep.pop(env); self = state->vm()->thread.get(); OnStack<1> os(state, self); self->hard_lock(state, gct, &cf, false); self->alive(state, cFalse); self->hard_unlock(state, gct, &cf); return ret; }