/***** * stack.cc * Andy Hammerlindl 2002/06/27 * * The general stack machine used to run compiled camp code. *****/ #include #include "stack.h" #include "program.h" #include "callable.h" #include "errormsg.h" #include "util.h" #include "runtime.h" #ifdef DEBUG_STACK #include namespace vm { void draw(ostream& out, frame *v); } #endif namespace run { void breakpoint(vm::stack *Stack, absyntax::runnable *r); } namespace vm { mem::list bplist; namespace { position curPos = nullPos; const program::label nulllabel; } #ifdef DEBUG_FRAME inline stack::vars_t stack::make_frame(string name, size_t size, vars_t closure) { vars_t vars = new frame(name, 1+size); (*vars)[0] = closure; return vars; } #else inline stack::vars_t stack::make_frame(size_t size, vars_t closure) { vars_t vars = new frame(1+size); (*vars)[0] = closure; return vars; } #endif void run(lambda *l) { func f; f.body = l; stack s; s.run(&f); } void stack::marshall(size_t args, vars_t vars) { for (size_t i = args; i > 0; --i) (*vars)[i] = pop(); } void stack::run(func *f) { lambda *body = f->body; #ifdef DEBUG_STACK #ifdef DEBUG_FRAME cout << "running lambda " + body->name + ": \n"; #else cout << "running lambda: \n"; #endif print(cout, body->code); cout << endl; #endif /* make new activation record */ #ifdef DEBUG_FRAME assert(!body->name.empty()); vars_t vars = make_frame(body->name, body->params, f->closure); #else vars_t vars = make_frame(body->params, f->closure); #endif marshall(body->params, vars); run(body->code, vars); } void stack::breakpoint(absyntax::runnable *r) { lastPos=curPos; indebugger=true; ::run::breakpoint(this,r); string s=vm::pop(this); debugOp=(s.length() > 0) ? s[0] : (char) 0; indebugger=false; } void stack::debug() { if(!curPos) return; if(indebugger) {em.clear(); return;} switch(debugOp) { case 'i': // inst breakpoint(); break; case 's': // step if((!curPos.match(lastPos.filename()) || !curPos.match(lastPos.Line()))) breakpoint(); break; case 'n': // next if(curPos.match(lastPos.filename()) && !curPos.match(lastPos.Line())) breakpoint(); break; case 'f': // file if(!curPos.match(lastPos.filename())) breakpoint(); break; case 'r': // return if(curPos.match(breakPos.filename())) breakpoint(); break; case 'c': // continue default: for(mem::list::iterator p=bplist.begin(); p != bplist.end(); ++p) { if(curPos.match(p->f.name()) && curPos.match(p->f.line()) && (newline || !curPos.match(breakPos.filename()) || !curPos.match(breakPos.Line()))) { breakPos=curPos; breakpoint(p->r); newline=false; break; } if(!newline && (curPos.match(lastPos.filename()) && !curPos.match(lastPos.Line()))) newline=true; } break; } } void stack::run(program *code, vars_t vars) { /* start the new function */ program::label ip = code->begin(); try { for (;;) { const inst &i = *ip; curPos = i.pos; #ifdef DEBUG_STACK cerr << curPos << "\n"; printInst(cerr, ip, code->begin()); cerr << "\n"; #endif if(settings::verbose > 4) em.trace(curPos); if(!bplist.empty()) debug(); if(errorstream::interrupt) throw interrupted(); switch (i.op) { case inst::pop: pop(); break; case inst::intpush: case inst::constpush: push(i.ref); break; case inst::varpush: push((*vars)[get(i)]); break; case inst::varsave: (*vars)[get(i)] = top(); break; case inst::fieldpush: { vars_t frame = pop(); if (!frame) error("dereference of null pointer"); push((*frame)[get(i)]); break; } case inst::fieldsave: { vars_t frame = pop(); if (!frame) error("dereference of null pointer"); (*frame)[get(i)] = top(); break; } case inst::builtin: { bltin func = get(i); func(this); break; } case inst::jmp: ip = get(i); continue; case inst::cjmp: if (pop()) { ip = get(i); continue; } break; case inst::njmp: if (!pop()) { ip = get(i); continue; } break; case inst::popcall: { /* get the function reference off of the stack */ callable* f = pop(); f->call(this); break; } case inst::pushclosure: push(vars); break; case inst::makefunc: { func *f = new func; f->closure = pop(); f->body = get(i); push((callable*)f); break; } case inst::ret: { return; } case inst::alloc: { vars->extend(get(i)); break; } case inst::pushframe: { #ifdef DEBUG_FRAME vars=make_frame("", 0, vars); #else vars=make_frame(0, vars); #endif break; } case inst::popframe: { vars=get((*vars)[0]); break; } default: error("Internal VM error: Bad stack operand"); } #ifdef DEBUG_STACK draw(cerr); vm::draw(cerr,vars); cerr << "\n"; #endif ++ip; } } catch (bad_item_value&) { error("Trying to use uninitialized value."); } } void stack::load(string index) { frame *inst=instMap[index]; if (inst) push(inst); else { func f; assert(initMap); f.body=(*initMap)[index]; assert(f.body); run(&f); instMap[index]=get(top()); } } #ifdef DEBUG_STACK const size_t MAX_ITEMS=20; void stack::draw(ostream& out) { // out.setf(out.hex); out << "operands:"; stack_t::const_iterator left = theStack.begin(); if (theStack.size() > MAX_ITEMS) { left = theStack.end()-MAX_ITEMS; out << " ..."; } else out << " "; while (left != theStack.end()) { if (left != theStack.begin()) out << " | " ; out << *left; left++; } out << "\n"; } void draw(ostream& out, frame* v) { out << "vars:" << endl; while (!!v) { out << " " << v->getName() << ":"; frame *parent=0; item link=(*v)[0]; try { parent = get(link); out << (parent ? " link" : " ----"); } catch (bad_item_value&) { out << " " << (*v)[0]; } for (size_t i = 1; i < MAX_ITEMS && i < v->size(); i++) { out << " | " << i << ": " << (*v)[i]; } if (v->size() > MAX_ITEMS) out << "..."; out << "\n"; v = parent; } } #endif // DEBUG_STACK position getPos() { return curPos; } void errornothrow(const char* message) { em.error(curPos); em << message; em.sync(); } void error(const char* message) { errornothrow(message); throw handled_error(); } void error(const ostringstream& message) { error(message.str().c_str()); } interactiveStack::interactiveStack() #ifdef DEBUG_FRAME : globals(new frame("globals", 0)) {} #else : globals(new frame(0)) {} #endif void interactiveStack::run(lambda *codelet) { stack::run(codelet->code, globals); } } // namespace vm