diff --git a/makefile b/makefile index 7317864..404ac74 100644 --- a/makefile +++ b/makefile @@ -127,12 +127,14 @@ build/nasal_builtin.o: \ build/coroutine.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/coroutine.h src/coroutine.cpp | build $(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o build/bits_lib.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/bits_lib.h src/bits_lib.cpp | build $(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o @@ -140,30 +142,35 @@ build/bits_lib.o: \ build/math_lib.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/math_lib.h src/math_lib.cpp | build $(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o build/io_lib.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/io_lib.h src/io_lib.cpp | build $(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o build/dylib_lib.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/dylib_lib.h src/dylib_lib.cpp | build $(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o build/unix_lib.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/unix_lib.h src/unix_lib.cpp | build $(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o build/fg_props.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/fg_props.h src/fg_props.cpp | build $(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o diff --git a/src/nasal_dbg.cpp b/src/nasal_dbg.cpp index e73fc46..491a2e1 100644 --- a/src/nasal_dbg.cpp +++ b/src/nasal_dbg.cpp @@ -159,7 +159,7 @@ void dbg::step_info() { begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3); end = (1+(ctx.pc>>3))<<3; - codestream::set(cnum, cstr, native.data(), files); + codestream::set(const_number, const_string, native_function.data(), files); std::clog << "next bytecode:\n"; for(u32 i = begin; i> "; std::getline(std::cin, cmd); - auto res=parse(cmd); + auto res = parse(cmd); if (res.size()==0) { step_info(); } else if (res.size()==1) { switch(get_cmd_type(res[0])) { case dbg_cmd::cmd_help: help(); break; - case dbg_cmd::cmd_backtrace: traceback(); break; + case dbg_cmd::cmd_backtrace: trace_back(); break; case dbg_cmd::cmd_continue: return; case dbg_cmd::cmd_list_file: list_file(); break; - case dbg_cmd::cmd_global: gstate(); break; - case dbg_cmd::cmd_local: lstate(); break; - case dbg_cmd::cmd_upval: ustate(); break; - case dbg_cmd::cmd_register: reginfo(); break; - case dbg_cmd::cmd_show_all: detail(); break; + case dbg_cmd::cmd_global: global_state(); break; + case dbg_cmd::cmd_local: local_state(); break; + case dbg_cmd::cmd_upval: upvalue_state(); break; + case dbg_cmd::cmd_register: register_info(); break; + case dbg_cmd::cmd_show_all: all_state_detail(); break; case dbg_cmd::cmd_next: next = true; return; case dbg_cmd::cmd_exit: std::exit(0); default: err(); break; diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index ed16232..97ec928 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -11,14 +11,14 @@ void vm::init( const std::vector& filenames, const std::vector& argv ) { - cnum = nums.data(); - cstr = strs.data(); + const_number = nums.data(); + const_string = strs.data(); bytecode = code.data(); files = filenames.data(); global_size = global_symbol.size(); /* set native functions */ - native = natives; + native_function = natives; /* set context and global */ if (!is_repl_mode || first_exec_flag) { @@ -64,7 +64,7 @@ void vm::context_and_global_init() { } } -void vm::valinfo(var& val) { +void vm::value_info(var& val) { const auto p = reinterpret_cast(val.val.gcobj); switch(val.type) { case vm_none: std::clog << "| null |"; break; @@ -106,10 +106,74 @@ void vm::valinfo(var& val) { std::clog << "\n"; } -void vm::traceback() { +void vm::function_detail_info(const nas_func& func) { + std::clog << "func@0x"; + std::clog << std::hex << reinterpret_cast(&func) << std::dec; + + std::vector argument_list = {}; + argument_list.resize(func.keys.size()); + for(const auto& key : func.keys) { + argument_list[key.second-1] = key.first; + } + + std::clog << "("; + for(const auto& key : argument_list) { + std::clog << key; + if (key != argument_list.back()) { + std::clog << ", "; + } + } + if (func.dynamic_parameter_index>=0) { + std::clog << (argument_list.size()? ", ":""); + std::clog << const_string[func.dynamic_parameter_index] << "..."; + } + std::clog << ") "; + std::clog << "{entry: 0x" << std::hex << func.entry << std::dec << "}"; +} + +void vm::function_call_trace() { var* bottom = ctx.stack; var* top = ctx.top; + // generate trace back + std::stack functions; + for(var* i = bottom; i<=top; ++i) { + if (i->type==vm_func && i-1>=bottom && (i-1)->type==vm_ret) { + functions.push(&i->func()); + } + } + if (functions.empty()) { + return; + } + + std::clog << "\ncall trace " << (ngc.cort? "(coroutine)":"(main)") << "\n"; + const nas_func* last = nullptr; + u32 same = 0; + for(auto func = last; !functions.empty(); functions.pop()) { + func = functions.top(); + if (last==func) { + ++same; + continue; + } else if (same) { + std::clog << " --> " << same << " same call(s)\n"; + same = 0; + } + + last = func; + std::clog << " call "; + function_detail_info(*func); + std::clog << "\n"; + } + if (same) { + std::clog << " --> " << same << " same call(s)\n"; + } +} + +void vm::trace_back() { + var* bottom = ctx.stack; + var* top = ctx.top; + + // generate trace back std::stack ret; for(var* i = bottom; i<=top; ++i) { if (i->type==vm_ret && i->ret()!=0) { @@ -118,10 +182,8 @@ void vm::traceback() { } ret.push(ctx.pc); // store the position program crashed - std::clog << "trace back (" - << (ngc.cort? "coroutine":"main") - << ")\n"; - codestream::set(cnum, cstr, native.data(), files); + std::clog << "\ntrace back " << (ngc.cort? "(coroutine)":"(main)") << "\n"; + codestream::set(const_number, const_string, native_function.data(), files); for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) { if ((p = ret.top())==prev) { ++same; @@ -132,17 +194,17 @@ void vm::traceback() { << std::setw(6) << std::setfill('0') << prev << std::dec << " " << same << " same call(s)\n"; + same = 0; } - same = 0; std::clog << " " << codestream(bytecode[p], p) << "\n"; } // the first called place has no same calls } -void vm::stackinfo(const u32 limit = 10) { +void vm::stack_info(const u32 limit = 10) { var* top = ctx.top; var* bottom = ctx.stack; - std::clog << "stack (0x" << std::hex << reinterpret_cast(bottom); + std::clog << "\nstack (0x" << std::hex << reinterpret_cast(bottom); std::clog << std::dec << ", limit " << limit << ", total "; std::clog << (top(top-bottom+1)) << ")\n"; for(u32 i = 0; i=bottom; ++i, --top) { @@ -150,12 +212,12 @@ void vm::stackinfo(const u32 limit = 10) { << std::setw(6) << std::setfill('0') << static_cast(top-bottom) << std::dec << " "; - valinfo(top[0]); + value_info(top[0]); } } -void vm::reginfo() { - std::clog << "registers (" << (ngc.cort? "coroutine":"main") +void vm::register_info() { + std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main") << ")\n" << std::hex << " [pc ] | pc | 0x" << ctx.pc << "\n" << " [global] | addr | 0x" @@ -169,45 +231,45 @@ void vm::reginfo() { << " [top ] | addr | 0x" << reinterpret_cast(ctx.top) << "\n" << std::dec; - std::clog << " [funcr ] "; valinfo(ctx.funcr); - std::clog << " [upval ] "; valinfo(ctx.upvalr); + std::clog << " [funcr ] "; value_info(ctx.funcr); + std::clog << " [upval ] "; value_info(ctx.upvalr); } -void vm::gstate() { +void vm::global_state() { if (!global_size || global[0].type==vm_none) { return; } - std::clog << "global (0x" << std::hex + std::clog << "\nglobal (0x" << std::hex << reinterpret_cast(global) << ")\n" << std::dec; for(usize i = 0; i(ctx.localr) + std::clog << "\nlocal (0x" << std::hex << reinterpret_cast(ctx.localr) << " <+" << static_cast(ctx.localr-ctx.stack) << ">)\n" << std::dec; for(u32 i = 0; i upval[" << i << "]:\n"; @@ -216,42 +278,77 @@ void vm::ustate() { std::clog << " 0x" << std::hex << std::setw(6) << std::setfill('0') << j << std::dec << " "; - valinfo(uv[j]); + value_info(uv[j]); } } } -void vm::detail() { - reginfo(); - gstate(); - lstate(); - ustate(); +void vm::all_state_detail() { + register_info(); + global_state(); + local_state(); + upvalue_state(); } std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const { - auto result = std::string("lack argument(s): "); + auto result = std::string("lack argument(s) when calling function:\n func("); std::vector argument_list = {}; argument_list.resize(func.keys.size()); for(const auto& i : func.keys) { argument_list[i.second-1] = i.first; } - for(u32 i = argc; i=0) { + result += argument_list.size()? ", ":""; + result += const_string[func.dynamic_parameter_index] + "[dynamic]"; + } + result += ") "; + std::stringstream out; + out << "{entry: 0x" << std::hex << func.entry << std::dec << "}"; + out << " @ 0x" << std::hex << reinterpret_cast(&func) << std::dec; + return result + out.str(); +} + +std::string vm::report_special_call_lack_arguments( + var* local, const nas_func& func) const { + auto result = std::string("lack argument(s) when calling function:\n func("); + std::vector argument_list = {}; + argument_list.resize(func.keys.size()); + for(const auto& i : func.keys) { + argument_list[i.second-1] = i.first; + } + for(const auto& key : argument_list) { + if (local[func.keys.at(key)].type==vm_none) { + result += key + ", "; + } else { + result += key + "[get], "; + } + } + result = result.substr(0, result.length()-2); + result += ") "; + std::stringstream out; + out << "{entry: 0x" << std::hex << func.entry << std::dec << "}"; + out << " @ 0x" << std::hex << reinterpret_cast(&func) << std::dec; + return result + out.str(); } void vm::die(const std::string& str) { std::cerr << "[vm] error: " << str << "\n"; - traceback(); - stackinfo(); + function_call_trace(); + trace_back(); + stack_info(); // show verbose crash info if (verbose) { - detail(); + all_state_detail(); } if (!ngc.cort) { diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 930b845..cbd4f68 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include "nasal_import.h" #include "nasal_gc.h" @@ -22,10 +24,10 @@ protected: context ctx; /* constants */ - const f64* cnum = nullptr; // constant numbers - const std::string* cstr = nullptr; // constant symbols and strings + const f64* const_number = nullptr; // constant numbers + const std::string* const_string = nullptr; // constant symbols and strings std::vector imm; // immediate number table - std::vector native; + std::vector native_function; /* garbage collector */ gc ngc; @@ -57,15 +59,18 @@ protected: /* debug functions */ bool verbose = false; - void valinfo(var&); - void traceback(); - void stackinfo(const u32); - void reginfo(); - void gstate(); - void lstate(); - void ustate(); - void detail(); + void value_info(var&); + void function_detail_info(const nas_func&); + void function_call_trace(); + void trace_back(); + void stack_info(const u32); + void register_info(); + void global_state(); + void local_state(); + void upvalue_state(); + void all_state_detail(); std::string report_lack_arguments(u32, const nas_func&) const; + std::string report_special_call_lack_arguments(var*, const nas_func&) const; void die(const std::string&); /* vm calculation functions*/ @@ -222,7 +227,7 @@ inline void vm::o_loadu() { } inline void vm::o_pnum() { - (++ctx.top)[0] = var::num(cnum[imm[ctx.pc]]); + (++ctx.top)[0] = var::num(const_number[imm[ctx.pc]]); } inline void vm::o_pnil() { @@ -269,14 +274,14 @@ inline void vm::o_newf() { } inline void vm::o_happ() { - ctx.top[-1].hash().elems[cstr[imm[ctx.pc]]] = ctx.top[0]; + ctx.top[-1].hash().elems[const_string[imm[ctx.pc]]] = ctx.top[0]; --ctx.top; } inline void vm::o_para() { auto& func = ctx.top[0].func(); // func->size has 1 place reserved for "me" - func.keys[cstr[imm[ctx.pc]]] = func.parameter_size; + func.keys[const_string[imm[ctx.pc]]] = func.parameter_size; func.local[func.parameter_size++] = var::none(); } @@ -284,7 +289,7 @@ inline void vm::o_deft() { var val = ctx.top[0]; auto& func = (--ctx.top)[0].func(); // func->size has 1 place reserved for "me" - func.keys[cstr[imm[ctx.pc]]] = func.parameter_size; + func.keys[const_string[imm[ctx.pc]]] = func.parameter_size; func.local[func.parameter_size++] = val; } @@ -370,14 +375,14 @@ inline void vm::o_lnk() { } #define op_calc_const(type)\ - ctx.top[0] = var::num(ctx.top[0].tonum() type cnum[imm[ctx.pc]]); + ctx.top[0] = var::num(ctx.top[0].tonum() type const_number[imm[ctx.pc]]); inline void vm::o_addc() {op_calc_const(+);} inline void vm::o_subc() {op_calc_const(-);} inline void vm::o_mulc() {op_calc_const(*);} inline void vm::o_divc() {op_calc_const(/);} inline void vm::o_lnkc() { - ctx.top[0] = ngc.newstr(ctx.top[0].tostr()+cstr[imm[ctx.pc]]); + ctx.top[0] = ngc.newstr(ctx.top[0].tostr()+const_string[imm[ctx.pc]]); } // top[0] stores the value of memr[0], to avoid being garbage-collected @@ -435,7 +440,9 @@ inline void vm::o_bxoreq() { // like this: func{a+=1;}(); the result of 'a+1' will no be used later, imm[pc]>>31=1 // but if b+=a+=1; the result of 'a+1' will be used later, imm[pc]>>31=0 #define op_calc_eq_const(type)\ - ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\ + ctx.top[0] = ctx.memr[0] = var::num(\ + ctx.memr[0].tonum() type const_number[imm[ctx.pc]]\ + );\ ctx.memr = nullptr; inline void vm::o_addeqc() {op_calc_eq_const(+);} @@ -443,12 +450,16 @@ inline void vm::o_subeqc() {op_calc_eq_const(-);} inline void vm::o_muleqc() {op_calc_eq_const(*);} inline void vm::o_diveqc() {op_calc_eq_const(/);} inline void vm::o_lnkeqc() { - ctx.top[0] = ctx.memr[0] = ngc.newstr(ctx.memr[0].tostr()+cstr[imm[ctx.pc]]); + ctx.top[0] = ctx.memr[0] = ngc.newstr( + ctx.memr[0].tostr()+const_string[imm[ctx.pc]] + ); ctx.memr = nullptr; } #define op_calc_eq_const_and_pop(type)\ - ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\ + ctx.top[0] = ctx.memr[0] = var::num(\ + ctx.memr[0].tonum() type const_number[imm[ctx.pc]]\ + );\ ctx.memr = nullptr;\ --ctx.top; @@ -458,7 +469,7 @@ inline void vm::o_mulecp() {op_calc_eq_const_and_pop(*);} inline void vm::o_divecp() {op_calc_eq_const_and_pop(/);} inline void vm::o_lnkecp() { ctx.top[0] = ctx.memr[0] = ngc.newstr( - ctx.memr[0].tostr()+cstr[imm[ctx.pc]] + ctx.memr[0].tostr()+const_string[imm[ctx.pc]] ); ctx.memr = nullptr; --ctx.top; @@ -515,7 +526,7 @@ inline void vm::o_grt() {op_cmp(>);} inline void vm::o_geq() {op_cmp(>=);} #define op_cmp_const(type)\ - ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])? one:zero; + ctx.top[0] = (ctx.top[0].tonum() type const_number[imm[ctx.pc]])? one:zero; inline void vm::o_lessc() {op_cmp_const(<);} inline void vm::o_leqc() {op_cmp_const(<=);} @@ -656,7 +667,7 @@ inline void vm::o_callh() { die("must call a hash"); return; } - const auto& str = cstr[imm[ctx.pc]]; + const auto& str = const_string[imm[ctx.pc]]; if (val.type==vm_hash) { ctx.top[0] = val.hash().get_val(str); } else { @@ -761,7 +772,7 @@ inline void vm::o_callfh() { // dynamic parameter is not allowed in this kind of function call if (func.dynamic_parameter_index>=0) { die("special call cannot use dynamic argument \"" + - cstr[func.dynamic_parameter_index] + "\"" + const_string[func.dynamic_parameter_index] + "\"" ); return; } @@ -782,15 +793,7 @@ inline void vm::o_callfh() { } } if (lack_arguments_flag) { - auto info = std::string("lack argument(s): "); - for(const auto& i : func.keys) { - const auto& key = i.first; - if (local[i.second].type==vm_none) { - info += key + ", "; - } - } - info = info.substr(0, info.length()-2); - die(info); + die(report_special_call_lack_arguments(local, func)); return; } @@ -809,7 +812,7 @@ inline void vm::o_callb() { // if running a native function about coroutine // (top) will be set to another context.top, instead of main_context.top - var tmp = (*native[imm[ctx.pc]].func)(ctx.localr, ngc); + var tmp = (*native_function[imm[ctx.pc]].func)(ctx.localr, ngc); // so we use tmp variable to store this return value // and set it to top[0] later @@ -949,7 +952,7 @@ inline void vm::o_mcallh() { die("must call a hash"); return; } - const auto& str = cstr[imm[ctx.pc]]; + const auto& str = const_string[imm[ctx.pc]]; if (hash.type==vm_map) { ctx.memr = hash.map().get_mem(str); if (!ctx.memr) {