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