66#include < utility>
77
88namespace phoenix {
9+ // / \brief A helper class for preventing stack corruption.
10+ // /
11+ // / This class can be used to guard against stack corruption when a value is expected to be
12+ // / pushed onto the stack but the push does not happen for some reason. Unless #inhibit is called,
13+ // / when an instance of this class is destructed, a default value of the given datatype is pushed
14+ // / into the stack.
15+ // /
16+ // / \code
17+ // / // construct the stack guard
18+ // / stack_guard guard {this, datatype::int_};
19+ // /
20+ // / // ... do something that might fail to push an int to the stack ...
21+ // /
22+ // / // make sure to inhibit the guard if pushing the value succeeded
23+ // / guard.inhibit();
24+ // / \endcode
25+ struct stack_guard {
26+ public:
27+ // / \brief Creates a new stack guard.
28+ // / \param machine The VM this instance is guarding.
29+ // / \param type The type of value to push if the guard is triggered.
30+ stack_guard (vm* machine, datatype type) : _m_type(type), _m_machine(machine) {}
31+
32+ // / \brief Triggers this guard.
33+ // /
34+ // / Unless #inhibit was called, this destructor will push a value of the datatype passed in the
35+ // / constructor onto the stack of the VM passed in the constructor.
36+ ~stack_guard () {
37+ if (_m_inhibited)
38+ return ;
39+
40+ switch (_m_type) {
41+ case datatype::void_:
42+ break ;
43+ case datatype::float_:
44+ _m_machine->push_float (0 );
45+ break ;
46+ case datatype::integer:
47+ case datatype::function:
48+ _m_machine->push_int (0 );
49+ break ;
50+ case datatype::string:
51+ _m_machine->push_string (" " );
52+ break ;
53+ case datatype::instance:
54+ _m_machine->push_instance (nullptr );
55+ break ;
56+ case datatype::class_:
57+ break ;
58+ case datatype::prototype:
59+ break ;
60+ }
61+ }
62+
63+ // / \brief Inhibits this guard.
64+ // /
65+ // / Calling this function will cause the guard to disengage and thus not push a
66+ // / value onto the stack.
67+ void inhibit () {
68+ _m_inhibited = true ;
69+ }
70+
71+ private:
72+ datatype _m_type;
73+ vm* _m_machine;
74+ bool _m_inhibited {false };
75+ };
76+
977 vm::vm (script&& scr, std::uint8_t flags) : script(std::move(scr)), _m_flags(flags) {
1078 _m_self_sym = find_symbol_by_name (" SELF" );
1179 _m_other_sym = find_symbol_by_name (" OTHER" );
@@ -128,9 +196,15 @@ namespace phoenix {
128196 // Check if the function is overridden and if it is, call the resulting external.
129197 auto cb = _m_function_overrides.find (instr.address );
130198 if (cb != _m_function_overrides.end ()) {
199+ // Guard against exceptions during external invocation.
200+ stack_guard guard {this , sym->rtype ()};
201+
131202 push_call (sym);
132203 cb->second (*this );
133204 pop_call ();
205+
206+ // The stack is left intact.
207+ guard.inhibit ();
134208 } else {
135209 sym = find_symbol_by_address (instr.address );
136210 if (sym == nullptr ) {
@@ -148,6 +222,9 @@ namespace phoenix {
148222 throw vm_exception {" be: no external found for index" };
149223 }
150224
225+ // Guard against exceptions during external invocation.
226+ stack_guard guard {this , sym->rtype ()};
227+
151228 auto cb = _m_externals.find (sym);
152229 if (cb == _m_externals.end ()) {
153230 if (_m_default_external.has_value ()) {
@@ -161,6 +238,9 @@ namespace phoenix {
161238 push_call (sym);
162239 cb->second (*this );
163240 pop_call ();
241+
242+ // The stack is left intact.
243+ guard.inhibit ();
164244 break ;
165245 }
166246 case opcode::pushi:
0 commit comments