VALUE capi_yield_backend(NativeMethodEnvironment* env, Object* blk, size_t arg_count, Object** arg_vals) { int marker = 0; if(!capi_check_interrupts(env->state(), env->current_call_frame(), &marker)) { env->current_ep()->return_to(env); } env->flush_cached_data(); // Unlock, we're leaving extension code. LEAVE_CAPI(env->state()); Object* ret = cNil; STATE = env->state(); CallFrame* call_frame = env->current_call_frame(); // Run in separate block so the arguments are destructed // properly before we make a potential longjmp. { Arguments args(G(sym_call), blk, arg_count, arg_vals); if(BlockEnvironment* be = try_as<BlockEnvironment>(blk)) { ret = be->call(state, call_frame, args); } else if(Proc* proc = try_as<Proc>(blk)) { ret = proc->yield(state, call_frame, args); } else if(blk->nil_p()) { state->raise_exception(Exception::make_lje(state, call_frame)); ret = NULL; } else { Dispatch dis(G(sym_call)); ret = dis.send(state, call_frame, args); } } // 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; }
VALUE capi_call_super_native(NativeMethodEnvironment* env, size_t arg_count, Object** args) { int marker = 0; if(!capi_check_interrupts(env->state(), env->current_call_frame(), &marker)) { env->current_ep()->return_to(env); } env->flush_cached_data(); NativeMethodFrame* frame = NativeMethodFrame::current(); Object* recv = env->get_object(frame->receiver()); Module* mod = c_as<Module>(env->get_object(frame->module())); Symbol* name = c_as<NativeMethod>(env->get_object(frame->method()))->name(); // Unlock, we're leaving extension code. LEAVE_CAPI(env->state()); Object* ret = cNil; // Use a block objects on the stack are properly deconstructed when // we do a potential longjmp. { LookupData lookup(recv, mod->superclass(), env->state()->globals().sym_private.get()); Arguments args_o(name, recv, arg_count, args); Dispatch dis(name); 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()); env->update_cached_data(); // An exception occurred if(!ret) env->current_ep()->return_to(env); return ret_handle; }
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; }
VALUE capi_funcall_backend_native(NativeMethodEnvironment* env, const char* file, int line, Object* recv, Symbol* method, size_t arg_count, Object** args, Object* block) { int marker = 0; if(!capi_check_interrupts(env->state(), env->current_call_frame(), &marker)) { env->current_ep()->return_to(env); } env->flush_cached_data(); // Unlock, we're leaving extension code. LEAVE_CAPI(env->state()); Object* ret = cNil; // Run in a block so objects are properly deconstructed when 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, block, 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()); env->update_cached_data(); // An exception occurred if(!ret) env->current_ep()->return_to(env); return ret_handle; }
/** * 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; }
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* 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; }