Example #1
0
  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;
  }
Example #2
0
  /**
   * 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");
    }
  }
Example #3
0
  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;
  }
Example #4
0
 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;
   }
 }
Example #5
0
  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;
  }
Example #6
0
    /**
     *  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);
    }
Example #7
0
  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;
  }
Example #8
0
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();
        }
    }
}
Example #9
0
  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;
  }
Example #10
0
  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);
  }
Example #11
0
  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;
  }
Example #12
0
    /**
     *  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;
    }
Example #14
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();
  }
    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;
    }
Example #16
0
    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 #17
0
  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;
    }
  }
Example #18
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 #19
0
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;
}
Example #20
0
  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;
  }
Example #21
0
    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);
    }