/* 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; }
Object* Thread::raise(STATE, Exception* error) { wakeup(state); MethodContext* ctx = task_->active(); ctx->reference(state); error->context(state, ctx); return task_->raise(state, error); }
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); }
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); }
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)); }
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; }
// 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; }
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)); }
void test_recycle_ignores_mature_contexts() { MethodContext* ctx = MethodContext::create(state, 10); ctx->zone = MatureObjectZone; // GROSS TS_ASSERT(!ctx->recycle(state)); }
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; }