|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | + |
| 3 | +#include "virtualmachine.h" |
| 4 | + |
| 5 | +#define DISPATCH() goto *dispatch_table[*++pos] |
| 6 | +#define FREE_REGS(count) m_regCount -= count |
| 7 | +#define ADD_RET_VALUE(value) *m_regs[m_regCount++] = value |
| 8 | +#define REPLACE_RET_VALUE(value, offset) *m_regs[m_regCount - offset] = value |
| 9 | +#define GET_NEXT_ARG() m_constValues[*++pos] |
| 10 | +#define READ_REG(index, count) m_regs[m_regCount - count + index] |
| 11 | +#define READ_LAST_REG() m_regs[m_regCount - 1] |
| 12 | + |
| 13 | +#define FIX_LIST_INDEX(index, listSize) \ |
| 14 | + if ((listSize == 0) || (index < 1) || (index > listSize)) \ |
| 15 | + index = 0 |
| 16 | + |
| 17 | +using namespace libscratchcpp; |
| 18 | +using namespace vm; |
| 19 | + |
| 20 | +/*! Constructs VirtualMachine. */ |
| 21 | +VirtualMachine::VirtualMachine() |
| 22 | +{ |
| 23 | + m_regs = new Value *[1024]; |
| 24 | + for (int i = 0; i < 1024; i++) |
| 25 | + m_regs[i] = new Value(); |
| 26 | +} |
| 27 | + |
| 28 | +/*! Destroys the VirtualMachine object. */ |
| 29 | +VirtualMachine::~VirtualMachine() |
| 30 | +{ |
| 31 | + for (int i = 0; i < 1024; i++) |
| 32 | + delete m_regs[i]; |
| 33 | + delete m_regs; |
| 34 | +} |
| 35 | + |
| 36 | +/*! Sets the list of functions. */ |
| 37 | +void VirtualMachine::setFunctions(const std::vector<BlockFunc> &functions) |
| 38 | +{ |
| 39 | + m_functionsVector = functions; |
| 40 | + m_functions = m_functionsVector.data(); |
| 41 | +} |
| 42 | + |
| 43 | +/*! Sets the list of constant values. */ |
| 44 | +void VirtualMachine::setConstValues(const std::vector<Value> &values) |
| 45 | +{ |
| 46 | + m_constValuesVector = values; |
| 47 | + m_constValues = m_constValuesVector.data(); |
| 48 | +} |
| 49 | + |
| 50 | +/*! Sets the bytecode of the script. */ |
| 51 | +void VirtualMachine::setBytecode(const std::vector<unsigned int> &code) |
| 52 | +{ |
| 53 | + m_bytecodeVector = code; |
| 54 | + m_bytecode = &m_bytecodeVector[0]; |
| 55 | +} |
| 56 | + |
| 57 | +/*! Runs the script. */ |
| 58 | +unsigned int *VirtualMachine::run(RunningScript *script) |
| 59 | +{ |
| 60 | + return run(m_bytecode, script); |
| 61 | +} |
| 62 | + |
| 63 | +unsigned int *VirtualMachine::run(unsigned int *pos, RunningScript *script) |
| 64 | +{ |
| 65 | + static const void *dispatch_table[] = { |
| 66 | + nullptr, &&do_halt, &&do_const, &&do_null, &&do_if, &&do_else, &&do_endif, &&do_repeat_loop, &&do_until_loop, &&do_begin_until_loop, |
| 67 | + &&do_loop_end, &&do_print, &&do_add, &&do_subtract, &&do_multiply, &&do_divide, &&do_random, &&do_greater_than, &&do_less_than, &&do_equals, |
| 68 | + &&do_and, &&do_or, &&do_not, &&do_set_var, &&do_change_var, &&do_read_var, &&do_read_list, &&do_list_append, &&do_list_del, &&do_list_del_all, |
| 69 | + &&do_list_insert, &&do_list_replace, &&do_list_get_item, &&do_list_index_of, &&do_list_length, &&do_list_contains, &&do_exec |
| 70 | + }; |
| 71 | + unsigned int *loopStart; |
| 72 | + unsigned int *loopEnd; |
| 73 | + size_t loopCount; |
| 74 | + DISPATCH(); |
| 75 | + |
| 76 | +do_halt: |
| 77 | + return pos; |
| 78 | + |
| 79 | +do_const: |
| 80 | + ADD_RET_VALUE(GET_NEXT_ARG()); |
| 81 | + DISPATCH(); |
| 82 | + |
| 83 | +do_null: |
| 84 | + ADD_RET_VALUE(Value()); |
| 85 | + DISPATCH(); |
| 86 | + |
| 87 | +do_if: |
| 88 | + if (!READ_LAST_REG()->toBool()) { |
| 89 | + while (*pos != OP_ELSE && *pos != OP_ENDIF && *pos != OP_HALT) |
| 90 | + pos += instruction_arg_count[*pos++]; |
| 91 | + } |
| 92 | + FREE_REGS(1); |
| 93 | + DISPATCH(); |
| 94 | + |
| 95 | +do_else: |
| 96 | + while (*pos != OP_ENDIF && *pos != OP_HALT) |
| 97 | + pos += instruction_arg_count[*pos++]; |
| 98 | + |
| 99 | +do_endif: |
| 100 | + DISPATCH(); |
| 101 | + |
| 102 | +do_repeat_loop: |
| 103 | + FREE_REGS(1); |
| 104 | + loopCount = READ_LAST_REG()->toLong(); |
| 105 | + if (loopCount <= 0) { |
| 106 | + loopEnd = pos; |
| 107 | + while (*loopEnd != OP_LOOP_END && *loopEnd != OP_HALT) |
| 108 | + loopEnd += instruction_arg_count[*loopEnd++]; |
| 109 | + } else { |
| 110 | + for (size_t i = 0; i < loopCount; i++) |
| 111 | + loopEnd = run(pos, script); |
| 112 | + } |
| 113 | + pos = loopEnd; |
| 114 | + DISPATCH(); |
| 115 | + |
| 116 | +do_until_loop: |
| 117 | + loopStart = run(pos, script); |
| 118 | + if (!READ_LAST_REG()->toBool()) { |
| 119 | + do { |
| 120 | + FREE_REGS(1); |
| 121 | + loopEnd = run(loopStart, script); |
| 122 | + run(pos, script); |
| 123 | + } while (!READ_LAST_REG()->toBool()); |
| 124 | + } else { |
| 125 | + loopEnd = loopStart; |
| 126 | + while (*loopEnd != OP_LOOP_END && *loopEnd != OP_HALT) |
| 127 | + loopEnd += instruction_arg_count[*loopEnd++]; |
| 128 | + } |
| 129 | + pos = loopEnd; |
| 130 | + DISPATCH(); |
| 131 | + |
| 132 | +do_begin_until_loop: |
| 133 | + return pos; |
| 134 | + |
| 135 | +do_loop_end: |
| 136 | + return pos; |
| 137 | + |
| 138 | +do_print: |
| 139 | + std::cout << READ_LAST_REG()->toString() << std::endl; |
| 140 | + FREE_REGS(1); |
| 141 | + DISPATCH(); |
| 142 | + |
| 143 | +do_add: |
| 144 | + REPLACE_RET_VALUE(*READ_REG(0, 2), 2); |
| 145 | + READ_REG(0, 2)->add(READ_REG(1, 2)); |
| 146 | + FREE_REGS(1); |
| 147 | + DISPATCH(); |
| 148 | + |
| 149 | +do_subtract: |
| 150 | + REPLACE_RET_VALUE(*READ_REG(0, 2), 2); |
| 151 | + READ_REG(0, 2)->subtract(READ_REG(1, 2)); |
| 152 | + FREE_REGS(1); |
| 153 | + DISPATCH(); |
| 154 | + |
| 155 | +do_multiply: |
| 156 | + REPLACE_RET_VALUE(*READ_REG(0, 2), 2); |
| 157 | + READ_REG(0, 2)->multiply(READ_REG(1, 2)); |
| 158 | + FREE_REGS(1); |
| 159 | + DISPATCH(); |
| 160 | + |
| 161 | +do_divide: |
| 162 | + REPLACE_RET_VALUE(*READ_REG(0, 2), 2); |
| 163 | + READ_REG(0, 2)->divide(READ_REG(1, 2)); |
| 164 | + FREE_REGS(1); |
| 165 | + DISPATCH(); |
| 166 | + |
| 167 | +do_random: |
| 168 | + REPLACE_RET_VALUE(randint<long>(READ_REG(0, 2)->toDouble(), READ_REG(1, 2)->toDouble()), 2); |
| 169 | + FREE_REGS(1); |
| 170 | + DISPATCH(); |
| 171 | + |
| 172 | +do_greater_than: |
| 173 | + REPLACE_RET_VALUE(*READ_REG(0, 2) > *READ_REG(1, 2), 2); |
| 174 | + FREE_REGS(1); |
| 175 | + DISPATCH(); |
| 176 | + |
| 177 | +do_less_than: |
| 178 | + REPLACE_RET_VALUE(*READ_REG(0, 2) < *READ_REG(1, 2), 2); |
| 179 | + FREE_REGS(1); |
| 180 | + DISPATCH(); |
| 181 | + |
| 182 | +do_equals: |
| 183 | + REPLACE_RET_VALUE(*READ_REG(0, 2) == *READ_REG(1, 2), 2); |
| 184 | + FREE_REGS(1); |
| 185 | + DISPATCH(); |
| 186 | + |
| 187 | +do_and: |
| 188 | + REPLACE_RET_VALUE(READ_REG(0, 2)->toBool() && READ_REG(1, 2)->toBool(), 2); |
| 189 | + FREE_REGS(1); |
| 190 | + DISPATCH(); |
| 191 | + |
| 192 | +do_or: |
| 193 | + REPLACE_RET_VALUE(READ_REG(0, 2)->toBool() || READ_REG(1, 2)->toBool(), 2); |
| 194 | + FREE_REGS(1); |
| 195 | + DISPATCH(); |
| 196 | + |
| 197 | +do_not: |
| 198 | + REPLACE_RET_VALUE(!READ_LAST_REG()->toBool(), 1); |
| 199 | + DISPATCH(); |
| 200 | + |
| 201 | +do_set_var: |
| 202 | + *m_variables[*++pos] = *READ_LAST_REG(); |
| 203 | + FREE_REGS(1); |
| 204 | + DISPATCH(); |
| 205 | + |
| 206 | +do_read_var: |
| 207 | + ADD_RET_VALUE(*m_variables[*++pos]); |
| 208 | + DISPATCH(); |
| 209 | + |
| 210 | +do_change_var: |
| 211 | + m_variables[*++pos]->add(*READ_LAST_REG()); |
| 212 | + FREE_REGS(1); |
| 213 | + DISPATCH(); |
| 214 | + |
| 215 | +do_read_list: |
| 216 | + ADD_RET_VALUE(m_lists[*++pos]->toString()); |
| 217 | + DISPATCH(); |
| 218 | + |
| 219 | +do_list_append: |
| 220 | + m_lists[*++pos]->push_back(*READ_LAST_REG()); |
| 221 | + FREE_REGS(1); |
| 222 | + DISPATCH(); |
| 223 | + |
| 224 | +do_list_del : { |
| 225 | + const Value *indexValue = READ_LAST_REG(); |
| 226 | + size_t index; |
| 227 | + List *list = m_lists[*++pos]; |
| 228 | + if (indexValue->isString()) { |
| 229 | + const std::string &str = indexValue->toString(); |
| 230 | + if (str == "last") { |
| 231 | + index = list->size(); |
| 232 | + } else if (str == "all") { |
| 233 | + list->clear(); |
| 234 | + index = 0; |
| 235 | + } else if (str == "random") { |
| 236 | + size_t size = list->size(); |
| 237 | + index = size == 0 ? 0 : randint<size_t>(1, size); |
| 238 | + } else |
| 239 | + index = 0; |
| 240 | + } else { |
| 241 | + index = indexValue->toLong(); |
| 242 | + FIX_LIST_INDEX(index, list->size()); |
| 243 | + } |
| 244 | + if (index != 0) |
| 245 | + list->removeAt(index - 1); |
| 246 | + FREE_REGS(1); |
| 247 | + DISPATCH(); |
| 248 | +} |
| 249 | + |
| 250 | +do_list_del_all: |
| 251 | + m_lists[*++pos]->clear(); |
| 252 | + DISPATCH(); |
| 253 | + |
| 254 | +do_list_insert : { |
| 255 | + const Value *indexValue = READ_REG(1, 2); |
| 256 | + size_t index; |
| 257 | + List *list = m_lists[*++pos]; |
| 258 | + if (indexValue->isString()) { |
| 259 | + const std::string &str = indexValue->toString(); |
| 260 | + if (str == "last") { |
| 261 | + list->push_back(*READ_REG(0, 2)); |
| 262 | + index = 0; |
| 263 | + } else if (str == "random") { |
| 264 | + size_t size = list->size(); |
| 265 | + index = size == 0 ? 1 : randint<size_t>(1, size); |
| 266 | + } else |
| 267 | + index = 0; |
| 268 | + } else { |
| 269 | + index = indexValue->toLong(); |
| 270 | + FIX_LIST_INDEX(index, list->size()); |
| 271 | + } |
| 272 | + if ((index != 0) || list->empty()) { |
| 273 | + if (list->empty()) |
| 274 | + list->push_back(*READ_REG(0, 2)); |
| 275 | + else |
| 276 | + list->insert(index - 1, *READ_REG(0, 2)); |
| 277 | + } |
| 278 | + FREE_REGS(2); |
| 279 | + DISPATCH(); |
| 280 | +} |
| 281 | + |
| 282 | +do_list_replace : { |
| 283 | + const Value *indexValue = READ_REG(0, 2); |
| 284 | + size_t index; |
| 285 | + List *list = m_lists[*++pos]; |
| 286 | + if (indexValue->isString()) { |
| 287 | + std::string str = indexValue->toString(); |
| 288 | + if (str == "last") |
| 289 | + index = list->size(); |
| 290 | + else if (str == "random") { |
| 291 | + size_t size = list->size(); |
| 292 | + index = size == 0 ? 0 : randint<size_t>(1, size); |
| 293 | + } else |
| 294 | + index = 0; |
| 295 | + } else { |
| 296 | + index = indexValue->toLong(); |
| 297 | + FIX_LIST_INDEX(index, list->size()); |
| 298 | + } |
| 299 | + if (index != 0) |
| 300 | + list->operator[](index - 1) = *READ_REG(1, 2); |
| 301 | + FREE_REGS(2); |
| 302 | + DISPATCH(); |
| 303 | +} |
| 304 | + |
| 305 | +do_list_get_item : { |
| 306 | + const Value *indexValue = READ_LAST_REG(); |
| 307 | + size_t index; |
| 308 | + List *list = m_lists[*++pos]; |
| 309 | + if (indexValue->isString()) { |
| 310 | + std::string str = indexValue->toString(); |
| 311 | + if (str == "last") |
| 312 | + index = list->size(); |
| 313 | + else if (str == "random") { |
| 314 | + size_t size = list->size(); |
| 315 | + index = size == 0 ? 0 : randint<size_t>(1, size); |
| 316 | + } else |
| 317 | + index = 0; |
| 318 | + } else { |
| 319 | + index = indexValue->toLong(); |
| 320 | + FIX_LIST_INDEX(index, list->size()); |
| 321 | + } |
| 322 | + if (index == 0) { |
| 323 | + REPLACE_RET_VALUE("", 1); |
| 324 | + } else { |
| 325 | + REPLACE_RET_VALUE(list->operator[](index - 1), 1); |
| 326 | + } |
| 327 | + DISPATCH(); |
| 328 | +} |
| 329 | + |
| 330 | +do_list_index_of: |
| 331 | + // TODO: Add size_t support to Value and remove the static_cast |
| 332 | + REPLACE_RET_VALUE(static_cast<long>(m_lists[*++pos]->indexOf(*READ_LAST_REG()) + 1), 1); |
| 333 | + DISPATCH(); |
| 334 | + |
| 335 | +do_list_length: |
| 336 | + // TODO: Add size_t support to Value and remove the static_cast |
| 337 | + ADD_RET_VALUE(static_cast<long>(m_lists[*++pos]->size())); |
| 338 | + DISPATCH(); |
| 339 | + |
| 340 | +do_list_contains: |
| 341 | + REPLACE_RET_VALUE(m_lists[*++pos]->contains(*READ_LAST_REG()), 1); |
| 342 | + DISPATCH(); |
| 343 | + |
| 344 | +do_exec: |
| 345 | + m_globalPos = pos; |
| 346 | + m_script = script; |
| 347 | + int ret = m_functions[*++pos](this); |
| 348 | + FREE_REGS(ret); |
| 349 | + pos += ret; |
| 350 | + DISPATCH(); |
| 351 | +} |
0 commit comments