From b8923a336f846bea85f3361942924db6d1376308 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:28:52 +0100 Subject: [PATCH 1/3] Fix VM instruction docs --- include/scratchcpp/virtualmachine.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/scratchcpp/virtualmachine.h b/include/scratchcpp/virtualmachine.h index c00a0996..3a66048a 100644 --- a/include/scratchcpp/virtualmachine.h +++ b/include/scratchcpp/virtualmachine.h @@ -71,16 +71,16 @@ enum Opcode OP_LIST_LENGTH, /*!< Stores the length of the list with the index in the argument, in the last register. */ OP_LIST_CONTAINS, /*!< Stores true in the last register if the list with the index in the argument contains the value from the last register. */ OP_STR_CONCAT, /*!< Concatenates the strings stored in the last 2 registers and stores the result in the last register, deleting the input registers. */ - OP_STR_AT, /*! Stores the character at index in the last register of the string in the second last register, in the last register. */ - OP_STR_LENGTH, /*! Stores the length of the string in the last register, in the last register. */ - OP_STR_CONTAINS, /*! Stores true in the last register if the string stored in the second last register contains the substring in the last register. */ + OP_STR_AT, /*!< Stores the character at index in the last register of the string in the second last register, in the last register. */ + OP_STR_LENGTH, /*!< Stores the length of the string in the last register, in the last register. */ + OP_STR_CONTAINS, /*!< Stores true in the last register if the string stored in the second last register contains the substring in the last register. */ OP_EXEC, /*!< Calls the function with the index in the argument. */ OP_INIT_PROCEDURE, /*!< Initializes the list of procedure (custom block) arguments. */ OP_CALL_PROCEDURE, /*! Calls the procedure (custom block) with the index in the argument. */ OP_ADD_ARG, /*!< Adds a procedure (custom block) argument with the value from the last register. */ OP_READ_ARG, /*!< Reads the procedure (custom block) argument with the index in the argument and stores the value in the last register. */ OP_BREAK_FRAME, /*!< Breaks current frame at the end of the loop. */ - OP_WARP /*! Runs the script without screen refresh. */ + OP_WARP /*!< Runs the script without screen refresh. */ }; } From 0471a59bacca2ea466d9984712ee2d8a4851428a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:29:44 +0100 Subject: [PATCH 2/3] Fix code style in VirtualMachinePrivate --- src/engine/virtualmachine_p.cpp | 779 ++++++++++++++++---------------- 1 file changed, 401 insertions(+), 378 deletions(-) diff --git a/src/engine/virtualmachine_p.cpp b/src/engine/virtualmachine_p.cpp index 80e74164..f3af91d8 100644 --- a/src/engine/virtualmachine_p.cpp +++ b/src/engine/virtualmachine_p.cpp @@ -215,11 +215,13 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset) #ifdef ENABLE_COMPUTED_GOTO DISPATCH(); #else - while (true) switch (*++pos) { + while (true) + switch (*++pos) { #endif -OP(HALT): - if (regCount > 0) { + OP(HALT) : + if (regCount > 0) + { std::cout << "warning: VM: " << regCount << " registers were leaked by the script; this is most likely a bug in the VM or in the compiler" << std::endl; } if (callTree.empty()) { @@ -239,22 +241,41 @@ OP(HALT): DISPATCH(); } -OP(CONST): - ADD_RET_VALUE(GET_NEXT_ARG()); + OP(CONST) : + ADD_RET_VALUE(GET_NEXT_ARG()); DISPATCH(); -OP(NULL): - ADD_RET_VALUE(Value()); + OP(NULL) : + ADD_RET_VALUE(Value()); DISPATCH(); -OP(CHECKPOINT): - checkpoint = pos - 1; + OP(CHECKPOINT) : + checkpoint = pos - 1; DISPATCH(); -OP(IF): { - if (!READ_LAST_REG()->toBool()) { + OP(IF) : + { + if (!READ_LAST_REG()->toBool()) { + unsigned int ifCounter = 1; + while (!((*pos == OP_ELSE && ifCounter == 1) || (*pos == OP_ENDIF && ifCounter == 0))) { + pos += instruction_arg_count[*pos++]; + + if ((*pos == OP_IF) || (*pos == OP_FOREVER_LOOP) || (*pos == OP_REPEAT_LOOP) || (*pos == OP_UNTIL_LOOP)) + ifCounter++; + else if ((*pos == OP_ENDIF) || (*pos == OP_LOOP_END)) { + assert(ifCounter > 0); + ifCounter--; + } + } + } + FREE_REGS(1); + DISPATCH(); + } + + OP(ELSE) : + { unsigned int ifCounter = 1; - while (!((*pos == OP_ELSE && ifCounter == 1) || (*pos == OP_ENDIF && ifCounter == 0))) { + while (!(*pos == OP_ENDIF && ifCounter == 0)) { pos += instruction_arg_count[*pos++]; if ((*pos == OP_IF) || (*pos == OP_FOREVER_LOOP) || (*pos == OP_REPEAT_LOOP) || (*pos == OP_UNTIL_LOOP)) @@ -263,41 +284,24 @@ OP(IF): { assert(ifCounter > 0); ifCounter--; } - } - } - FREE_REGS(1); - DISPATCH(); -} - -OP(ELSE): { - unsigned int ifCounter = 1; - while (!(*pos == OP_ENDIF && ifCounter == 0)) { - pos += instruction_arg_count[*pos++]; - if ((*pos == OP_IF) || (*pos == OP_FOREVER_LOOP) || (*pos == OP_REPEAT_LOOP) || (*pos == OP_UNTIL_LOOP)) - ifCounter++; - else if ((*pos == OP_ENDIF) || (*pos == OP_LOOP_END)) { - assert(ifCounter > 0); - ifCounter--; + assert(!(*pos == OP_ELSE && ifCounter == 1)); } - - assert(!(*pos == OP_ELSE && ifCounter == 1)); } -} -OP(ENDIF): - DISPATCH(); + OP(ENDIF) : + DISPATCH(); -OP(FOREVER_LOOP): - Loop l; + OP(FOREVER_LOOP) : + Loop l; l.isRepeatLoop = true; l.start = pos; l.index = -1; loops.push_back(l); DISPATCH(); -OP(REPEAT_LOOP): - loopCount = std::round(READ_LAST_REG()->toDouble()); + OP(REPEAT_LOOP) : + loopCount = std::round(READ_LAST_REG()->toDouble()); FREE_REGS(1); if (loopCount <= 0) { loopEnd = pos; @@ -323,24 +327,26 @@ OP(REPEAT_LOOP): } DISPATCH(); -OP(REPEAT_LOOP_INDEX): { - assert(!loops.empty()); - Loop &l = loops.back(); - assert(l.isRepeatLoop); - ADD_RET_VALUE(static_cast(l.index)); - DISPATCH(); -} + OP(REPEAT_LOOP_INDEX) : + { + assert(!loops.empty()); + Loop &l = loops.back(); + assert(l.isRepeatLoop); + ADD_RET_VALUE(static_cast(l.index)); + DISPATCH(); + } -OP(REPEAT_LOOP_INDEX1): { - assert(!loops.empty()); - Loop &l = loops.back(); - assert(l.isRepeatLoop); - ADD_RET_VALUE(static_cast(l.index + 1)); - DISPATCH(); -} + OP(REPEAT_LOOP_INDEX1) : + { + assert(!loops.empty()); + Loop &l = loops.back(); + assert(l.isRepeatLoop); + ADD_RET_VALUE(static_cast(l.index + 1)); + DISPATCH(); + } -OP(UNTIL_LOOP): - loopStart = run(pos, false); + OP(UNTIL_LOOP) : + loopStart = run(pos, false); if (!READ_LAST_REG()->toBool()) { Loop l; l.isRepeatLoop = false; @@ -364,443 +370,460 @@ OP(UNTIL_LOOP): FREE_REGS(1); DISPATCH(); -OP(BEGIN_UNTIL_LOOP): - return pos; + OP(BEGIN_UNTIL_LOOP) : + return pos; -OP(LOOP_END): { - assert(!loops.empty()); - Loop &l = loops.back(); - if (l.isRepeatLoop) { - if ((l.index == -1) || (++l.index < l.max)) - pos = l.start; - else - loops.pop_back(); - if (!noBreak && !warp) - return pos; - DISPATCH(); - } else { - if (!noBreak && !warp) - return pos - 1; - loopStart = run(l.start, false); - if (!READ_LAST_REG()->toBool()) - pos = loopStart; - else - loops.pop_back(); - FREE_REGS(1); - DISPATCH(); + OP(LOOP_END) : + { + assert(!loops.empty()); + Loop &l = loops.back(); + if (l.isRepeatLoop) { + if ((l.index == -1) || (++l.index < l.max)) + pos = l.start; + else + loops.pop_back(); + if (!noBreak && !warp) + return pos; + DISPATCH(); + } else { + if (!noBreak && !warp) + return pos - 1; + loopStart = run(l.start, false); + if (!READ_LAST_REG()->toBool()) + pos = loopStart; + else + loops.pop_back(); + FREE_REGS(1); + DISPATCH(); + } } -} -OP(PRINT): - std::cout << READ_LAST_REG()->toString() << std::endl; + OP(PRINT) : + std::cout << READ_LAST_REG()->toString() << std::endl; FREE_REGS(1); DISPATCH(); -OP(ADD): - READ_REG(0, 2)->add(*READ_REG(1, 2)); + OP(ADD) : + READ_REG(0, 2)->add(*READ_REG(1, 2)); FREE_REGS(1); DISPATCH(); -OP(SUBTRACT): - READ_REG(0, 2)->subtract(*READ_REG(1, 2)); + OP(SUBTRACT) : + READ_REG(0, 2)->subtract(*READ_REG(1, 2)); FREE_REGS(1); DISPATCH(); -OP(MULTIPLY): - READ_REG(0, 2)->multiply(*READ_REG(1, 2)); + OP(MULTIPLY) : + READ_REG(0, 2)->multiply(*READ_REG(1, 2)); FREE_REGS(1); DISPATCH(); -OP(DIVIDE): - READ_REG(0, 2)->divide(*READ_REG(1, 2)); + OP(DIVIDE) : + READ_REG(0, 2)->divide(*READ_REG(1, 2)); FREE_REGS(1); DISPATCH(); -OP(MOD): - READ_REG(0, 2)->mod(*READ_REG(1, 2)); + OP(MOD) : + READ_REG(0, 2)->mod(*READ_REG(1, 2)); FREE_REGS(1); DISPATCH(); -OP(RANDOM): - if ((READ_REG(0, 2)->type() == Value::Type::Integer) && (READ_REG(1, 2)->type() == Value::Type::Integer)) - REPLACE_RET_VALUE(rng->randint(READ_REG(0, 2)->toInt(), READ_REG(1, 2)->toInt()), 2); - else - REPLACE_RET_VALUE(rng->randintDouble(READ_REG(0, 2)->toDouble(), READ_REG(1, 2)->toDouble()), 2); + OP(RANDOM) : + if ((READ_REG(0, 2)->type() == Value::Type::Integer) && (READ_REG(1, 2)->type() == Value::Type::Integer)) REPLACE_RET_VALUE(rng->randint(READ_REG(0, 2)->toInt(), READ_REG(1, 2)->toInt()), 2); + else REPLACE_RET_VALUE(rng->randintDouble(READ_REG(0, 2)->toDouble(), READ_REG(1, 2)->toDouble()), 2); FREE_REGS(1); DISPATCH(); -OP(ROUND): { - const Value *v = READ_REG(0, 1); - if (!v->isInfinity() && !v->isNegativeInfinity()) { - if (v->toDouble() < 0) { - REPLACE_RET_VALUE(static_cast(std::floor(v->toDouble() + 0.5)), 1); - } else - REPLACE_RET_VALUE(static_cast(v->toDouble() + 0.5), 1); + OP(ROUND) : + { + const Value *v = READ_REG(0, 1); + if (!v->isInfinity() && !v->isNegativeInfinity()) { + if (v->toDouble() < 0) { + REPLACE_RET_VALUE(static_cast(std::floor(v->toDouble() + 0.5)), 1); + } else + REPLACE_RET_VALUE(static_cast(v->toDouble() + 0.5), 1); + } + DISPATCH(); } - DISPATCH(); -} - -OP(ABS): { - const Value *v = READ_REG(0, 1); - if (v->isNegativeInfinity()) - REPLACE_RET_VALUE(Value(Value::SpecialValue::Infinity), 1); - else if (!v->isInfinity()) - REPLACE_RET_VALUE(std::abs(v->toDouble()), 1); - DISPATCH(); -} - -OP(FLOOR): { - const Value *v = READ_REG(0, 1); - if (!v->isInfinity() && !v->isNegativeInfinity()) - REPLACE_RET_VALUE(std::floor(v->toDouble()), 1); - DISPATCH(); -} -OP(CEIL): { - const Value *v = READ_REG(0, 1); - if (!v->isInfinity() && !v->isNegativeInfinity()) - REPLACE_RET_VALUE(std::ceil(v->toDouble()), 1); - DISPATCH(); -} + OP(ABS) : + { + const Value *v = READ_REG(0, 1); + if (v->isNegativeInfinity()) + REPLACE_RET_VALUE(Value(Value::SpecialValue::Infinity), 1); + else if (!v->isInfinity()) + REPLACE_RET_VALUE(std::abs(v->toDouble()), 1); + DISPATCH(); + } -OP(SQRT): { - const Value &v = *READ_REG(0, 1); - if (v < 0) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); - else if (!v.isInfinity()) - REPLACE_RET_VALUE(std::sqrt(v.toDouble()), 1); - DISPATCH(); -} + OP(FLOOR) : + { + const Value *v = READ_REG(0, 1); + if (!v->isInfinity() && !v->isNegativeInfinity()) + REPLACE_RET_VALUE(std::floor(v->toDouble()), 1); + DISPATCH(); + } -OP(SIN): { - const Value *v = READ_REG(0, 1); - if (v->isInfinity() || v->isNegativeInfinity()) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); - else - REPLACE_RET_VALUE(std::sin(v->toDouble() * pi / 180), 1); - DISPATCH(); -} + OP(CEIL) : + { + const Value *v = READ_REG(0, 1); + if (!v->isInfinity() && !v->isNegativeInfinity()) + REPLACE_RET_VALUE(std::ceil(v->toDouble()), 1); + DISPATCH(); + } -OP(COS): { - const Value *v = READ_REG(0, 1); - if (v->isInfinity() || v->isNegativeInfinity()) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); - else - REPLACE_RET_VALUE(std::cos(v->toDouble() * pi / 180), 1); - DISPATCH(); -} + OP(SQRT) : + { + const Value &v = *READ_REG(0, 1); + if (v < 0) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); + else if (!v.isInfinity()) + REPLACE_RET_VALUE(std::sqrt(v.toDouble()), 1); + DISPATCH(); + } -OP(TAN): { - const Value *v = READ_REG(0, 1); - if (v->isInfinity() || v->isNegativeInfinity()) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); - else { - long mod; - if (v->toLong() < 0) - mod = (v->toLong() + 360) % 360; + OP(SIN) : + { + const Value *v = READ_REG(0, 1); + if (v->isInfinity() || v->isNegativeInfinity()) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); else - mod = v->toLong() % 360; - if (mod == 90) - REPLACE_RET_VALUE(Value(Value::SpecialValue::Infinity), 1); - else if (mod == 270) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NegativeInfinity), 1); + REPLACE_RET_VALUE(std::sin(v->toDouble() * pi / 180), 1); + DISPATCH(); + } + + OP(COS) : + { + const Value *v = READ_REG(0, 1); + if (v->isInfinity() || v->isNegativeInfinity()) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); else - REPLACE_RET_VALUE(std::tan(v->toDouble() * pi / 180), 1); + REPLACE_RET_VALUE(std::cos(v->toDouble() * pi / 180), 1); + DISPATCH(); } - DISPATCH(); -} -OP(ASIN): { - const Value &v = *READ_REG(0, 1); - if (v < -1 || v > 1) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); - else - REPLACE_RET_VALUE(std::asin(v.toDouble()) * 180 / pi, 1); - DISPATCH(); -} + OP(TAN) : + { + const Value *v = READ_REG(0, 1); + if (v->isInfinity() || v->isNegativeInfinity()) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); + else { + long mod; + if (v->toLong() < 0) + mod = (v->toLong() + 360) % 360; + else + mod = v->toLong() % 360; + if (mod == 90) + REPLACE_RET_VALUE(Value(Value::SpecialValue::Infinity), 1); + else if (mod == 270) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NegativeInfinity), 1); + else + REPLACE_RET_VALUE(std::tan(v->toDouble() * pi / 180), 1); + } + DISPATCH(); + } -OP(ACOS): { - const Value &v = *READ_REG(0, 1); - if (v < -1 || v > 1) - REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); - else - REPLACE_RET_VALUE(std::acos(v.toDouble()) * 180 / pi, 1); - DISPATCH(); -} + OP(ASIN) : + { + const Value &v = *READ_REG(0, 1); + if (v < -1 || v > 1) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); + else + REPLACE_RET_VALUE(std::asin(v.toDouble()) * 180 / pi, 1); + DISPATCH(); + } -OP(ATAN): { - const Value &v = *READ_REG(0, 1); - if (v.isInfinity()) - REPLACE_RET_VALUE(90, 1); - else if (v.isNegativeInfinity()) - REPLACE_RET_VALUE(-90, 1); - else - REPLACE_RET_VALUE(std::atan(v.toDouble()) * 180 / pi, 1); - DISPATCH(); -} + OP(ACOS) : + { + const Value &v = *READ_REG(0, 1); + if (v < -1 || v > 1) + REPLACE_RET_VALUE(Value(Value::SpecialValue::NaN), 1); + else + REPLACE_RET_VALUE(std::acos(v.toDouble()) * 180 / pi, 1); + DISPATCH(); + } + + OP(ATAN) : + { + const Value &v = *READ_REG(0, 1); + if (v.isInfinity()) + REPLACE_RET_VALUE(90, 1); + else if (v.isNegativeInfinity()) + REPLACE_RET_VALUE(-90, 1); + else + REPLACE_RET_VALUE(std::atan(v.toDouble()) * 180 / pi, 1); + DISPATCH(); + } -OP(GREATER_THAN): - REPLACE_RET_VALUE(*READ_REG(0, 2) > *READ_REG(1, 2), 2); + OP(GREATER_THAN) : + REPLACE_RET_VALUE(*READ_REG(0, 2) > *READ_REG(1, 2), 2); FREE_REGS(1); DISPATCH(); -OP(LESS_THAN): - REPLACE_RET_VALUE(*READ_REG(0, 2) < *READ_REG(1, 2), 2); + OP(LESS_THAN) : + REPLACE_RET_VALUE(*READ_REG(0, 2) < *READ_REG(1, 2), 2); FREE_REGS(1); DISPATCH(); -OP(EQUALS): - REPLACE_RET_VALUE(*READ_REG(0, 2) == *READ_REG(1, 2), 2); + OP(EQUALS) : + REPLACE_RET_VALUE(*READ_REG(0, 2) == *READ_REG(1, 2), 2); FREE_REGS(1); DISPATCH(); -OP(AND): - REPLACE_RET_VALUE(READ_REG(0, 2)->toBool() && READ_REG(1, 2)->toBool(), 2); + OP(AND) : + REPLACE_RET_VALUE(READ_REG(0, 2)->toBool() && READ_REG(1, 2)->toBool(), 2); FREE_REGS(1); DISPATCH(); -OP(OR): - REPLACE_RET_VALUE(READ_REG(0, 2)->toBool() || READ_REG(1, 2)->toBool(), 2); + OP(OR) : + REPLACE_RET_VALUE(READ_REG(0, 2)->toBool() || READ_REG(1, 2)->toBool(), 2); FREE_REGS(1); DISPATCH(); -OP(NOT): - REPLACE_RET_VALUE(!READ_LAST_REG()->toBool(), 1); + OP(NOT) : + REPLACE_RET_VALUE(!READ_LAST_REG()->toBool(), 1); DISPATCH(); -OP(SET_VAR): - *variables[*++pos] = *READ_LAST_REG(); + OP(SET_VAR) : + *variables[*++pos] = *READ_LAST_REG(); FREE_REGS(1); DISPATCH(); -OP(READ_VAR): - ADD_RET_VALUE(*variables[*++pos]); + OP(READ_VAR) : + ADD_RET_VALUE(*variables[*++pos]); DISPATCH(); -OP(CHANGE_VAR): - variables[*++pos]->add(*READ_LAST_REG()); + OP(CHANGE_VAR) : + variables[*++pos]->add(*READ_LAST_REG()); FREE_REGS(1); DISPATCH(); -OP(READ_LIST): - ADD_RET_VALUE(lists[*++pos]->toString()); + OP(READ_LIST) : + ADD_RET_VALUE(lists[*++pos]->toString()); DISPATCH(); -OP(LIST_APPEND): - lists[*++pos]->push_back(*READ_LAST_REG()); + OP(LIST_APPEND) : + lists[*++pos]->push_back(*READ_LAST_REG()); FREE_REGS(1); DISPATCH(); -OP(LIST_DEL): { - const Value *indexValue = READ_LAST_REG(); - size_t index; - List *list = lists[*++pos]; - if (indexValue->isString()) { - const std::string &str = indexValue->toString(); - if (str == "last") { - index = list->size(); - } else if (str == "all") { - list->clear(); - index = 0; - } else if (str == "random") { - size_t size = list->size(); - index = size == 0 ? 0 : rng->randint(1, size); - } else - index = 0; - } else { - index = indexValue->toLong(); - FIX_LIST_INDEX(index, list->size()); + OP(LIST_DEL) : + { + const Value *indexValue = READ_LAST_REG(); + size_t index; + List *list = lists[*++pos]; + if (indexValue->isString()) { + const std::string &str = indexValue->toString(); + if (str == "last") { + index = list->size(); + } else if (str == "all") { + list->clear(); + index = 0; + } else if (str == "random") { + size_t size = list->size(); + index = size == 0 ? 0 : rng->randint(1, size); + } else + index = 0; + } else { + index = indexValue->toLong(); + FIX_LIST_INDEX(index, list->size()); + } + if (index != 0) + list->removeAt(index - 1); + FREE_REGS(1); + DISPATCH(); } - if (index != 0) - list->removeAt(index - 1); - FREE_REGS(1); - DISPATCH(); -} -OP(LIST_DEL_ALL): - lists[*++pos]->clear(); - DISPATCH(); - -OP(LIST_INSERT): { - const Value *indexValue = READ_REG(1, 2); - size_t index; - List *list = lists[*++pos]; - if (indexValue->isString()) { - const std::string &str = indexValue->toString(); - if (str == "last") { - list->push_back(*READ_REG(0, 2)); - index = 0; - } else if (str == "random") { - size_t size = list->size(); - index = size == 0 ? 1 : rng->randint(1, size); - } else - index = 0; - } else { - index = indexValue->toLong(); - FIX_LIST_INDEX(index, list->size()); - } - if ((index != 0) || list->empty()) { - if (list->empty()) - list->push_back(*READ_REG(0, 2)); - else - list->insert(index - 1, *READ_REG(0, 2)); - } - FREE_REGS(2); + OP(LIST_DEL_ALL) : + lists[*++pos]->clear(); DISPATCH(); -} -OP(LIST_REPLACE): { - const Value *indexValue = READ_REG(0, 2); - size_t index; - List *list = lists[*++pos]; - if (indexValue->isString()) { - std::string str = indexValue->toString(); - if (str == "last") - index = list->size(); - else if (str == "random") { - size_t size = list->size(); - index = size == 0 ? 0 : rng->randint(1, size); - } else - index = 0; - } else { - index = indexValue->toLong(); - FIX_LIST_INDEX(index, list->size()); + OP(LIST_INSERT) : + { + const Value *indexValue = READ_REG(1, 2); + size_t index; + List *list = lists[*++pos]; + if (indexValue->isString()) { + const std::string &str = indexValue->toString(); + if (str == "last") { + list->push_back(*READ_REG(0, 2)); + index = 0; + } else if (str == "random") { + size_t size = list->size(); + index = size == 0 ? 1 : rng->randint(1, size); + } else + index = 0; + } else { + index = indexValue->toLong(); + FIX_LIST_INDEX(index, list->size()); + } + if ((index != 0) || list->empty()) { + if (list->empty()) + list->push_back(*READ_REG(0, 2)); + else + list->insert(index - 1, *READ_REG(0, 2)); + } + FREE_REGS(2); + DISPATCH(); } - if (index != 0) - list->operator[](index - 1) = *READ_REG(1, 2); - FREE_REGS(2); - DISPATCH(); -} -OP(LIST_GET_ITEM): { - const Value *indexValue = READ_LAST_REG(); - size_t index; - List *list = lists[*++pos]; - if (indexValue->isString()) { - std::string str = indexValue->toString(); - if (str == "last") - index = list->size(); - else if (str == "random") { - size_t size = list->size(); - index = size == 0 ? 0 : rng->randint(1, size); - } else - index = 0; - } else { - index = indexValue->toLong(); - FIX_LIST_INDEX(index, list->size()); + OP(LIST_REPLACE) : + { + const Value *indexValue = READ_REG(0, 2); + size_t index; + List *list = lists[*++pos]; + if (indexValue->isString()) { + std::string str = indexValue->toString(); + if (str == "last") + index = list->size(); + else if (str == "random") { + size_t size = list->size(); + index = size == 0 ? 0 : rng->randint(1, size); + } else + index = 0; + } else { + index = indexValue->toLong(); + FIX_LIST_INDEX(index, list->size()); + } + if (index != 0) + list->operator[](index - 1) = *READ_REG(1, 2); + FREE_REGS(2); + DISPATCH(); } - if (index == 0) { - REPLACE_RET_VALUE("", 1); - } else { - REPLACE_RET_VALUE(list->operator[](index - 1), 1); + + OP(LIST_GET_ITEM) : + { + const Value *indexValue = READ_LAST_REG(); + size_t index; + List *list = lists[*++pos]; + if (indexValue->isString()) { + std::string str = indexValue->toString(); + if (str == "last") + index = list->size(); + else if (str == "random") { + size_t size = list->size(); + index = size == 0 ? 0 : rng->randint(1, size); + } else + index = 0; + } else { + index = indexValue->toLong(); + FIX_LIST_INDEX(index, list->size()); + } + if (index == 0) { + REPLACE_RET_VALUE("", 1); + } else { + REPLACE_RET_VALUE(list->operator[](index - 1), 1); + } + DISPATCH(); } - DISPATCH(); -} -OP(LIST_INDEX_OF): - // TODO: Add size_t support to Value and remove the static_cast - REPLACE_RET_VALUE(static_cast(lists[*++pos]->indexOf(*READ_LAST_REG()) + 1), 1); + OP(LIST_INDEX_OF) : + // TODO: Add size_t support to Value and remove the static_cast + REPLACE_RET_VALUE(static_cast(lists[*++pos]->indexOf(*READ_LAST_REG()) + 1), 1); DISPATCH(); -OP(LIST_LENGTH): - // TODO: Add size_t support to Value and remove the static_cast - ADD_RET_VALUE(static_cast(lists[*++pos]->size())); + OP(LIST_LENGTH) : + // TODO: Add size_t support to Value and remove the static_cast + ADD_RET_VALUE(static_cast(lists[*++pos]->size())); DISPATCH(); -OP(LIST_CONTAINS): - REPLACE_RET_VALUE(lists[*++pos]->contains(*READ_LAST_REG()), 1); + OP(LIST_CONTAINS) : + REPLACE_RET_VALUE(lists[*++pos]->contains(*READ_LAST_REG()), 1); DISPATCH(); -OP(STR_CONCAT): - REPLACE_RET_VALUE(READ_REG(0, 2)->toString() + READ_REG(1, 2)->toString(), 2); + OP(STR_CONCAT) : + REPLACE_RET_VALUE(READ_REG(0, 2)->toString() + READ_REG(1, 2)->toString(), 2); FREE_REGS(1); DISPATCH(); -OP(STR_AT): { - size_t index = READ_REG(1, 2)->toLong() - 1; + OP(STR_AT) : { - std::u16string str = READ_REG(0, 2)->toUtf16(); - if (index < 0 || index >= str.size()) - REPLACE_RET_VALUE("", 2); - else - REPLACE_RET_VALUE(utf8::utf16to8(std::u16string({ str[index] })), 2); - FREE_REGS(1); + size_t index = READ_REG(1, 2)->toLong() - 1; + { + std::u16string str = READ_REG(0, 2)->toUtf16(); + if (index < 0 || index >= str.size()) + REPLACE_RET_VALUE("", 2); + else + REPLACE_RET_VALUE(utf8::utf16to8(std::u16string({ str[index] })), 2); + FREE_REGS(1); + } + DISPATCH(); } - DISPATCH(); -} -OP(STR_LENGTH): - REPLACE_RET_VALUE(static_cast(READ_REG(0, 1)->toUtf16().size()), 1); + OP(STR_LENGTH) : + REPLACE_RET_VALUE(static_cast(READ_REG(0, 1)->toUtf16().size()), 1); DISPATCH(); -OP(STR_CONTAINS): - REPLACE_RET_VALUE(READ_REG(0, 2)->toUtf16().find(READ_REG(1, 2)->toUtf16()) != std::u16string::npos, 2); + OP(STR_CONTAINS) : + REPLACE_RET_VALUE(READ_REG(0, 2)->toUtf16().find(READ_REG(1, 2)->toUtf16()) != std::u16string::npos, 2); FREE_REGS(1); DISPATCH(); -OP(EXEC): { - auto ret = functions[*++pos](vm); - if (updatePos) { - pos = this->pos; - updatePos = false; - } - if (stop) { - stop = false; - if (goBack) { - goBack = false; - pos -= instruction_arg_count[OP_EXEC] + 1; - // NOTE: Going back leaks all registers for the next time the same function is called. - // This is for example used in the wait block (to call it again with the same time value). - } else - FREE_REGS(ret); - - if (!warp) // TODO: This should always return if there's a "warp timer" enabled - return pos; - - DISPATCH(); // this avoids freeing registers after "stopping" a warp script + OP(EXEC) : + { + auto ret = functions[*++pos](vm); + if (updatePos) { + pos = this->pos; + updatePos = false; + } + if (stop) { + stop = false; + if (goBack) { + goBack = false; + pos -= instruction_arg_count[OP_EXEC] + 1; + // NOTE: Going back leaks all registers for the next time the same function is called. + // This is for example used in the wait block (to call it again with the same time value). + } else + FREE_REGS(ret); + + if (!warp) // TODO: This should always return if there's a "warp timer" enabled + return pos; + + DISPATCH(); // this avoids freeing registers after "stopping" a warp script + } + FREE_REGS(ret); + DISPATCH(); } - FREE_REGS(ret); - DISPATCH(); -} -OP(INIT_PROCEDURE): - procedureArgTree.push_back({}); + OP(INIT_PROCEDURE) : + procedureArgTree.push_back({}); if (procedureArgTree.size() >= 2) procedureArgs = &procedureArgTree[procedureArgTree.size() - 2]; nextProcedureArgs = &procedureArgTree.back(); DISPATCH(); -OP(CALL_PROCEDURE): { - unsigned int *procedurePos = procedures[pos[1]]; + OP(CALL_PROCEDURE) : + { + unsigned int *procedurePos = procedures[pos[1]]; - if (procedurePos) { - callTree.push_back(++pos); - procedureArgs = nextProcedureArgs; - nextProcedureArgs = nullptr; - pos = procedurePos; - } else - pos++; + if (procedurePos) { + callTree.push_back(++pos); + procedureArgs = nextProcedureArgs; + nextProcedureArgs = nullptr; + pos = procedurePos; + } else + pos++; - DISPATCH(); -} + DISPATCH(); + } -OP(ADD_ARG): - nextProcedureArgs->push_back(*READ_LAST_REG()); + OP(ADD_ARG) : + nextProcedureArgs->push_back(*READ_LAST_REG()); FREE_REGS(1); DISPATCH(); -OP(READ_ARG): - ADD_RET_VALUE(procedureArgs->operator[](*++pos)); + OP(READ_ARG) : + ADD_RET_VALUE(procedureArgs->operator[](*++pos)); DISPATCH(); -OP(BREAK_FRAME): - noBreak = false; + OP(BREAK_FRAME) : + noBreak = false; DISPATCH(); -OP(WARP): - warp = true; + OP(WARP) : + warp = true; DISPATCH(); #if !defined(ENABLE_COMPUTED_GOTO) - } +} #endif } From 61ff8bcb0fd5a61893ac1f7943cee6b7663c0e55 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:39:17 +0100 Subject: [PATCH 3/3] Add simple promise API to VirtualMachine --- include/scratchcpp/virtualmachine.h | 2 ++ src/engine/virtualmachine.cpp | 20 +++++++++++++ src/engine/virtualmachine_p.h | 1 + test/virtual_machine/virtual_machine_test.cpp | 29 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/include/scratchcpp/virtualmachine.h b/include/scratchcpp/virtualmachine.h index 3a66048a..58541495 100644 --- a/include/scratchcpp/virtualmachine.h +++ b/include/scratchcpp/virtualmachine.h @@ -133,6 +133,8 @@ class LIBSCRATCHCPP_EXPORT VirtualMachine void moveToLastCheckpoint(); void stop(bool savePos = true, bool breakFrame = false, bool goBack = false); + void promise(); + void resolvePromise(); bool atEnd() const; diff --git a/src/engine/virtualmachine.cpp b/src/engine/virtualmachine.cpp index f066e46e..98b12248 100644 --- a/src/engine/virtualmachine.cpp +++ b/src/engine/virtualmachine.cpp @@ -166,6 +166,9 @@ void VirtualMachine::run() { impl->running = true; + if (impl->promisePending) + return; + unsigned int *ret = impl->run(impl->pos); assert(ret); @@ -187,6 +190,7 @@ void VirtualMachine::reset() { impl->pos = impl->bytecode; impl->atEnd = false; + impl->promisePending = false; if (!impl->running) // Registers will be freed when the script stops running impl->regCount = 0; @@ -216,6 +220,22 @@ void VirtualMachine::stop(bool savePos, bool breakFrame, bool goBack) impl->goBack = goBack; } +/*! + * Use this to pause the execution of the script. + * The execution will continue after calling resolvePromise() + */ +void VirtualMachine::promise() +{ + impl->promisePending = true; + stop(); +} + +/*! Resolves the promise so that the VM can continue with the execution. */ +void VirtualMachine::resolvePromise() +{ + impl->promisePending = false; +} + /*! Returns true if the VM has reached the vm::OP_HALT instruction. */ bool VirtualMachine::atEnd() const { diff --git a/src/engine/virtualmachine_p.h b/src/engine/virtualmachine_p.h index da5dc7d1..173db80e 100644 --- a/src/engine/virtualmachine_p.h +++ b/src/engine/virtualmachine_p.h @@ -56,6 +56,7 @@ struct VirtualMachinePrivate bool savePos = true; bool goBack = false; bool updatePos = false; + bool promisePending = false; unsigned int **procedures = nullptr; BlockFunc *functions = nullptr; diff --git a/test/virtual_machine/virtual_machine_test.cpp b/test/virtual_machine/virtual_machine_test.cpp index 587d562b..0f702476 100644 --- a/test/virtual_machine/virtual_machine_test.cpp +++ b/test/virtual_machine/virtual_machine_test.cpp @@ -1639,6 +1639,35 @@ TEST(VirtualMachineTest, ResetAndKill) ASSERT_TRUE(vm.atEnd()); } +unsigned int promiseTest(VirtualMachine *vm) +{ + vm->promise(); + return 1; +} + +TEST(VirtualMachineTest, Promise) +{ + static unsigned int bytecode[] = { OP_START, OP_NULL, OP_EXEC, 0, vm::OP_NULL, vm::OP_NULL, vm::OP_NULL, OP_HALT }; + static BlockFunc functions[] = { &promiseTest }; + + VirtualMachine vm; + vm.setBytecode(bytecode); + vm.setFunctions(functions); + + vm.run(); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(vm.registerCount(), 0); + + vm.run(); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(vm.registerCount(), 0); + + vm.resolvePromise(); + vm.run(); + ASSERT_TRUE(vm.atEnd()); + ASSERT_EQ(vm.registerCount(), 3); +} + TEST(VirtualMachineTest, NoCrashWhenRepeatingZeroTimes) { // Regtest for #362