495#if ARK_USE_COMPUTED_GOTOS
496# define TARGET(op) TARGET_##op:
497# define DISPATCH_GOTO() \
498 _Pragma("GCC diagnostic push") \
499 _Pragma("GCC diagnostic ignored \"-Wpedantic\"") goto* opcode_targets[inst];
500 _Pragma(
"GCC diagnostic pop")
501# define GOTO_HALT() goto dispatch_end
503# define TARGET(op) case op:
504# define DISPATCH_GOTO() goto dispatch_opcode
505# define GOTO_HALT() break
511 inst = m_state.inst(context.pp, context.ip); \
512 padding = m_state.inst(context.pp, context.ip + 1); \
513 arg = static_cast<uint16_t>((m_state.inst(context.pp, context.ip + 2) << 8) + \
514 m_state.inst(context.pp, context.ip + 3)); \
516 context.inst_exec_counter = (context.inst_exec_counter + 1) % VMOverflowBufferSize; \
517 if (context.inst_exec_counter < 2 && context.sp >= VMStackSize) \
519 if (context.pp != 0) \
520 throw Error("Stack overflow. You could consider rewriting your function to make use of tail-call optimization."); \
522 throw Error("Stack overflow. Are you trying to call a function with too many arguments?"); \
528#define UNPACK_ARGS() \
531 secondary_arg = static_cast<uint16_t>((padding << 4) | (arg & 0xf000) >> 12); \
532 primary_arg = arg & 0x0fff; \
535#if ARK_USE_COMPUTED_GOTOS
536# pragma GCC diagnostic push
537# pragma GCC diagnostic ignored "-Wpedantic"
538 constexpr std::array opcode_targets = {
541 &&TARGET_LOAD_SYMBOL,
542 &&TARGET_LOAD_SYMBOL_BY_INDEX,
544 &&TARGET_POP_JUMP_IF_TRUE,
547 &&TARGET_POP_JUMP_IF_FALSE,
551 &&TARGET_PUSH_RETURN_ADDRESS,
554 &&TARGET_RENAME_NEXT_CAPTURE,
557 &&TARGET_MAKE_CLOSURE,
563 &&TARGET_APPEND_IN_PLACE,
564 &&TARGET_CONCAT_IN_PLACE,
566 &&TARGET_POP_LIST_IN_PLACE,
567 &&TARGET_SET_AT_INDEX,
568 &&TARGET_SET_AT_2_INDEX,
570 &&TARGET_SHORTCIRCUIT_AND,
571 &&TARGET_SHORTCIRCUIT_OR,
572 &&TARGET_CREATE_SCOPE,
573 &&TARGET_RESET_SCOPE_JUMP,
575 &&TARGET_GET_CURRENT_PAGE_ADDR,
600 &&TARGET_LOAD_CONST_LOAD_CONST,
601 &&TARGET_LOAD_CONST_STORE,
602 &&TARGET_LOAD_CONST_SET_VAL,
604 &&TARGET_STORE_FROM_INDEX,
605 &&TARGET_SET_VAL_FROM,
606 &&TARGET_SET_VAL_FROM_INDEX,
608 &&TARGET_INCREMENT_BY_INDEX,
609 &&TARGET_INCREMENT_STORE,
611 &&TARGET_DECREMENT_BY_INDEX,
612 &&TARGET_DECREMENT_STORE,
614 &&TARGET_STORE_TAIL_BY_INDEX,
616 &&TARGET_STORE_HEAD_BY_INDEX,
618 &&TARGET_SET_VAL_TAIL,
619 &&TARGET_SET_VAL_TAIL_BY_INDEX,
620 &&TARGET_SET_VAL_HEAD,
621 &&TARGET_SET_VAL_HEAD_BY_INDEX,
622 &&TARGET_CALL_BUILTIN,
623 &&TARGET_CALL_BUILTIN_WITHOUT_RETURN_ADDRESS,
624 &&TARGET_LT_CONST_JUMP_IF_FALSE,
625 &&TARGET_LT_CONST_JUMP_IF_TRUE,
626 &&TARGET_LT_SYM_JUMP_IF_FALSE,
627 &&TARGET_GT_CONST_JUMP_IF_TRUE,
628 &&TARGET_GT_CONST_JUMP_IF_FALSE,
629 &&TARGET_GT_SYM_JUMP_IF_FALSE,
630 &&TARGET_EQ_CONST_JUMP_IF_TRUE,
631 &&TARGET_EQ_SYM_INDEX_JUMP_IF_TRUE,
632 &&TARGET_NEQ_CONST_JUMP_IF_TRUE,
633 &&TARGET_NEQ_SYM_JUMP_IF_FALSE,
634 &&TARGET_CALL_SYMBOL,
635 &&TARGET_CALL_CURRENT_PAGE,
636 &&TARGET_GET_FIELD_FROM_SYMBOL,
637 &&TARGET_GET_FIELD_FROM_SYMBOL_INDEX,
639 &&TARGET_AT_SYM_INDEX_SYM_INDEX,
640 &&TARGET_AT_SYM_INDEX_CONST,
641 &&TARGET_CHECK_TYPE_OF,
642 &&TARGET_CHECK_TYPE_OF_BY_INDEX,
643 &&TARGET_APPEND_IN_PLACE_SYM,
644 &&TARGET_APPEND_IN_PLACE_SYM_INDEX,
646 &&TARGET_LT_LEN_SYM_JUMP_IF_FALSE
649 static_assert(opcode_targets.size() ==
static_cast<std::size_t
>(Instruction::InstructionsCount) &&
"Some instructions are not implemented in the VM");
650# pragma GCC diagnostic pop
658 uint16_t primary_arg = 0;
659 uint16_t secondary_arg = 0;
666#if !ARK_USE_COMPUTED_GOTOS
671#pragma region "Instructions"
751 push(std::move(ip_or_val), context);
754 if (context.
fc <= untilFrameCount)
820 var->usertypeRef().del();
852 push(std::move(l), context);
863 std::vector<Value> args = { *list };
864 for (uint16_t i = 0; i < arg; ++i)
872 const auto size =
static_cast<uint16_t
>(list->
constList().size());
875 obj.
list().reserve(size + arg);
877 for (uint16_t i = 0; i < arg; ++i)
879 push(std::move(obj), context);
890 for (uint16_t i = 0; i < arg; ++i)
900 std::ranges::copy(next->
list(), std::back_inserter(obj.list()));
902 push(std::move(obj), context);
918 for (uint16_t i = 0; i < arg; ++i)
928 std::ranges::copy(next->
list(), std::back_inserter(list->
list()));
945 long idx =
static_cast<long>(number.
number());
946 idx = idx < 0 ? static_cast<long>(list.
list().size()) + idx : idx;
947 if (std::cmp_greater_equal(idx, list.
list().size()) || idx < 0)
950 fmt::format(
"pop index ({}) out of range (list size: {})", idx, list.
list().size()));
952 list.
list().erase(list.
list().begin() + idx);
970 long idx =
static_cast<long>(number.
number());
971 idx = idx < 0 ? static_cast<long>(list->
list().size()) + idx : idx;
972 if (std::cmp_greater_equal(idx, list->
list().size()) || idx < 0)
975 fmt::format(
"pop! index ({}) out of range (list size: {})", idx, list->
list().size()));
977 list->
list().erase(list->
list().begin() + idx);
1000 { *list, number, new_value });
1003 long idx =
static_cast<long>(number.
number());
1004 idx = idx < 0 ? static_cast<long>(size) + idx : idx;
1005 if (std::cmp_greater_equal(idx, size) || idx < 0)
1008 fmt::format(
"@= index ({}) out of range (indexable size: {})", idx, size));
1011 list->
list()[
static_cast<std::size_t
>(idx)] = new_value;
1013 list->
stringRef()[
static_cast<std::size_t
>(idx)] = new_value.
string()[0];
1034 { *list, x, y, new_value });
1036 long idx_y =
static_cast<long>(x.
number());
1037 idx_y = idx_y < 0 ? static_cast<long>(list->
list().size()) + idx_y : idx_y;
1038 if (std::cmp_greater_equal(idx_y, list->
list().size()) || idx_y < 0)
1041 fmt::format(
"@@= index (y: {}) out of range (list size: {})", idx_y, list->
list().size()));
1043 if (!list->
list()[
static_cast<std::size_t
>(idx_y)].isIndexable() ||
1057 { *list, x, y, new_value });
1059 const bool is_list = list->
list()[
static_cast<std::size_t
>(idx_y)].valueType() ==
ValueType::List;
1060 const std::size_t size =
1062 ? list->
list()[
static_cast<std::size_t
>(idx_y)].list().size()
1063 : list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef().size();
1065 long idx_x =
static_cast<long>(y.
number());
1066 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
1067 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
1070 fmt::format(
"@@= index (x: {}) out of range (inner indexable size: {})", idx_x, size));
1073 list->
list()[
static_cast<std::size_t
>(idx_y)].list()[
static_cast<std::size_t
>(idx_x)] = new_value;
1075 list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef()[
static_cast<std::size_t
>(idx_x)] = new_value.
string()[0];
1112 context.
locals.back().reset();
1119 context.
locals.pop_back();
1132#pragma region "Operators"
1188 throwVMError(ErrorKind::DivisionByZero, fmt::format(
"Can not compute expression (/ {} {})", a->toString(*
this), b->
toString(*
this)));
1357 long idx_y =
static_cast<long>(y->
number());
1358 idx_y = idx_y < 0 ? static_cast<long>(list.
list().size()) + idx_y : idx_y;
1359 if (std::cmp_greater_equal(idx_y, list.
list().size()) || idx_y < 0)
1362 fmt::format(
"@@ index ({}) out of range (list size: {})", idx_y, list.
list().size()));
1364 const bool is_list = list.
list()[
static_cast<std::size_t
>(idx_y)].valueType() ==
ValueType::List;
1365 const std::size_t size =
1367 ? list.
list()[
static_cast<std::size_t
>(idx_y)].list().size()
1368 : list.
list()[
static_cast<std::size_t
>(idx_y)].stringRef().size();
1370 long idx_x =
static_cast<long>(x->
number());
1371 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
1372 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
1375 fmt::format(
"@@ index (x: {}) out of range (inner indexable size: {})", idx_x, size));
1378 push(list.
list()[
static_cast<std::size_t
>(idx_y)].list()[
static_cast<std::size_t
>(idx_x)], context);
1380 push(
Value(std::string(1, list.
list()[
static_cast<std::size_t
>(idx_y)].stringRef()[
static_cast<std::size_t
>(idx_x)])), context);
1413 { *closure, *field });
1422 auto id =
static_cast<std::uint16_t
>(std::distance(
m_state.
m_symbols.begin(), it));
1437#pragma region "Super Instructions"
1505 { *var,
Value(secondary_arg) });
1526 { *var,
Value(secondary_arg) });
1544 setVal(primary_arg, &val, context);
1550 { *var,
Value(secondary_arg) });
1571 { *var,
Value(secondary_arg) });
1592 { *var,
Value(secondary_arg) });
1610 setVal(primary_arg, &val, context);
1616 { *var,
Value(secondary_arg) });
1627 store(secondary_arg, &tail, context);
1638 store(secondary_arg, &tail, context);
1649 store(secondary_arg, &head, context);
1660 store(secondary_arg, &head, context);
1670 store(secondary_arg, &l, context);
1681 setVal(secondary_arg, &tail, context);
1692 setVal(secondary_arg, &tail, context);
1703 setVal(secondary_arg, &head, context);
1714 setVal(secondary_arg, &head, context);
1744 jump(secondary_arg, context);
1753 jump(secondary_arg, context);
1761 if (!(*sym < *
loadSymbol(primary_arg, context)))
1762 jump(secondary_arg, context);
1772 jump(secondary_arg, context);
1782 jump(secondary_arg, context);
1792 jump(secondary_arg, context);
1801 jump(secondary_arg, context);
1810 jump(secondary_arg, context);
1819 jump(secondary_arg, context);
1827 if (*sym == *
loadSymbol(primary_arg, context))
1828 jump(secondary_arg, context);
1845 call(context, secondary_arg,
nullptr,
static_cast<PageAddr_t>(context.
pp));
1937 len =
Value(
static_cast<int>(a->
string().size()));
1944 store(secondary_arg, &len, context);
1959 size =
Value(
static_cast<int>(sym->
string().size()));
1968 jump(secondary_arg, context);
1974#if ARK_USE_COMPUTED_GOTOS
1982 catch (
const Error& e)
1984 if (fail_with_exception)
1986 std::stringstream stream;
1995 catch (
const std::exception& e)
1997 if (fail_with_exception)
1999 std::stringstream stream;
2008 if (fail_with_exception)
2011#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2014 fmt::println(
"Unknown error");
2141 const std::size_t saved_ip = context.
ip;
2142 const std::size_t saved_pp = context.
pp;
2143 const uint16_t saved_sp = context.
sp;
2144 constexpr std::size_t max_consecutive_traces = 7;
2154 .filename = filename,
2155 .start =
FilePos { .line = maybe_location->line, .column = 0 },
2156 .end = std::nullopt },
2160 fmt::println(os,
"");
2168 std::string previous_trace;
2169 std::size_t displayed_traces = 0;
2170 std::size_t consecutive_similar_traces = 0;
2172 while (context.
fc != 0 && context.
pp != 0)
2175 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2182 if (func_name + loc_as_text != previous_trace)
2186 "[{:4}] In function `{}'{}",
2187 fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2188 fmt::styled(func_name, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()),
2190 previous_trace = func_name + loc_as_text;
2192 consecutive_similar_traces = 0;
2194 else if (consecutive_similar_traces == 0)
2196 fmt::println(os,
" ...");
2197 ++consecutive_similar_traces;
2210 if (displayed_traces > max_consecutive_traces)
2212 fmt::println(os,
" ...");
2217 if (context.
pp == 0)
2220 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2221 fmt::println(os,
"[{:4}] In global scope{}", fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), loc_as_text);
2225 fmt::println(os,
"\nCurrent scope variables values:");
2226 for (std::size_t i = 0, size = old_scope.
size(); i < size; ++i)
2231 fmt::styled(
m_state.
m_symbols[old_scope.
atPos(i).first], colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2232 old_scope.
atPos(i).second.toString(*
this));
2238 "At IP: {}, PP: {}, SP: {}",
2240 fmt::styled(saved_ip / 4, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2241 fmt::styled(saved_pp, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()),
2242 fmt::styled(saved_sp, colorize ? fmt::fg(fmt::color::yellow) : fmt::text_style()));