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 | disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function"); | 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 | EXEC_DECL(mcall) | 1513 | EXEC_DECL(mcall) |
| 1511 | { | 1514 | { |
| 1512 | auto target_module = at_val<vm_module_ref_t>(r.dest); | 1515 | auto target_module = at_val<vm_module_ref_t>(r.dest); |
| @@ -1551,22 +1554,52 @@ namespace · 38 unmodified lines | |||
| 1551 | r.mp_base = r.module_ref->mp_base; | 1554 | r.mp_base = r.module_ref->mp_base; |
| 1552 | r.next_pc = function_pc; | 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]; | 1560 | r.mp_base->add_ref(); |
| 1557 | assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)"); | 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 | inst.native(r, vm); | 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. | 1581 | // Handle callee clean-up semantics |
| 1564 | if (r.current_thread_state == vm_thread_state_t::release) | 1582 | ret(r, vm); |
| 1565 | r.current_thread_state = vm_thread_state_t::running; | ||
| 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 | if (target_frame != r.stack.peek_frame()) | 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 | // Re-initialize the current frame | 1765 | // Re-initialize the current frame |