ArkScript
A small, fast, functional and scripting language for video games
VM.hpp
Go to the documentation of this file.
1/**
2 * @file VM.hpp
3 * @author Alexandre Plateau (lexplt.dev@gmail.com)
4 * @brief The ArkScript virtual machine
5 * @version 0.3
6 * @date 2020-10-27
7 *
8 * @copyright Copyright (c) 2020-2021
9 *
10 */
11
12#ifndef ARK_VM_VM_HPP
13#define ARK_VM_VM_HPP
14
15#include <array>
16#include <vector>
17#include <string>
18#include <cinttypes>
19#include <algorithm>
20#include <optional>
21#include <memory>
22#include <unordered_map>
23#include <utility>
24#include <mutex>
25
26#include <Ark/VM/Value.hpp>
27#include <Ark/VM/Scope.hpp>
28#include <Ark/VM/State.hpp>
31#include <Ark/Platform.hpp>
32#include <Ark/VM/Plugin.hpp>
33
34#undef abs
35#include <cmath>
36
37namespace Ark
38{
39 using namespace std::string_literals;
40
41 /**
42 * @brief The ArkScript virtual machine, executing ArkScript bytecode
43 *
44 */
45 class ARK_API VM
46 {
47 public:
48 /**
49 * @brief Construct a new vm t object
50 *
51 * @param state a reference to an ArkScript state, which can be reused for multiple VMs
52 */
53 explicit VM(State& state) noexcept;
54
55 [[deprecated("Use VM(State&) instead of VM(State*)")]] explicit VM(State* state) noexcept :
56 VM(*state)
57 {}
58
59 /**
60 * @brief Run the bytecode held in the state
61 *
62 * @return int the exit code (default to 0 if no error)
63 */
64 int run() noexcept;
65
66 /**
67 * @brief Retrieve a value from the virtual machine, given its symbol name
68 *
69 * @param name the name of the variable to retrieve
70 * @return Value&
71 */
72 Value& operator[](const std::string& name) noexcept;
73
74 /**
75 * @brief Call a function from ArkScript, by giving it arguments
76 *
77 * @tparam Args
78 * @param name the function name in the ArkScript code
79 * @param args C++ argument list, converted to internal representation
80 * @return Value
81 */
82 template <typename... Args>
83 Value call(const std::string& name, Args&&... args);
84
85 // ================================================
86 // function calling from plugins
87 // ================================================
88
89 /**
90 * @brief Resolving a function call (called by plugins)
91 *
92 * @tparam Args
93 * @param val the ArkScript function object
94 * @param args C++ argument list
95 * @return Value
96 */
97 template <typename... Args>
98 Value resolve(const Value* val, Args&&... args);
99
100 /**
101 * @brief Ask the VM to exit with a given exit code
102 *
103 * @param code an exit code
104 */
105 void exit(int code) noexcept;
106
107 /**
108 * @brief Set the User Pointer object
109 *
110 * @param ptr Pointer to data NOT owned by the VM, to be used later
111 */
112 void setUserPointer(void* ptr) noexcept;
113
114 /**
115 * @brief Retrieves the stored pointer
116 *
117 * @return void*
118 */
119 void* getUserPointer() noexcept;
120
121 friend class Value;
122 friend class Repl;
123
124 private:
125 State& m_state;
126 std::vector<std::unique_ptr<internal::ExecutionContext>> m_execution_contexts;
127 int m_exit_code; ///< VM exit code, defaults to 0. Can be changed through `sys:exit`
128 uint16_t m_fc; ///< current frames count
129 bool m_running;
130 std::size_t m_until_frame_count;
131 std::mutex m_mutex;
132 std::vector<std::shared_ptr<internal::SharedLibrary>> m_shared_lib_objects;
133
134 // just a nice little trick for operator[] and for pop
135 Value m_no_value = internal::Builtins::nil;
136
137 void* m_user_pointer; ///< needed to pass data around when binding ArkScript in a program
138 // Note: This is a non owned pointer.
139
140 /**
141 * @brief Run ArkScript bytecode inside a try catch to retrieve all the exceptions and display a stack trace if needed
142 *
143 * @param context
144 * @param untilFrameCount the frame count we need to reach before stopping the VM
145 * @return int the exit code
146 */
147 int safeRun(internal::ExecutionContext& context, std::size_t untilFrameCount = 0);
148
149 /**
150 * @brief Initialize the VM according to the parameters
151 *
152 */
153 void init() noexcept;
154
155 /**
156 * @brief Read a 2 bytes number from the current bytecode page, starting at the current instruction
157 * @details Modify the instruction pointer to point on the instruction right after the number.
158 *
159 * @param context
160 * @return uint16_t
161 */
162 inline uint16_t readNumber(internal::ExecutionContext& context);
163
164 // ================================================
165 // stack related
166 // ================================================
167
168 /**
169 * @brief Pop a value from the stack
170 *
171 * @param context
172 * @return Value*
173 */
174 inline Value* pop(internal::ExecutionContext& context);
175
176 /**
177 * @brief Push a value on the stack
178 *
179 * @param val
180 * @param context
181 */
182 inline void push(const Value& val, internal::ExecutionContext& context);
183
184 /**
185 * @brief Push a value on the stack
186 *
187 * @param val
188 * @param context
189 */
190 inline void push(Value&& val, internal::ExecutionContext& context);
191
192 /**
193 * @brief Push a value on the stack as a reference
194 *
195 * @param valptr
196 * @param context
197 */
198 inline void push(Value* valptr, internal::ExecutionContext& context);
199
200 /**
201 * @brief Pop a value from the stack and resolve it if possible, then return it
202 *
203 * @param context
204 * @return Value*
205 */
206 inline Value* popAndResolveAsPtr(internal::ExecutionContext& context);
207
208 /**
209 * @brief Move stack values around and invert them
210 * @details values: 1, 2, 3, _, _
211 * wanted: pp, ip, 3, 2, 1
212 * positions: 0, 1, 2, 3, 4
213 *
214 * @param argc number of arguments to swap around
215 * @param context
216 */
217 inline void swapStackForFunCall(uint16_t argc, internal::ExecutionContext& context);
218
219 // ================================================
220 // locals related
221 // ================================================
222
223 inline void createNewScope(internal::ExecutionContext& context) noexcept;
224
225 /**
226 * @brief Find the nearest variable of a given id
227 *
228 * @param id the id to find
229 * @param context
230 * @return Value*
231 */
232 inline Value* findNearestVariable(uint16_t id, internal::ExecutionContext& context) noexcept;
233
234 /**
235 * @brief Destroy the current frame and get back to the previous one, resuming execution
236 *
237 * Doing the job nobody wants to do: cleaning after everyone has finished to play.
238 * This is a sort of primitive garbage collector
239 *
240 * @param context
241 */
242 inline void returnFromFuncCall(internal::ExecutionContext& context);
243
244 /**
245 * @brief Load a plugin from a constant id
246 *
247 * @param id Id of the constant
248 * @param context
249 */
250 void loadPlugin(uint16_t id, internal::ExecutionContext& context);
251
252 // ================================================
253 // error handling
254 // ================================================
255
256 /**
257 * @brief Find the nearest variable id with a given value
258 *
259 * Only used to display the call stack traceback
260 *
261 * @param value the value to search for
262 * @param context
263 * @return uint16_t
264 */
265 uint16_t findNearestVariableIdWithValue(const Value& value, internal::ExecutionContext& context) const noexcept;
266
267 /**
268 * @brief Throw a VM error message
269 *
270 * @param message
271 */
272 void throwVMError(const std::string& message);
273
274 /**
275 * @brief Display a backtrace when the VM encounter an exception
276 *
277 * @param context
278 */
279 void backtrace(internal::ExecutionContext& context) noexcept;
280
281 /**
282 * @brief Function called when the CALL instruction is met in the bytecode
283 *
284 * @param context
285 * @param argc_ number of arguments already sent, default to -1 if it needs to search for them by itself
286 */
287 inline void call(internal::ExecutionContext& context, int16_t argc_ = -1);
288 };
289
290#include "inline/VM.inl"
291
292 /// ArkScript Nil value
294 /// ArkScript False value
296 /// ArkScript True value
298}
299
300#endif
Host the declaration of all the ArkScript builtins.
Keeping track of the internal data needed by the VM.
ArkScript configuration macros.
#define ARK_API
Definition: Platform.hpp:41
Loads .dll/.so/.dynlib files.
The virtual machine scope system.
State used by the virtual machine: it loads the bytecode, can compile it if needed,...
Ark state to handle the dirty job of loading and compiling ArkScript code.
Definition: State.hpp:31
The ArkScript virtual machine, executing ArkScript bytecode.
Definition: VM.hpp:46
VM(State *state) noexcept
Definition: VM.hpp:55
Handling a shared library as an ArkScript plugin.
Definition: Plugin.hpp:38
Definition: Builtins.hpp:21
const Value False
ArkScript False value.
Definition: VM.hpp:295
const Value True
ArkScript True value.
Definition: VM.hpp:297
const Value Nil
ArkScript Nil value.
Definition: VM.hpp:293