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 #include <Ark/VM/Future.hpp>
34 
35 #undef abs
36 #include <cmath>
37 
38 namespace Ark
39 {
40  using namespace std::string_literals;
41 
42  /**
43  * @brief The ArkScript virtual machine, executing ArkScript bytecode
44  *
45  */
46  class ARK_API VM
47  {
48  public:
49  /**
50  * @brief Construct a new vm t object
51  *
52  * @param state a reference to an ArkScript state, which can be reused for multiple VMs
53  */
54  explicit VM(State& state) noexcept;
55 
56  [[deprecated("Use VM(State&) instead of VM(State*)")]] explicit VM(State* state) noexcept :
57  VM(*state)
58  {}
59 
60  /**
61  * @brief Run the bytecode held in the state
62  *
63  * @return int the exit code (default to 0 if no error)
64  */
65  int run() noexcept;
66 
67  /**
68  * @brief Retrieve a value from the virtual machine, given its symbol name
69  *
70  * @param name the name of the variable to retrieve
71  * @return Value&
72  */
73  Value& operator[](const std::string& name) noexcept;
74 
75  /**
76  * @brief Call a function from ArkScript, by giving it arguments
77  *
78  * @tparam Args
79  * @param name the function name in the ArkScript code
80  * @param args C++ argument list, converted to internal representation
81  * @return Value
82  */
83  template <typename... Args>
84  Value call(const std::string& name, Args&&... args);
85 
86  // ================================================
87  // function calling from plugins
88  // ================================================
89 
90  /**
91  * @brief Resolving a function call (called by plugins and builtins)
92  *
93  * @tparam Args
94  * @param val the ArkScript function object
95  * @param args C++ argument list
96  * @return Value
97  */
98  template <typename... Args>
99  Value resolve(const Value* val, Args&&... args);
100 
101  /**
102  * @brief Resolves a function call (called by plugins and builtins)
103  *
104  * @param context the execution context to use
105  * @param n the function and its arguments
106  * @return Value
107  */
108  inline Value resolve(internal::ExecutionContext* context, std::vector<Value>& n);
109 
110  /**
111  * @brief Ask the VM to exit with a given exit code
112  *
113  * @param code an exit code
114  */
115  void exit(int code) noexcept;
116 
117  /**
118  * @brief Set the User Pointer object
119  *
120  * @param ptr Pointer to data NOT owned by the VM, to be used later
121  */
122  void setUserPointer(void* ptr) noexcept;
123 
124  /**
125  * @brief Retrieves the stored pointer
126  *
127  * @return void*
128  */
129  void* getUserPointer() noexcept;
130 
131  /**
132  * @brief Create an execution context and returns it
133  * @details This method is thread-safe VM wise.
134  *
135  * @return internal::ExecutionContext*
136  */
137  internal::ExecutionContext* createAndGetContext();
138 
139  /**
140  * @brief Free a given execution context
141  * @details This method is thread-safe VM wise.
142  *
143  * @param ec
144  */
145  void deleteContext(internal::ExecutionContext* ec);
146 
147  /**
148  * @brief Create a Future object from a function and its arguments and return a managed pointer to it
149  * @details This method is thread-safe VM wise.
150  *
151  * @param args
152  * @return internal::Future*
153  */
154  internal::Future* createFuture(std::vector<Value>& args);
155 
156  /**
157  * @brief Free a given future
158  * @details This method is thread-safe VM wise.
159  *
160  * @param f
161  */
162  void deleteFuture(internal::Future* f);
163 
164  friend class Value;
165  friend class internal::Closure;
166  friend class Repl;
167 
168  private:
169  State& m_state;
170  std::vector<std::unique_ptr<internal::ExecutionContext>> m_execution_contexts;
171  int m_exit_code; ///< VM exit code, defaults to 0. Can be changed through `sys:exit`
172  bool m_running;
173  std::mutex m_mutex;
174  std::vector<std::shared_ptr<internal::SharedLibrary>> m_shared_lib_objects;
175  std::vector<std::unique_ptr<internal::Future>> m_futures; ///< Storing the promises while we are resolving them
176 
177  // just a nice little trick for operator[] and for pop
178  Value m_no_value = internal::Builtins::nil;
179  Value m_undefined_value;
180 
181  void* m_user_pointer; ///< needed to pass data around when binding ArkScript in a program
182  // Note: This is a non owned pointer.
183 
184  /**
185  * @brief Run ArkScript bytecode inside a try catch to retrieve all the exceptions and display a stack trace if needed
186  *
187  * @param context
188  * @param untilFrameCount the frame count we need to reach before stopping the VM
189  * @return int the exit code
190  */
191  int safeRun(internal::ExecutionContext& context, std::size_t untilFrameCount = 0);
192 
193  /**
194  * @brief Initialize the VM according to the parameters
195  *
196  */
197  void init() noexcept;
198 
199  /**
200  * @brief Read a 2 bytes number from the current bytecode page, starting at the current instruction
201  * @details Modify the instruction pointer to point on the instruction right after the number.
202  *
203  * @param context
204  * @return uint16_t
205  */
206  inline uint16_t readNumber(internal::ExecutionContext& context);
207 
208  // ================================================
209  // stack related
210  // ================================================
211 
212  /**
213  * @brief Pop a value from the stack
214  *
215  * @param context
216  * @return Value*
217  */
218  inline Value* pop(internal::ExecutionContext& context);
219 
220  /**
221  * @brief Push a value on the stack
222  *
223  * @param val
224  * @param context
225  */
226  inline void push(const Value& val, internal::ExecutionContext& context);
227 
228  /**
229  * @brief Push a value on the stack
230  *
231  * @param val
232  * @param context
233  */
234  inline void push(Value&& val, internal::ExecutionContext& context);
235 
236  /**
237  * @brief Push a value on the stack as a reference
238  *
239  * @param valptr
240  * @param context
241  */
242  inline void push(Value* valptr, internal::ExecutionContext& context);
243 
244  /**
245  * @brief Pop a value from the stack and resolve it if possible, then return it
246  *
247  * @param context
248  * @return Value*
249  */
250  inline Value* popAndResolveAsPtr(internal::ExecutionContext& context);
251 
252  /**
253  * @brief Move stack values around and invert them
254  * @details values: 1, 2, 3, _, _
255  * wanted: pp, ip, 3, 2, 1
256  * positions: 0, 1, 2, 3, 4
257  *
258  * @param argc number of arguments to swap around
259  * @param context
260  */
261  inline void swapStackForFunCall(uint16_t argc, internal::ExecutionContext& context);
262 
263  // ================================================
264  // locals related
265  // ================================================
266 
267  inline void createNewScope(internal::ExecutionContext& context) noexcept;
268 
269  /**
270  * @brief Find the nearest variable of a given id
271  *
272  * @param id the id to find
273  * @param context
274  * @return Value*
275  */
276  inline Value* findNearestVariable(uint16_t id, internal::ExecutionContext& context) noexcept;
277 
278  /**
279  * @brief Destroy the current frame and get back to the previous one, resuming execution
280  *
281  * Doing the job nobody wants to do: cleaning after everyone has finished to play.
282  * This is a sort of primitive garbage collector
283  *
284  * @param context
285  */
286  inline void returnFromFuncCall(internal::ExecutionContext& context);
287 
288  /**
289  * @brief Load a plugin from a constant id
290  *
291  * @param id Id of the constant
292  * @param context
293  */
294  void loadPlugin(uint16_t id, internal::ExecutionContext& context);
295 
296  // ================================================
297  // error handling
298  // ================================================
299 
300  /**
301  * @brief Find the nearest variable id with a given value
302  *
303  * Only used to display the call stack traceback
304  *
305  * @param value the value to search for
306  * @param context
307  * @return uint16_t
308  */
309  uint16_t findNearestVariableIdWithValue(const Value& value, internal::ExecutionContext& context) const noexcept;
310 
311  /**
312  * @brief Throw a VM error message
313  *
314  * @param message
315  */
316  void throwVMError(const std::string& message);
317 
318  /**
319  * @brief Display a backtrace when the VM encounter an exception
320  *
321  * @param context
322  */
323  void backtrace(internal::ExecutionContext& context) noexcept;
324 
325  /**
326  * @brief Function called when the CALL instruction is met in the bytecode
327  *
328  * @param context
329  * @param argc_ number of arguments already sent, default to -1 if it needs to search for them by itself
330  */
331  inline void call(internal::ExecutionContext& context, int16_t argc_ = -1);
332  };
333 
334 #include "inline/VM.inl"
335 
336  /// ArkScript Nil value
338  /// ArkScript False value
340  /// ArkScript True value
342 }
343 
344 #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:47
VM(State *state) noexcept
Definition: VM.hpp:56
Closure management.
Definition: Closure.hpp:45
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:339
const Value True
ArkScript True value.
Definition: VM.hpp:341
const Value Nil
ArkScript Nil value.
Definition: VM.hpp:337