Skip to content

Commit a2fbc65

Browse files
committed
Fix broadcasts calling themselves
1 parent 39614bc commit a2fbc65

File tree

5 files changed

+39
-5
lines changed

5 files changed

+39
-5
lines changed

src/blocks/eventblocks.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ void EventBlocks::compileWhenBroadcastReceived(Compiler *compiler)
4444

4545
unsigned int EventBlocks::broadcast(VirtualMachine *vm)
4646
{
47-
vm->engine()->broadcast(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString()));
47+
vm->engine()->broadcast(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString()), vm);
4848
return 1;
4949
}
5050

5151
unsigned int EventBlocks::broadcastByIndex(VirtualMachine *vm)
5252
{
53-
vm->engine()->broadcast(vm->getInput(0, 1)->toLong());
53+
vm->engine()->broadcast(vm->getInput(0, 1)->toLong(), vm);
5454
return 1;
5555
}

src/engine/engine.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ void Engine::frame()
121121
m_breakFrame = false;
122122

123123
do {
124-
m_scriptPositions[i] = script->run(m_scriptPositions[i]);
124+
auto ret = script->run(m_scriptPositions[i]);
125+
if (script->savePos())
126+
m_scriptPositions[i] = ret;
125127
if (script->atEnd())
126128
m_scriptsToRemove.push_back(script);
127129
} while (!script->atEnd() && !m_breakFrame);
@@ -181,7 +183,7 @@ void Engine::startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<T
181183
}
182184

183185
/*! Starts the script of the broadcast with the given index. */
184-
void libscratchcpp::Engine::broadcast(unsigned int index)
186+
void libscratchcpp::Engine::broadcast(unsigned int index, VirtualMachine *sourceScript)
185187
{
186188
const std::vector<VirtualMachine *> scripts = m_broadcastMap[index];
187189
for (auto vm : scripts) {
@@ -190,6 +192,8 @@ void libscratchcpp::Engine::broadcast(unsigned int index)
190192
// Reset the script if it's already running
191193
auto i = it - m_runningScripts.begin();
192194
m_scriptPositions[i] = m_runningScripts[i]->bytecode();
195+
if (vm == sourceScript)
196+
vm->stop(false, true);
193197
} else {
194198
m_runningScripts.push_back(vm);
195199
m_scriptPositions.push_back(vm->bytecode());

src/engine/engine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class LIBSCRATCHCPP_EXPORT Engine
3535
void start();
3636
void stop();
3737
void startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target);
38-
void broadcast(unsigned int index);
38+
void broadcast(unsigned int index, VirtualMachine *sourceScript);
3939
void stopScript(VirtualMachine *vm);
4040
void stopTarget(Target *target, VirtualMachine *exceptScript);
4141
void run();

src/engine/virtualmachine.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,19 @@ do_list_get_item : {
614614

615615
do_exec:
616616
FREE_REGS(m_functions[*++pos](this));
617+
if (m_stop) {
618+
m_stop = false;
619+
if (m_regCount > 0) {
620+
std::cout << "warning: VM: " << m_regCount << " registers were leaked by the script; this is most likely a bug in the VM or in the compiler" << std::endl;
621+
}
622+
m_callTree.clear();
623+
m_procedureArgTree.clear();
624+
m_procedureArgs = nullptr;
625+
m_nextProcedureArgs = nullptr;
626+
if (!m_atomic)
627+
m_engine->breakFrame();
628+
return pos;
629+
}
617630
DISPATCH();
618631

619632
do_init_procedure:

src/engine/virtualmachine.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,24 @@ class LIBSCRATCHCPP_EXPORT VirtualMachine
111111
unsigned int *run();
112112
unsigned int *run(unsigned int *pos);
113113

114+
/*!
115+
* Use this to stop the script from a function.
116+
* \param[in] savePos Changes the return value of savePos().
117+
* \param[in] breakAtomic Whether to break the frame after stopping the script.
118+
*/
119+
void stop(bool savePos = true, bool breakAtomic = false)
120+
{
121+
m_stop = true;
122+
m_savePos = savePos;
123+
m_atomic = !breakAtomic;
124+
}
125+
114126
/*! Returns true if the VM has reached the vm::OP_HALT instruction. */
115127
bool atEnd() const { return m_atEnd; };
116128

129+
/*! Used by Engine to check whether the position can be preserved. */
130+
bool savePos() const { return m_savePos; }
131+
117132
private:
118133
static inline const unsigned int instruction_arg_count[] = {
119134
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0
@@ -138,6 +153,8 @@ class LIBSCRATCHCPP_EXPORT VirtualMachine
138153
std::vector<Value> *m_procedureArgs = nullptr;
139154
std::vector<Value> *m_nextProcedureArgs = nullptr;
140155
bool m_atomic;
156+
bool m_stop = false;
157+
bool m_savePos = true;
141158

142159
unsigned int **m_procedures = nullptr;
143160
std::vector<unsigned int *> m_proceduresVector;

0 commit comments

Comments
 (0)