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