Unified · Split
| @@ -171,18 +171,26 @@ void vm_array_t::copy_from(const vm_array_t &source, word_t this_begin_index) · 170 unmodified lines | ||
| 171 | 171 | |
| 172 | 172 | auto dest_arr = _arr + (this_begin_index * _element_type->size_in_bytes); |
| 173 | 173 | |
| 174 | − // Increment all pointers if we have reference types. | |
| 174 | + // If we have reference types, increment references in the | |
| 175 | + // source array and decrement them in the destination array. | |
| 175 | 176 | if (_element_type->map_in_bytes != 0) |
| 176 | 177 | { |
| 177 | − auto &type_desc = *_element_type; | |
| 178 | + // [PERF] There is a chance this copy is within the same array, therefore | |
| 179 | + // a check could be made to not ref count memory that is going to be moved. | |
| 180 | + // The Inferno implementation of slicela handles this case (libinterp/xec.c). | |
| 181 | + const auto &type_desc = *_element_type; | |
| 178 | 182 | auto source_array_element = source._arr; |
| 183 | + auto dest_array_element = dest_arr; | |
| 179 | 184 | for (auto i = word_t{ 0 }; i < source._length; ++i) |
| 180 | 185 | { |
| 181 | − source_array_element += (i * type_desc.size_in_bytes); | |
| 182 | 186 | inc_ref_count_in_memory(type_desc, source_array_element); |
| 187 | + source_array_element += type_desc.size_in_bytes; | |
| 188 | + | |
| 189 | + destroy_memory(type_desc, dest_array_element); | |
| 190 | + dest_array_element += type_desc.size_in_bytes; | |
| 183 | 191 | } |
| 184 | 192 | } |
| 185 | 193 | |
| 186 | − // Moving data within the same array is permitted so use memmove(). | |
| 194 | + // Copy data from within the same array is permitted. | |
| 187 | 195 | std::memmove(dest_arr, source._arr, (source._length * _element_type->size_in_bytes)); |
| 188 | 196 | } |
| @@ -79,7 +79,7 @@ namespace · 78 unmodified lines | ||
| 79 | 79 | vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src); |
| 80 | 80 | // Right shifting a negative number is implementation dependent in C++. |
| 81 | 81 | // Assert the current compiler defaults to arithmetic shifting, not logical. |
| 82 | − assert(vt_ref<word_t>(r.mid) >= 0 || (vt_ref<word_t>(r.mid) < 0 && vt_ref<word_t>(r.dest) < 0)); | |
| 82 | + assert(vt_ref<big_t>(r.mid) >= 0 || (vt_ref<big_t>(r.mid) < 0 && vt_ref<big_t>(r.dest) < 0)); | |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); } |
| @@ -206,8 +206,8 @@ namespace · 120 unmodified lines | ||
| 206 | 206 | assert(static_cast<std::size_t>(len) <= buffer.size()); |
| 207 | 207 | |
| 208 | 208 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| 209 | − auto str = at_val<vm_string_t>(r.dest); | |
| 210 | − dec_ref_count_and_free(str); | |
| 209 | + auto prev = at_val<vm_alloc_t>(r.dest); | |
| 210 | + dec_ref_count_and_free(prev); | |
| 211 | 211 | |
| 212 | 212 | pt_ref(r.dest) = new_string->get_allocation(); |
| 213 | 213 | } |
| @@ -233,9 +233,10 @@ namespace · 19 unmodified lines | ||
| 233 | 233 | |
| 234 | 234 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| 235 | 235 | |
| 236 | − auto str = at_val<vm_string_t>(r.dest); | |
| 236 | + auto prev = at_val<vm_alloc_t>(r.dest); | |
| 237 | + dec_ref_count_and_free(prev); | |
| 238 | + | |
| 237 | 239 | pt_ref(r.dest) = new_string->get_allocation(); |
| 238 | − dec_ref_count_and_free(str); | |
| 239 | 240 | } |
| 240 | 241 | |
| 241 | 242 | EXEC_DECL(cvtcl) |
| @@ -259,9 +260,10 @@ namespace · 17 unmodified lines | ||
| 259 | 260 | |
| 260 | 261 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| 261 | 262 | |
| 262 | − auto str = at_val<vm_string_t>(r.dest); | |
| 263 | + auto prev = at_val<vm_alloc_t>(r.dest); | |
| 264 | + dec_ref_count_and_free(prev); | |
| 265 | + | |
| 263 | 266 | pt_ref(r.dest) = new_string->get_allocation(); |
| 264 | − dec_ref_count_and_free(str); | |
| 265 | 267 | } |
| 266 | 268 | |
| 267 | 269 | // |
| @@ -725,9 +727,9 @@ namespace · 457 unmodified lines | ||
| 725 | 727 | auto index = vt_ref<word_t>(r.mid); |
| 726 | 728 | auto str = at_val<vm_string_t>(r.dest); |
| 727 | 729 | if (str == nullptr) |
| 728 | − str = new vm_string_t(); | |
| 730 | + str = new vm_string_t{}; | |
| 729 | 731 | else if (str->get_ref_count() > 1) |
| 730 | − str = new vm_string_t(*str, 0, str->get_length()); | |
| 732 | + str = new vm_string_t{ *str, 0, str->get_length() }; | |
| 731 | 733 | |
| 732 | 734 | // Set the rune at the supplied index. |
| 733 | 735 | str->set_rune(index, rune); |
| @@ -740,7 +742,8 @@ namespace · 6 unmodified lines | ||
| 740 | 742 | } |
| 741 | 743 | } |
| 742 | 744 | |
| 743 | − vm_string_t *_addc(vm_string_t *s1, const vm_string_t *s2, /* in out */ bool &append_to_s1) | |
| 745 | + // Returns a string instance only if it is new | |
| 746 | + vm_string_t *concat_string(vm_string_t *s1, const vm_string_t *s2, bool try_append_to_s1) | |
| 744 | 747 | { |
| 745 | 748 | if (s1 == nullptr) |
| 746 | 749 | { |
| @@ -750,19 +753,23 @@ namespace · 3 unmodified lines | ||
| 750 | 753 | return new vm_string_t{ *s2, 0, s2->get_length() }; |
| 751 | 754 | } |
| 752 | 755 | |
| 753 | − // There are other references to this string so we will not alter it. | |
| 756 | + // If there are other references to the string, it is immutable. | |
| 754 | 757 | if (s1->get_ref_count() > 1) |
| 755 | − append_to_s1 = false; | |
| 758 | + try_append_to_s1 = false; | |
| 756 | 759 | |
| 757 | 760 | if (s2 == nullptr) |
| 758 | 761 | { |
| 759 | − if (append_to_s1) | |
| 760 | − return s1; | |
| 762 | + if (try_append_to_s1) | |
| 763 | + return nullptr; | |
| 761 | 764 | |
| 762 | 765 | return new vm_string_t(*s1, 0, s1->get_length()); |
| 763 | 766 | } |
| 764 | 767 | |
| 765 | − return (append_to_s1) ? &(s1->append(*s2)) : new vm_string_t(*s1, *s2); | |
| 768 | + if (!try_append_to_s1) | |
| 769 | + return new vm_string_t(*s1, *s2); | |
| 770 | + | |
| 771 | + s1->append(*s2); | |
| 772 | + return nullptr; | |
| 766 | 773 | } |
| 767 | 774 | |
| 768 | 775 | // [SPEC] 'Concat strings' op code |
| @@ -775,20 +782,18 @@ namespace · 6 unmodified lines | ||
| 775 | 782 | // dst = src2 + src1 |
| 776 | 783 | EXEC_DECL(addc) |
| 777 | 784 | { |
| 785 | + const auto try_append_to_s1 = r.mid == r.dest; | |
| 778 | 786 | auto s1 = at_val<vm_string_t>(r.mid); |
| 779 | 787 | const auto s2 = at_val<vm_string_t>(r.src); |
| 780 | − auto dest_maybe = at_val<vm_string_t>(r.dest); | |
| 781 | 788 | |
| 782 | − // If the source is the same as the destination and the | |
| 783 | − // destination is not null, append the string. This can | |
| 784 | − // change during the concat process. | |
| 785 | − auto append_to_source = dest_maybe != nullptr && s1 == dest_maybe; | |
| 786 | − const auto str = _addc(s1, s2, append_to_source); | |
| 787 | − | |
| 788 | − if (!append_to_source) | |
| 789 | + // If the source is the same as the destination, try to append the string during concatenation. | |
| 790 | + const auto new_str_maybe = concat_string(s1, s2, try_append_to_s1); | |
| 791 | + if (new_str_maybe != nullptr) | |
| 789 | 792 | { |
| 790 | − pt_ref(r.dest) = str->get_allocation(); | |
| 793 | + auto dest_maybe = at_val<vm_alloc_t>(r.dest); | |
| 791 | 794 | dec_ref_count_and_free(dest_maybe); |
| 795 | + | |
| 796 | + pt_ref(r.dest) = new_str_maybe->get_allocation(); | |
| 792 | 797 | } |
| 793 | 798 | } |
| 794 | 799 | |
| @@ -1659,6 +1664,7 @@ namespace · 864 unmodified lines | ||
| 1659 | 1664 | // The remainder of the type is user-defined. |
| 1660 | 1665 | auto content = e->get_allocation(); |
| 1661 | 1666 | exception_id = at_val<vm_string_t>(content); |
| 1667 | + assert(exception_id != nullptr); | |
| 1662 | 1668 | |
| 1663 | 1669 | if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) |
| 1664 | 1670 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<", exception_id->str()); |
| @@ -169,6 +169,12 @@ namespace · 168 unmodified lines | ||
| 169 | 169 | // Length + 1 (null) + space for string expansion |
| 170 | 170 | return (current_length + 1 + (current_length / 4)); |
| 171 | 171 | } |
| 172 | + | |
| 173 | + template<typename T, word_t S> | |
| 174 | + constexpr word_t compute_max_length(const T(&)[S]) | |
| 175 | + { | |
| 176 | + return S - 1; | |
| 177 | + } | |
| 172 | 178 | } |
| 173 | 179 | |
| 174 | 180 | vm_string_t::vm_string_t() |
| @@ -177,7 +183,7 @@ vm_string_t::vm_string_t() · 2 unmodified lines | ||
| 177 | 183 | , _encoded_str{ nullptr } |
| 178 | 184 | , _is_alloc{ false } |
| 179 | 185 | , _length{ 0 } |
| 180 | − , _length_max{ sizeof(_mem.local) } | |
| 186 | + , _length_max{ compute_max_length(_mem.local) } | |
| 181 | 187 | , _mem{} |
| 182 | 188 | { } |
| 183 | 189 | |
| @@ -187,7 +193,7 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 3 unmodified lines | ||
| 187 | 193 | , _encoded_str{ nullptr } |
| 188 | 194 | , _is_alloc{ false } |
| 189 | 195 | , _length{ 0 } |
| 190 | − , _length_max{ sizeof(_mem.local) } | |
| 196 | + , _length_max{ compute_max_length(_mem.local) } | |
| 191 | 197 | , _mem{} |
| 192 | 198 | { |
| 193 | 199 | assert((encoded_str_len > 0 && encoded_str != nullptr) || encoded_str_len == 0); |
| @@ -203,11 +209,11 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 9 unmodified lines | ||
| 203 | 209 | _character_size = (encoded_str_len != utf8_length.codepoint_count) ? sizeof(rune_t) : sizeof(char); |
| 204 | 210 | |
| 205 | 211 | _length = static_cast<word_t>(utf8_length.codepoint_count); |
| 206 | − _length_max = sizeof(_mem.local) / _character_size; | |
| 212 | + _length_max = compute_max_length(_mem.local) / _character_size; | |
| 207 | 213 | |
| 208 | 214 | // Allocate memory based on character count and size |
| 209 | 215 | auto dest = _mem.local; |
| 210 | − if ((encoded_str_len + 1) > sizeof(_mem.local)) | |
| 216 | + if (encoded_str_len > static_cast<std::size_t>(compute_max_length(_mem.local))) | |
| 211 | 217 | { |
| 212 | 218 | // If we are going to allocate, make sure it is larger than needed. |
| 213 | 219 | _length_max = compute_max_length(_length); |
| @@ -268,7 +274,7 @@ vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t en · 54 unmodified lines | ||
| 268 | 274 | , _length_max{ compute_max_length(end_index - begin_index) } |
| 269 | 275 | , _mem{} |
| 270 | 276 | { |
| 271 | − if (end_index <= begin_index) | |
| 277 | + if (end_index < begin_index) | |
| 272 | 278 | throw out_of_range_memory{}; |
| 273 | 279 | |
| 274 | 280 | if (begin_index < 0) |
| @@ -346,7 +352,7 @@ vm_string_t &vm_string_t::append(const vm_string_t &other) · 71 unmodified lines | ||
| 346 | 352 | |
| 347 | 353 | // Define the values for the appended string |
| 348 | 354 | const auto new_length = local_length + other_length; |
| 349 | − const auto new_length_max = (new_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(new_length); | |
| 355 | + const auto new_length_max = (new_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(new_length); | |
| 350 | 356 | const auto new_is_rune = local_is_rune || other_is_rune; |
| 351 | 357 | uint8_t *new_source = nullptr; |
| 352 | 358 | |
| @@ -442,7 +448,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 89 unmodified lines | ||
| 442 | 448 | _character_size = static_cast<uint8_t>(new_character_size); |
| 443 | 449 | assert(_character_size == sizeof(rune_t)); |
| 444 | 450 | |
| 445 | − _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length); | |
| 451 | + _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); | |
| 446 | 452 | auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size); |
| 447 | 453 | |
| 448 | 454 | // Copy source into the new alloc. |
| @@ -460,9 +466,9 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 11 unmodified lines | ||
| 460 | 466 | } |
| 461 | 467 | else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character. |
| 462 | 468 | { |
| 463 | − _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length); | |
| 469 | + _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); | |
| 464 | 470 | auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size); |
| 465 | − ::memcpy(new_alloc, source, _length * _character_size); | |
| 471 | + std::memcpy(new_alloc, source, _length * _character_size); | |
| 466 | 472 | |
| 467 | 473 | // Free memory, if allocated |
| 468 | 474 | if (_is_alloc) |
| @@ -503,7 +509,7 @@ const char *vm_string_t::str() const · 34 unmodified lines | ||
| 503 | 509 | if (_encoded_str != nullptr) |
| 504 | 510 | return _encoded_str; |
| 505 | 511 | |
| 506 | − // If the string is ASCII then just return the memory | |
| 512 | + // If the string is ASCII then return the memory | |
| 507 | 513 | if (_character_size == sizeof(char)) |
| 508 | 514 | { |
| 509 | 515 | _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local); |
| @@ -44,30 +44,32 @@ word_t disvm::runtime::sys::printf_to_buffer( · 43 unmodified lines | ||
| 44 | 44 | const auto fmt_len = msg_fmt.get_length(); |
| 45 | 45 | auto i = word_t{ 0 }; |
| 46 | 46 | |
| 47 | + auto format = std::vector<char>{}; | |
| 48 | + format.reserve(32); // Default size in Inferno. | |
| 49 | + | |
| 47 | 50 | // Loop over the entire format string |
| 48 | 51 | while (i < fmt_len) |
| 49 | 52 | { |
| 50 | 53 | if (b_curr >= b_end) |
| 51 | 54 | return -1; |
| 52 | 55 | |
| 53 | − auto c = msg_fmt.get_rune(i); | |
| 54 | − ++i; | |
| 56 | + auto c = msg_fmt.get_rune(i++); | |
| 55 | 57 | if (c != '%') |
| 56 | 58 | { |
| 57 | 59 | b_curr = reinterpret_cast<char *>(utf8::encode(c, reinterpret_cast<uint8_t *>(b_curr))); |
| 58 | 60 | continue; |
| 59 | 61 | } |
| 60 | 62 | |
| 61 | − auto format = std::vector<char>{}; | |
| 63 | + format.clear(); | |
| 62 | 64 | format.push_back('%'); |
| 63 | 65 | auto wb = int{ 0 }; |
| 64 | 66 | auto big_flag = false; |
| 67 | + auto unsigned_flag = false; | |
| 65 | 68 | |
| 66 | 69 | // Decode the format specifier |
| 67 | 70 | while (i < fmt_len) |
| 68 | 71 | { |
| 69 | − c = msg_fmt.get_rune(i); | |
| 70 | − ++i; | |
| 72 | + c = msg_fmt.get_rune(i++); | |
| 71 | 73 | switch (c) |
| 72 | 74 | { |
| 73 | 75 | // Format flags |
| @@ -79,6 +81,21 @@ word_t disvm::runtime::sys::printf_to_buffer( · 5 unmodified lines | ||
| 79 | 81 | format.push_back('l'); |
| 80 | 82 | big_flag = true; |
| 81 | 83 | continue; |
| 84 | + case '*': // Placeholder for word argument | |
| 85 | + { | |
| 86 | + const auto w = *reinterpret_cast<word_t *>(msg_args); | |
| 87 | + std::array<char, sizeof("-2147483648")> buffer; | |
| 88 | + const auto len = std::sprintf(buffer.data(), "%d", w); | |
| 89 | + if (len < 0) | |
| 90 | + return -1; | |
| 91 | + | |
| 92 | + format.insert(std::end(format), buffer.data(), buffer.data() + len); | |
| 93 | + msg_args += sizeof(w); | |
| 94 | + continue; | |
| 95 | + } | |
| 96 | + case 'u': | |
| 97 | + unsigned_flag = true; | |
| 98 | + continue; | |
| 82 | 99 | |
| 83 | 100 | // Format verbs |
| 84 | 101 | case '%': // Escaped specifier |
| @@ -100,10 +117,13 @@ word_t disvm::runtime::sys::printf_to_buffer( · 15 unmodified lines | ||
| 100 | 117 | msg_args += sizeof(r); |
| 101 | 118 | break; |
| 102 | 119 | } |
| 103 | − case 'o': // octal | |
| 104 | 120 | case 'd': // decimal |
| 121 | + if (unsigned_flag) | |
| 122 | + c = 'u'; | |
| 123 | + case 'o': // octal | |
| 105 | 124 | case 'x': // hexadecimal |
| 106 | 125 | case 'X': // Upper-case hexadecimal |
| 126 | + case 'c': // Unicode | |
| 107 | 127 | format.push_back(static_cast<char>(c)); |
| 108 | 128 | format.push_back('\0'); |
| 109 | 129 | if (big_flag) |
| @@ -121,6 +141,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 11 unmodified lines | ||
| 121 | 141 | msg_args += sizeof(w); |
| 122 | 142 | } |
| 123 | 143 | break; |
| 144 | + case 'q': | |
| 145 | + assert(false && "'q' format not implemented"); | |
| 124 | 146 | case 's': // string |
| 125 | 147 | { |
| 126 | 148 | auto p = *reinterpret_cast<pointer_t *>(msg_args); |