VALUE rb_struct_define(const char *name, ...) { va_list args; va_start(args, name); NativeMethodEnvironment* env = NativeMethodEnvironment::get(); VALUE nm; if(name) { nm = rb_str_new2(name); } else { nm = Qnil; } char *sym; Array* array = Array::create(env->state(), 0); while((sym = va_arg(args, char*)) != 0) { array->append(env->state(), env->get_object(rb_intern(sym))); } va_end(args); return rb_funcall(rb_cStruct, rb_intern("make_struct"), 2, nm, env->get_handle(array)); }
unsigned long rb_big2ulong(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Object* object = env->get_object(obj); if(object->nil_p()) { rb_raise(rb_eTypeError, "no implicit conversion from nil to unsigned long"); } else if(Bignum* big = try_as<Bignum>(object)) { if((size_t)mp_count_bits(big->mp_val()) > sizeof(long) * CHAR_BIT) rb_raise(rb_eRangeError, "bignum too big to convert into long"); unsigned long val = big->to_ulong(); if(big->mp_val()->sign == MP_NEG) { if((long)val < 0) rb_raise(rb_eRangeError, "bignum out of range of unsigned long"); return -val; } return val; } rb_raise(rb_eArgError, "parameter is not a Bignum"); return 0; }
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); thr->group(state, state->vm()->thread.get()->group()); VALUE thr_handle = env->get_handle(thr); thr->fork(state); return thr_handle; }
void* rb_thread_call_with_gvl(void* (*func)(void*), void* data) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); env->state()->shared().enter_capi(env->state()); env->state()->gc_dependent(); void* ret = (*func)(data); env->state()->gc_independent(); env->state()->shared().leave_capi(env->state()); return ret; }
/** @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); }
VALUE rb_const_get_from(VALUE module_handle, ID id_name) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); Symbol* name = reinterpret_cast<Symbol*>(id_name); Module* module = c_as<Module>(env->get_object(module_handle)); ConstantMissingReason reason = vNonExistent; while(!module->nil_p()) { Object* val = module->get_const(state, name, G(sym_private), &reason); if(reason == vFound) { if(Autoload* autoload = try_as<Autoload>(val)) { return capi_fast_call(env->get_handle(autoload), rb_intern("call"), 1, module_handle); } return env->get_handle(val); } module = module->superclass(); } return const_missing(module_handle, id_name); }
// 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; }
// 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->vm()->thread_interrupted_p(state)) { 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()); { UnmanagedPhase unmanaged(env->state()); ret = (*func)(data1); } ENTER_CAPI(env->state()); state->vm()->clear_waiter(); return ret; }
/* * rb_thread_wait_fd actually waits until a read is * available on the given fd */ void rb_thread_wait_fd(int fd) { fd_set fds; FD_ZERO(&fds); FD_SET((int_fd_t)fd, &fds); NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); LEAVE_CAPI(state); { GCIndependent guard(env); state->vm()->interrupt_with_signal(); state->vm()->thread->sleep(state, cTrue); int ready = 0; while(!ready) { ready = select(fd+1, &fds, 0, 0, 0); } state->vm()->thread->sleep(state, cFalse); state->vm()->clear_waiter(); } ENTER_CAPI(env->state()); }
/** @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; }
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(;;) { { GlobalLock::UnlockGuard guard(env); ret = select(max, read, write, except, tvp); } 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(); } } 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; }
/** @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); }
VALUE rb_const_get(VALUE module_handle, ID id_name) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Symbol* name = reinterpret_cast<Symbol*>(id_name); Module* module = c_as<Module>(env->get_object(module_handle)); bool found = false; while(!module->nil_p()) { Object* val = module->get_const(env->state(), name, &found); if(found) { if(Autoload* autoload = try_as<Autoload>(val)) { return capi_fast_call(env->get_handle(autoload), rb_intern("call"), 0); } return env->get_handle(val); } module = module->superclass(); } // Try from Object as well. module = env->state()->globals().object.get(); while(!module->nil_p()) { Object* val = module->get_const(env->state(), name, &found); if(found) { if(Autoload* autoload = try_as<Autoload>(val)) { return capi_fast_call(env->get_handle(autoload), rb_intern("call"), 0); } return env->get_handle(val); } module = module->superclass(); } return const_missing(module_handle, id_name); }
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()); }
VALUE rb_block_call(VALUE obj, ID meth, int argc, VALUE* argv, VALUE(*cb)(ANYARGS), VALUE cb_data) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); if(cb) { Proc* prc = capi::wrap_c_function((void*)cb, cb_data, C_BLOCK_CALL); env->set_outgoing_block(env->get_handle(prc)); } else { env->set_outgoing_block(env->get_handle(env->block())); } return rb_funcall2(obj, meth, argc, argv); }
/** @note Shares code with rb_define_module_under, change there too. --rue */ VALUE rb_define_class_id_under(VALUE outer, ID 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 = reinterpret_cast<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. { OnStack<3> os(env->state(), module, superclass, constant); opened_class = rubinius::Helpers::open_class(env->state(), 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; }
VALUE rb_path2class(const char* path) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); Module* mod = G(object); char* pathd = strdup(path); char* ptr = pathd; char* context; char* name; ConstantMissingReason reason = vNonExistent; while((name = strtok_r(ptr, ":", &context))) { ptr = NULL; Object* val = mod->get_const(env->state(), env->state()->symbol(name), G(sym_private), &reason); if(reason != vFound) { free(pathd); rb_raise(rb_eArgError, "undefined class or module %s", path); } if(Autoload* autoload = try_as<Autoload>(val)) { VALUE m = capi_fast_call(env->get_handle(autoload), rb_intern("call"), 1, env->get_handle(mod)); val = env->get_object(m); } if(!(mod = try_as<Module>(val))) { free(pathd); capi_raise_type_error(Module::type, val); } } free(pathd); return env->get_handle(mod); }
VALUE rb_rescue2(VALUE (*func)(ANYARGS), VALUE arg1, VALUE (*raise_func)(ANYARGS), VALUE arg2, ...) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); VALUE ret = Qnil; ExceptionPoint ep(env); va_list exc_classes; PLACE_EXCEPTION_POINT(ep); if(unlikely(ep.jumped_to())) { ep.pop(env); if(env->state()->thread_state()->raise_reason() != cException) { //Something bad happened, we shouldn't be here. return Qnil; } VALUE exc_handle = env->get_handle(env->state()->thread_state()->as_object(env->state())); bool handle_exc = false; va_start(exc_classes, arg2); while(VALUE eclass = va_arg(exc_classes, VALUE)) { if(rb_obj_is_kind_of(exc_handle, eclass) == Qtrue) { handle_exc = true; break; } } va_end(exc_classes); if(handle_exc) { ret = (*raise_func)(arg2); env->state()->thread_state()->clear_exception(); } else { env->current_ep()->return_to(env); } } else { ret = (*func)(arg1); ep.pop(env); } return ret; }
VALUE rb_data_object_alloc(VALUE klass, void* ptr, RUBY_DATA_FUNC mark, RUBY_DATA_FUNC free) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Class* data_klass = c_as<Class>(env->get_object(klass)); Data* data = Data::create(env->state(), ptr, mark, free); data->klass(env->state(), data_klass); // Data objects are directly manipulated, so we have to always // track them. env->state()->om->remember_object(data); return env->get_handle(data); }
VALUE rb_enc_associate_index(VALUE obj, int index) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Encoding* enc = as<Encoding>( Encoding::encoding_list(env->state())->get(env->state(), index)); Object* val = env->get_object(obj); if(String* str = try_as<String>(val)) { str->encoding(env->state(), enc); } else if(Regexp* reg = try_as<Regexp>(val)) { reg->encoding(env->state(), enc); } else if(Symbol* sym = try_as<Symbol>(val)) { sym->encoding(env->state(), enc); } else { rb_raise(rb_eArgError, "object does not have an associated Encoding"); } return obj; }
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 rb_ary_new4(unsigned long length, const VALUE* objects) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Array* array = Array::create(env->state(), length); array->start(env->state(), Fixnum::from(0)); array->total(env->state(), Fixnum::from(length)); if(objects) { for(std::size_t i = 0; i < length; ++i) { // @todo determine if we need to check these objects for whether // they are arrays and flush any caches Object* object = env->get_object(objects[i]); array->set(env->state(), i, object); } } return env->get_handle(array); }
VALUE rb_time_timespec_new(const struct timespec *ts, int offset) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Time* time = Time::at(env->state(), ts->tv_sec, ts->tv_nsec); if(-86400 < offset && offset < 86400) { /* fixed offset */ time->offset(env->state(), Fixnum::from(offset)); time->is_gmt(env->state(), cFalse); time->zone(env->state(), cNil); } else if(offset == INT_MAX) { /* localtime */ } else if(offset == INT_MAX - 1) { /* UTC */ time->is_gmt(env->state(), cTrue); } else { rb_raise(rb_eArgError, "utc_offset out of range"); } return env->get_handle(time); }
VALUE rb_int_positive_pow(long x, unsigned long y) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); Integer* base = Integer::from(state, x); Integer* exp = Integer::from(state, y); if(Fixnum* base_fix = try_as<Fixnum>(base)) { if(Fixnum* exp_fix = try_as<Fixnum>(exp)) { return env->get_handle(base_fix->pow(state, exp_fix)); } else { return env->get_handle(base_fix->pow(state, as<Bignum>(exp))); } } else { if(Fixnum* exp_fix = try_as<Fixnum>(exp)) { return env->get_handle(as<Bignum>(base)->pow(state, exp_fix)); } else { return env->get_handle(as<Bignum>(base)->pow(state, as<Bignum>(exp))); } } }
int rb_to_encoding_index(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Encoding* enc; switch(TYPE(obj)) { case T_ENCODING: enc = c_as<Encoding>(env->get_object(obj)); break; case T_STRING: enc = Encoding::find(env->state(), RSTRING_PTR(obj)); break; default: obj = rb_funcall(obj, rb_intern("to_str"), 0); enc = Encoding::find(env->state(), RSTRING_PTR(obj)); } if(enc->nil_p()) return -1; return Encoding::find_index(env->state(), enc->name()->c_str(env->state())); }
VALUE rb_ary_to_ary(VALUE object) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Object* obj = env->get_object(object); if(kind_of<Array>(obj)) { return object; } ID to_ary_id = rb_intern("to_ary"); if(rb_respond_to(object, to_ary_id)) { return rb_funcall(object, to_ary_id, 0); } else { Array* array = Array::create(env->state(), 1); array->set(env->state(), 0, env->get_object(object)); return env->get_handle(array); } }
int rb_enc_get_index(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Object* val = env->get_object(obj); Encoding* enc; if(String* str = try_as<String>(val)) { enc = str->encoding(env->state()); } else if(Regexp* reg = try_as<Regexp>(val)) { enc = reg->encoding(env->state()); } else if(Symbol* sym = try_as<Symbol>(val)) { enc = sym->encoding(env->state()); } else { rb_raise(rb_eArgError, "object does not have an associated Encoding"); } if(enc->nil_p()) return -1; return Encoding::find_index(env->state(), enc->get_encoding()->name); }
VALUE rb_ary_new3(unsigned long length, ...) { va_list args; va_start(args, length); NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Array* array = Array::create(env->state(), length); array->start(env->state(), Fixnum::from(0)); array->total(env->state(), Fixnum::from(length)); for(unsigned long i = 0; i < length; ++i) { // @todo determine if we need to check these objects for whether // they are arrays and flush any caches Object* object = env->get_object(va_arg(args, VALUE)); array->set(env->state(), i, object); } va_end(args); return env->get_handle(array); }
VALUE rb_funcall2(VALUE receiver, ID method_name, int arg_count, const VALUE* v_args) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Object* args[arg_count]; for(int i = 0; i < arg_count; i++) { args[i] = env->get_object(v_args[i]); } Object* blk = cNil; if(VALUE blk_handle = env->outgoing_block()) { blk = env->get_object(blk_handle); env->set_outgoing_block(0); } return capi_funcall_backend_native(env, "", 0, env->get_object(receiver), reinterpret_cast<Symbol*>(method_name), arg_count, args, blk); }
// THAR BE DRAGONS. // // When venturing through the valleys of the unmanaged, our hero must // remain vigilant and disiplined! 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. VALUE rb_thread_blocking_region(rb_blocking_function_t func, void* data, rb_unblock_function_t ubf, void* ubf_data) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); State* state = env->state(); VALUE ret = Qnil; if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) { state->vm()->interrupt_with_signal(); } else { state->vm()->wait_on_custom_function(ubf, ubf_data); } env->state()->shared().leave_capi(env->state()); { GCIndependent guard(env); ret = (*func)(data); } env->state()->shared().enter_capi(env->state()); state->vm()->clear_waiter(); return ret; }