?? engine.cpp.svn-base
字號:
};
class ODStack: public VStack<ODS_Record,ODS_STACKSIZE> // wuz Stack<...
{
private:
ODS_Record orec;
public:
void push_object(PClass pc, void* obj)
{
orec.m_class = pc;
orec.m_obj = obj;
#ifdef DEBUG_ODS
if (fb == Parser::temp_fun_block()) orec.m_fn = NULL;
else orec.m_fn = Function::from_fun_block(fb);
orec.m_offs = current_ip();
#endif
push(orec);
}
void pop_object(PClass& pc, void*& obj)
{
orec = pop();
pc = orec.m_class;
obj = orec.m_obj;
}
void fetch_object(int idx, PClass& pc, void*& obj)
{
orec = get(idx);
//orec = *ref_to_TOS(-idx);
pc = orec.m_class;
obj = orec.m_obj;
}
#ifdef DEBUG_ODS
void current_context(PClass& pc, PFunction& fn, int& offs)
{ // assumes basically that either pop_object() or fetch_object() has been called...
fn = orec.m_fn;
offs = orec.m_offs;
pc = orec.m_class;
}
#endif
};
static ODStack mODS;
void end_function(); // forward...
#ifdef DEBUG_ODS
void destruct_error(char *msg)
{
PFunction fn;
int ofs;
PClass pc;
mODS.current_context(pc,fn,ofs);
string name = fn ? fn->name() : string("<temp>");
string cname = pc ? pc->name() : string("<none>");
cerr << "bad ODS entry: class " << cname << " fn " << name << '(' << ofs << ')' << endl;
exec_message(msg,true);
}
#else
void destruct_error(char *msg) { exec_message(msg,true); }
#endif
void do_unwind(void *p, ODStack& o_ds)
{
// unwind the Object Destruction Stack (ODS)
FBlock * old_fb = fb;
Instruction *old_ppcode = ppcode;
int *old_baseSP = baseSP;
int *old_mSP = mSP;
PClass pc;
// *fix 0.9.5 Before unwinding, we must check whether this object is in fact on the ODS
if (p != NULL) {
bool found = false;
int n = o_ds.depth();
if (!n) return; // ODS empty....
for(int i = 0; i < n; i++)
{
void* s_obj;
o_ds.fetch_object(i,pc,s_obj);
if (s_obj == p) {
found = true;
break;
}
}
if (! found)
return;
}
// the stack unwinding loop
char *lastOP = mOP;
void *obj;
if (o_ds.depth() > 0)
do {
o_ds.pop_object(pc,obj);
mOP = (char *)obj;
Function *dfn;
try {
dfn = pc->destructor();
} catch(...) {
destruct_error("bad ODS entry ");
break;
}
if (dfn != NULL) try {
if (Engine::execute(dfn->fun_block())==CRASHED)
destruct_error("destructor failed");
} catch(...) {
destruct_error("bad destructor");
return;
}
} while (obj != p && o_ds.depth() > 0);
mOP = lastOP;
fb = old_fb;
// check_fun_block(fb);
ppcode = old_ppcode;
baseSP = old_baseSP;
mSP = old_mSP;
}
#define DEFER_MARKER ((PClass *)(0x777))
#ifdef DEBUG_ODS
void check_ODS()
{
int n = mODS.depth();
if (!n) return;
for(int i = 0; i < n; i++)
{
PClass pc;
void *obj;
PFunction fn;
int ofs;
mODS.fetch_object(i,pc,obj);
bool fooked = false;
if (pc->has_VMT()) try {
PClass *vmt = *VMT(obj);
if (vmt != DEFER_MARKER) fooked = pc != *vmt;
}
catch(...) { fooked = true; }
if (fooked) {
mODS.current_context(pc,fn,ofs);
string name = fn ? fn->name() : string("<temp>");
cerr << "bad ODS: class " << pc->name() << " fn "
<< name << '(' << ofs << ')' << "ptr " << obj << endl;
exec_message("bad ODS entry ",true);
break;
}
}
}
#define CHECK_ODS check_ODS()
#else
#define CHECK_ODS
#endif
int throw_exception(Type t, void *thrown_obj)
// returns a valid offset into the catching function if successful;
// -1 otherwise
{
try {
static CatchHandler *curr_except = NULL;
if (t == t_void) {
// we are re-raising this exception! (see Parser::do_throw())
// we assume that the stack has already been unwound past the
// try block marker we hit last...
t = curr_except->thrown_type();
thrown_obj = curr_except->thrown_object();
} else curr_except = NULL;
int n = mODS.depth();
if (!n) return -1;
for(int i = 0; i < n; i++)
{
PClass pc;
void *obj;
mODS.fetch_object(i,pc,obj);
if (pc == Parser::try_block_class()) {
// first field is our catch handler
curr_except = (CatchHandler *) *(unsigned int *)obj;
int ip = curr_except->match_thrown_object(t,thrown_obj);
if (ip != -1) {
do_unwind(obj,mODS); // unwind up to (&including) the try block marker
while (fb != curr_except->fun_block()) end_function();
return ip;
}// if (...we matched a catch block...)
} // if (...we hit a try block marker ...)
} // for(...all entries in ODS .....
} catch(...) {
cerr << "bomb out handling exception" << endl;
}
// *fix 0.9.5 Will attempt to unwind the stack if the exception is uncaught
do_unwind(NULL,mODS);
return -1; // nobody wanted to catch this type!!
}
void *ventry(int slot, int offs)
{
return (*(void ***)(mOP+offs))[slot];
}
//--------------- THE STACK ENGINE --------------------------
#define REF(T,p) (*(T *)(p))
#define REFS(T) (*(T *)pop())
#define REFA(T,p,a) (*((T *)(p)+(a)))
#define REFP(T,p) (*(T **)(p))
#define NEXT(p) ((int *)p)+1
int switch_jump(int *sb, int val)
// Switch jump tables look like this:
// (num. entries) (default jump) (1st val) (1st jmp) .....
{
int sz = *sb++, def = *sb++;
while (sz && *sb != val) { sb++; sb++; sz--; }
if (sz) return *(sb+1);
else return def;
}
void Engine::kill(int retcode)
{
ppcode = (Instruction *)end_of_code;
}
void reset_stacks()
{
if (fs.depth() > 0) {
cerr << "reseting stacks\n";
reset_execution_stack();
fs.clear();
}
// *fix 1.2.8 reset_stacks() will ALWAYS clear out the resume state
resume_state.m_fb = NULL; // flag us as NOT paused
}
// *change 1.2.4 separated out code for determining full function name
string fname_from_fblock(FBlock* fun_block)
{
if (fun_block == Parser::temp_fun_block()) return "<temp>";
return Function::from_fun_block(fun_block)->as_str();
}
// *change 1.2.3 Function tracing is now controlled by the XTrace object
// *add 1.2.4 Can switch off default exit behaviour
// *change 1.2.4 Now dumps out full function name when tracing
// *change 1.2.4 No return value for enter() and leave() methods
XTrace::XTrace(bool on_exit)
: m_on_entry(true), m_on_exit(on_exit) { }
void XTrace::enter(XExecState* xs)
{
cerr << "*ENTER " << fname_from_fblock(xs->fb) << endl;
}
void XTrace::leave(XExecState* xs)
{
cerr << "*LEAVE " << fname_from_fblock(xs->fb) << endl;
}
void get_stack_frame(int fi, ExecutionState& xs);
XExecState* get_exec_state()
{
static XExecState xs;
ExecutionState exs;
get_stack_frame(1,exs);
xs.fb = fb;
xs.ip = ppcode;
xs.op = mOP;
xs.sp = mSP;
xs.bp = baseSP;
xs.last_fb = exs.m_fb;
xs.last_ip = exs.m_ppcode;
xs.last_bp = exs.m_baseSP;
return &xs;
}
// *add 1.2.4 Can switch tracing on & off globally
static bool gCanTrace = true;
void engine_set_tracing(bool yesno)
{ gCanTrace = yesno; }
void start_function(FBlock *_fb)
{
#ifdef _DEBUG
if (gFunBlock == fb)
__break(3);
#endif
fs.push(ppcode);
if (fs.depth() > FUNCTION_STACK_DEPTH) {
reset_stacks();
throw Exception("Function Stack Overflow");
}
fs.push(fb);
ppcode = _fb->pstart;
fb = _fb;
fs.push(baseSP);
if (ppcode == NULL) { // *add 1.2.4 report full function name
static string tmps = fname_from_fblock(_fb) + " is not defined yet";
throw Exception(tmps.c_str()); // shd be cool for a _static_ string...
}
if (resume_state.m_fb == NULL) baseSP = mSP;
else if (_fb->nlocal == 100) { baseSP = resume_state.m_baseSP; return; } //fiddle!
else baseSP = mSP;
mSP -= _fb->nlocal;
// *add 0.9.4 Stack Overflow check in start_function
if ((long)mSP < (long)_stack_) throw Exception("Exec Stack overflow");
ptr_check = Parser::debug.ptr_check;
// *add 1.2.4 Can control default entry behaviour for trace
if (gCanTrace && _fb->trace && _fb->trace->do_enter()) {
// suppress tracing while in custom trace method
XExecState* xs = get_exec_state();
gCanTrace = false;
_fb->trace->enter(xs);
gCanTrace = true;
// and copy function state back to allow for re-dispatching
fb = xs->fb;
ppcode = (Instruction*)xs->ip;
mSP = xs->sp;
baseSP = xs->bp;
}
}
// *add 1.2.4 Can switch off default exit behaviour for trace
void end_function()
{
#ifdef _DEBUG
if (gFunBlock == fb)
__break(3);
#endif
if (fb->trace && gCanTrace && fb->trace->do_leave()) {
gCanTrace = false;
fb->trace->leave(get_exec_state());
gCanTrace = true;
}
baseSP = (int *)fs.pop();
fb = (FBlock *)fs.pop();
ppcode = (Instruction *)fs.pop();
}
void *fs_ptr(int offs)
{
return *fs.ref_to_TOS(offs);
}
// *add 1.1.1 Dumping the stack frame after a breakpoint halt
void get_stack_frame(int fi, ExecutionState& xs)
{
if (fi == 0) {
xs.m_baseSP = baseSP;
xs.m_fb = fb;
xs.m_ppcode = ppcode;
} else {
int offs = -3*(fi-1);
xs.m_baseSP = (int *)fs_ptr(offs);
xs.m_fb = (FBlock *)fs_ptr(offs-1);
xs.m_ppcode = (Instruction *)fs_ptr(offs-2);
}
}
int stack_frame_depth()
{ return fs.depth()/3 - 1; } // i.e. not interested in <temp>!
// *change 1.2.8 write out full name of function....
// the report is now in a more parser-friendly format.
void dump_fun_frame(int i, const ExecutionState& xs)
{
LineInfo li;
FBlock *this_fb = xs.m_fb;
if (this_fb == START_FB) cmsg << i << " <DLL> 0 <function>" << endl;
else {
FunctionContext *fcxt = (FunctionContext *)this_fb->context;
li.ip_offset = (xs.m_ppcode - this_fb->pstart)/sizeof(Instruction);
if (fcxt && fcxt->ip_to_line(li))
;
else {
li.file = "<none>";
li.line = -2;
}
cmsg << i << ' ' << li.file << ' ' << li.line+2 << ' '
<< fname_from_fblock(this_fb) << endl;
}
}
void dump_fun_stack()
{
ExecutionState xs;
for(int i = 0, n = stack_frame_depth(); i < n; i++) {
get_stack_frame(i,xs);
dump_fun_frame(i,xs);
}
}
int *find_main_baseSP(char *fct)
{
ExecutionState xs;
for(int i = 0, n = stack_frame_depth(); i < n; i++) {
get_stack_frame(i,xs);
if (xs.m_fb->function->name()==fct) {
get_stack_frame(i-1,xs);
cout << "called from main: " << xs.m_fb->function->name() << endl;
return xs.m_baseSP;
}
}
return NULL;
}
void Engine::attach_main_context(char *fct)
{
Table *cntxt = Function::lookup(fct)->context();
Parser::state.push_context(cntxt);
baseSP = find_main_baseSP(fct);
}
// *add 1.1.1 Selecting a stack frame while halted
void Engine::set_frame(int i, bool verbose)
{
ExecutionState xs;
LineInfo li;
Parser::state.pop_context();
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -