Code analysis violations Bug in scheduler - race during with worker thread shutdown.
Showing the changes to src/vm/sys/Sysmod.cpp (1 of 18 files). Show all
Unified · Split
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <algorithm> | ||
| 8 | #include <chrono> | 9 | #include <chrono> |
| 9 | #include <thread> | 10 | #include <thread> |
| 10 | #include <iostream> | 11 | #include <iostream> |
| @@ -13,6 +14,7 @@ · 2 unmodified lines | |||
| 13 | #include <exceptions.h> | 14 | #include <exceptions.h> |
| 14 | #include <debug.h> | 15 | #include <debug.h> |
| 15 | #include <utf8.h> | 16 | #include <utf8.h> |
| 17 | #include <vm_memory.h> | ||
| 16 | #include "Sysmod.h" | 18 | #include "Sysmod.h" |
| 17 | #include "sys_utils.h" | 19 | #include "sys_utils.h" |
| 18 | 20 | ||
| @@ -69,99 +71,153 @@ namespace · 50 unmodified lines | |||
| 69 | }; | 71 | }; |
| 70 | 72 | ||
| 71 | const word_t Sysmodlen = 43; | 73 | const word_t Sysmodlen = 43; |
| 72 | } | ||
| 73 | 74 | ||
| 74 | std::shared_ptr<const type_descriptor_t> T_Qid; | 75 | std::shared_ptr<const type_descriptor_t> T_Qid; |
| 75 | std::shared_ptr<const type_descriptor_t> T_Dir; | 76 | std::shared_ptr<const type_descriptor_t> T_Dir; |
| 76 | std::shared_ptr<const type_descriptor_t> T_FD; | 77 | std::shared_ptr<const type_descriptor_t> T_FD; |
| 77 | std::shared_ptr<const type_descriptor_t> T_Connection; | 78 | std::shared_ptr<const type_descriptor_t> T_Connection; |
| 78 | std::shared_ptr<const type_descriptor_t> T_FileIO; | 79 | std::shared_ptr<const type_descriptor_t> T_FileIO; |
| 79 | 80 | ||
| 80 | struct vm_fd_t final : public Sys_FD | 81 | class vm_fd_t final : public Sys_FD |
| 81 | { | ||
| 82 | static void finalizer(vm_alloc_t *fileHandle) | ||
| 83 | { | 82 | { |
| 84 | auto fd = fileHandle->get_allocation<vm_fd_t>(); | 83 | public: // static |
| 85 | fd->~vm_fd_t(); | 84 | static void finalizer(vm_alloc_t *fileHandle) |
| 86 | } | 85 | { |
| 86 | auto fd = fileHandle->get_allocation<vm_fd_t>(); | ||
| 87 | |||
| 88 | if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>()) | ||
| 89 | { | ||
| 90 | debug::log_msg( | ||
| 91 | debug::component_trace_t::builtin, | ||
| 92 | debug::log_level_t::debug, | ||
| 93 | "sys: fd: finalize: %d\n", | ||
| 94 | fd->fd); | ||
| 95 | } | ||
| 96 | |||
| 97 | sys::drop_fd_record(fd->fd); | ||
| 98 | fd->~vm_fd_t(); | ||
| 99 | } | ||
| 87 | 100 | ||
| 88 | vm_fd_t(word_t fd, std::unique_ptr<std::iostream> s) | 101 | static std::tuple<word_t, vm_alloc_t *> create(std::unique_ptr<std::iostream> s, word_t fd_mode, vm_string_t *fd_path) |
| 89 | : Sys_FD{ fd } | 102 | { |
| 90 | , _istream{ s.get() } | 103 | assert(s != nullptr); |
| 91 | , _ostream{ s.get() } | 104 | auto alloc = vm_alloc_t::allocate(T_FD); |
| 92 | { | 105 | const auto fd = sys::create_fd_record(alloc); |
| 93 | assert(s != nullptr); | 106 | auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path }; |
| 94 | _stream = std::move(s); | ||
| 95 | } | ||
| 96 | 107 | ||
| 97 | vm_fd_t(word_t fd, std::unique_ptr<std::istream> s) | 108 | return std::make_tuple(vm_fd->fd, alloc); |
| 98 | : Sys_FD{ fd } | 109 | } |
| 99 | , _istream{ s.get() } | ||
| 100 | , _ostream{ nullptr } | ||
| 101 | { | ||
| 102 | assert(s != nullptr); | ||
| 103 | _stream = std::move(s); | ||
| 104 | } | ||
| 105 | 110 | ||
| 106 | vm_fd_t(word_t fd, std::unique_ptr<std::ostream> s) | 111 | private: |
| 107 | : Sys_FD{ fd } | 112 | vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path) |
| 108 | , _istream{ nullptr } | 113 | : Sys_FD{ fd } |
| 109 | , _ostream{ s.get() } | 114 | , _fd_mode{ fd_mode } |
| 110 | { | 115 | , _fd_path{ fd_path } |
| 111 | assert(s != nullptr); | 116 | , _istream{ s.get() } |
| 112 | _stream = std::move(s); | 117 | , _ostream{ s.get() } |
| 113 | } | 118 | { |
| 119 | _stream = std::move(s); | ||
| 114 | 120 | ||
| 115 | vm_fd_t(word_t fd, std::istream &s) | 121 | if (_fd_path != nullptr) |
| 116 | : Sys_FD{ fd } | 122 | _fd_path->add_ref(); |
| 117 | , _istream{ &s } | 123 | } |
| 118 | , _ostream{ nullptr } | ||
| 119 | { | ||
| 120 | } | ||
| 121 | 124 | ||
| 122 | vm_fd_t(word_t fd, std::ostream &s) | 125 | public: |
| 123 | : Sys_FD{ fd } | 126 | vm_fd_t(const word_t fd, std::istream &is) |
| 124 | , _istream{ nullptr } | 127 | : Sys_FD{ fd } |
| 125 | , _ostream{ &s } | 128 | , _fd_mode{} |
| 126 | { | 129 | , _fd_path{ nullptr } |
| 127 | } | 130 | , _istream{ &is } |
| 131 | , _ostream{ nullptr } | ||
| 132 | { | ||
| 133 | } | ||
| 128 | 134 | ||
| 129 | ~vm_fd_t() | 135 | vm_fd_t(const word_t fd, std::ostream &os) |
| 130 | { | 136 | : Sys_FD{ fd } |
| 131 | debug::assign_debug_pointer(&_istream); | 137 | , _fd_mode{} |
| 132 | debug::assign_debug_pointer(&_ostream); | 138 | , _fd_path{ nullptr } |
| 139 | , _istream{ nullptr } | ||
| 140 | , _ostream{ &os } | ||
| 141 | { | ||
| 142 | } | ||
| 133 | 143 | ||
| 134 | if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) | 144 | ~vm_fd_t() |
| 135 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: file descriptor: %d", fd); | 145 | { |
| 136 | } | 146 | debug::assign_debug_pointer(&_istream); |
| 147 | debug::assign_debug_pointer(&_ostream); | ||
| 148 | if (_stream != nullptr) | ||
| 149 | _stream.reset(); | ||
| 137 | 150 | ||
| 138 | std::istream& input() const | 151 | // Honor the close flag |
| 139 | { | 152 | if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr) |
| 140 | if (_istream == nullptr) | 153 | std::remove(_fd_path->str()); |
| 141 | throw vm_user_exception{ "File descriptor not open for write" }; | ||
| 142 | 154 | ||
| 143 | return *_istream; | 155 | dec_ref_count_and_free(_fd_path); |
| 144 | } | 156 | debug::assign_debug_pointer(&_fd_path); |
| 157 | } | ||
| 158 | |||
| 159 | word_t get_original_mode() const | ||
| 160 | { | ||
| 161 | return _fd_mode; | ||
| 162 | } | ||
| 163 | |||
| 164 | vm_string_t *get_original_path() const | ||
| 165 | { | ||
| 166 | return _fd_path; | ||
| 167 | } | ||
| 168 | |||
| 169 | word_t read(const word_t buffer_size_in_bytes, void *buffer) | ||
| 170 | { | ||
| 171 | if (_istream == nullptr) | ||
| 172 | throw vm_system_exception{ "File descriptor not open for read operation" }; | ||
| 173 | |||
| 174 | _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); | ||
| 175 | if (_istream->bad()) | ||
| 176 | throw vm_user_exception{ "Read operation error" }; | ||
| 145 | 177 | ||
| 146 | std::ostream& output() const | 178 | return static_cast<word_t>(_istream->gcount()); |
| 179 | } | ||
| 180 | |||
| 181 | void write(const word_t buffer_size_in_bytes, void *buffer) | ||
| 182 | { | ||
| 183 | if (_ostream == nullptr) | ||
| 184 | throw vm_system_exception{ "File descriptor not open for write operation" }; | ||
| 185 | |||
| 186 | _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); | ||
| 187 | if (_ostream->bad()) | ||
| 188 | throw vm_user_exception{ "Write operation error" }; | ||
| 189 | } | ||
| 190 | |||
| 191 | private: | ||
| 192 | const word_t _fd_mode; | ||
| 193 | vm_string_t *_fd_path; | ||
| 194 | std::istream *_istream; | ||
| 195 | std::ostream *_ostream; | ||
| 196 | std::unique_ptr<std::ios> _stream; | ||
| 197 | }; | ||
| 198 | |||
| 199 | vm_fd_t *fd_stdin = nullptr; | ||
| 200 | vm_fd_t *fd_stdout = nullptr; | ||
| 201 | vm_fd_t *fd_stderr = nullptr; | ||
| 202 | |||
| 203 | std::ios::openmode convert_to_openmode(const word_t mode) | ||
| 147 | { | 204 | { |
| 148 | if (_ostream == nullptr) | 205 | auto om = std::ios::openmode{}; |
| 149 | throw vm_user_exception{ "File descriptor not open for read" }; | ||
| 150 | 206 | ||
| 151 | return *_ostream; | 207 | // Lowest bit is for read/write. |
| 152 | } | 208 | if (mode & Sys_OWRITE) |
| 209 | om |= std::ios::out; | ||
| 210 | else | ||
| 211 | om |= std::ios::in; // Sys_OREAD | ||
| 153 | 212 | ||
| 154 | private: | 213 | if (mode & Sys_ORDWR) |
| 155 | std::istream *_istream; | 214 | om |= (std::ios::in | std::ios::out); |
| 156 | std::ostream *_ostream; | ||
| 157 | std::unique_ptr<std::ios> _stream; | ||
| 158 | }; | ||
| 159 | 215 | ||
| 160 | namespace | 216 | if (mode & Sys_OTRUNC) |
| 161 | { | 217 | om |= std::ios::trunc; |
| 162 | vm_alloc_t *vm_stdin = nullptr; | 218 | |
| 163 | vm_alloc_t *vm_stdout = nullptr; | 219 | return om; |
| 164 | vm_alloc_t *vm_stderr = nullptr; | 220 | } |
| 165 | } | 221 | } |
| 166 | 222 | ||
| 167 | void | 223 | void |
| @@ -176,14 +232,26 @@ Sysmodinit(void) · 8 unmodified lines | |||
| 176 | 232 | ||
| 177 | // Initialize default file descriptors | 233 | // Initialize default file descriptors |
| 178 | 234 | ||
| 179 | vm_stdin = vm_alloc_t::allocate(T_FD); | 235 | { |
| 180 | ::new(vm_stdin->get_allocation()) vm_fd_t{ 0, std::cin }; | 236 | auto vm_stdin = vm_alloc_t::allocate(T_FD); |
| 237 | const auto fd = sys::create_fd_record(vm_stdin); | ||
| 238 | fd_stdin = ::new(vm_stdin->get_allocation())vm_fd_t{ fd, std::cin }; | ||
| 239 | debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdin: %d\n", fd); | ||
| 240 | } | ||
| 181 | 241 | ||
| 182 | vm_stdout = vm_alloc_t::allocate(T_FD); | 242 | { |
| 183 | ::new(vm_stdout->get_allocation()) vm_fd_t{ 1, std::cout }; | 243 | auto vm_stdout = vm_alloc_t::allocate(T_FD); |
| 244 | const auto fd = sys::create_fd_record(vm_stdout); | ||
| 245 | fd_stdout = ::new(vm_stdout->get_allocation())vm_fd_t{ fd, std::cout }; | ||
| 246 | debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdout: %d\n", fd); | ||
| 247 | } | ||
| 184 | 248 | ||
| 185 | vm_stderr = vm_alloc_t::allocate(T_FD); | 249 | { |
| 186 | ::new(vm_stderr->get_allocation()) vm_fd_t{ 2, std::cerr }; | 250 | auto vm_stderr = vm_alloc_t::allocate(T_FD); |
| 251 | const auto fd = sys::create_fd_record(vm_stderr); | ||
| 252 | fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr }; | ||
| 253 | debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stderr: %d\n", fd); | ||
| 254 | } | ||
| 187 | } | 255 | } |
| 188 | 256 | ||
| 189 | void | 257 | void |
| @@ -260,19 +328,19 @@ Sys_char2byte(vm_registers_t &r, vm_t &vm) · 70 unmodified lines | |||
| 260 | 328 | ||
| 261 | const auto rune = static_cast<rune_t>(fp.c); | 329 | const auto rune = static_cast<rune_t>(fp.c); |
| 262 | 330 | ||
| 263 | static_assert(Sys_UTFmax == sizeof(rune_t), "UTF max should be same size as rune"); | 331 | static_assert(Sys_UTFmax == sizeof(rune), "UTF max should be same size as rune"); |
| 264 | uint8_t buffer_local[Sys_UTFmax]; | 332 | uint8_t buffer_local[Sys_UTFmax]; |
| 265 | auto begin = buffer_local; | 333 | auto begin = buffer_local; |
| 266 | 334 | ||
| 267 | auto end = utf8::encode(rune, begin); | 335 | auto end = utf8::encode(rune, begin); |
| 268 | const auto count = static_cast<word_t>(end - begin); | 336 | const auto count = static_cast<word_t>(end - begin); |
| 269 | 337 | ||
| 270 | // Initialize for failure. | ||
| 271 | *fp.ret = 0; | ||
| 272 | |||
| 273 | const auto final_index = n + count; | 338 | const auto final_index = n + count; |
| 274 | if (final_index > buffer->get_length()) | 339 | if (final_index > buffer->get_length()) |
| 340 | { | ||
| 341 | *fp.ret = 0; | ||
| 275 | return; | 342 | return; |
| 343 | } | ||
| 276 | 344 | ||
| 277 | // Insert the bytes into the buffer | 345 | // Insert the bytes into the buffer |
| 278 | for (auto i = word_t{ n }; i < final_index; ++i) | 346 | for (auto i = word_t{ n }; i < final_index; ++i) |
| @@ -295,7 +363,41 @@ void · 16 unmodified lines | |||
| 295 | Sys_create(vm_registers_t &r, vm_t &vm) | 363 | Sys_create(vm_registers_t &r, vm_t &vm) |
| 296 | { | 364 | { |
| 297 | auto &fp = r.stack.peek_frame()->base<F_Sys_create>(); | 365 | auto &fp = r.stack.peek_frame()->base<F_Sys_create>(); |
| 298 | throw vm_system_exception{ "Instruction not implemented" }; | 366 | |
| 367 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); | ||
| 368 | const auto open_mode = convert_to_openmode(fp.mode); | ||
| 369 | |||
| 370 | auto cfs_flag = sys::cfs_flags_t::none; | ||
| 371 | if (fp.mode & Sys_OEXCL) | ||
| 372 | cfs_flag = (sys::cfs_flags_t::atomic | sys::cfs_flags_t::ensure_create); | ||
| 373 | |||
| 374 | // [TODO] Apply the requested permissions to the file. | ||
| 375 | auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag); | ||
| 376 | |||
| 377 | // Return null if file stream is not open. | ||
| 378 | if (file_stream == nullptr) | ||
| 379 | { | ||
| 380 | *fp.ret = nullptr; | ||
| 381 | return; | ||
| 382 | } | ||
| 383 | |||
| 384 | vm_alloc_t *fd_alloc; | ||
| 385 | auto fd = word_t{}; | ||
| 386 | std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path); | ||
| 387 | |||
| 388 | if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>()) | ||
| 389 | { | ||
| 390 | debug::log_msg( | ||
| 391 | debug::component_trace_t::builtin, | ||
| 392 | debug::log_level_t::debug, | ||
| 393 | "sys: create: %d >>%s<< %#x %#x\n", | ||
| 394 | fd, | ||
| 395 | path->str(), | ||
| 396 | fp.mode, | ||
| 397 | fp.perm); | ||
| 398 | } | ||
| 399 | |||
| 400 | *fp.ret = fd_alloc->get_allocation(); | ||
| 299 | } | 401 | } |
| 300 | 402 | ||
| 301 | void | 403 | void |
| @@ -417,7 +519,36 @@ void · 115 unmodified lines | |||
| 417 | Sys_open(vm_registers_t &r, vm_t &vm) | 519 | Sys_open(vm_registers_t &r, vm_t &vm) |
| 418 | { | 520 | { |
| 419 | auto &fp = r.stack.peek_frame()->base<F_Sys_open>(); | 521 | auto &fp = r.stack.peek_frame()->base<F_Sys_open>(); |
| 420 | throw vm_system_exception{ "Instruction not implemented" }; | 522 | |
| 523 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); | ||
| 524 | const auto open_mode = convert_to_openmode(fp.mode); | ||
| 525 | |||
| 526 | // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234) | ||
| 527 | auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary)); | ||
| 528 | |||
| 529 | // Return null if file stream is not open. | ||
| 530 | if (file_stream == nullptr) | ||
| 531 | { | ||
| 532 | *fp.ret = nullptr; | ||
| 533 | return; | ||
| 534 | } | ||
| 535 | |||
| 536 | vm_alloc_t *fd_alloc; | ||
| 537 | auto fd = word_t{}; | ||
| 538 | std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path); | ||
| 539 | |||
| 540 | if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>()) | ||
| 541 | { | ||
| 542 | debug::log_msg( | ||
| 543 | debug::component_trace_t::builtin, | ||
| 544 | debug::log_level_t::debug, | ||
| 545 | "sys: open: %d >>%s<< %#x\n", | ||
| 546 | fd, | ||
| 547 | path->str(), | ||
| 548 | fp.mode); | ||
| 549 | } | ||
| 550 | |||
| 551 | *fp.ret = fd_alloc->get_allocation(); | ||
| 421 | } | 552 | } |
| 422 | 553 | ||
| 423 | void | 554 | void |
| @@ -453,16 +584,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 29 unmodified lines | |||
| 453 | 584 | ||
| 454 | auto static_buffer = std::array<char, 1024>{}; | 585 | auto static_buffer = std::array<char, 1024>{}; |
| 455 | auto msg_args = &fp.vargs; | 586 | auto msg_args = &fp.vargs; |
| 456 | auto written = printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); | 587 | auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); |
| 457 | if (written >= 0) | 588 | if (written >= 0) |
| 458 | { | 589 | { |
| 459 | vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data(); | 590 | fd_stdout->write(written, static_buffer.data()); |
| 460 | } | 591 | } |
| 461 | else | 592 | else |
| 462 | { | 593 | { |
| 463 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); | 594 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); |
| 464 | written = printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer); | 595 | written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer); |
| 465 | vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data(); | 596 | fd_stdout->write(written, dynamic_buffer.data()); |
| 466 | } | 597 | } |
| 467 | 598 | ||
| 468 | *fp.ret = written; | 599 | *fp.ret = written; |
| @@ -479,7 +610,26 @@ void · 10 unmodified lines | |||
| 479 | Sys_read(vm_registers_t &r, vm_t &vm) | 610 | Sys_read(vm_registers_t &r, vm_t &vm) |
| 480 | { | 611 | { |
| 481 | auto &fp = r.stack.peek_frame()->base<F_Sys_read>(); | 612 | auto &fp = r.stack.peek_frame()->base<F_Sys_read>(); |
| 482 | throw vm_system_exception{ "Instruction not implemented" }; | 613 | |
| 614 | const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf); | ||
| 615 | assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes); | ||
| 616 | |||
| 617 | auto n = fp.n; | ||
| 618 | if (buffer == nullptr || n <= 0) | ||
| 619 | { | ||
| 620 | *fp.ret = 0; | ||
| 621 | return; | ||
| 622 | } | ||
| 623 | |||
| 624 | // [SPEC] Supplying a size greater than the buffer length is | ||
| 625 | // equivalent to indicating the entire buffer should be filled. | ||
| 626 | n = std::min(n, buffer->get_length()); | ||
| 627 | |||
| 628 | auto fd_alloc = vm_alloc_t::from_allocation(fp.fd); | ||
| 629 | assert(fd_alloc->alloc_type == T_FD); | ||
| 630 | |||
| 631 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); | ||
| 632 | *fp.ret = fd->read(n, buffer->at(0)); | ||
| 483 | } | 633 | } |
| 484 | 634 | ||
| 485 | void | 635 | void |
| @@ -511,9 +661,7 @@ Sys_sleep(vm_registers_t &r, vm_t &vm) · 25 unmodified lines | |||
| 511 | const auto period_ms = fp.period; | 661 | const auto period_ms = fp.period; |
| 512 | 662 | ||
| 513 | // [TODO] Thread should yield to another according to the sleep() specification. | 663 | // [TODO] Thread should yield to another according to the sleep() specification. |
| 514 | r.current_thread_state = vm_thread_state_t::release; | ||
| 515 | std::this_thread::sleep_for(std::chrono::milliseconds(period_ms)); | 664 | std::this_thread::sleep_for(std::chrono::milliseconds(period_ms)); |
| 516 | r.current_thread_state = vm_thread_state_t::ready; | ||
| 517 | 665 | ||
| 518 | *fp.ret = 0; | 666 | *fp.ret = 0; |
| 519 | } | 667 | } |
| @@ -539,11 +687,120 @@ Sys_stream(vm_registers_t &r, vm_t &vm) · 19 unmodified lines | |||
| 539 | throw vm_system_exception{ "Instruction not implemented" }; | 687 | throw vm_system_exception{ "Instruction not implemented" }; |
| 540 | } | 688 | } |
| 541 | 689 | ||
| 690 | namespace | ||
| 691 | { | ||
| 692 | bool is_delim(const rune_t c, const vm_string_t &delim) | ||
| 693 | { | ||
| 694 | const auto max = delim.get_length(); | ||
| 695 | assert(max > 0); | ||
| 696 | |||
| 697 | // Optimization for 3 or less delimiters | ||
| 698 | switch (max) | ||
| 699 | { | ||
| 700 | default: | ||
| 701 | break; | ||
| 702 | case 1: | ||
| 703 | return (delim.get_rune(0) == c); | ||
| 704 | case 2: | ||
| 705 | return (delim.get_rune(0) == c) || (delim.get_rune(1) == c); | ||
| 706 | case 3: | ||
| 707 | return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c); | ||
| 708 | } | ||
| 709 | |||
| 710 | for (auto i = word_t{ 0 }; i < max; ++i) | ||
| 711 | { | ||
| 712 | if (delim.get_rune(i) == c) | ||
| 713 | return true; | ||
| 714 | } | ||
| 715 | |||
| 716 | return false; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 542 | void | 720 | void |
| 543 | Sys_tokenize(vm_registers_t &r, vm_t &vm) | 721 | Sys_tokenize(vm_registers_t &r, vm_t &vm) |
| 544 | { | 722 | { |
| 545 | auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>(); | 723 | auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>(); |
| 546 | throw vm_system_exception{ "Instruction not implemented" }; | 724 | |
| 725 | // Initialize for failure. | ||
| 726 | fp.ret->t0 = 0; | ||
| 727 | dec_ref_count_and_free(vm_alloc_t::from_allocation(fp.ret->t1)); | ||
| 728 | fp.ret->t1 = nullptr; | ||
| 729 | |||
| 730 | auto s = vm_alloc_t::from_allocation<vm_string_t>(fp.s); | ||
| 731 | if (s == nullptr || s->get_length() == 0) | ||
| 732 | return; | ||
| 733 | |||
| 734 | auto curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() }; | ||
| 735 | |||
| 736 | // Set the head of the list | ||
| 737 | fp.ret->t1 = curr_node->get_allocation(); | ||
| 738 | |||
| 739 | auto delim = vm_alloc_t::from_allocation<vm_string_t>(fp.delim); | ||
| 740 | if (delim == nullptr || delim->get_length() == 0) | ||
| 741 | { | ||
| 742 | // Add supplied string to list | ||
| 743 | pt_ref(curr_node->value()) = s->get_allocation(); | ||
| 744 | s->add_ref(); | ||
| 745 | |||
| 746 | fp.ret->t0 = 1; | ||
| 747 | return; | ||
| 748 | } | ||
| 749 | |||
| 750 | // Tokenize the string | ||
| 751 | vm_list_t *prev_node = nullptr; | ||
| 752 | auto n = word_t{ 0 }; | ||
| 753 | auto begin = word_t{ 0 }; | ||
| 754 | auto end = word_t{ 0 }; | ||
| 755 | const auto len = s->get_length(); | ||
| 756 | while (begin < len) | ||
| 757 | { | ||
| 758 | // Consume delimiters | ||
| 759 | while (begin < len) | ||
| 760 | { | ||
| 761 | const auto rune = s->get_rune(begin); | ||
| 762 | if (!is_delim(rune, *delim)) | ||
| 763 | break; | ||
| 764 | ++begin; | ||
| 765 | } | ||
| 766 | |||
| 767 | end = begin; | ||
| 768 | |||
| 769 | // Consume non-delimiters | ||
| 770 | while (end < len) | ||
| 771 | { | ||
| 772 | const auto rune = s->get_rune(end); | ||
| 773 | if (is_delim(rune, *delim)) | ||
| 774 | break; | ||
| 775 | ++end; | ||
| 776 | } | ||
| 777 | |||
| 778 | // No progress - It is and isn't a delimiter (i.e. no more characters). | ||
| 779 | if (begin == end) | ||
| 780 | break; | ||
| 781 | |||
| 782 | // Current node is initially non-null | ||
| 783 | if (curr_node == nullptr) | ||
| 784 | curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() }; | ||
| 785 | |||
| 786 | const auto token = new vm_string_t{ *s, begin, end }; | ||
| 787 | pt_ref(curr_node->value()) = token->get_allocation(); | ||
| 788 | |||
| 789 | // Previous node is initially null | ||
| 790 | if (prev_node != nullptr) | ||
| 791 | { | ||
| 792 | prev_node->set_tail(curr_node); | ||
| 793 | curr_node->release(); | ||
| 794 | } | ||
| 795 | |||
| 796 | prev_node = curr_node; | ||
| 797 | curr_node = nullptr; | ||
| 798 | |||
| 799 | begin = end; | ||
| 800 | ++n; | ||
| 801 | } | ||
| 802 | |||
| 803 | fp.ret->t0 = n; | ||
| 547 | } | 804 | } |
| 548 | 805 | ||
| 549 | void | 806 | void |
| @@ -571,7 +828,28 @@ void · 21 unmodified lines | |||
| 571 | Sys_write(vm_registers_t &r, vm_t &vm) | 828 | Sys_write(vm_registers_t &r, vm_t &vm) |
| 572 | { | 829 | { |
| 573 | auto &fp = r.stack.peek_frame()->base<F_Sys_write>(); | 830 | auto &fp = r.stack.peek_frame()->base<F_Sys_write>(); |
| 574 | throw vm_system_exception{ "Instruction not implemented" }; | 831 | |
| 832 | const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf); | ||
| 833 | assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes); | ||
| 834 | |||
| 835 | auto n = fp.n; | ||
| 836 | if (buffer == nullptr || n <= 0) | ||
| 837 | { | ||
| 838 | *fp.ret = 0; | ||
| 839 | return; | ||
| 840 | } | ||
| 841 | |||
| 842 | // [SPEC] Supplying a size greater than the buffer length is | ||
| 843 | // equivalent to indicating the entire buffer should be written. | ||
| 844 | n = std::min(n, buffer->get_length()); | ||
| 845 | |||
| 846 | auto fd_alloc = vm_alloc_t::from_allocation(fp.fd); | ||
| 847 | assert(fd_alloc->alloc_type == T_FD); | ||
| 848 | |||
| 849 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); | ||
| 850 | fd->write(n, buffer->at(0)); | ||
| 851 | |||
| 852 | *fp.ret = n; | ||
| 575 | } | 853 | } |
| 576 | 854 | ||
| 577 | void | 855 | void |