Object* run_instance(STATE) { /* These are all referenced, so OnStack is not necessary. Additionally, * thread is pinned, so we do not need to worry about it moving. */ Thread* thread = state->vm()->thread.get(); Array* args = thread->args(); Object* block = thread->block(); if(thread->initialized()->false_p() || args->nil_p() || block->nil_p()) { return cNil; } Object* value = block->send(state, G(sym_call), args, block); /* We explicitly set the current CallFrame reference to NULL because we * are at the top of the stack in terms of managed code. */ state->vm()->set_call_frame(NULL); thread->exception(state, state->vm()->thread_state()->current_exception()); if(state->vm()->thread_state()->raise_reason() == cThreadKill) { thread->value(state, cNil); } else if(value) { thread->value(state, value); } Object* mirror = G(mirror)->send(state, state->symbol("reflect"), Array::from_tuple(state, Tuple::from(state, 1, thread))); mirror->send(state, state->symbol("finish")); return value; }
/** * Runs rbx from the filesystem. Searches for the Rubinius runtime files * according to the algorithm in find_runtime(). */ void Environment::run_from_filesystem() { int i = 0; state->vm()->set_root_stack(reinterpret_cast<uintptr_t>(&i), VM::cStackDepthMax); std::string runtime = system_prefix() + RBX_RUNTIME_PATH; load_platform_conf(runtime); boot_vm(); start_finalizer(); load_argv(argc_, argv_); start_signals(); state->vm()->initialize_config(); load_tool(); G(rubinius)->set_const(state, "Signature", Integer::from(state, signature_)); if(LANGUAGE_20_ENABLED(state)) { runtime += "/20"; } else if(LANGUAGE_19_ENABLED(state)) { runtime += "/19"; } else { runtime += "/18"; } G(rubinius)->set_const(state, "RUNTIME_PATH", String::create(state, runtime.c_str(), runtime.size())); load_kernel(runtime); shared->finalizer_handler()->start_thread(state); run_file(runtime + "/loader.rbc"); state->vm()->thread_state()->clear(); Object* loader = G(rubinius)->get_const(state, state->symbol("Loader")); if(loader->nil_p()) { rubinius::bug("Unable to find loader"); } OnStack<1> os(state, loader); Object* inst = loader->send(state, 0, state->symbol("new")); if(inst) { OnStack<1> os2(state, inst); inst->send(state, 0, state->symbol("main")); } else { rubinius::bug("Unable to instantiate loader"); } }
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; }
Object* rbx_cast_for_splat_block_arg(STATE, CallFrame* call_frame, Arguments& args) { if(args.total() == 1) { Object* obj = args.get_argument(0); if(!kind_of<Array>(obj)) { /* Yes, you are reading this code correctly: In Ruby 1.8, calling a * block with these forms { |*| } and { |*a| } with a single argument * that is not an Array and which responds to #to_ary will cause #to_ary * to be called and its return value ignored. Ultimately, the original * object itself is wrapped in an Array and passed to the block. */ if(CBOOL(obj->respond_to(state, state->symbol("to_ary"), cFalse))) { OnStack<1> os(state, obj); Object* ignored = obj->send(state, call_frame, state->symbol("to_ary")); if(!ignored->nil_p() && !kind_of<Array>(ignored)) { Exception::type_error(state, "to_ary must return an Array", call_frame); return 0; } } } Array* ary = Array::create(state, 1); ary->set(state, 0, obj); return ary; } else { Array* ary = Array::create(state, args.total()); for(size_t i = 0; i < args.total(); i++) { ary->set(state, i, args.get_argument(i)); } return ary; } }
Object* rbx_cast_for_multi_block_arg(STATE, CallFrame* call_frame, Arguments& args) { /* If there is only one argument and that thing is an array... AND the thing being invoked is not a lambda... */ if(!(call_frame->flags & CallFrame::cIsLambda) && args.total() == 1) { Object* obj = args.get_argument(0); if(kind_of<Array>(obj)) { return obj; } else if(CBOOL(obj->respond_to(state, state->symbol("to_ary"), cFalse))) { obj = obj->send(state, call_frame, state->symbol("to_ary")); if(kind_of<Array>(obj)) { return obj; } else { Exception::type_error(state, "to_ary must return an Array", call_frame); return 0; } } // One argument and it's not Array or Array-ish at all, so fall through // and let it be wrapped in an array. } Array* ary = Array::create(state, args.total()); assert(kind_of<Array>(ary)); for(size_t i = 0; i < args.total(); i++) { assert(kind_of<Array>(ary)); ary->set(state, i, args.get_argument(i)); } assert(kind_of<Array>(ary)); return ary; }
/** * 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); }
Object* Thread::main_thread(STATE) { GCTokenImpl gct; state->vm()->thread->hard_unlock(state, gct, 0); std::string& runtime = state->shared().env()->runtime_path(); G(rubinius)->set_const(state, "Signature", Integer::from(state, state->shared().env()->signature())); G(rubinius)->set_const(state, "RUNTIME_PATH", String::create(state, runtime.c_str(), runtime.size())); state->vm()->thread->pid(state, Fixnum::from(gettid())); state->shared().env()->load_core(state, runtime); state->vm()->thread->alive(state, cTrue); state->vm()->thread_state()->clear(); state->shared().start_console(state); state->shared().start_metrics(state); Object* klass = G(rubinius)->get_const(state, state->symbol("Loader")); if(klass->nil_p()) { rubinius::bug("unable to find class Rubinius::Loader"); } Object* instance = 0; OnStack<1> os(state, instance); instance = klass->send(state, 0, state->symbol("new")); if(instance) { state->shared().env()->set_loader(instance); } else { rubinius::bug("unable to instantiate Rubinius::Loader"); } // Enable the JIT after the core library has loaded G(jit)->enable(state); Object* exit = instance->send(state, 0, state->symbol("main")); state->shared().signals()->system_exit(state->vm()->thread_state()->raise_value()); return exit; }
void ArrayObject::send() { for (size_t i = 0; i < _objs.size(); i++) { Object* item = _objs[i]; if (item->pyObject() == PyList_GetItem(_obj, i)) { item->send(); } } }
Object* Thread::main_thread(STATE) { state->vm()->managed_phase(state); state->vm()->thread()->pid(state, Fixnum::from(gettid())); state->shared().env()->load_core(state); state->vm()->thread_state()->clear(); state->shared().start_console(state); state->shared().start_diagnostics(state); state->shared().start_profiler(state); state->shared().start_jit(state); Object* klass = G(rubinius)->get_const(state, state->symbol("Loader")); if(klass->nil_p()) { state->shared().env()->missing_core("unable to find class Rubinius::Loader"); return 0; } Object* instance = 0; OnStack<1> os(state, instance); instance = klass->send(state, state->symbol("new")); if(instance) { state->shared().env()->set_loader(instance); } else { state->shared().env()->missing_core("unable to instantiate Rubinius::Loader"); return 0; } // Enable the JIT after the core library has loaded G(jit)->enable(state); Object* value = instance->send(state, state->symbol("main")); state->shared().signals()->system_exit(state->vm()->thread_state()->raise_value()); return value; }
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); }
Object* rbx_destructure_args(STATE, CallFrame* call_frame, Arguments& args) { if(args.total() == 1) { Object* obj = args.get_argument(0); if(Array* ary = try_as<Array>(obj)) { args.use_array(ary); } else if(CBOOL(obj->respond_to(state, state->symbol("to_ary"), cFalse))) { obj = obj->send(state, call_frame, state->symbol("to_ary")); if(Array* ary2 = try_as<Array>(obj)) { args.use_array(ary2); } else { Exception::type_error(state, "to_ary must return an Array", call_frame); return 0; } } } return cNil; }
/** * 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; }
inline bool cast_for_splat_block_arg(STATE, CallFrame* call_frame) { if(!call_frame->arguments) { Exception::internal_error(state, "no arguments object"); return false; } if(call_frame->arguments->total() == 1) { Object* obj = call_frame->arguments->get_argument(0); if(!rubinius::kind_of<Array>(obj)) { /* Yes, you are reading this code correctly: In Ruby 1.8, calling a * block with these forms { |*| } and { |*a| } with a single argument * that is not an Array and which responds to #to_ary will cause #to_ary * to be called and its return value ignored. Ultimately, the original * object itself is wrapped in an Array and passed to the block. */ if(CBOOL(obj->respond_to(state, G(sym_to_ary), cFalse))) { OnStack<1> os(state, obj); Object* ignored = obj->send(state, G(sym_to_ary)); if(!ignored) return false; if(!ignored->nil_p() && !rubinius::kind_of<Array>(ignored)) { Exception::type_error(state, "to_ary must return an Array"); return false; } } } Array* ary = Array::create(state, 1); ary->set(state, 0, obj); stack_push(ary); } else { Array* ary = Array::create(state, call_frame->arguments->total()); for(size_t i = 0; i < call_frame->arguments->total(); i++) { ary->set(state, i, call_frame->arguments->get_argument(i)); } stack_push(ary); } return true; }
int rbx_destructure_args(STATE, CallFrame* call_frame, Arguments& args) { Object* obj = args.get_argument(0); Array* ary = 0; if(!(ary = try_as<Array>(obj))) { if(CBOOL(obj->respond_to(state, G(sym_to_ary), cFalse))) { if(!(obj = obj->send(state, call_frame, G(sym_to_ary)))) { return -1; } if(!(ary = try_as<Array>(obj)) && !obj->nil_p()) { Exception::type_error(state, "to_ary must return an Array", call_frame); return -1; } } } if(ary) { args.use_array(ary); } return args.total(); }
inline bool cast_for_multi_block_arg(STATE, CallFrame* call_frame) { if(!call_frame->arguments) { Exception::internal_error(state, "no arguments object"); return false; } /* If there is only one argument and that thing is an array... AND the thing being invoked is not a lambda... */ if(!(call_frame->flags & CallFrame::cIsLambda) && call_frame->arguments->total() == 1) { Object* obj = call_frame->arguments->get_argument(0); if(rubinius::kind_of<Array>(obj)) { stack_push(obj); } else if(CBOOL(obj->respond_to(state, G(sym_to_ary), cFalse))) { obj = obj->send(state, G(sym_to_ary)); if(!obj) return false; if(rubinius::kind_of<Array>(obj)) { stack_push(obj); } else { Exception::type_error(state, "to_ary must return an Array"); return false; } } else { Array* ary = Array::create(state, 1); ary->set(state, 0, obj); stack_push(ary); } } else { Array* ary = Array::create(state, call_frame->arguments->total()); for(size_t i = 0; i < call_frame->arguments->total(); i++) { ary->set(state, i, call_frame->arguments->get_argument(i)); } stack_push(ary); } return true; }
static bool call(STATE, CallFrame* call_frame, MachineCode* mcode, StackVariables* scope, Arguments& args, int flags) { /* There are 5 types of arguments, illustrated here: * m(a, b=1, *c, d, e: 2) * * where: * a is a head (pre optional/splat) fixed position argument * b is an optional argument * c is a rest argument * d is a post (optional/splat) argument * e is a keyword argument, which may be required (having no default * value), optional, or keyword rest argument (**kw). * * The arity checking above ensures that we have at least one argument * on the stack for each fixed position argument (ie arguments a and d * above). * * We assign the arguments in the following order: first the fixed * arguments (head and post) and possibly the keyword argument, then the * optional arguments, and the remainder (if any) are combined in an * array for the rest argument. * * We assign values from the sender's side to local variables on the * receiver's side. Which values to assign are computed as follows: * * sender indexes (arguments) * -v-v-v-v-v-v-v-v-v-v-v-v-- * * 0...H H...H+ON H+ON...N-P-K N-P-K...N-K N-K * | | | | | * H O R P K * | | | | | * 0...H H...H+O RI PI...PI+P KI * * -^-^-^-^-^-^-^-^-^-^-^-^- * receiver indexes (locals) * * where: * * arguments passed by sender * -------------------------- * N : total number of arguments passed * H* : number of head arguments * E : number of extra arguments * ON : number or arguments assigned to optional parameters * RN : number of arguments assigned to the rest argument * P* : number of post arguments * K : number of keyword arguments passed, 1 if the last argument is * a Hash or if #to_hash returns a Hash, 0 otherwise * HL : maximum number of head arguments passed * PM : post arguments missing when N < M * * parameters defined by receiver * ------------------------------ * T : total number of parameters * M : number of head + post parameters * H* : number of head parameters * O : number of optional parameters * RP : true if a rest parameter is defined, false otherwise * RI : index of rest parameter if RP is true, else -1 * P* : number of post parameters * PI : index of the first post parameter * KP : true if a keyword parameter is defined, false otherwise * KA : true if the keyword argument was extracted from the passed * argument leaving a remaining value * KI : index of keyword rest parameter * * (*) The values of H and P are fixed and they represent the same * values at both the sender and receiver, so they are named the same. * * formulas * -------- * K = KP && !KA && N > M ? 1 : 0 * E = N - M - K * O = T - M - (keywords ? 1 : 0) * ON = (X = MIN(O, E)) > 0 ? X : 0 * RN = RP && (X = E - ON) > 0 ? X : 0 * PI = H + O + (RP ? 1 : 0) * KI = RP ? T : T - 1 * HL = (H - N) > 0 ? MIN(N, H - N) : H * PM = N - H > 0 ? P - (N - H) : P * */ native_int N = args.total(); const native_int T = mcode->total_args; const native_int M = mcode->required_args; const native_int O = T - M - (mcode->keywords ? 1 : 0); /* TODO: Clean up usage to uniformly refer to 'splat' as N arguments * passed from sender at a single position and 'rest' as N arguments * collected into a single argument at the receiver. */ const native_int RI = mcode->splat_position; const bool RP = (RI >= 0); // expecting 0, got 0. if(T == 0 && N == 0) { if(RP) { scope->set_local(mcode->splat_position, Array::create(state, 0)); } return true; } const bool lambda = ((flags & CallFrame::cIsLambda) == CallFrame::cIsLambda); // Only do destructuring in non-lambda mode if(!lambda) { /* If only one argument was yielded and the block takes more than one * argument or has form { |a, | }: * * 1. If the object is an Array, assign elements to arguments. * 2. If the object returns 'nil' from #to_ary, assign the object * to the first argument. * 3. If the object returns an Array from #to_ary, assign the * elements to the arguments. * 4. If the object returns non-Array from #to_ary, raise a TypeError. */ if(N == 1 && (T > 1 || (RP && T > 0) || RI < -2)) { Object* obj = args.get_argument(0); Array* ary = 0; if(!(ary = try_as<Array>(obj))) { if(CBOOL(obj->respond_to(state, G(sym_to_ary), cFalse))) { if(!(obj = obj->send(state, call_frame, G(sym_to_ary)))) { return false; } if(!(ary = try_as<Array>(obj)) && !obj->nil_p()) { Exception::type_error(state, "to_ary must return an Array", call_frame); return false; } } } if(ary) { if(RI == -4 && M == 1) { args.use_argument(ary); } else { args.use_array(ary); } N = args.total(); } } } const native_int P = mcode->post_args; const native_int H = M - P; // Too many args (no rest argument!) if(!RP && N > T) { if(lambda) return false; N = T; } // Too few args! if(lambda && N < M) return false; Object* kw = 0; Object* kw_remainder = 0; bool KP = false; bool KA = false; if(mcode->keywords && N > M) { Object* obj = args.get_argument(args.total() - 1); OnStack<1> os(state, obj); Object* arguments[2]; arguments[0] = obj; arguments[1] = RBOOL(O > 0 || RP); Arguments args(G(sym_keyword_object), G(runtime), 2, arguments); Dispatch dis(G(sym_keyword_object)); Object* kw_result = dis.send(state, call_frame, args); if(kw_result) { if(Array* ary = try_as<Array>(kw_result)) { Object* o = 0; if(!(o = ary->get(state, 0))->nil_p()) { kw_remainder = o; KA = true; } kw = ary->get(state, 1); KP = true; } } else { return false; } } // A single kwrest argument if(mcode->keywords && !RP && !KP && N >= T) { if(lambda) return false; N = T - 1; } const native_int K = (KP && !KA && N > M) ? 1 : 0; const native_int N_M_K = N - M - K; const native_int E = N_M_K > 0 ? N_M_K : 0; native_int X; const native_int ON = (X = MIN(O, E)) > 0 ? X : 0; const native_int RN = (RP && (X = E - ON) > 0) ? X : 0; const native_int PI = H + O + (RP ? 1 : 0); const native_int KI = RP ? T : T - 1; native_int a = 0; // argument index native_int l = 0; // local index // head arguments if(H > 0) { for(; l < H && a < N; l++, a++) { scope->set_local(l, args.get_argument(a)); } for(; l < H; l++) { scope->set_local(l, cNil); } } // optional arguments if(O > 0) { for(; l < H + O && a < MIN(N, H + ON); l++, a++) { if(unlikely(kw_remainder && !RP && (a == N - 1))) { scope->set_local(l, kw_remainder); } else { scope->set_local(l, args.get_argument(a)); } } for(; l < H + O; l++) { scope->set_local(l, G(undefined)); } } // rest arguments if(RP) { Array* ary; if(RN > 0) { ary = Array::create(state, RN); for(int i = 0; i < RN && a < N - P - K; i++, a++) { if(unlikely(kw_remainder && (a == N - 1))) { ary->set(state, i, kw_remainder); } else { ary->set(state, i, args.get_argument(a)); } } } else { ary = Array::create(state, 0); } scope->set_local(RI, ary); } // post arguments if(P > 0) { const native_int N_K = (X = MIN(N, N - K)) > 0 ? X : N; for(l = PI; l < PI + P && a < N_K; l++, a++) { scope->set_local(l, args.get_argument(a)); } for(; l < PI + P; l++) { scope->set_local(l, cNil); } } // keywords if(kw) { scope->set_local(KI, kw); } return true; }
Object* Proc::call(STATE, CallFrame* call_frame, Arguments& args) { bool lambda_style = CBOOL(lambda_); int flags = 0; Proc* self = this; OnStack<1> os(state, self); // Check the arity in lambda mode if(lambda_style && !block_->nil_p()) { flags = CallFrame::cIsLambda; int total = self->block_->compiled_code()->total_args()->to_native(); int required = self->block_->compiled_code()->required_args()->to_native(); bool arity_ok = false; if(Fixnum* fix = try_as<Fixnum>(self->block_->compiled_code()->splat())) { switch(fix->to_native()) { case -2: arity_ok = true; break; case -4: // splat = -4 means { |(a, b)| } if(args.total() == 1) { Array* ary = 0; Object* obj = args.get_argument(0); if(!(ary = try_as<Array>(obj))) { if(CBOOL(obj->respond_to(state, G(sym_to_ary), cFalse))) { obj = obj->send(state, call_frame, G(sym_to_ary)); if(!(ary = try_as<Array>(obj))) { Exception::type_error(state, "to_ary must return an Array", call_frame); return 0; } } } if(ary) args.use_argument(ary); } // fall through for arity check case -3: // splat = -3 is used to distinguish { |a, | } from { |a| } if(args.total() == (size_t)required) arity_ok = true; break; default: if(args.total() >= (size_t)required) { arity_ok = true; } } } else { arity_ok = args.total() <= (size_t)total && args.total() >= (size_t)required; } if(!arity_ok) { Exception* exc = Exception::make_argument_error(state, required, args.total(), block_->compiled_code()->name()); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); return NULL; } } if(self->bound_method_->nil_p()) { if(self->block_->nil_p()) { Dispatch dis(state->symbol("__yield__")); return dis.send(state, call_frame, args); } else { return self->block_->call(state, call_frame, args, flags); } } else if(NativeMethod* nm = try_as<NativeMethod>(self->bound_method_)) { return nm->execute(state, call_frame, nm, G(object), args); } else if(NativeFunction* nf = try_as<NativeFunction>(self->bound_method_)) { return nf->call(state, args, call_frame); } else { Exception* exc = Exception::make_type_error(state, BlockEnvironment::type, self->bound_method_, "NativeMethod nor NativeFunction bound to proc"); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); return NULL; } }
static bool call(STATE, CallFrame* call_frame, MachineCode* mcode, StackVariables* scope, Arguments& args, int flags) { const bool has_splat = (mcode->splat_position >= 0); native_int total_args = args.total(); // expecting 0, got 0. if(mcode->total_args == 0 && total_args == 0) { if(has_splat) { scope->set_local(mcode->splat_position, Array::create(state, 0)); } return true; } // Only do destructuring in non-lambda mode if((flags & CallFrame::cIsLambda) == 0) { /* If only one argument was yielded and: * * 1. the block takes two or more arguments * 2. OR takes one argument and a splat * 3. OR has the form { |a, | } * 4. OR has the form { |(a, b)| } * 5. OR has the form { |(a, b), c| } * * then we check if the one argument is an Array. If it is not, call * #to_ary to convert it to an Array and raise if #to_ary does not * return an Array. * * Finally, in cases 1-3, and 5 above, we destructure the Array into * the block's arguments. */ if(total_args == 1 && (mcode->required_args > 1 || (mcode->required_args == 1 && (has_splat || mcode->splat_position < -2)))) { Object* obj = args.get_argument(0); Array* ary = 0; if(!(ary = try_as<Array>(obj))) { if(CBOOL(obj->respond_to(state, G(sym_to_ary), cFalse))) { obj = obj->send(state, call_frame, G(sym_to_ary)); if(!obj) return false; if(!(ary = try_as<Array>(obj))) { Exception::type_error(state, "to_ary must return an Array", call_frame); return false; } } } if(ary) { if(mcode->splat_position == -4 && mcode->required_args == 1) { args.use_argument(ary); } else { args.use_array(ary); } } } } const native_int P = mcode->post_args; const native_int R = mcode->required_args; // M is for mandatory const native_int M = R - P; const native_int T = args.total(); // DT is for declared total const native_int DT = mcode->total_args; const native_int O = DT - R; // HS is for has splat const native_int HS = mcode->splat_position >= 0 ? 1 : 0; // CT is for clamped total const native_int CT = HS ? T : MIN(T, DT); // Z is for the available # of post args const native_int Z = CT - M; // U is for the available # of optional args const native_int U = Z - P; // PAO is for the post-args offset // PLO is for the post-arg locals offset const native_int PAO = CT - MIN(Z, P); const native_int PLO = M + O + HS; /* There are 4 types of arguments, illustrated here: * m(a, b=1, *c, d) * * where: * a is a (pre optional/splat) fixed position argument * b is an optional argument * c is a splat argument * d is a post (optional/splat) argument * * The arity checking above ensures that we have at least one argument * on the stack for each fixed position argument (ie arguments a and d * above). * * The number of (pre) fixed arguments is 'required_args - post_args'. * * The number of optional arguments is 'total_args - required_args'. * * We fill in the required arguments, then the optional arguments, and * the rest (if any) go into an array for the splat. */ // Phase 1, mandatory args for(native_int i = 0, l = MIN(M,T); i < l; i++) { scope->set_local(i, args.get_argument(i)); } // Phase 2, post args for(native_int i = 0; i < MIN(Z, P); i++) { scope->set_local(PLO + i, args.get_argument(PAO + i)); } // Phase 3, optionals for(native_int i = M, limit = M + MIN(U, O); i < limit; i++) { scope->set_local(i, args.get_argument(i)); } if(has_splat) { Array* ary; /* There is a splat. So if the passed in arguments are greater * than the total number of fixed arguments, put the rest of the * arguments into the Array. * * Otherwise, generate an empty Array. * * NOTE: remember that total includes the number of fixed arguments, * even if they're optional, so we can get args.total() == 0, and * total == 1 */ int splat_size = T - DT; if(splat_size > 0) { ary = Array::create(state, splat_size); for(int i = 0, n = M + O; i < splat_size; i++, n++) { ary->set(state, i, args.get_argument(n)); } } else { ary = Array::create(state, 0); } scope->set_local(mcode->splat_position, ary); } return true; }
int main( int argc, char **argv ) { if( !co::init( argc, argv )) return EXIT_FAILURE; co::ConnectionDescriptionPtr remote; size_t packetSize = 1048576u; // needs to be modulo 8 uint32_t nPackets = 0xFFFFFFFFu; uint32_t waitTime = 0; bool useZeroconf = true; bool useObjects = false; try // command line parsing { TCLAP::CmdLine command( "nodeperf - Collage node-to-node network benchmark tool", ' ', co::Version::getString( )); TCLAP::ValueArg< std::string > remoteArg( "c", "connect", "connect to remote node, implies --disableZeroconf", false, "", "IP[:port][:protocol]", command ); TCLAP::SwitchArg zcArg( "d", "disableZeroconf", "Disable automatic connect using zeroconf", command, false ); TCLAP::SwitchArg objectsArg( "o", "object", "Benchmark object-object instead of node-node communication", command, false ); TCLAP::ValueArg<size_t> sizeArg( "p", "packetSize", "packet size", false, packetSize, "unsigned", command ); TCLAP::ValueArg<size_t> packetsArg( "n", "numPackets", "number of packets to send", false, nPackets, "unsigned", command ); TCLAP::ValueArg<uint32_t> waitArg( "w", "wait", "wait time (ms) between sends", false, 0, "unsigned", command ); command.parse( argc, argv ); if( remoteArg.isSet( )) { remote = new co::ConnectionDescription; remote->port = 4242; remote->fromString( remoteArg.getValue( )); } useZeroconf = !zcArg.isSet(); useObjects = objectsArg.isSet(); if( sizeArg.isSet( )) packetSize = sizeArg.getValue(); if( packetsArg.isSet( )) nPackets = uint32_t( packetsArg.getValue( )); if( waitArg.isSet( )) waitTime = waitArg.getValue(); } catch( TCLAP::ArgException& exception ) { LBERROR << "Command line parse error: " << exception.error() << " for argument " << exception.argId() << std::endl; co::exit(); return EXIT_FAILURE; } // Set up local node co::LocalNodePtr localNode = new PerfNode; if( !localNode->initLocal( argc, argv )) { co::exit(); return EXIT_FAILURE; } localNode->getZeroconf().set( "coNodeperf", co::Version::getString( )); Object object; object.setID( _objectID + localNode->getNodeID( )); LBCHECK( localNode->registerObject( &object )); // run if( remote ) { co::NodePtr node = new PerfNodeProxy; node->addConnectionDescription( remote ); localNode->connect( node ); } else if( useZeroconf ) { co::Zeroconf zeroconf = localNode->getZeroconf(); const co::Strings& instances = localNode->getZeroconf().getInstances(); BOOST_FOREACH( const std::string& instance, instances ) { if( !zeroconf.get( instance, "coNodeperf" ).empty( )) localNode->connect( co::uint128_t( instance )); } } { lunchbox::ScopedFastRead _mutex( nodes_ ); if( nodes_->empty( )) // Add default listener so others can connect to me localNode->addListener( new co::ConnectionDescription ); } co::Nodes nodes; while( true ) { lunchbox::Thread::yield(); lunchbox::ScopedFastRead _mutex( nodes_ ); if( !nodes_->empty( )) break; } Buffer buffer; const size_t bufferElems = packetSize / sizeof( uint64_t ); buffer.resize( bufferElems ); for( size_t i = 0; i < bufferElems; ++i ) buffer[i] = i; const float mBytesSec = buffer.getNumBytes() / 1024.0f / 1024.0f * 1000.0f; lunchbox::Clock clock; size_t sentPackets = 0; clock.reset(); while( nPackets-- ) { { lunchbox::ScopedFastRead _mutex( nodes_ ); if( nodes != *nodes_ ) { for( co::NodesCIter i = nodes_->begin(); i != nodes_->end(); ++i) { co::NodePtr node = *i; co::NodesCIter j = stde::find( nodes, node ); if( j == nodes.end( )) { // new node, map perf object LBASSERT( node->getType() == 0xC0FFEEu ); PerfNodeProxyPtr peer = static_cast< PerfNodeProxy* >( node.get( )); LBCHECK( localNode->mapObject( &peer->object, _objectID + peer->getNodeID( ))); } } nodes = *nodes_; } } if( nodes.empty( )) break; for( co::NodesCIter i = nodes.begin(); i != nodes.end(); ++i ) { co::NodePtr node = *i; if( node->getType() != 0xC0FFEEu ) continue; if( useObjects ) { const size_t j = (object.getID().low() + nPackets) % bufferElems; buffer[ j ] = nPackets; object.send( node, co::CMD_OBJECT_CUSTOM ) << nPackets <<buffer; buffer[ j ] = 0xDEADBEEFu; } else { const size_t j = (node->getNodeID().low() + nPackets) % bufferElems; buffer[ j ] = nPackets; node->send( co::CMD_NODE_CUSTOM ) << nPackets << buffer; buffer[ j ] = 0xDEADBEEFu; } ++sentPackets; if( waitTime > 0 ) lunchbox::sleep( waitTime ); } const float time = clock.getTimef(); if( time > 1000.f ) { const lunchbox::ScopedMutex<> mutex( print_ ); std::cerr << "Send perf: " << mBytesSec / time * sentPackets << "MB/s (" << sentPackets / time * 1000.f << "pps)" << std::endl; sentPackets = 0; clock.reset(); } } const float time = clock.getTimef(); if( time > 1000.f ) { const lunchbox::ScopedMutex<> mutex( print_ ); std::cerr << "Send perf: " << mBytesSec / time * sentPackets << "MB/s (" << sentPackets / time * 1000.f << "pps)" << std::endl; sentPackets = 0; clock.reset(); } localNode->deregisterObject( &object ); LBCHECK( localNode->exitLocal( )); LBCHECK( co::exit( )); return EXIT_SUCCESS; }
Object* Proc::call(STATE, CallFrame* call_frame, Arguments& args) { bool lambda_style = RTEST(lambda_); int flags = 0; // Check the arity in lambda mode if(lambda_style) { flags = CallFrame::cIsLambda; int total = block_->code()->total_args()->to_native(); int required = block_->code()->required_args()->to_native(); bool arity_ok = false; if(Fixnum* fix = try_as<Fixnum>(block_->code()->splat())) { switch(fix->to_native()) { case -2: arity_ok = true; break; case -4: // splat = -4 means { |(a, b)| } if(args.total() == 1) { Array* ary = 0; Object* obj = args.get_argument(0); if(!(ary = try_as<Array>(obj))) { if(RTEST(obj->respond_to(state, state->symbol("to_ary"), Qfalse))) { obj = obj->send(state, call_frame, state->symbol("to_ary")); if(!(ary = try_as<Array>(obj))) { Exception::type_error(state, "to_ary must return an Array", call_frame); return 0; } } } if(ary) args.use_argument(ary); } // fall through for arity check case -3: // splat = -3 is used to distinguish { |a, | } from { |a| } if(args.total() == (size_t)required) arity_ok = true; break; default: if(args.total() >= (size_t)required) { arity_ok = true; } } /* For blocks taking one argument { |a| }, in 1.8, there is a warning * issued but no exception raised when less than or more than one * argument is passed. If more than one is passed, 'a' receives an Array * of all the arguments. */ } else if(required == 1 && LANGUAGE_18_ENABLED(state)) { arity_ok = true; } else { arity_ok = args.total() <= (size_t)total && args.total() >= (size_t)required; } if(!arity_ok) { Exception* exc = Exception::make_argument_error(state, required, args.total(), state->symbol("__block__")); exc->locations(state, Location::from_call_stack(state, call_frame)); state->thread_state()->raise_exception(exc); return NULL; } } Object* ret; if(bound_method_->nil_p()) { ret = block_->call(state, call_frame, args, flags); } else if(NativeMethod* nm = try_as<NativeMethod>(bound_method_)) { ret = nm->execute(state, call_frame, nm, G(object), args); } else { Dispatch dis(state->symbol("__yield__")); ret = dis.send(state, call_frame, args); } return ret; }
inline bool zsuper(STATE, CallFrame* call_frame, intptr_t literal) { Object* block = stack_pop(); Object* const recv = call_frame->self(); VariableScope* scope = call_frame->method_scope(state); interp_assert(scope); MachineCode* mc = scope->method()->machine_code(); Object* splat_obj = 0; Array* splat = 0; size_t arg_count = mc->total_args; if(mc->splat_position >= 0) { splat_obj = scope->get_local(state, mc->splat_position); splat = try_as<Array>(splat_obj); if(splat) { arg_count += splat->size(); } else { arg_count++; } } Tuple* tup = Tuple::create(state, arg_count); native_int tup_index = 0; native_int fixed_args; if(splat) { fixed_args = mc->splat_position; } else if(mc->keywords) { fixed_args = mc->total_args - 1; } else { fixed_args = mc->total_args; } for(native_int i = 0; i < fixed_args; i++) { tup->put(state, tup_index++, scope->get_local(state, i)); } if(splat) { for(native_int i = 0; i < splat->size(); i++) { tup->put(state, tup_index++, splat->get(state, i)); } } else if(splat_obj) { tup->put(state, tup_index++, splat_obj); } if(mc->post_args) { native_int post_position = mc->splat_position + 1; for(native_int i = post_position; i < post_position + mc->post_args; i++) { tup->put(state, tup_index++, scope->get_local(state, i)); } } if(mc->keywords) { native_int placeholder_position = splat_obj ? mc->total_args : mc->total_args - 1; native_int keywords_position = placeholder_position + 1; Object* placeholder = scope->get_local(state, placeholder_position); Array* ary = Array::create(state, 2); for(native_int i = keywords_position; i <= mc->keywords_count; i++) { ary->set(state, 0, as<Symbol>(call_frame->compiled_code->local_names()->at(state, i))); ary->set(state, 1, scope->get_local(state, i)); placeholder->send(state, state->symbol("[]="), ary); } tup->put(state, tup_index++, scope->get_local(state, placeholder_position)); } CallSite* call_site = reinterpret_cast<CallSite*>(literal); Arguments new_args(call_site->name(), recv, block, arg_count, 0); new_args.use_tuple(tup, arg_count); Object* ret; Symbol* current_name = call_frame->original_name(); if(call_site->name() != current_name) { call_site->name(state, current_name); } ret = call_site->execute(state, new_args); state->vm()->checkpoint(state); CHECK_AND_PUSH(ret); }