409#if ARK_USE_COMPUTED_GOTOS
410# define TARGET(op) TARGET_##op:
411# define DISPATCH_GOTO() \
412 _Pragma("GCC diagnostic push") \
413 _Pragma("GCC diagnostic ignored \"-Wpedantic\"") goto* opcode_targets[inst];
414 _Pragma(
"GCC diagnostic pop")
415# define GOTO_HALT() goto dispatch_end
417# define TARGET(op) case op:
418# define DISPATCH_GOTO() goto dispatch_opcode
419# define GOTO_HALT() break
425 inst = m_state.inst(context.pp, context.ip); \
426 padding = m_state.inst(context.pp, context.ip + 1); \
427 arg = static_cast<uint16_t>((m_state.inst(context.pp, context.ip + 2) << 8) + \
428 m_state.inst(context.pp, context.ip + 3)); \
430 context.inst_exec_counter = (context.inst_exec_counter + 1) % VMOverflowBufferSize; \
431 if (context.inst_exec_counter < 2 && context.sp >= VMStackSize) \
433 if (context.pp != 0) \
434 throw Error("Stack overflow. You could consider rewriting your function to make use of tail-call optimization."); \
436 throw Error("Stack overflow. Are you trying to call a function with too many arguments?"); \
442#define UNPACK_ARGS() \
445 secondary_arg = static_cast<uint16_t>((padding << 4) | (arg & 0xf000) >> 12); \
446 primary_arg = arg & 0x0fff; \
449#if ARK_USE_COMPUTED_GOTOS
450# pragma GCC diagnostic push
451# pragma GCC diagnostic ignored "-Wpedantic"
452 constexpr std::array opcode_targets = {
456 &&TARGET_LOAD_FAST_BY_INDEX,
457 &&TARGET_LOAD_SYMBOL,
459 &&TARGET_POP_JUMP_IF_TRUE,
463 &&TARGET_POP_JUMP_IF_FALSE,
467 &&TARGET_PUSH_RETURN_ADDRESS,
469 &&TARGET_TAIL_CALL_SELF,
471 &&TARGET_RENAME_NEXT_CAPTURE,
474 &&TARGET_MAKE_CLOSURE,
476 &&TARGET_GET_FIELD_AS_CLOSURE,
481 &&TARGET_APPEND_IN_PLACE,
482 &&TARGET_CONCAT_IN_PLACE,
484 &&TARGET_POP_LIST_IN_PLACE,
485 &&TARGET_SET_AT_INDEX,
486 &&TARGET_SET_AT_2_INDEX,
488 &&TARGET_SHORTCIRCUIT_AND,
489 &&TARGET_SHORTCIRCUIT_OR,
490 &&TARGET_CREATE_SCOPE,
491 &&TARGET_RESET_SCOPE_JUMP,
518 &&TARGET_LOAD_CONST_LOAD_CONST,
519 &&TARGET_LOAD_CONST_STORE,
520 &&TARGET_LOAD_CONST_SET_VAL,
522 &&TARGET_STORE_FROM_INDEX,
523 &&TARGET_SET_VAL_FROM,
524 &&TARGET_SET_VAL_FROM_INDEX,
526 &&TARGET_INCREMENT_BY_INDEX,
527 &&TARGET_INCREMENT_STORE,
529 &&TARGET_DECREMENT_BY_INDEX,
530 &&TARGET_DECREMENT_STORE,
532 &&TARGET_STORE_TAIL_BY_INDEX,
534 &&TARGET_STORE_HEAD_BY_INDEX,
536 &&TARGET_SET_VAL_TAIL,
537 &&TARGET_SET_VAL_TAIL_BY_INDEX,
538 &&TARGET_SET_VAL_HEAD,
539 &&TARGET_SET_VAL_HEAD_BY_INDEX,
540 &&TARGET_CALL_BUILTIN,
541 &&TARGET_CALL_BUILTIN_WITHOUT_RETURN_ADDRESS,
542 &&TARGET_LT_CONST_JUMP_IF_FALSE,
543 &&TARGET_LT_CONST_JUMP_IF_TRUE,
544 &&TARGET_LT_SYM_JUMP_IF_FALSE,
545 &&TARGET_GT_CONST_JUMP_IF_TRUE,
546 &&TARGET_GT_CONST_JUMP_IF_FALSE,
547 &&TARGET_GT_SYM_JUMP_IF_FALSE,
548 &&TARGET_EQ_CONST_JUMP_IF_TRUE,
549 &&TARGET_EQ_SYM_INDEX_JUMP_IF_TRUE,
550 &&TARGET_NEQ_CONST_JUMP_IF_TRUE,
551 &&TARGET_NEQ_SYM_JUMP_IF_FALSE,
552 &&TARGET_CALL_SYMBOL,
553 &&TARGET_CALL_SYMBOL_BY_INDEX,
554 &&TARGET_CALL_CURRENT_PAGE,
555 &&TARGET_GET_FIELD_FROM_SYMBOL,
556 &&TARGET_GET_FIELD_FROM_SYMBOL_INDEX,
558 &&TARGET_AT_SYM_INDEX_SYM_INDEX,
559 &&TARGET_AT_SYM_INDEX_CONST,
560 &&TARGET_CHECK_TYPE_OF,
561 &&TARGET_CHECK_TYPE_OF_BY_INDEX,
562 &&TARGET_APPEND_IN_PLACE_SYM,
563 &&TARGET_APPEND_IN_PLACE_SYM_INDEX,
565 &&TARGET_LT_LEN_SYM_JUMP_IF_FALSE,
567 &&TARGET_MUL_BY_INDEX,
568 &&TARGET_MUL_SET_VAL,
572 static_assert(opcode_targets.size() ==
static_cast<std::size_t
>(Instruction::InstructionsCount) &&
"Some instructions are not implemented in the VM");
573# pragma GCC diagnostic pop
581 uint16_t primary_arg = 0;
582 uint16_t secondary_arg = 0;
589#if !ARK_USE_COMPUTED_GOTOS
594#pragma region "Instructions"
643 store(arg, tmp, context);
683 push(std::move(ts), context);
693 push(std::move(ts), context);
705 "Unhandled case when returning from function call. TS=({}){}, TS1=({}){}",
711 if (context.
fc <= untilFrameCount)
744 context.
locals.back().reset();
784 var->usertypeRef().del();
823 push(std::move(l), context);
834 std::vector<Value> args = { *list };
835 for (uint16_t i = 0; i < arg; ++i)
843 const auto size =
static_cast<uint16_t
>(list->
constList().size());
846 obj.
list().reserve(size + arg);
848 for (uint16_t i = 0; i < arg; ++i)
850 push(std::move(obj), context);
861 for (uint16_t i = 0; i < arg; ++i)
871 std::ranges::copy(next->
list(), std::back_inserter(obj.list()));
873 push(std::move(obj), context);
889 for (uint16_t i = 0; i < arg; ++i)
899 std::ranges::copy(next->
list(), std::back_inserter(list->
list()));
916 long idx =
static_cast<long>(number.
number());
917 idx = idx < 0 ? static_cast<long>(list.
list().size()) + idx : idx;
918 if (std::cmp_greater_equal(idx, list.
list().size()) || idx < 0)
921 fmt::format(
"pop index ({}) out of range (list size: {})", idx, list.
list().size()));
923 list.
list().erase(list.
list().begin() + idx);
941 long idx =
static_cast<long>(number.
number());
942 idx = idx < 0 ? static_cast<long>(list->
list().size()) + idx : idx;
943 if (std::cmp_greater_equal(idx, list->
list().size()) || idx < 0)
946 fmt::format(
"pop! index ({}) out of range (list size: {})", idx, list->
list().size()));
952 number = list->
list()[
static_cast<std::size_t
>(idx)];
953 list->
list().erase(list->
list().begin() + idx);
955 push(number, context);
978 { *list, number, new_value });
981 long idx =
static_cast<long>(number.
number());
982 idx = idx < 0 ? static_cast<long>(size) + idx : idx;
983 if (std::cmp_greater_equal(idx, size) || idx < 0)
986 fmt::format(
"@= index ({}) out of range (indexable size: {})", idx, size));
990 list->
list()[
static_cast<std::size_t
>(idx)] = new_value;
992 push(new_value, context);
996 list->
stringRef()[
static_cast<std::size_t
>(idx)] = new_value.
string()[0];
1020 { *list, x, y, new_value });
1022 long idx_y =
static_cast<long>(x.
number());
1023 idx_y = idx_y < 0 ? static_cast<long>(list->
list().size()) + idx_y : idx_y;
1024 if (std::cmp_greater_equal(idx_y, list->
list().size()) || idx_y < 0)
1027 fmt::format(
"@@= index (y: {}) out of range (list size: {})", idx_y, list->
list().size()));
1029 if (!list->
list()[
static_cast<std::size_t
>(idx_y)].isIndexable() ||
1043 { *list, x, y, new_value });
1045 const bool is_list = list->
list()[
static_cast<std::size_t
>(idx_y)].valueType() ==
ValueType::List;
1046 const std::size_t size =
1048 ? list->
list()[
static_cast<std::size_t
>(idx_y)].list().size()
1049 : list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef().size();
1051 long idx_x =
static_cast<long>(y.
number());
1052 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
1053 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
1056 fmt::format(
"@@= index (x: {}) out of range (inner indexable size: {})", idx_x, size));
1060 list->
list()[
static_cast<std::size_t
>(idx_y)].list()[
static_cast<std::size_t
>(idx_x)] = new_value;
1062 push(new_value, context);
1066 list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef()[
static_cast<std::size_t
>(idx_x)] = new_value.
string()[0];
1106 context.
locals.back().reset();
1113 context.
locals.pop_back();
1137 { func, args_list });
1140 push(func, context);
1144 call(context,
static_cast<uint16_t
>(args_list.
constList().size()));
1151#pragma region "Operators"
1156 bool breakpoint_active =
true;
1164 m_debugger->resetContextToSavedState(context);
1227 throwVMError(ErrorKind::DivisionByZero, fmt::format(
"Can not compute expression (/ {} {})", a->toString(*
this), b->
toString(*
this)));
1412 { *closure, *field });
1421 auto id =
static_cast<std::uint16_t
>(std::distance(
m_state.
m_symbols.begin(), it));
1436#pragma region "Super Instructions"
1504 { *var,
Value(secondary_arg) });
1525 { *var,
Value(secondary_arg) });
1543 setVal(primary_arg, &val, context);
1549 { *var,
Value(secondary_arg) });
1570 { *var,
Value(secondary_arg) });
1591 { *var,
Value(secondary_arg) });
1609 setVal(primary_arg, &val, context);
1615 { *var,
Value(secondary_arg) });
1626 store(secondary_arg, &tail, context);
1637 store(secondary_arg, &tail, context);
1648 store(secondary_arg, &head, context);
1659 store(secondary_arg, &head, context);
1669 store(secondary_arg, &l, context);
1680 setVal(secondary_arg, &tail, context);
1691 setVal(secondary_arg, &tail, context);
1702 setVal(secondary_arg, &head, context);
1713 setVal(secondary_arg, &head, context);
1755 jump(secondary_arg, context);
1764 jump(secondary_arg, context);
1772 if (!(*sym < *
loadSymbol(primary_arg, context)))
1773 jump(secondary_arg, context);
1783 jump(secondary_arg, context);
1793 jump(secondary_arg, context);
1803 jump(secondary_arg, context);
1812 jump(secondary_arg, context);
1821 jump(secondary_arg, context);
1830 jump(secondary_arg, context);
1838 if (*sym == *
loadSymbol(primary_arg, context))
1839 jump(secondary_arg, context);
1865 call(context, secondary_arg,
nullptr,
static_cast<PageAddr_t>(context.
pp));
1957 len =
Value(
static_cast<int>(a->
string().size()));
1964 store(secondary_arg, &len, context);
1979 size =
Value(
static_cast<int>(sym->
string().size()));
1988 jump(secondary_arg, context);
1998 const int other =
static_cast<int>(secondary_arg) - 2048;
2010 { *var,
Value(other) });
2020 const int other =
static_cast<int>(secondary_arg) - 2048;
2032 { *var,
Value(other) });
2042 const int other =
static_cast<int>(secondary_arg) - 2048;
2051 setVal(primary_arg, &val, context);
2057 { *var,
Value(other) });
2064 const auto op1 =
static_cast<Instruction>(padding),
2065 op2 =
static_cast<Instruction>((arg & 0xff00) >> 8),
2067 const std::size_t arg_count = (op1 !=
NOP) + (op2 !=
NOP) + (op3 !=
NOP);
2084 { *b,
Value(temp) });
2089 else if (arg_count == 3)
2096 { *a,
Value(temp) });
2104 "FUSED_MATH got {} arguments, expected 2 or 3. Arguments: {:x}{:x}{:x}. There is a bug in the codegen!",
2105 arg_count,
static_cast<uint8_t
>(op1),
static_cast<uint8_t
>(op2),
static_cast<uint8_t
>(op3)));
2110#if ARK_USE_COMPUTED_GOTOS
2118 catch (
const Error& e)
2120 if (fail_with_exception)
2122 std::stringstream stream;
2131 catch (
const std::exception& e)
2133 if (fail_with_exception)
2135 std::stringstream stream;
2144 if (fail_with_exception)
2147#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2150 fmt::println(
"Unknown error");
2312 constexpr std::size_t max_consecutive_traces = 7;
2322 .filename = filename,
2323 .start =
FilePos { .line = maybe_location->line, .column = 0 },
2324 .end = std::nullopt,
2325 .maybe_content = std::nullopt },
2329 fmt::println(os,
"");
2337 std::string previous_trace;
2338 std::size_t displayed_traces = 0;
2339 std::size_t consecutive_similar_traces = 0;
2341 while (context.
fc != 0 && context.
pp != 0 && context.
sp > 0)
2344 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2351 if (func_name + loc_as_text != previous_trace)
2355 "[{:4}] In function `{}'{}",
2356 fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2357 fmt::styled(func_name, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()),
2359 previous_trace = func_name + loc_as_text;
2361 consecutive_similar_traces = 0;
2363 else if (consecutive_similar_traces == 0)
2365 fmt::println(os,
" ...");
2366 ++consecutive_similar_traces;
2379 if (displayed_traces > max_consecutive_traces)
2381 fmt::println(os,
" ...");
2386 if (context.
pp == 0)
2389 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2390 fmt::println(os,
"[{:4}] In global scope{}", fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), loc_as_text);
2394 fmt::println(os,
"\nCurrent scope variables values:");
2395 for (std::size_t i = 0, size = old_scope.
size(); i < size; ++i)
2400 fmt::styled(
m_state.
m_symbols[old_scope.
atPos(i).first], colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2401 old_scope.
atPos(i).second.toString(*
this));