예제 #1
0
  /* This is the execute implementation used by normal Ruby code,
   * as opposed to Primitives or FFI functions.
   * It prepares a Ruby method for execution.
   * Here, +exec+ is a VMMethod instance accessed via the +vmm+ slot on
   * CompiledMethod.
   */
  ExecuteStatus VMMethod::execute(STATE, Task* task, Message& msg) {
    CompiledMethod* cm = as<CompiledMethod>(msg.method);

    MethodContext* ctx = MethodContext::create(state, msg.recv, cm);

    VMMethod* vmm = cm->backend_method_;

    // Copy in things we all need.
    ctx->module(state, msg.module);
    ctx->name(state, msg.name);

    ctx->block(state, msg.block);
    ctx->args = msg.args();

    // If argument handling fails..
    GenericArguments args;
    if(args.call(state, vmm, ctx, msg) == false) {
      // Clear the values from the caller
      task->active()->clear_stack(msg.stack);

      // TODO we've got full control here, we should just raise the exception
      // in the runtime here rather than throwing a C++ exception and raising
      // it later.

      Exception::argument_error(state, vmm->required_args, msg.args());
      // never reached!
    }

#if 0
    if(!probe_->nil_p()) {
      probe_->start_method(this, msg);
    }
#endif

    // Clear the values from the caller
    task->active()->clear_stack(msg.stack);

    task->make_active(ctx);

    if(unlikely(task->profiler)) {
      profiler::Method* prof_meth;
      if(MetaClass* mc = try_as<MetaClass>(msg.module)) {
        Object* attached = mc->attached_instance();
        if(Module* mod = try_as<Module>(attached)) {
          prof_meth = task->profiler->enter_method(msg.name, mod->name(), profiler::kNormal);
        } else {
          prof_meth = task->profiler->enter_method(msg.name, attached->id(state), profiler::kNormal);
        }
      } else {
        prof_meth = task->profiler->enter_method(msg.name, msg.module->name(), profiler::kSingleton);
      }

      if(!prof_meth->file()) {
        prof_meth->set_position(cm->file(), cm->start_line(state));
      }
    }

    return cExecuteRestart;
  }
예제 #2
0
  Object* Thread::raise(STATE, Exception* error) {
    wakeup(state);

    MethodContext* ctx = task_->active();
    ctx->reference(state);
    error->context(state, ctx);

    return task_->raise(state, error);
  }
예제 #3
0
  void test_recycle() {
    MethodContext* ctx = MethodContext::create(state, 10);

    TS_ASSERT(state->om->context_on_stack_p(ctx));
    TS_ASSERT(!state->om->context_referenced_p(ctx));

    TS_ASSERT(ctx->recycle(state));

    MethodContext* ctx2 = MethodContext::create(state, 10);
    TS_ASSERT_EQUALS(ctx, ctx2);
  }
예제 #4
0
  void test_reference() {
    MethodContext* ctx = MethodContext::create(state, 10);
    TS_ASSERT(!state->om->context_referenced_p(ctx));
    TS_ASSERT_EQUALS(ctx->klass_, Qnil);

    ctx->reference(state);

    TS_ASSERT_EQUALS(ctx->klass(), G(methctx));
    TS_ASSERT(state->om->context_referenced_p(ctx));

    MethodContext* ctx2 = MethodContext::create(state, 10);
    TS_ASSERT(ctx != ctx2);
  }
예제 #5
0
  void test_line() {
    MethodContext* ctx = MethodContext::create(state, 10);
    ctx->ip = 0;
    ctx->cm(state, CompiledMethod::create(state));

    ctx->cm()->lines(state, Tuple::from(state, 1,
                                      Tuple::from(state, 3,
                                             Fixnum::from(0),
                                             Fixnum::from(20),
                                             Fixnum::from(10))));

    TS_ASSERT_EQUALS(10, ctx->line(state));
  }
예제 #6
0
  NativeMethodContext* NativeMethodContext::create(VM* state,
                                                   Message* msg,
                                                   Task* task,
                                                   NativeMethod* method)
  {
    NativeMethodContext* nmc = state->new_struct<NativeMethodContext>(
        G(nativectx), DEFAULT_STACK_SIZE);

    /* MethodContext stuff. */
    MethodContext* sender = task->active();
    sender->reference(state);
    nmc->sender(state, sender);
    nmc->home(state, nmc);
    nmc->self(state, msg->recv);
    nmc->module(state, msg->module);
    nmc->name(state, msg->name);
    nmc->block(state, msg->block);

    /* Fake that which is not needed. */
    nmc->cm(state, reinterpret_cast<CompiledMethod*>(Qnil));
    nmc->vmm = NULL;

    /* Instead of storing the memory within as MethodContexts, we heap-allocate. */
    nmc->stack_size = DEFAULT_STACK_SIZE;

    nmc->action_          = ORIGINAL_CALL;
    nmc->handles_         = new HandleStorage();
    nmc->message_         = msg;
    nmc->message_from_c_  = new Message(state);
    nmc->method_          = method;
    nmc->return_value_    = Qnil;
    nmc->stack_           = static_cast<void*>(new char[nmc->stack_size]);
    nmc->state_           = state;
    nmc->task_            = task;

    nmc->current_file_    = ::strdup("<no file set>");
    nmc->current_line_    = 0;

    /* Add the basic Handles. Always crossref with ruby.h when changing. */
    nmc->handles_->push_back(Qfalse);
    nmc->handles_->push_back(Qtrue);
    nmc->handles_->push_back(Qnil);
    nmc->handles_->push_back(Qundef);

    nmc->message_from_c_->set_caller(nmc);

    return nmc;
  }
예제 #7
0
    // Allocate a MethodContext object containing +stack_slots+
    // stack positions.
    MethodContext* allocate_context(size_t stack_slots) {
      size_t full_size = sizeof(MethodContext) + (stack_slots * sizeof(Object*));

      // Off the end
      if(!contexts.enough_space_p(full_size)) return NULL;

      MethodContext* ctx = static_cast<MethodContext*>(
          contexts.allocate(full_size));

      // Masquerade as being in the Young zone so the write barrier
      // stays happy.
      ctx->init_header(YoungObjectZone,
                      ((sizeof(MethodContext) - sizeof(ObjectHeader))/ sizeof(Object*)) + stack_slots);

      ctx->full_size = full_size;
      return ctx;
    }
예제 #8
0
  void test_dup() {
    // Create a realistic MethodContext
    // Is there a better way to do this?
    Task* task = Task::create(state);

    // create a target CM
    CompiledMethod* target = CompiledMethod::create(state);
    target->iseq(state, InstructionSequence::create(state, 1));
    target->iseq()->opcodes()->put(state, 0, Fixnum::from(InstructionSequence::insn_ret));
    target->total_args(state, Fixnum::from(0));
    target->required_args(state, target->total_args());
    target->stack_size(state, Fixnum::from(10));
    target->formalize(state);

    // create a containing CM
    CompiledMethod* cm = CompiledMethod::create(state);
    cm->iseq(state, InstructionSequence::create(state, 10));
    cm->stack_size(state, Fixnum::from(10));
    cm->local_count(state, Fixnum::from(0));
    cm->literals(state, Tuple::create(state, 10));

    Symbol* name = state->symbol("blah");
    G(true_class)->method_table()->store(state, name, target);
    SendSite* ss = SendSite::create(state, name);

    cm->literals()->put(state, 0, ss);

    cm->formalize(state);

    MethodContext* ctx = MethodContext::create(state, Qnil, cm);
    task->make_active(ctx);
    task->push(Qtrue);

    // Dup right before we run so we can compare later
    MethodContext* dup = ctx->dup(state);

    // Compare the dup'd with the original
    TS_ASSERT_EQUALS(ctx->sender(), dup->sender());
    TS_ASSERT_EQUALS(dup, dup->home());
    TS_ASSERT_EQUALS(ctx->self(), dup->self());
    TS_ASSERT_EQUALS(ctx->cm(), dup->cm());
    TS_ASSERT_EQUALS(ctx->module(), dup->module());
    TS_ASSERT_EQUALS(ctx->block(), dup->block());
    TS_ASSERT_EQUALS(ctx->name(), dup->name());

    TS_ASSERT_EQUALS(ctx->vmm, dup->vmm);
    TS_ASSERT_EQUALS(ctx->ip, dup->ip);
    TS_ASSERT_EQUALS(ctx->args, dup->args);
    TS_ASSERT_EQUALS(ctx->stack_size, dup->stack_size);

    TS_ASSERT_SAME_DATA(&ctx->js, &dup->js, sizeof(dup->js));
  }
예제 #9
0
 void test_recycle_ignores_mature_contexts() {
   MethodContext* ctx = MethodContext::create(state, 10);
   ctx->zone = MatureObjectZone; // GROSS
   TS_ASSERT(!ctx->recycle(state));
 }
예제 #10
0
int verbTOC::DoWork(const char *nameOfInput)
{
    LogVerbose("Indexing from '%s' into '%s.mct'", nameOfInput, nameOfInput);

    MethodContextIterator mci;
    if (!mci.Initialize(nameOfInput))
        return -1;

    int savedCount = 0;

    TOCElementNode *head = nullptr;
    TOCElementNode *curElem = nullptr;

    while (mci.MoveNext())
    {
        MethodContext* mc = mci.Current();

        TOCElementNode *nxt = new TOCElementNode(mci.MethodContextNumber(), mci.CurrentPos());
        mc->dumpMethodMD5HashToBuffer(nxt->tocElement.Hash, MD5_HASH_BUFFER_SIZE);

        if (curElem != nullptr)
        {
            curElem->Next = nxt;
        }
        else
        {
            head = nxt;
        }
        curElem = nxt;
        savedCount++;
    }

    size_t maxLen = strlen(nameOfInput) + 5;
    char *nameOfOutput = (char*)_alloca(maxLen);
    strcpy_s(nameOfOutput, maxLen, nameOfInput);
    strcat_s(nameOfOutput, maxLen, ".mct");
    HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFileOut == INVALID_HANDLE_VALUE)
    {
        LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
        return -1;
    }

    DWORD written;
    // Write out the signature "INDX" and then the element count
    LARGE_INTEGER token;
    token.u.LowPart = *(const int*)"INDX"; // cuz Type Safety is for languages that have good IO facilities
    token.u.HighPart = savedCount;
    if (!WriteFile(hFileOut, &token, sizeof(token), &written, nullptr) || written != sizeof(token))
    {
        LogError("Failed to write index header. GetLastError()=%u", GetLastError());
    }

    // Now just dump sizeof(TOCElement) byte chunks into the file.
    // I could probably do this more efficiently, but I don't think it matters
    DWORD chunkSize = sizeof(TOCElement);
    for (curElem = head; curElem != nullptr; curElem = curElem->Next)
    {
        if (!WriteFile(hFileOut, &curElem->tocElement, chunkSize, &written, nullptr) || written != chunkSize)
        {
            LogError("Failed to write index element '%d'. GetLastError()=%u", curElem->tocElement.Number, GetLastError());
            return -1;
        }
    }
    // Now write out a final "INDX" to flag the end of the file...
    if (!WriteFile(hFileOut, &token.u.LowPart, sizeof(token.u.LowPart), &written, nullptr) || (written != sizeof(token.u.LowPart)))
    {
        LogError("Failed to write index terminal. GetLastError()=%u", GetLastError());
    }

    LogInfo("Loaded %d, added %d to Table of Contents", mci.MethodContextNumber(), savedCount);

    if (CloseHandle(hFileOut) == 0)
    {
        LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
        return -1;
    }

    if (!mci.Destroy())
        return -1;

    return 0;
}