static bool call(STATE, MachineCode* mcode, StackVariables* scope, Arguments& args) { if(args.total() != 3) return false; scope->set_local(0, args.get_argument(0)); scope->set_local(1, args.get_argument(1)); scope->set_local(2, args.get_argument(2)); return true; }
static bool call(STATE, VMMethod* vmm, StackVariables* scope, Arguments& args) { const bool has_splat = (vmm->splat_position >= 0); // expecting 0, got 0. if(vmm->total_args == 0 and args.total() == 0) { if(has_splat) { scope->set_local(vmm->splat_position, Array::create(state, 0)); } return true; } // Too few args! if((native_int)args.total() < vmm->required_args) return false; // Too many args (no splat!) if(!has_splat && (native_int)args.total() > vmm->total_args) return false; // Umm... something too do with figuring out how to handle // splat and optionals. native_int fixed_args = vmm->total_args; if((native_int)args.total() < vmm->total_args) { fixed_args = (native_int)args.total(); } // Copy in the normal, fixed position arguments for(native_int i = 0; i < fixed_args; 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 */ if((native_int)args.total() > vmm->total_args) { size_t splat_size = args.total() - vmm->total_args; ary = Array::create(state, splat_size); for(size_t i = 0, n = vmm->total_args; i < splat_size; i++, n++) { ary->set(state, i, args.get_argument(n)); } } else { ary = Array::create(state, 0); } scope->set_local(vmm->splat_position, ary); } return true; }
static bool call(STATE, MachineCode* mcode, StackVariables* scope, Arguments& args) { if((native_int)args.total() != mcode->total_args) return false; for(native_int i = 0; i < mcode->total_args; i++) { scope->set_local(i, args.get_argument(i)); } return true; }
static bool call(STATE, VMMethod* vmm, StackVariables* scope, Arguments& args) { if((native_int)args.total() != vmm->total_args) return false; for(native_int i = 0; i < vmm->total_args; i++) { scope->set_local(i, args.get_argument(i)); } return true; }
static bool call(STATE, MachineCode* mcode, StackVariables* scope, Arguments& args) { const size_t total = args.total(); Array* ary = Array::create(state, total); for(size_t i = 0; i < total; i++) { ary->set(state, i, args.get_argument(i)); } scope->set_local(mcode->splat_position, ary); return true; }
void Fiber::unpack_arguments(STATE, Arguments& args) { switch(args.total()) { case 0: state->vm()->thread()->fiber_value(state, cNil); break; case 1: state->vm()->thread()->fiber_value(state, args.get_argument(0)); break; default: state->vm()->thread()->fiber_value(state, args.as_array(state)); break; } }
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(); }
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; }
static bool call(STATE, MachineCode* mcode, StackVariables* scope, Arguments& args) { 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; } // Too few args! if(total_args < mcode->required_args) return false; // Too many args (no splat!) if(!has_splat && total_args > mcode->total_args) return false; /* 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. */ 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 = total_args; // 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 = has_splat ? 1 : 0; // Phase 1, mandatory args for(native_int i = 0; i < M; i++) { scope->set_local(i, args.get_argument(i)); } // Phase 2, post args for(native_int i = T - P, l = M + O + HS; i < T; i++, l++) { scope->set_local(l, args.get_argument(i)); } // Phase 3, optionals for(native_int i = M, limit = M + MIN(O, T-R); i < limit; i++) { scope->set_local(i, args.get_argument(i)); } // Phase 4, splat 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; }
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; }
static bool call(STATE, VMMethod* vmm, StackVariables* scope, Arguments& args) { if(args.total() != 2) return false; scope->set_local(0, args.get_argument(0)); scope->set_local(1, args.get_argument(1)); return true; }