void Environment::run(RunningContext *context) { const Instruction &ins = m_ipos; m_context = context; if (!context) { m_running = false; } else if (!m_running) { clear_vstore(); TRACE_PRINTF(TRACE_VM, TRACE_INFO, "Entering running state with %p\n", context); m_running = true; const SourcePos *last_pos = NULL; // used for debug output below. const Instruction *ipos; while (m_context && (ipos = m_context->next())) { size_t output = -1; size_t expected = -1; # define CHECK_STACK(in, out) TRACE_OUT(TRACE_VM, TRACE_SPAM) {\ assert(m_context->stack_count() >= (size_t)(in)); \ if ((out) != -1) { assert((long)(expected = m_context->stack_count() - (size_t)(in) + (size_t)(out)) >= 0); } \ tprintf("Stack info: before(%d), in(%d), out(%d), expected(%d)\n", (int)m_context->stack_count(), (int)(in), (int)(out), (int)expected); \ output = (out); \ } m_ipos = *ipos; TRACE_DO(TRACE_VM, TRACE_INFO) { const SourcePos &pos = m_context->m_instructions->source_pos(ipos); if (!last_pos || *last_pos != pos) tprintf("Source Position: %s:%d:%d (%s)\n", pos.file.c_str(), (int)pos.line_num, (int)pos.column, pos.annotation.c_str()); last_pos = &pos; } TRACE_OUT(TRACE_VM, TRACE_SPAM) { tprintf("Instruction: %s@%d\n", inspect(ins).c_str(), (int)(ipos - &*m_context->instructions().begin()) ); tprintf("Stack: %d deep", (int)m_context->stack_count()); const Value *it; for (it = m_context->stack_begin(); it != m_context->stack_pos(); it++) { tprintf("\n %s", inspect(*it).c_str()); } tprintf(" <--\n"); } switch(ins.instruction) { case NOP: CHECK_STACK(0, 0); break; case DEBUGGER: CHECK_STACK(0, 0); { String *action = ptr<String>(ins.arg1); if (*action == "interrupt") { DO_DEBUG __asm__("int3"); } else if (*action == "trace_enable") { trace_mute = false; } else if (*action == "trace_disable") { trace_mute = true; } } break; case POP: CHECK_STACK(1, 0); m_context->pop(); break; case PUSH: CHECK_STACK(0, 1); m_context->push(ins.arg1); break; case SWAP: { CHECK_STACK(2, 2); Value tmp1 = m_context->top(); m_context->pop(); Value tmp2 = m_context->top(); m_context->pop(); m_context->push(tmp1); m_context->push(tmp2); } break; case DUP_TOP: CHECK_STACK(1, 2); m_context->push(m_context->top()); break; case IS: { CHECK_STACK(1, 1); Value res = bvalue(ins.arg1 == m_context->top()); m_context->pop(); m_context->push(res); } break; case IS_EQ: { CHECK_STACK(2, 1); Value first = m_context->top(); m_context->pop(); Value second = m_context->top(); m_context->pop(); m_context->push(bvalue(first == second)); } break; case IS_NOT: { CHECK_STACK(1, 1); Value res = bvalue(ins.arg1 != m_context->top()); m_context->pop(); m_context->push(res); } break; case IS_NOT_EQ:{ CHECK_STACK(2, 1); Value first = m_context->top(); m_context->pop(); Value second = m_context->top(); m_context->pop(); m_context->push(bvalue(first != second)); } break; case JMP: CHECK_STACK(0, 0); GC::safe_point(); m_context->jump(ins.cache.machine_num); break; case JMP_IF: CHECK_STACK(1, 0); GC::safe_point(); if (is_truthy(m_context->top())) m_context->jump(ins.cache.machine_num); m_context->pop(); break; case JMP_IF_NOT: CHECK_STACK(1, 0); GC::safe_point(); if (!is_truthy(m_context->top())) m_context->jump(ins.cache.machine_num); m_context->pop(); break; case LEXICAL_CLEAN_SCOPE: { CHECK_STACK(0, 0); VariableFrame *frame = new_variable_frame(m_context->m_instructions); m_context->new_scope(frame); } break; case LEXICAL_LINKED_SCOPE: { CHECK_STACK(0, 0); VariableFrame *frame = new_variable_frame(m_context->m_instructions); frame->link_frame(m_context->m_lexicalvars); m_context->new_scope(frame); } break; case FRAME_GET: { CHECK_STACK(0, 1); size_t frameid = (size_t)ins.cache.machine_num; TRACE_PRINTF(TRACE_VM, TRACE_SPAM, "Getting frame var %s (%d)\n", ptr<String>(ins.arg1)->c_str(), (int)frameid); m_context->push(m_context->get_framevar(frameid)); } break; case FRAME_SET: { CHECK_STACK(1, 0); size_t frameid = (size_t)ins.cache.machine_num; TRACE_PRINTF(TRACE_VM, TRACE_SPAM, "Setting frame var %s (%d) to: %s\n", ptr<String>(ins.arg1)->c_str(), (int)frameid, inspect(m_context->top()).c_str()); m_context->set_framevar(frameid, m_context->top()); m_context->pop(); } break; case LOCAL_GET: { CHECK_STACK(0, 1); size_t localid = (size_t)ins.cache.machine_num; TRACE_PRINTF(TRACE_VM, TRACE_SPAM, "Getting local var %s (%d)\n", ptr<String>(ins.arg1)->c_str(), (int)localid); m_context->push(m_context->get_localvar(localid)); } break; case LOCAL_SET: { CHECK_STACK(1, 0); size_t localid = (size_t)ins.cache.machine_num; TRACE_PRINTF(TRACE_VM, TRACE_SPAM, "Setting local var %s (%d) to: %s\n", ptr<String>(ins.arg1)->c_str(), (int)localid, inspect(m_context->top()).c_str()); m_context->set_localvar(localid, m_context->top()); m_context->pop(); } break; case LEXICAL_GET: { CHECK_STACK(0, 1); size_t depth = ins.arg1.machine_num; size_t localid = (size_t)ins.cache.machine_num; TRACE_PRINTF(TRACE_VM, TRACE_SPAM, "lexical_get %u@%u: %i\n", (unsigned)localid, (unsigned)depth, type(m_context->get_lexicalvar(localid, depth))); if (depth == 0) m_context->push(m_context->get_lexicalvar(localid)); else m_context->push(m_context->get_lexicalvar(localid, depth)); } break; case LEXICAL_SET: { CHECK_STACK(1, 0); size_t depth = ins.arg1.machine_num; size_t localid = (size_t)ins.cache.machine_num; TRACE_PRINTF(TRACE_VM, TRACE_SPAM, "lexical_set %u@%u: %i\n", (unsigned)localid, (unsigned)depth, type(m_context->top())); if (depth == 0) m_context->set_lexicalvar(localid, m_context->top()); else m_context->set_lexicalvar(localid, depth, m_context->top()); m_context->pop(); } break; case CHANNEL_NEW: { CHECK_STACK(0, 1); const Value &octx = vstore(value(m_context)); RunnableContext *nctx = new_context(octx); nctx->jump(ins.cache.machine_num); m_context->push(value(nctx)); clear_vstore(); } break; case CHANNEL_SPECIAL: CHECK_STACK(0, 1); m_context->push(special_channel(*ptr<String>(ins.arg1))); break; case CHANNEL_SEND: { CHECK_STACK(3, -1); GC::safe_point(); const Value &val = vstore(m_context->top()); m_context->pop(); const Value &ret = vstore(m_context->top()); m_context->pop(); const Value &channel = vstore(m_context->top()); m_context->pop(); // A context that's been channel_sended from should never be returned to, // so invalidate it. m_context->invalidate(); channel_send(this, channel, val, ret); clear_vstore(); } break; case CHANNEL_CALL:{ CHECK_STACK(2, -1); GC::safe_point(); const Value &val = vstore(m_context->top()); m_context->pop(); const Value &channel = vstore(m_context->top()); m_context->pop(); const Value &ret = vstore(value(m_context)); channel_send(this, channel, val, ret); clear_vstore(); } break; case CHANNEL_RET:{ CHECK_STACK(2, -1); const Value &val = vstore(m_context->top()); m_context->pop(); const Value &channel = vstore(m_context->top()); m_context->pop(); // A context that's been channel_reted from should never be returned to, // so invalidate it. m_context->invalidate(); channel_send(this, channel, val, value(&no_return_ctx)); clear_vstore(); } break; case MESSAGE_NEW: { long long id = ins.cache.machine_num; long long sysarg_count = ins.arg2.machine_num, sysarg_counter = sysarg_count - 1; long long arg_count = ins.arg3.machine_num, arg_counter = arg_count - 1; CHECK_STACK(sysarg_count + arg_count, 1); Message *msg = new_message(id, sysarg_count, arg_count); while (arg_count > 0 && arg_counter >= 0) { msg->args()[arg_counter] = m_context->top(); m_context->pop(); --arg_counter; } while (sysarg_count > 0 && sysarg_counter >= 0) { msg->sysargs()[sysarg_counter] = m_context->top(); m_context->pop(); --sysarg_counter; } m_context->push(value(msg)); } break; case MESSAGE_SPLAT: { CHECK_STACK(2, 1); const Tuple &tuple = *ptr<Tuple>(m_context->top()); m_context->pop(); const Message &msg = *ptr<Message>(m_context->top()); m_context->pop(); Message *nmsg = new_message(msg.m_id, msg.sysarg_count(), msg.arg_count() + tuple.size()); std::copy(msg.sysargs(), msg.sysargs_end(), nmsg->sysargs()); std::copy(msg.args(), msg.args_end(), nmsg->args()); std::copy(tuple.begin(), tuple.end(), nmsg->args() + msg.arg_count()); m_context->push(value(nmsg)); } break; case MESSAGE_ADD: { long long count = ins.arg1.machine_num, counter = 0; CHECK_STACK(1 + count, 1); const Message &msg = *ptr<Message>(*(m_context->stack_pos() - 1 - count)); Message *nmsg = new_message(msg.m_id, msg.sysarg_count(), msg.arg_count() + count); std::copy(msg.sysargs(), msg.sysargs_end(), nmsg->sysargs()); std::copy(msg.args(), msg.args_end(), nmsg->args()); Message::iterator out = nmsg->args() + msg.arg_count(); while (count > 0 && counter < count) { *out++ = m_context->top(); m_context->pop(); ++counter; } m_context->pop(); // this is the message we pulled directly off the stack before. m_context->push(value(nmsg)); } break; case MESSAGE_COUNT: CHECK_STACK(1, 2); m_context->push(value((long long)ptr<Message>(m_context->top())->arg_count())); break; case MESSAGE_IS: CHECK_STACK(1, 2); m_context->push(bvalue(ptr<Message>(m_context->top())->m_id == (uint64_t)ins.cache.machine_num)); break; case MESSAGE_IS_PROTO: CHECK_STACK(1, 2); m_context->push(bvalue(ptr<Message>(m_context->top())->protocol_id() == (uint64_t)ins.cache.machine_num)); break; case MESSAGE_ID: { CHECK_STACK(1, 2); Message *msg = ptr<Message>(m_context->top()); m_context->push(value((long long)msg->m_id)); } break; case MESSAGE_SPLIT_ID: { CHECK_STACK(1, 3); Message *msg = ptr<Message>(m_context->top()); m_context->push(value((long long)msg->message_id())); m_context->push(value((long long)msg->protocol_id())); } break; case MESSAGE_CHECK: CHECK_STACK(1, 1); if (!is(m_context->top(), MESSAGE)) { m_context->pop(); m_context->push(False); } break; case MESSAGE_FORWARD: { CHECK_STACK(1, 1); long long id = ins.cache.machine_num; const Message &msg = *ptr<Message>(m_context->top()); Message *nmsg = new_message(id, msg.sysarg_count(), msg.arg_count() + 1); std::copy(msg.sysargs(), msg.sysargs_end(), nmsg->sysargs()); nmsg->args()[0] = value(new_string(msg.name())); std::copy(msg.args(), msg.args_end(), nmsg->args() + 1); m_context->pop(); m_context->push(value(nmsg)); break; } case MESSAGE_SYS_PREFIX: { long long count = ins.arg1.machine_num, counter = 0; CHECK_STACK(1 + count, 1); const Value *sdata = m_context->stack_pos() - 1 - count; const Message &msg = *ptr<Message>(*sdata++); Message *nmsg = new_message(msg.m_id, msg.sysarg_count() + count, msg.arg_count()); Message::iterator to = nmsg->sysargs(); while (counter++ < count) { *to++ = *sdata++; } std::copy(msg.sysargs(), msg.sysargs_end(), to); std::copy(msg.args(), msg.args_end(), nmsg->args()); while (--counter != 0) { m_context->pop(); } m_context->pop(); // message we read off stack before. m_context->push(value(nmsg)); break; } case MESSAGE_SYS_UNPACK: { long long count = ins.arg1.machine_num, pos = count - 1; CHECK_STACK(1, 1 + count); const Message *msg = ptr<Message>(m_context->top()); Message::const_iterator args = msg->sysargs(); long long len = (long long)msg->sysarg_count(); while (pos >= 0) { if (pos >= len) m_context->push(Undef); else m_context->push(args[pos]); pos -= 1; } } break; case MESSAGE_UNPACK: { long long first_count = ins.arg1.machine_num, first_counter = 0; long long splat = ins.arg2.machine_num; long long last_count = ins.arg3.machine_num, last_counter = 0; CHECK_STACK(1, 1 + first_count + last_count + (splat?1:0)); const Message *msg = ptr<Message>(m_context->top()); Message::const_iterator args = msg->args(); long long len = (long long)msg->arg_count(), pos = len - 1; while (last_counter < last_count && pos >= first_count) { if (pos >= len) m_context->push(Nil); else m_context->push(args[pos]); pos -= 1; last_counter += 1; } if (splat != 0) { if (pos >= first_count) { Tuple *splat_tuple = new_tuple(pos - first_count + 1); Tuple::iterator out = splat_tuple->end(); while (pos >= first_count) { --out; *out = args[pos]; pos -= 1; } m_context->push(value(splat_tuple)); } else { m_context->push(value(new_tuple(0))); } } pos = first_count - 1; while (first_counter < first_count && pos >= 0) { if (pos >= len) m_context->push(Nil); else m_context->push(args[pos]); pos -= 1; first_counter += 1; } } break; case STRING_COERCE: { const Value &coerce = ins.arg1; const Value &val = vstore(m_context->top()); if (is(val, STRING)) { CHECK_STACK(1, 2); // push a nil like we called the method. m_context->push(Nil); } else { CHECK_STACK(1, -1); m_context->pop(); channel_send(this, val, value(new_message(coerce)), value(m_context)); } } clear_vstore(); break; case STRING_NEW: { long long count = ins.arg1.machine_num, counter = 0; CHECK_STACK(count, 1); const Value *sp = m_context->stack_pos() - 1; size_t len = 0; while (count > 0 && counter < count) { assert(is(sp[-counter], STRING)); len += ptr<String>(sp[-counter])->length(); ++counter; } String *res = new_string(len); String::iterator out = res->begin(); counter = 0; while (count > 0 && counter < count) { const String *val = ptr<String>(m_context->top()); out = std::copy(val->begin(), val->end(), out); m_context->pop(); ++counter; } m_context->push(value(res)); } break; case TUPLE_NEW: { long long count = ins.arg1.machine_num, counter = 0; CHECK_STACK(count, 1); Tuple *tuple = new_tuple(count); Tuple::iterator out = tuple->begin(); while (count > 0 && counter < count) { *out++ = m_context->top(); m_context->pop(); ++counter; } m_context->push(value(tuple)); } break; case TUPLE_SPLAT: { CHECK_STACK(2, 1); const Tuple *tuple2 = ptr<Tuple>(m_context->top()); m_context->pop(); const Tuple *tuple1 = ptr<Tuple>(m_context->top()); m_context->pop(); Tuple *tuple = join_tuple(tuple1, tuple2); m_context->push(value(tuple)); } break; case TUPLE_UNPACK: { long long first_count = ins.arg1.machine_num, first_counter = 0; long long splat = ins.arg2.machine_num; long long last_count = ins.arg3.machine_num, last_counter = 0; CHECK_STACK(1, 1 + first_count + last_count + (splat?1:0)); const Tuple &tuple = *ptr<Tuple>(m_context->top()); long long len = tuple.size(), pos = tuple.size() - 1; while (last_counter < last_count && pos >= first_count) { if (pos >= len) m_context->push(Nil); else m_context->push(tuple[pos]); pos -= 1; last_counter += 1; } if (splat != 0) { if (pos >= first_count) { Tuple *splat_tuple = new_tuple(pos - first_count + 1); Tuple::iterator out = splat_tuple->end(); while (pos >= first_count) { --out; *out = tuple[pos]; pos -= 1; } m_context->push(value(splat_tuple)); } else { m_context->push(value(new_tuple(0))); } } pos = first_count - 1; while (first_counter < first_count && pos >= 0) { if (pos >= len) m_context->push(Nil); else m_context->push(tuple[pos]); pos -= 1; first_counter += 1; } } break; default: printf("Panic! Unknown instruction %d\n", ins.instruction); exit(1); } TRACE_OUT(TRACE_VM, TRACE_SPAM) { tprintf("Output: %d", (int)output); if ((long)output > 0) { const Value *it = std::max( m_context->stack_begin(), m_context->stack_pos() - output); for (; it != m_context->stack_pos(); it++) { tprintf("\n %s", inspect(*it).c_str()); } } tprintf(" <--\n"); tprintf("----------------\n"); }
inline void tuple_channel_simple(Environment *cenv, const Value &ctx, const Value &oself, const Value &msg_val) { Message &msg = *ptr<Message>(msg_val); switch (msg.message_id()) { case MESSAGE_AT: if (msg.arg_count() == 1 && is_number(msg.args()[0])) { const Tuple &self = *ptr<Tuple>(oself); ssize_t idx = (ssize_t)msg.args()[0].machine_num; if (idx >= 0 && (size_t)idx < self.size()) { return channel_send(cenv, ctx, self[idx], Nil); } else if (idx < 0 && (ssize_t)self.size() >= -idx) { return channel_send(cenv, ctx, self[self.size() + idx], Nil); } } break; case MESSAGE_SUBARY: if (msg.arg_count() == 2 && is_number(msg.args()[0]) && is_number(msg.args()[1])) { const Tuple *self = ptr<Tuple>(oself); ssize_t first = (ssize_t)msg.args()[0].machine_num, last = (ssize_t)msg.args()[1].machine_num; if (first >= 0 && last >= 0 && first <= last && (size_t)first <= self->size() && (size_t)last <= self->size()) { return channel_send(cenv, ctx, value(sub_tuple(self, first, last - first)), Nil); } } break; case MESSAGE_LENGTH: return channel_send(cenv, ctx, value((long long)ptr<Tuple>(oself)->size()), Nil); case MESSAGE_PLUS: return channel_send(cenv, ctx, value(join_tuple(ptr<Tuple>(oself), ptr<Tuple>(msg.args()[0]))), Nil); case MESSAGE_PUSH: return channel_send(cenv, ctx, value(join_tuple(ptr<Tuple>(oself), msg.args()[0])), Nil); case MESSAGE_POP: { Tuple *tuple = ptr<Tuple>(oself); if (tuple->m_count > 0) return channel_send(cenv, ctx, value(sub_tuple(tuple, 0, tuple->m_count - 1)), Nil); } break; case MESSAGE_FRONT_PUSH: return channel_send(cenv, ctx, value(join_tuple(msg.args()[0], ptr<Tuple>(oself))), Nil); case MESSAGE_FRONT_POP: { Tuple *tuple = ptr<Tuple>(oself); if (tuple->m_count > 0) return channel_send(cenv, ctx, value(sub_tuple(tuple, 1, tuple->m_count - 1)), Nil); } break; case MESSAGE_REPLACE: { Tuple *tuple = ptr<Tuple>(oself); long long idx = msg.args()[0].machine_num; Value val = msg.args()[1]; if (idx >= 0) return channel_send(cenv, ctx, value(replace_tuple(tuple, (size_t)idx, val)), Nil); } break; case MESSAGE_DELETE: { Tuple *tuple = ptr<Tuple>(oself); long long idx = msg.args()[0].machine_num; if (idx >= 0 && size_t(idx) < tuple->m_count) return channel_send(cenv, ctx, value(remove_tuple(tuple, (size_t)idx)), Nil); } break; case MESSAGE_FIRST: { Tuple *tuple = ptr<Tuple>(oself); if (tuple->m_count > 0) return channel_send(cenv, ctx, tuple->m_data[0], Nil); } break; case MESSAGE_LAST: { Tuple *tuple = ptr<Tuple>(oself); if (tuple->m_count > 0) return channel_send(cenv, ctx, tuple->m_data[tuple->m_count-1], Nil); } break; } Value def = cenv->special_channel("Channel9::Primitive::Tuple"); forward_primitive_call(cenv, def, ctx, oself, msg_val); }