842 lines
26 KiB
C++
842 lines
26 KiB
C++
#include "nasal_vm.h"
|
|
#include "util/util.h"
|
|
|
|
namespace nasal {
|
|
|
|
void vm::vm_init_enrty(const std::vector<std::string>& strs,
|
|
const std::vector<f64>& nums,
|
|
const std::vector<nasal_builtin_table>& natives,
|
|
const std::vector<opcode>& code,
|
|
const std::unordered_map<std::string, u64>& global_symbol,
|
|
const std::vector<std::string>& filenames,
|
|
const std::vector<std::string>& argv) {
|
|
const_number = nums.data();
|
|
const_string = strs.data();
|
|
bytecode = code.data();
|
|
files = filenames.data();
|
|
global_size = global_symbol.size();
|
|
|
|
/* set native functions */
|
|
native_function = natives;
|
|
|
|
/* set context and global */
|
|
if (!is_repl_mode || first_exec_flag) {
|
|
context_and_global_init();
|
|
first_exec_flag = false;
|
|
}
|
|
|
|
/* init gc */
|
|
ngc.set(&ctx, global, global_size);
|
|
ngc.init(strs, argv);
|
|
|
|
/* init vm globals */
|
|
auto map_instance = ngc.alloc(vm_type::vm_map);
|
|
global_symbol_name.resize(global_symbol.size());
|
|
global[global_symbol.at("globals")] = map_instance;
|
|
for (const auto& i : global_symbol) {
|
|
map_instance.map().mapper[i.first] = global + i.second;
|
|
global_symbol_name[i.second] = i.first;
|
|
}
|
|
|
|
/* init vm arg */
|
|
auto arg_instance = ngc.alloc(vm_type::vm_vec);
|
|
global[global_symbol.at("arg")] = arg_instance;
|
|
arg_instance.vec().elems = ngc.env_argv;
|
|
}
|
|
|
|
void vm::context_and_global_init() {
|
|
/* set canary and program counter */
|
|
ctx.pc = 0;
|
|
ctx.localr = nullptr;
|
|
ctx.memr = nullptr;
|
|
ctx.funcr = nil;
|
|
ctx.upvalr = nil;
|
|
|
|
/* set canary = stack[VM_STACK_DEPTH-1] */
|
|
ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
|
|
|
|
/* nothing is on stack */
|
|
ctx.top = ctx.stack - 1;
|
|
|
|
/* clear main stack and global */
|
|
for (u32 i = 0; i<VM_STACK_DEPTH; ++i) {
|
|
ctx.stack[i] = nil;
|
|
global[i] = nil;
|
|
}
|
|
}
|
|
|
|
void vm::return_address_info(const var& val) {
|
|
std::clog << "0x";
|
|
std::clog << std::hex << val.ret() << std::dec;
|
|
}
|
|
|
|
void vm::memory_address_info(const var& val) {
|
|
std::clog << "0x";
|
|
std::clog << std::hex << reinterpret_cast<u64>(val.addr()) << std::dec;
|
|
}
|
|
|
|
void vm::raw_string_info(var& val) {
|
|
std::clog << "\"" << util::rawstr(val.str(), 24) << "\"";
|
|
}
|
|
|
|
void vm::upvalue_info(var& val) {
|
|
std::clog << "[" << val.upval().size << " val] ";
|
|
if (val.upval().on_stack) {
|
|
std::clog << "offset:0x" << std::hex;
|
|
std::clog << reinterpret_cast<u64>(val.upval().stack_frame_offset);
|
|
std::clog << std::dec;
|
|
}
|
|
}
|
|
|
|
void vm::vector_value_info(var& val) {
|
|
std::clog << "[" << val.vec().size() << " val]";
|
|
}
|
|
|
|
void vm::hash_value_info(var& val, const usize max_show_elems) {
|
|
std::clog << "{";
|
|
usize count = 0;
|
|
for (const auto& i : val.hash().elems) {
|
|
++count;
|
|
if (count>max_show_elems) {
|
|
break;
|
|
}
|
|
|
|
std::clog << i.first;
|
|
if (count!=val.hash().size()) {
|
|
std::clog << ", ";
|
|
}
|
|
}
|
|
if (val.hash().size()>max_show_elems) {
|
|
std::clog << "...";
|
|
}
|
|
std::clog << "}";
|
|
}
|
|
|
|
void vm::coroutine_value_info(var& val) {
|
|
std::clog << "[ ";
|
|
switch(val.co().status) {
|
|
case nas_co::status::dead: std::clog << "dead"; break;
|
|
case nas_co::status::running: std::clog << "running"; break;
|
|
case nas_co::status::suspended: std::clog << "suspended"; break;
|
|
}
|
|
std::clog << " ] @0x";
|
|
std::clog << std::hex << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
|
|
}
|
|
|
|
void vm::namespace_value_info(var& val, const usize max_show_elems) {
|
|
std::clog << "{";
|
|
usize count = 0;
|
|
for (const auto& i : val.map().mapper) {
|
|
++count;
|
|
if (count>max_show_elems) {
|
|
break;
|
|
}
|
|
|
|
std::clog << i.first;
|
|
if (count!=val.map().size()) {
|
|
std::clog << ", ";
|
|
}
|
|
}
|
|
if (val.map().size()>max_show_elems) {
|
|
std::clog << "...";
|
|
}
|
|
std::clog << "}";
|
|
}
|
|
|
|
void vm::value_name_form(const var& val) {
|
|
std::clog << "| ";
|
|
switch(val.type) {
|
|
case vm_type::vm_none: std::clog << "null "; break;
|
|
case vm_type::vm_ret: std::clog << "ret "; break;
|
|
case vm_type::vm_addr: std::clog << "addr "; break;
|
|
case vm_type::vm_cnt: std::clog << "cnt "; break;
|
|
case vm_type::vm_nil: std::clog << "nil "; break;
|
|
case vm_type::vm_num: std::clog << "num "; break;
|
|
case vm_type::vm_str: std::clog << "str "; break;
|
|
case vm_type::vm_func: std::clog << "func "; break;
|
|
case vm_type::vm_upval: std::clog << "upval"; break;
|
|
case vm_type::vm_vec: std::clog << "vec "; break;
|
|
case vm_type::vm_hash: std::clog << "hash "; break;
|
|
case vm_type::vm_ghost: std::clog << "ghost"; break;
|
|
case vm_type::vm_co: std::clog << "co "; break;
|
|
case vm_type::vm_map: std::clog << "map "; break;
|
|
default: std::clog << "err "; break;
|
|
}
|
|
std::clog << " | ";
|
|
}
|
|
|
|
void vm::value_info(var& val) {
|
|
value_name_form(val);
|
|
|
|
switch(val.type) {
|
|
case vm_type::vm_none: break;
|
|
case vm_type::vm_ret: return_address_info(val); break;
|
|
case vm_type::vm_addr: memory_address_info(val); break;
|
|
case vm_type::vm_cnt: std::clog << val.cnt(); break;
|
|
case vm_type::vm_nil: break;
|
|
case vm_type::vm_num: std::clog << val.num(); break;
|
|
case vm_type::vm_str: raw_string_info(val); break;
|
|
case vm_type::vm_func: std::clog << val.func(); break;
|
|
case vm_type::vm_upval: upvalue_info(val); break;
|
|
case vm_type::vm_vec: vector_value_info(val); break;
|
|
case vm_type::vm_hash: hash_value_info(val, 4); break;
|
|
case vm_type::vm_ghost: std::clog << val.ghost(); break;
|
|
case vm_type::vm_co: coroutine_value_info(val); break;
|
|
case vm_type::vm_map: namespace_value_info(val, 4); break;
|
|
default: std::clog << "unknown"; break;
|
|
}
|
|
std::clog << "\n";
|
|
}
|
|
|
|
void vm::function_detail_info(const nas_func& func) {
|
|
std::clog << "func@";
|
|
std::clog << std::hex << reinterpret_cast<u64>(&func) << std::dec;
|
|
|
|
std::vector<std::string> 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 << func.dynamic_parameter_name << "...";
|
|
}
|
|
std::clog << ") ";
|
|
const auto& code = bytecode[func.entry];
|
|
std::clog << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
|
|
}
|
|
|
|
void vm::function_call_trace() {
|
|
util::windows_code_page_manager cp;
|
|
cp.set_utf8_output();
|
|
|
|
var* bottom = ctx.stack;
|
|
var* top = ctx.top;
|
|
|
|
// generate trace back
|
|
std::stack<const nas_func*> functions;
|
|
std::stack<u64> callsite;
|
|
|
|
// load call trace, from bottom to top
|
|
for (var* i = bottom; i <= top; ++i) {
|
|
// i-1 is the callsite program counter of this function
|
|
// +-------+----------------+
|
|
// | func | func() {..} | <-- i + 2
|
|
// +-------+----------------+
|
|
// | ret | 0x3bf | <-- i + 1 (value should not be 0x0)
|
|
// +-------+----------------+
|
|
// | addr | 0x7ff5f61ae020 | <-- i
|
|
// +-------+----------------+
|
|
// TODO: op_cnt may destroy the order, so maybe this need refact
|
|
if (i + 2 > top) {
|
|
continue;
|
|
}
|
|
if (!i->is_addr()) {
|
|
continue;
|
|
}
|
|
if ((i+1)->is_ret() && (i+1)->ret()>0 && (i+2)->is_func()) {
|
|
functions.push(&(i+2)->func());
|
|
callsite.push((i+1)->ret());
|
|
}
|
|
}
|
|
|
|
// another condition may exist
|
|
// have ret pc on stack, but no function at the top of the ret pc
|
|
for (var * i = top; i >= bottom; --i) {
|
|
// +-------+----------------+
|
|
// | xxxx | xxxx | <-- i + 2 (not function or not exist)
|
|
// +-------+----------------+
|
|
// | ret | 0x3bf | <-- i + 1 (value should not be 0x0)
|
|
// +-------+----------------+
|
|
// | addr | 0x7ff5f61ae020 | <-- i
|
|
// +-------+----------------+
|
|
if ((i->is_addr() && i+2<=top && (i+1)->is_ret() && !(i+2)->is_func()) ||
|
|
(i->is_addr() && i+1<=top && i+2>top && (i+1)->is_ret())) {
|
|
functions.push(&ctx.funcr.func());
|
|
callsite.push((i+1)->ret());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// function register stores the latest-called function
|
|
if (functions.empty() && !ctx.funcr.is_func()) {
|
|
cp.restore_code_page();
|
|
return;
|
|
}
|
|
|
|
std::clog << "\ncall trace ";
|
|
std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
|
std::clog << " crash occurred at\n ";
|
|
function_detail_info(ctx.funcr.func());
|
|
std::clog << " at " << files[bytecode[ctx.pc].fidx] << ":";
|
|
std::clog << bytecode[ctx.pc].line << "\n";
|
|
|
|
const nas_func* last = nullptr;
|
|
u64 last_callsite = SIZE_MAX;
|
|
u64 same_count = 0;
|
|
for (; !functions.empty() && !callsite.empty(); functions.pop(), callsite.pop()) {
|
|
auto func = functions.top();
|
|
auto place = callsite.top();
|
|
|
|
if (last==func && last_callsite==place) {
|
|
++same_count;
|
|
continue;
|
|
} else if (same_count) {
|
|
std::clog << " `--> " << same_count << " same call(s)\n";
|
|
same_count = 0;
|
|
}
|
|
|
|
last = func;
|
|
last_callsite = place;
|
|
|
|
// output called function
|
|
std::clog << " call ";
|
|
function_detail_info(*func);
|
|
|
|
// output callsite
|
|
std::clog << " from ";
|
|
std::clog << files[bytecode[place].fidx] << ":";
|
|
std::clog << bytecode[place].line << "\n";
|
|
}
|
|
if (same_count) {
|
|
std::clog << " `--> " << same_count << " same call(s)\n";
|
|
}
|
|
|
|
cp.restore_code_page();
|
|
}
|
|
|
|
void vm::trace_back() {
|
|
// generate trace back
|
|
std::stack<u64> ret;
|
|
for (var* i = ctx.stack; i<=ctx.top; ++i) {
|
|
if (i->is_ret() && i->ret()!=0) {
|
|
ret.push(i->ret());
|
|
}
|
|
}
|
|
|
|
// store the position program crashed
|
|
ret.push(ctx.pc);
|
|
|
|
std::clog << "\nback trace ";
|
|
std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
|
codestream::set(
|
|
const_number,
|
|
const_string,
|
|
global_symbol_name,
|
|
native_function.data(),
|
|
files
|
|
);
|
|
|
|
for (u64 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
|
if ((p = ret.top())==prev) {
|
|
++same;
|
|
continue;
|
|
} else if (same) {
|
|
std::clog << " 0x" << std::hex
|
|
<< std::setw(8) << std::setfill('0')
|
|
<< prev << std::dec << " "
|
|
<< same << " same call(s)\n";
|
|
same = 0;
|
|
}
|
|
std::clog << " " << codestream(bytecode[p], p) << "\n";
|
|
}
|
|
// the first called place has no same calls
|
|
}
|
|
|
|
void vm::stack_info(const u64 limit) {
|
|
var* top = ctx.top;
|
|
var* bottom = ctx.stack;
|
|
const auto stack_address = reinterpret_cast<u64>(bottom);
|
|
|
|
std::clog << "\nstack (0x" << std::hex << stack_address << std::dec;
|
|
std::clog << ", limit " << limit << ", total ";
|
|
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
|
|
|
|
for (u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
|
std::clog << " 0x" << std::hex
|
|
<< std::setw(8) << std::setfill('0')
|
|
<< static_cast<u64>(top-bottom) << std::dec
|
|
<< " ";
|
|
value_info(top[0]);
|
|
}
|
|
}
|
|
|
|
void vm::register_info() {
|
|
std::clog << "\nregister (" << (ngc.cort? "coroutine":"main") << ")\n";
|
|
std::clog << std::hex
|
|
<< " [ pc ] | pc | 0x" << ctx.pc << "\n"
|
|
<< " [ global ] | addr | 0x"
|
|
<< reinterpret_cast<u64>(global) << "\n"
|
|
<< " [ local ] | addr | 0x"
|
|
<< reinterpret_cast<u64>(ctx.localr) << "\n"
|
|
<< " [ memr ] | addr | 0x"
|
|
<< reinterpret_cast<u64>(ctx.memr) << "\n"
|
|
<< " [ canary ] | addr | 0x"
|
|
<< reinterpret_cast<u64>(ctx.canary) << "\n"
|
|
<< " [ top ] | addr | 0x"
|
|
<< reinterpret_cast<u64>(ctx.top) << "\n"
|
|
<< std::dec;
|
|
std::clog << " [ funcr ] "; value_info(ctx.funcr);
|
|
std::clog << " [ upval ] "; value_info(ctx.upvalr);
|
|
}
|
|
|
|
void vm::global_state() {
|
|
if (!global_size || global[0].is_none()) {
|
|
return;
|
|
}
|
|
std::clog << "\nglobal (0x" << std::hex
|
|
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
|
|
for (usize i = 0; i<global_size; ++i) {
|
|
std::clog << " 0x" << std::hex << std::setw(8)
|
|
<< std::setfill('0') << static_cast<u64>(i) << std::dec
|
|
<< " ";
|
|
auto name = global_symbol_name[i];
|
|
if (name.length()>=10) {
|
|
name = name.substr(0, 7) + "...";
|
|
} else {
|
|
|
|
}
|
|
std::clog << "| " << std::left << std::setw(10)
|
|
<< std::setfill(' ') << name << " "
|
|
<< std::internal;
|
|
value_info(global[i]);
|
|
}
|
|
}
|
|
|
|
void vm::local_state() {
|
|
if (!ctx.localr || !ctx.funcr.func().local_size) {
|
|
return;
|
|
}
|
|
const u32 lsize = ctx.funcr.func().local_size;
|
|
std::clog << "\nlocal (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
|
|
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
|
|
<< ">)\n" << std::dec;
|
|
for (u32 i = 0; i<lsize; ++i) {
|
|
std::clog << " 0x" << std::hex << std::setw(8)
|
|
<< std::setfill('0') << i << std::dec
|
|
<< " ";
|
|
value_info(ctx.localr[i]);
|
|
}
|
|
}
|
|
|
|
void vm::upvalue_state() {
|
|
if (ctx.funcr.is_nil() || ctx.funcr.func().upval.empty()) {
|
|
return;
|
|
}
|
|
std::clog << "\nupvalue\n";
|
|
auto& upval = ctx.funcr.func().upval;
|
|
for (u32 i = 0; i<upval.size(); ++i) {
|
|
std::clog << " -> upval[" << i << "]:\n";
|
|
auto& uv = upval[i].upval();
|
|
for (u32 j = 0; j<uv.size; ++j) {
|
|
std::clog << " 0x" << std::hex << std::setw(8)
|
|
<< std::setfill('0') << j << std::dec
|
|
<< " ";
|
|
value_info(uv[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
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) when calling function:\n func(");
|
|
std::vector<std::string> argument_list = {};
|
|
argument_list.resize(func.keys.size());
|
|
for (const auto& i : func.keys) {
|
|
argument_list[i.second-1] = i.first;
|
|
}
|
|
for (u32 i = 0; i<argument_list.size(); ++i) {
|
|
result += argument_list[i];
|
|
if (i<argc) {
|
|
result += "[get]";
|
|
}
|
|
if (i!=argument_list.size()-1) {
|
|
result += ", ";
|
|
}
|
|
}
|
|
if (func.dynamic_parameter_index>=0) {
|
|
result += argument_list.size()? ", ":"";
|
|
result += const_string[func.dynamic_parameter_index] + "[dynamic]";
|
|
}
|
|
result += ") ";
|
|
std::stringstream out;
|
|
const auto& code = bytecode[func.entry];
|
|
out << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
|
|
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&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<std::string> 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)].is_none()) {
|
|
result += key + ", ";
|
|
} else {
|
|
result += key + "[get], ";
|
|
}
|
|
}
|
|
result = result.substr(0, result.length()-2);
|
|
result += ") ";
|
|
std::stringstream out;
|
|
const auto& code = bytecode[func.entry];
|
|
out << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
|
|
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
|
|
return result + out.str();
|
|
}
|
|
|
|
std::string vm::report_key_not_found(const std::string& not_found,
|
|
const nas_hash& hash) const {
|
|
auto result = "member \"" + not_found + "\" doesn't exist in hash {";
|
|
for (const auto& i : hash.elems) {
|
|
result += i.first + ", ";
|
|
}
|
|
if (hash.elems.size()) {
|
|
result = result.substr(0, result.length()-2);
|
|
}
|
|
result += "}";
|
|
return result;
|
|
}
|
|
|
|
std::string vm::report_out_of_range(f64 index, usize real_size) const {
|
|
auto result = "index out of range: " + std::to_string(index);
|
|
result += " but max size is " + std::to_string(real_size);
|
|
if (!real_size) {
|
|
return result;
|
|
}
|
|
result += ", index range is -" + std::to_string(real_size);
|
|
result += "~" + std::to_string(real_size-1);
|
|
return result;
|
|
}
|
|
|
|
std::string vm::type_name_string(const var& value) const {
|
|
switch(value.type) {
|
|
case vm_type::vm_none: return "none";
|
|
case vm_type::vm_cnt: return "counter";
|
|
case vm_type::vm_addr: return "address";
|
|
case vm_type::vm_ret: return "program counter";
|
|
case vm_type::vm_nil: return "nil";
|
|
case vm_type::vm_num: return "number";
|
|
case vm_type::vm_str: return "string";
|
|
case vm_type::vm_vec: return "vector";
|
|
case vm_type::vm_hash: return "hash";
|
|
case vm_type::vm_func: return "function";
|
|
case vm_type::vm_upval: return "upvalue";
|
|
case vm_type::vm_ghost: return "ghost type";
|
|
case vm_type::vm_co: return "coroutine";
|
|
case vm_type::vm_map: return "namespace";
|
|
default: break;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
void vm::die(const std::string& str) {
|
|
std::cerr << "[vm] error: " << str << "\n";
|
|
function_call_trace();
|
|
|
|
// trace back contains bytecode info, dump in verbose mode
|
|
if (verbose) {
|
|
trace_back();
|
|
}
|
|
|
|
// verbose will dump more values on stack
|
|
if (verbose) {
|
|
stack_info(64);
|
|
}
|
|
|
|
// show verbose crash info
|
|
if (verbose) {
|
|
all_state_detail();
|
|
}
|
|
|
|
if (!ngc.cort) {
|
|
if (!verbose) {
|
|
std::cerr << "\n[vm] use <-d> for detailed crash info.\n\n";
|
|
}
|
|
// in main context, exit directly
|
|
std::exit(1);
|
|
}
|
|
|
|
// in coroutine, shut down the coroutine and return to main context
|
|
ctx.pc = 0; // mark coroutine 'dead'
|
|
ngc.context_reserve(); // switch context to main
|
|
ctx.top[0] = nil; // generate return value 'nil'
|
|
}
|
|
|
|
void vm::run(const codegen& gen,
|
|
const linker& linker,
|
|
const std::vector<std::string>& argv) {
|
|
vm_init_enrty(
|
|
gen.strs(),
|
|
gen.nums(),
|
|
gen.natives(),
|
|
gen.codes(),
|
|
gen.globals(),
|
|
linker.get_file_list(),
|
|
argv
|
|
);
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
// interrupt check macro for computed goto mode.
|
|
#define CHECK_INTERRUPT { \
|
|
if (interrupt_ptr && interrupt_ptr->load()) { \
|
|
throw std::runtime_error("VM execution interrupted by timeout"); \
|
|
} \
|
|
}
|
|
|
|
// using labels as values/computed goto
|
|
const void* oprs[] = {
|
|
&&vmexit,
|
|
&&repl,
|
|
&&intl,
|
|
&&loadg,
|
|
&&loadl,
|
|
&&loadu,
|
|
&&dup,
|
|
&&pnum,
|
|
&&pnil,
|
|
&&pstr,
|
|
&&newv,
|
|
&&newh,
|
|
&&newf,
|
|
&&happ,
|
|
&¶,
|
|
&&deft,
|
|
&&dyn,
|
|
&&lnot,
|
|
&&usub,
|
|
&&bnot,
|
|
&&btor,
|
|
&&btxor,
|
|
&&btand,
|
|
&&add,
|
|
&&sub,
|
|
&&mul,
|
|
&&div,
|
|
&&lnk,
|
|
&&addc,
|
|
&&subc,
|
|
&&mulc,
|
|
&&divc,
|
|
&&lnkc,
|
|
&&addeq,
|
|
&&subeq,
|
|
&&muleq,
|
|
&&diveq,
|
|
&&lnkeq,
|
|
&&bandeq,
|
|
&&boreq,
|
|
&&bxoreq,
|
|
&&addeqc,
|
|
&&subeqc,
|
|
&&muleqc,
|
|
&&diveqc,
|
|
&&lnkeqc,
|
|
&&addecp,
|
|
&&subecp,
|
|
&&mulecp,
|
|
&&divecp,
|
|
&&lnkecp,
|
|
&&meq,
|
|
&&eq,
|
|
&&neq,
|
|
&&less,
|
|
&&leq,
|
|
&&grt,
|
|
&&geq,
|
|
&&lessc,
|
|
&&leqc,
|
|
&&grtc,
|
|
&&geqc,
|
|
&&pop,
|
|
&&jmp,
|
|
&&jt,
|
|
&&jf,
|
|
&&cnt,
|
|
&&findex,
|
|
&&feach,
|
|
&&callg,
|
|
&&calll,
|
|
&&upval,
|
|
&&callv,
|
|
&&callvi,
|
|
&&callh,
|
|
&&callfv,
|
|
&&callfh,
|
|
&&callb,
|
|
&&slcbeg,
|
|
&&slcend,
|
|
&&slc,
|
|
&&slc2,
|
|
&&mcallg,
|
|
&&mcalll,
|
|
&&mupval,
|
|
&&mcallv,
|
|
&&mcallh,
|
|
&&ret
|
|
};
|
|
std::vector<const void*> code;
|
|
for (const auto& i : gen.codes()) {
|
|
code.push_back(oprs[i.op]);
|
|
imm.push_back(i.num);
|
|
}
|
|
CHECK_INTERRUPT;
|
|
// goto the first operand
|
|
goto *code[ctx.pc];
|
|
#else
|
|
std::vector<nasal_vm_func> code;
|
|
for (const auto& i : gen.codes()) {
|
|
code.push_back(operand_function[i.op]);
|
|
imm.push_back(i.num);
|
|
}
|
|
while (code[ctx.pc]) {
|
|
if (interrupt_ptr && interrupt_ptr->load()) {
|
|
throw std::runtime_error("VM execution interrupted by timeout");
|
|
}
|
|
(this->*code[ctx.pc])();
|
|
if (ctx.top>=ctx.canary) {
|
|
die("stack overflow");
|
|
}
|
|
++ctx.pc;
|
|
}
|
|
#endif
|
|
// all nasal programs should end here
|
|
vmexit:
|
|
if (verbose) {
|
|
ngc.status.dump_info();
|
|
}
|
|
imm.clear();
|
|
if (!is_repl_mode) {
|
|
ngc.clear();
|
|
}
|
|
return;
|
|
|
|
#ifndef _MSC_VER
|
|
// IR which may cause stackoverflow
|
|
#define exec_check(op) {\
|
|
op();\
|
|
CHECK_INTERRUPT;\
|
|
if (ctx.top<ctx.canary)\
|
|
goto *code[++ctx.pc];\
|
|
die("stack overflow");\
|
|
goto *code[++ctx.pc];\
|
|
}
|
|
// IR which does not cause stackoverflow
|
|
#define exec_nodie(op) {\
|
|
op();\
|
|
CHECK_INTERRUPT;\
|
|
goto *code[++ctx.pc];\
|
|
}
|
|
|
|
repl: exec_nodie(o_repl ); // 0
|
|
intl: exec_nodie(o_intl ); // -0
|
|
loadg: exec_nodie(o_loadg ); // -1
|
|
loadl: exec_nodie(o_loadl ); // -1
|
|
loadu: exec_nodie(o_loadu ); // -1
|
|
dup: exec_check(o_dup ); // +1
|
|
pnum: exec_check(o_pnum ); // +1
|
|
pnil: exec_check(o_pnil ); // +1
|
|
pstr: exec_check(o_pstr ); // +1
|
|
newv: exec_check(o_newv ); // +1-imm[pc]
|
|
newh: exec_check(o_newh ); // +1
|
|
newf: exec_check(o_newf ); // +1
|
|
happ: exec_nodie(o_happ ); // -1
|
|
para: exec_nodie(o_para ); // -0
|
|
deft: exec_nodie(o_deft ); // -1
|
|
dyn: exec_nodie(o_dyn ); // -0
|
|
lnot: exec_nodie(o_lnot ); // -0
|
|
usub: exec_nodie(o_usub ); // -0
|
|
bnot: exec_nodie(o_bnot ); // -0
|
|
btor: exec_nodie(o_btor ); // -1
|
|
btxor: exec_nodie(o_btxor ); // -1
|
|
btand: exec_nodie(o_btand ); // -1
|
|
add: exec_nodie(o_add ); // -1
|
|
sub: exec_nodie(o_sub ); // -1
|
|
mul: exec_nodie(o_mul ); // -1
|
|
div: exec_nodie(o_div ); // -1
|
|
lnk: exec_nodie(o_lnk ); // -1
|
|
addc: exec_nodie(o_addc ); // -0
|
|
subc: exec_nodie(o_subc ); // -0
|
|
mulc: exec_nodie(o_mulc ); // -0
|
|
divc: exec_nodie(o_divc ); // -0
|
|
lnkc: exec_nodie(o_lnkc ); // -0
|
|
addeq: exec_nodie(o_addeq ); // -1
|
|
subeq: exec_nodie(o_subeq ); // -1
|
|
muleq: exec_nodie(o_muleq ); // -1
|
|
diveq: exec_nodie(o_diveq ); // -1
|
|
lnkeq: exec_nodie(o_lnkeq ); // -1
|
|
bandeq: exec_nodie(o_bandeq); // -1
|
|
boreq: exec_nodie(o_boreq ); // -1
|
|
bxoreq: exec_nodie(o_bxoreq); // -1
|
|
addeqc: exec_nodie(o_addeqc); // -0
|
|
subeqc: exec_nodie(o_subeqc); // -0
|
|
muleqc: exec_nodie(o_muleqc); // -0
|
|
diveqc: exec_nodie(o_diveqc); // -0
|
|
lnkeqc: exec_nodie(o_lnkeqc); // -0
|
|
addecp: exec_nodie(o_addecp); // -1
|
|
subecp: exec_nodie(o_subecp); // -1
|
|
mulecp: exec_nodie(o_mulecp); // -1
|
|
divecp: exec_nodie(o_divecp); // -1
|
|
lnkecp: exec_nodie(o_lnkecp); // -1
|
|
meq: exec_nodie(o_meq ); // -1
|
|
eq: exec_nodie(o_eq ); // -1
|
|
neq: exec_nodie(o_neq ); // -1
|
|
less: exec_nodie(o_less ); // -1
|
|
leq: exec_nodie(o_leq ); // -1
|
|
grt: exec_nodie(o_grt ); // -1
|
|
geq: exec_nodie(o_geq ); // -1
|
|
lessc: exec_nodie(o_lessc ); // -0
|
|
leqc: exec_nodie(o_leqc ); // -0
|
|
grtc: exec_nodie(o_grtc ); // -0
|
|
geqc: exec_nodie(o_geqc ); // -0
|
|
pop: exec_nodie(o_pop ); // -1
|
|
jmp: exec_nodie(o_jmp ); // -0
|
|
jt: exec_nodie(o_jt ); // -0
|
|
jf: exec_nodie(o_jf ); // -1
|
|
cnt: exec_nodie(o_cnt ); // -0
|
|
findex: exec_check(o_findex); // +1
|
|
feach: exec_check(o_feach ); // +1
|
|
callg: exec_check(o_callg ); // +1
|
|
calll: exec_check(o_calll ); // +1
|
|
upval: exec_check(o_upval ); // +1
|
|
callv: exec_nodie(o_callv ); // -0
|
|
callvi: exec_nodie(o_callvi); // -0
|
|
callh: exec_nodie(o_callh ); // -0
|
|
callfv: exec_nodie(o_callfv); // check in the function
|
|
callfh: exec_nodie(o_callfh); // check in the function
|
|
callb: exec_nodie(o_callb ); // -0
|
|
slcbeg: exec_check(o_slcbeg); // +1
|
|
slcend: exec_nodie(o_slcend); // -1
|
|
slc: exec_nodie(o_slc ); // -1
|
|
slc2: exec_nodie(o_slc2 ); // -2
|
|
mcallg: exec_check(o_mcallg); // +1
|
|
mcalll: exec_check(o_mcalll); // +1
|
|
mupval: exec_check(o_mupval); // +1
|
|
mcallv: exec_nodie(o_mcallv); // -0
|
|
mcallh: exec_nodie(o_mcallh); // -0
|
|
ret: exec_nodie(o_ret ); // -2
|
|
#endif
|
|
}
|
|
|
|
}
|