base = array_stack[--array_stack_pointer]; assert(base != nullptr); #ifndef NDEBUG array_stack[array_stack_pointer] = nullptr;#endif break; case datum_type_t::value_bit64: data_dest = read_8byte_segments(reader, datum_type_t::value_bit64, count, data_dest); break; default: throw module_reader_exception{ "Unknown item data type" }; } } } // See module name in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) void read_module_name(util::buffered_reader_t &reader, vm_module_t &modobj) { auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read module name" }; modobj.module_name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); assert(modobj.module_name->get_length() <= module_constants::max_module_name_bytes); } // See link section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) void read_link_section(util::buffered_reader_t &reader, vm_module_t &modobj) { auto success = bool{}; const auto export_count = modobj.header.export_size; // Allocate memory for the export entries modobj.export_section.reserve(export_count); for (auto c = word_t{ 0 }; c < export_count; ++c) { // Read in single export item auto item = export_function_t{}; std::tie(success, item.pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read linkage pc" }; std::tie(success, item.frame_type) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read linkage frame type index" }; std::tie(success, item.sig) = read_next_word(reader); if (!success) throw module_reader_exception{ "Failed to read linkage signature" }; auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read linkage name" }; item.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); const auto sig = item.sig; modobj.export_section.emplace(sig, std::move(item)); } } // See import section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) // [SPEC] The specification does not seem to match what is observed in an actual module. // The function is modeled after the disassembler (dis.b and libinterp/load.c) instead. void read_import_section(util::buffered_reader_t &reader, vm_module_t &modobj) { auto success = bool{}; auto module_import_count = operand_t{}; std::tie(success, module_import_count) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read module import count" }; // Allocate memory for module imports modobj.import_section.reserve(module_import_count); for (auto c = operand_t{ 0 }; c < module_import_count; ++c) { // Read in single module import import_vm_module_t module{}; auto func_import_count = operand_t{}; std::tie(success, func_import_count) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read function import count" }; // Allocate memory for function imports module.functions.reserve(func_import_count); for (auto k = operand_t{ 0 }; k < func_import_count; ++k) { // Read in single function import import_function_t func{}; std::tie(success, func.sig) = read_next_word(reader); if (!success) throw module_reader_exception{ "Failed to read import function signature" }; auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read import function name" }; func.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); module.functions.push_back(std::move(func)); } modobj.import_section.push_back(std::move(module)); } byte_t value; std::tie(success, value) = reader.get_next_byte(); if (!success || value != 0) throw module_reader_exception{ "Failed to read final byte from import section" }; } // See handler section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) void read_handler_section(util::buffered_reader_t &reader, vm_module_t &modobj) { auto success = bool{}; auto handler_count = operand_t{}; std::tie(success, handler_count) = read_next_operand(reader); // Allocate memory for handlers modobj.handler_section.reserve(handler_count); for (auto c = operand_t{ 0 }; c < handler_count; ++c) { // Read in single handler handler_t handler{}; std::tie(success, handler.exception_offset) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read exception offset" }; std::tie(success, handler.begin_pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read handler begin program counter" }; std::tie(success, handler.end_pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read handler end program counter" }; auto type_desc_number = operand_t{}; std::tie(success, type_desc_number) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read handler type description ID" }; if (type_desc_number != module_constants::no_error_handle_type_desc) { assert(0 <= type_desc_number && type_desc_number < static_cast<operand_t>(modobj.type_section.size())); handler.type_desc = modobj.type_section[type_desc_number]; assert(handler.type_desc != nullptr); } auto handler_cases = operand_t{}; std::tie(success, handler_cases) = read_next_operand(reader); // [SPEC] The operand read above contains two pieces of data. // The higher 16-bits represent the number of exception cases that catch exception types. // The lower 16-bits represent the total number of handled cases. // In the limbo language, strings and exceptions can be caught in an exception handler. // Based on Inferno implementation libinterp/load.c. handler.exception_type_count = word_t{ handler_cases >> 16 }; const auto total_count = (handler_cases & 0xffff); // Allocate memory for exceptions plus the wild card. handler.exception_table.reserve(total_count + 1); for (auto k = operand_t{ 0 }; k < total_count; ++k) { // Read in single exception exception_t except{}; auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read exception name" }; except.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); std::tie(success, except.pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read exception pc" }; handler.exception_table.push_back(std::move(except)); } // Read in wildcard exception exception_t wildcard_exception{}; std::tie(success, wildcard_exception.pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read wildcard exception pc" }; handler.exception_table.push_back(std::move(wildcard_exception)); modobj.handler_section.push_back(std::move(handler)); } byte_t value; std::tie(success, value) = reader.get_next_byte(); if (!success || value != 0) throw module_reader_exception{ "Failed to read final byte from handler section" }; }} export_function_t::export_function_t(export_function_t &&other) : pc{ other.pc } , frame_type{ other.frame_type } , sig{ other.sig } , name{ std::move(other.name) }{} export_function_t::~export_function_t(){ if (name != nullptr) name->release();} import_function_t::import_function_t(import_function_t &&other) : sig{ other.sig } , name{ std::move(other.name) }{} import_function_t::~import_function_t(){ if (name != nullptr) name->release();} import_vm_module_t::import_vm_module_t(import_vm_module_t &&other) : functions{ std::move(other.functions) }{} exception_t::exception_t(exception_t &&other) : pc{ other.pc } , name{ std::move(other.name) }{} exception_t::~exception_t(){ if (name != nullptr) name->release();} handler_t::handler_t(handler_t &&other) : exception_offset{ other.exception_offset } , begin_pc{ other.begin_pc } , end_pc{ other.end_pc } , type_desc{ std::move(other.type_desc) } , exception_table{ std::move(other.exception_table) } , exception_type_count{ other.exception_type_count }{} vm_module_t::~vm_module_t(){ if (module_name != nullptr) module_name->release(); if (original_mp != nullptr) original_mp->release();} std::unique_ptr<vm_module_t> disvm::read_module(const char *path){ assert(path != nullptr); auto module_file = std::ifstream{ path, std::ifstream::binary }; if (!module_file.is_open()) { assert(false && "Failed to open module"); throw vm_user_exception{ "Failed to open module" }; } return read_module(module_file);} std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data){ util::buffered_reader_t reader{ data }; auto modobj = std::make_unique<vm_module_t>(); read_header(reader, *modobj); if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_import_deprecated)) throw module_reader_exception{ "Obsolete module" }; read_code_section(reader, *modobj); read_type_section(reader, *modobj); read_data_section(reader, *modobj); read_module_name(reader, *modobj); read_link_section(reader, *modobj); if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_import)) read_import_section(reader, *modobj); if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_handler)) read_handler_section(reader, *modobj); // [TODO] Native code generation - runtime_flags_t::must_compile assert(!util::has_flag(modobj->header.runtime_flag, runtime_flags_t::must_compile)); return modobj;}Lines 501–800 of 800. Previous lines
base = array_stack[--array_stack_pointer]; assert(base != nullptr); #ifndef NDEBUG array_stack[array_stack_pointer] = nullptr; #endif break; case datum_type_t::value_bit64: data_dest = read_8byte_segments(reader, datum_type_t::value_bit64, count, data_dest); break; default: throw module_reader_exception{ "Unknown item data type" }; } } } // See module name in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) void read_module_name(util::buffered_reader_t &reader, vm_module_t &modobj) { auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read module name" }; modobj.module_name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); assert(modobj.module_name->get_length() <= module_constants::max_module_name_bytes); } // See link section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) void read_link_section(util::buffered_reader_t &reader, vm_module_t &modobj) { auto success = bool{}; const auto export_count = modobj.header.export_size; // Allocate memory for the export entries modobj.export_section.reserve(export_count); for (auto c = word_t{ 0 }; c < export_count; ++c) { // Read in single export item auto item = export_function_t{}; std::tie(success, item.pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read linkage pc" }; std::tie(success, item.frame_type) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read linkage frame type index" }; std::tie(success, item.sig) = read_next_word(reader); if (!success) throw module_reader_exception{ "Failed to read linkage signature" }; auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read linkage name" }; item.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); const auto sig = item.sig; modobj.export_section.emplace(sig, std::move(item)); } } // See import section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) // [SPEC] The specification does not seem to match what is observed in an actual module. // The function is modeled after the disassembler (dis.b and libinterp/load.c) instead. void read_import_section(util::buffered_reader_t &reader, vm_module_t &modobj) { auto success = bool{}; auto module_import_count = operand_t{}; std::tie(success, module_import_count) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read module import count" }; // Allocate memory for module imports modobj.import_section.reserve(module_import_count); for (auto c = operand_t{ 0 }; c < module_import_count; ++c) { // Read in single module import import_vm_module_t module{}; auto func_import_count = operand_t{}; std::tie(success, func_import_count) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read function import count" }; // Allocate memory for function imports module.functions.reserve(func_import_count); for (auto k = operand_t{ 0 }; k < func_import_count; ++k) { // Read in single function import import_function_t func{}; std::tie(success, func.sig) = read_next_word(reader); if (!success) throw module_reader_exception{ "Failed to read import function signature" }; auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read import function name" }; func.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); module.functions.push_back(std::move(func)); } modobj.import_section.push_back(std::move(module)); } byte_t value; std::tie(success, value) = reader.get_next_byte(); if (!success || value != 0) throw module_reader_exception{ "Failed to read final byte from import section" }; } // See handler section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html) void read_handler_section(util::buffered_reader_t &reader, vm_module_t &modobj) { auto success = bool{}; auto handler_count = operand_t{}; std::tie(success, handler_count) = read_next_operand(reader); // Allocate memory for handlers modobj.handler_section.reserve(handler_count); for (auto c = operand_t{ 0 }; c < handler_count; ++c) { // Read in single handler handler_t handler{}; std::tie(success, handler.exception_offset) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read exception offset" }; std::tie(success, handler.begin_pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read handler begin program counter" }; std::tie(success, handler.end_pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read handler end program counter" }; auto type_desc_number = operand_t{}; std::tie(success, type_desc_number) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read handler type description ID" }; if (type_desc_number != module_constants::no_error_handle_type_desc) { assert(0 <= type_desc_number && type_desc_number < static_cast<operand_t>(modobj.type_section.size())); handler.type_desc = modobj.type_section[type_desc_number]; assert(handler.type_desc != nullptr); } auto handler_cases = operand_t{}; std::tie(success, handler_cases) = read_next_operand(reader); // [SPEC] The operand read above contains two pieces of data. // The higher 16-bits represent the number of exception cases that catch exception types. // The lower 16-bits represent the total number of handled cases. // In the limbo language, strings and exceptions can be caught in an exception handler. // Based on Inferno implementation libinterp/load.c. handler.exception_type_count = word_t{ handler_cases >> 16 }; const auto total_count = (handler_cases & 0xffff); // Allocate memory for exceptions plus the wild card. handler.exception_table.reserve(total_count + 1); for (auto k = operand_t{ 0 }; k < total_count; ++k) { // Read in single exception exception_t except{}; auto string_buffer = std::vector<uint8_t>{}; if (!read_next_string(reader, string_buffer)) throw module_reader_exception{ "Failed to read exception name" }; except.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data()); std::tie(success, except.pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read exception pc" }; handler.exception_table.push_back(std::move(except)); } // Read in wildcard exception exception_t wildcard_exception{}; std::tie(success, wildcard_exception.pc) = read_next_operand(reader); if (!success) throw module_reader_exception{ "Failed to read wildcard exception pc" }; handler.exception_table.push_back(std::move(wildcard_exception)); modobj.handler_section.push_back(std::move(handler)); } byte_t value; std::tie(success, value) = reader.get_next_byte(); if (!success || value != 0) throw module_reader_exception{ "Failed to read final byte from handler section" }; } } export_function_t::export_function_t(export_function_t &&other) : pc{ other.pc } , frame_type{ other.frame_type } , sig{ other.sig } , name{ std::move(other.name) } { } export_function_t::~export_function_t() { if (name != nullptr) name->release(); } import_function_t::import_function_t(import_function_t &&other) : sig{ other.sig } , name{ std::move(other.name) } { } import_function_t::~import_function_t() { if (name != nullptr) name->release(); } import_vm_module_t::import_vm_module_t(import_vm_module_t &&other) : functions{ std::move(other.functions) } { } exception_t::exception_t(exception_t &&other) : pc{ other.pc } , name{ std::move(other.name) } { } exception_t::~exception_t() { if (name != nullptr) name->release(); } handler_t::handler_t(handler_t &&other) : exception_offset{ other.exception_offset } , begin_pc{ other.begin_pc } , end_pc{ other.end_pc } , type_desc{ std::move(other.type_desc) } , exception_table{ std::move(other.exception_table) } , exception_type_count{ other.exception_type_count } { } vm_module_t::~vm_module_t() { if (module_name != nullptr) module_name->release(); if (original_mp != nullptr) original_mp->release(); } std::unique_ptr<vm_module_t> disvm::read_module(const char *path) { assert(path != nullptr); auto module_file = std::ifstream{ path, std::ifstream::binary }; if (!module_file.is_open()) { assert(false && "Failed to open module"); throw vm_user_exception{ "Failed to open module" }; } return read_module(module_file); } std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data) { util::buffered_reader_t reader{ data }; auto modobj = std::make_unique<vm_module_t>(); read_header(reader, *modobj); if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_import_deprecated)) throw module_reader_exception{ "Obsolete module" }; read_code_section(reader, *modobj); read_type_section(reader, *modobj); read_data_section(reader, *modobj); read_module_name(reader, *modobj); read_link_section(reader, *modobj); if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_import)) read_import_section(reader, *modobj); if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_handler)) read_handler_section(reader, *modobj); // [TODO] Native code generation - runtime_flags_t::must_compile assert(!util::has_flag(modobj->header.runtime_flag, runtime_flags_t::must_compile)); return modobj; }