Skip to content

Commit 89b7bbd

Browse files
committed
Add VirtualMachine class
1 parent f936e4f commit 89b7bbd

File tree

4 files changed

+464
-0
lines changed

4 files changed

+464
-0
lines changed

src/engine/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ target_sources(scratchcpp
22
PRIVATE
33
engine.cpp
44
blockargs.cpp
5+
virtualmachine.cpp
56
PUBLIC
67
engine.h
78
global.h
89
blockargs.h
10+
virtualmachine.h
911
iblocksection.h
1012
iextension.h
1113
)

src/engine/global.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,22 @@
1010
namespace libscratchcpp
1111
{
1212

13+
class LIBSCRATCHCPP_EXPORT VirtualMachine;
14+
1315
/*!
1416
* \typedef BlockImpl
1517
*
1618
* BlockImpl is a function pointer for block implementation functions.
1719
*/
1820
using BlockImpl = Value (*)(const BlockArgs &);
1921

22+
/*!
23+
* \typedef BlockFunc
24+
*
25+
* BlockFunc is a function pointer for block implementation functions.
26+
*/
27+
using BlockFunc = unsigned int (*)(VirtualMachine *vm);
28+
2029
/*! Generates a random number in the given interval like the random.randint() function in Python. */
2130
template<typename T>
2231
inline T randint(T start, T end)

src/engine/virtualmachine.cpp

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
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

Comments
 (0)