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