Showing the changes to src/vm/execution_table.cpp (1 of 3 files). Show all
Unified · Split
| @@ -1507,6 +1507,9 @@ namespace · 1506 unmodified lines | ||
| 1507 | 1507 | disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function"); |
| 1508 | 1508 | } |
| 1509 | 1509 | |
| 1510 | + // Forward declaration | |
| 1511 | + EXEC_DECL(raise); | |
| 1512 | + | |
| 1510 | 1513 | EXEC_DECL(mcall) |
| 1511 | 1514 | { |
| 1512 | 1515 | auto target_module = at_val<vm_module_ref_t>(r.dest); |
| @@ -1551,22 +1554,52 @@ namespace · 38 unmodified lines | ||
| 1551 | 1554 | r.mp_base = r.module_ref->mp_base; |
| 1552 | 1555 | r.next_pc = function_pc; |
| 1553 | 1556 | |
| 1554 | − if (r.module_ref->is_builtin_module()) | |
| 1557 | + // Non-built-in modules ref count and return | |
| 1558 | + if (!r.module_ref->is_builtin_module()) | |
| 1555 | 1559 | { |
| 1556 | − const auto &inst = r.module_ref->code_section[function_pc]; | |
| 1557 | − assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)"); | |
| 1560 | + r.mp_base->add_ref(); | |
| 1561 | + return; | |
| 1562 | + } | |
| 1563 | + | |
| 1564 | + // Calling into a built-in module is a bit complicated since control leaves the VM and thus | |
| 1565 | + // the callee clean-up semantics of DisVM are difficult to enforced. The solution here is | |
| 1566 | + // to release VM control of the thread and clean-up the stack on behalf of the callee when | |
| 1567 | + // control returns to the VM. | |
| 1568 | + // Built-in modules are also permitted to throw marshallable exceptions which requires | |
| 1569 | + // raising the exception manually. | |
| 1570 | + | |
| 1571 | + const auto &inst = r.module_ref->code_section[function_pc]; | |
| 1572 | + assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)"); | |
| 1558 | 1573 | |
| 1559 | − r.current_thread_state = vm_thread_state_t::release; | |
| 1574 | + vm_alloc_t *marshallable_err{}; | |
| 1575 | + r.current_thread_state = vm_thread_state_t::release; | |
| 1576 | + | |
| 1577 | + try | |
| 1578 | + { | |
| 1560 | 1579 | inst.native(r, vm); |
| 1561 | − ret(r, vm); | |
| 1562 | 1580 | |
| 1563 | − // Only reset the thread if it is in the state set prior to native call. | |
| 1564 | − if (r.current_thread_state == vm_thread_state_t::release) | |
| 1565 | − r.current_thread_state = vm_thread_state_t::running; | |
| 1581 | + // Handle callee clean-up semantics | |
| 1582 | + ret(r, vm); | |
| 1566 | 1583 | } |
| 1567 | − else | |
| 1584 | + catch (const marshallable_user_exception &e) | |
| 1568 | 1585 | { |
| 1569 | − r.mp_base->add_ref(); | |
| 1586 | + auto msg = e.what(); | |
| 1587 | + marshallable_err = new vm_string_t{ std::strlen(msg), reinterpret_cast<const uint8_t*>(msg) }; | |
| 1588 | + } | |
| 1589 | + | |
| 1590 | + // Only reset the thread if it is in the state set prior to native call. | |
| 1591 | + if (r.current_thread_state == vm_thread_state_t::release) | |
| 1592 | + r.current_thread_state = vm_thread_state_t::running; | |
| 1593 | + | |
| 1594 | + // Raise marshallable exception | |
| 1595 | + if (marshallable_err != nullptr) | |
| 1596 | + { | |
| 1597 | + assert(r.current_thread_state == vm_thread_state_t::running && "Throwing a marshallable error should not result in an altered VM thread state"); | |
| 1598 | + auto err = marshallable_err->get_allocation(); | |
| 1599 | + r.src = reinterpret_cast<pointer_t>(&err); | |
| 1600 | + raise(r, vm); | |
| 1601 | + | |
| 1602 | + disvm::runtime::dec_ref_count_and_free(marshallable_err); | |
| 1570 | 1603 | } |
| 1571 | 1604 | } |
| 1572 | 1605 | |
| @@ -1710,10 +1743,23 @@ namespace · 137 unmodified lines | ||
| 1710 | 1743 | |
| 1711 | 1744 | if (target_frame != r.stack.peek_frame()) |
| 1712 | 1745 | { |
| 1713 | − while (target_frame != r.stack.pop_frame()) | |
| 1746 | + // Unwind stack to find target frame | |
| 1747 | + do | |
| 1714 | 1748 | { |
| 1715 | − // Remove frames from the stack until the target frame is found. | |
| 1749 | + auto curr_frame = r.stack.peek_frame(); | |
| 1750 | + | |
| 1751 | + // Update the module reference register during the stack unwind | |
| 1752 | + if (curr_frame->prev_module_ref() != nullptr) | |
| 1753 | + { | |
| 1754 | + dec_ref_count_and_free(r.module_ref); | |
| 1755 | + r.module_ref = curr_frame->prev_module_ref(); | |
| 1756 | + curr_frame->prev_module_ref() = nullptr; | |
| 1757 | + | |
| 1758 | + dec_ref_count_and_free(r.mp_base); | |
| 1759 | + r.mp_base = r.module_ref->mp_base; | |
| 1760 | + } | |
| 1716 | 1761 | } |
| 1762 | + while (target_frame != r.stack.pop_frame()); | |
| 1717 | 1763 | } |
| 1718 | 1764 | |
| 1719 | 1765 | // Re-initialize the current frame |