Example #1
0
 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;
 }
Example #2
0
    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;
    }
Example #3
0
    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;
    }
Example #4
0
    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;
    }
Example #5
0
    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;
    }
Example #6
0
 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;
   }
 }
Example #7
0
  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;
    }
Example #9
0
    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;
    }
Example #10
0
    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;
    }
Example #11
0
 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;
 }