14#ifdef ARK_PROFILER_MIPS
26 using namespace internal;
29 m_state(state), m_exit_code(0), m_running(
false)
31 m_execution_contexts.emplace_back(std::make_unique<ExecutionContext>())->locals.reserve(4);
55 context.
locals.emplace_back();
72 const auto it = std::ranges::find(m_state.m_symbols, name);
73 if (it == m_state.m_symbols.end())
79 const auto id =
static_cast<uint16_t
>(std::distance(m_state.m_symbols.begin(), it));
80 Value* var = findNearestVariable(
id, context);
89 namespace fs = std::filesystem;
93 std::string path = file;
96 path = (fs::path(
m_state.
m_filename).parent_path() / fs::path(file)).relative_path().string();
98 std::shared_ptr<SharedLibrary> lib;
101 lib = std::make_shared<SharedLibrary>(path);
106 std::string lib_path = (fs::path(v) / fs::path(file)).
string();
110 return (val->path() == path || val->path() == lib_path);
117 lib = std::make_shared<SharedLibrary>(lib_path);
125 auto lib_path = std::accumulate(
129 [](
const std::string& a,
const fs::path& b) -> std::string {
130 return a +
"\n\t- " + b.string();
134 fmt::format(
"Could not find module '{}'. Searched under\n\t- {}\n\t- {}", file, path, lib_path));
145 while (map[i].name !=
nullptr)
155 catch (
const std::system_error& e)
160 "An error occurred while loading module '{}': {}\nIt is most likely because the versions of the module and the language don't match.",
173 const std::lock_guard lock(
m_mutex);
181 ctx->
locals.push_back(local);
188 const std::lock_guard lock(
m_mutex);
191 [ec](
const std::unique_ptr<ExecutionContext>& ctx) {
192 return ctx.get() == ec;
204 const std::lock_guard lock(
m_mutex);
205 m_futures.push_back(std::make_unique<Future>(ctx,
this, args));
212 const std::lock_guard lock(
m_mutex);
214 const auto it = std::ranges::remove_if(
m_futures,
215 [f](
const std::unique_ptr<Future>& future) {
216 return future.get() == f;
229 const mapping* map = shared_lib->template get<mapping* (*)()>(
"getFunctionsMapping")();
232 while (map[i].name !=
nullptr)
245 catch (
const std::system_error& e)
260#ifdef ARK_PROFILER_MIPS
261 auto start_time = std::chrono::system_clock::now();
262 unsigned long long instructions_executed = 0;
278#pragma region "Instructions"
288 push(var->reference(), context);
314 context.
ip =
static_cast<int16_t
>(arg) * 4;
327 *var->reference() = val;
343 if (
auto val = (context.
locals.back())[arg]; val !=
nullptr) [[unlikely]]
348 context.
locals.back().push_back(arg, val);
356 context.
ip =
static_cast<int16_t
>(arg) * 4;
362 context.
ip =
static_cast<int16_t
>(arg) * 4;
393 push(std::move(ip_or_val), context);
410 "Maximum recursion depth exceeded. You could consider rewriting your function `{}' to make use of tail-call optimization.",
443 if (local ==
nullptr) [[likely]]
444 context.
locals.back().push_back(arg, val);
456 var->usertypeRef().del();
477 fmt::format(
"`{}' is a {}, not a closure, can not get the field `{}' from it",
487 push(field, context);
505 l.
list().reserve(arg);
507 for (uint16_t i = 0; i < arg; ++i)
509 push(std::move(l), context);
522 const uint16_t size = list->
constList().size();
525 obj.
list().reserve(size + arg);
527 for (uint16_t i = 0; i < arg; ++i)
529 push(std::move(obj), context);
544 for (uint16_t i = 0; i < arg; ++i)
554 for (
auto& val : next->
list())
557 push(std::move(obj), context);
566 throwVMError(ErrorKind::Mutability,
"Can not modify a constant list using `append!'");
573 for (uint16_t i = 0; i < arg; ++i)
584 throwVMError(ErrorKind::Mutability,
"Can not modify a constant list using `concat!'");
591 for (uint16_t i = 0; i < arg; ++i)
601 for (
auto& it : next->
list())
619 long idx =
static_cast<long>(number.
number());
620 idx = (idx < 0 ? list.
list().size() + idx : idx);
621 if (
static_cast<std::size_t
>(idx) >= list.
list().size())
624 fmt::format(
"pop index ({}) out of range (list size: {})", idx, list.
list().size()));
626 list.
list().erase(list.
list().begin() + idx);
637 throwVMError(ErrorKind::Mutability,
"Can not modify a constant list using `pop!'");
644 long idx =
static_cast<long>(number.
number());
645 idx = (idx < 0 ? list->
list().size() + idx : idx);
646 if (
static_cast<std::size_t
>(idx) >= list->
list().size())
649 fmt::format(
"pop! index ({}) out of range (list size: {})", idx, list->
list().size()));
651 list->
list().erase(list->
list().begin() + idx);
663#pragma region "Operators"
719 throwVMError(ErrorKind::DivisionByZero, fmt::format(
"Can not compute expression (/ {} {})", a->toString(*
this), b->
toString(*
this)));
817 std::vector<Value> tmp(a->
constList().size() - 1);
818 for (std::size_t i = 1, end = a->
constList().size(); i < end; ++i)
825 if (a->
string().size() < 2)
830 b.
stringRef().erase(b.stringRef().begin());
831 push(std::move(b), context);
931 long idx =
static_cast<long>(b->
number());
935 if (
static_cast<std::size_t
>(std::abs(idx)) < a.
list().size())
936 push(a.
list()[idx < 0 ? a.
list().size() + idx : idx], context);
940 fmt::format(
"{} out of range {} (length {})", idx, a.
toString(*
this), a.
list().size()));
944 if (
static_cast<std::size_t
>(std::abs(idx)) < a.
string().size())
949 fmt::format(
"{} out of range \"{}\" (length {})", idx, a.
string(), a.
string().size()));
1004 { *closure, *field });
1013 auto id =
static_cast<uint16_t
>(std::distance(
m_state.
m_symbols.begin(), it));
1030 throwVMError(ErrorKind::VM, fmt::format(
"Unknown instruction: {:x}{:x}{:x}", padding, inst, arg));
1034#ifdef ARK_PROFILER_MIPS
1035 ++instructions_executed;
1039 catch (
const std::exception& e)
1041 std::printf(
"%s\n", e.what());
1047 std::printf(
"Unknown error\n");
1051#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1056#ifdef ARK_PROFILER_MIPS
1057 auto end_time = std::chrono::system_clock::now();
1058 auto d = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
1060 std::cout <<
"\nInstructions executed: " << instructions_executed <<
"\n"
1061 <<
"Time spent: " << d.count() <<
" us\n"
1062 << (
static_cast<double>(instructions_executed) / d.count()) <<
" MIPS\n";
1070 for (
auto& local : std::ranges::reverse_view(context.locals))
1072 if (
const auto id = local.idFromValue(value);
id < m_state.m_symbols.size())
1075 return std::numeric_limits<uint16_t>::max();
1080 throw std::runtime_error(std::string(
errorKinds[
static_cast<std::size_t
>(kind)]) +
": " + message +
"\n");
1085 const int saved_ip = context.ip;
1086 const std::size_t saved_pp = context.pp;
1087 const uint16_t saved_sp = context.sp;
1089 if (
const uint16_t original_frame_count = context.fc; original_frame_count > 1)
1092 const Scope old_scope = context.locals.back();
1094 while (context.fc != 0)
1096 std::cerr <<
"[" << termcolor::cyan << context.fc << termcolor::reset <<
"] ";
1097 if (context.pp != 0)
1099 const uint16_t
id = findNearestVariableIdWithValue(
1103 if (
id < m_state.m_symbols.size())
1104 std::cerr <<
"In function `" << termcolor::green << m_state.m_symbols[id] << termcolor::reset <<
"'\n";
1106 std::cerr <<
"In function `" << termcolor::yellow <<
"???" << termcolor::reset <<
"'\n";
1111 ip = popAndResolveAsPtr(context);
1115 context.pp = pop(context)->pageAddr();
1116 returnFromFuncCall(context);
1120 std::printf(
"In global scope\n");
1124 if (original_frame_count - context.fc > 7)
1126 std::printf(
"...\n");
1132 std::printf(
"\nCurrent scope variables values:\n");
1133 for (std::size_t i = 0, size = old_scope.
size(); i < size; ++i)
1135 std::cerr << termcolor::cyan << m_state.m_symbols[old_scope.
m_data[i].first] << termcolor::reset
1136 <<
" = " << old_scope.
m_data[i].second.toString(*
this);
1140 while (context.fc != 1)
1142 Value* tmp = pop(context);
1151 std::cerr << termcolor::reset
1152 <<
"At IP: " << (saved_ip != -1 ? saved_ip / 4 : 0)
1153 <<
", PP: " << saved_pp
1154 <<
", SP: " << saved_sp
Lots of utilities about string, filesystem and more.
Lots of utilities about the filesystem.
The different instructions used by the compiler and virtual machine.
The ArkScript virtual machine.
An assertion error, only triggered from ArkScript code through (assert expr error-message)
Ark state to handle the dirty job of loading and compiling ArkScript code.
std::vector< std::filesystem::path > m_libenv
std::vector< Value > m_constants
std::unordered_map< std::string, Value > m_binded
std::vector< std::string > m_symbols
std::vector< bytecode_t > m_pages
The ArkScript virtual machine, executing ArkScript bytecode.
int run() noexcept
Run the bytecode held in the state.
void deleteContext(internal::ExecutionContext *ec)
Free a given execution context.
std::vector< std::unique_ptr< internal::Future > > m_futures
Storing the promises while we are resolving them.
int m_exit_code
VM exit code, defaults to 0. Can be changed through sys:exit
Value & operator[](const std::string &name) noexcept
Retrieve a value from the virtual machine, given its symbol name.
Value * popAndResolveAsPtr(internal::ExecutionContext &context)
Pop a value from the stack and resolve it if possible, then return it.
std::vector< std::shared_ptr< internal::SharedLibrary > > m_shared_lib_objects
std::vector< std::unique_ptr< internal::ExecutionContext > > m_execution_contexts
uint16_t findNearestVariableIdWithValue(const Value &value, internal::ExecutionContext &context) const noexcept
Find the nearest variable id with a given value.
bool forceReloadPlugins() const
Used by the REPL to force reload all the plugins and their bound methods.
internal::ExecutionContext * createAndGetContext()
Create an execution context and returns it.
void loadPlugin(uint16_t id, internal::ExecutionContext &context)
Load a plugin from a constant id.
void deleteFuture(internal::Future *f)
Free a given future.
Value call(const std::string &name, Args &&... args)
Call a function from ArkScript, by giving it arguments.
Value * pop(internal::ExecutionContext &context)
Pop a value from the stack.
void returnFromFuncCall(internal::ExecutionContext &context)
Destroy the current frame and get back to the previous one, resuming execution.
void backtrace(internal::ExecutionContext &context) noexcept
Display a backtrace when the VM encounter an exception.
void push(const Value &value, internal::ExecutionContext &context)
Push a value on the stack.
friend class internal::Closure
int safeRun(internal::ExecutionContext &context, std::size_t untilFrameCount=0)
Run ArkScript bytecode inside a try catch to retrieve all the exceptions and display a stack trace if...
void init() noexcept
Initialize the VM according to the parameters.
Value * findNearestVariable(uint16_t id, internal::ExecutionContext &context) noexcept
Find the nearest variable of a given id.
static void throwVMError(internal::ErrorKind kind, const std::string &message)
Throw a VM error message.
VM(State &state) noexcept
Construct a new vm t object.
internal::Future * createFuture(std::vector< Value > &args)
Create a Future object from a function and its arguments and return a managed pointer to it.
void exit(int code) noexcept
Ask the VM to exit with a given exit code.
const std::vector< Value > & constList() const
std::vector< Value > & list()
bool isConst() const noexcept
internal::Closure & refClosure()
Value * reference() const
void push_back(const Value &value)
Add an element to the list held by the value (if the value type is set to list)
void setConst(const bool value) noexcept
ValueType valueType() const noexcept
const std::string & string() const
std::string toString(VM &vm) const noexcept
internal::PageAddr_t pageAddr() const
std::string & stringRef()
const std::shared_ptr< Scope > & scopePtr() const
std::string toString(VM &vm) const noexcept
Print the closure to a string.
Scope & refScope() const noexcept
A class to handle the VM scope more efficiently.
std::vector< std::pair< uint16_t, Value > > m_data
std::size_t size() const noexcept
Return the size of the scope.
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
bool isDouble(const std::string &s, double *output=nullptr)
Checks if a string is a valid double.
const std::vector< std::pair< std::string, Value > > builtins
constexpr std::array< std::string_view, 8 > errorKinds
ARK_API void generateError(std::string_view funcname, const std::vector< Contract > &contracts, const std::vector< Value > &args)
Generate an error message based on a given set of types contracts provided argument list.
constexpr std::size_t VMStackSize
@ Any
Used only for typechecking.
const std::array< std::string, 13 > types_to_str
int ip
Instruction pointer.
std::optional< Scope > saved_scope
std::size_t pp
Page pointer.
std::vector< Scope > locals
uint16_t sp
Stack pointer.
std::vector< std::shared_ptr< Scope > > stacked_closure_scopes
A contract is a list of typed arguments that a function can follow.
A type definition within a contract.
Ark::Value(* value)(std::vector< Ark::Value > &, Ark::VM *)