Skip to content

Commit d4ca5cb

Browse files
authored
Merge pull request #531 from scratchcpp/fix_undefined_variable
Fix #446: Create missing variables and lists used by inputs and fields
2 parents bfb548d + 588955d commit d4ca5cb

File tree

6 files changed

+90
-3
lines changed

6 files changed

+90
-3
lines changed

src/engine/compiler.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ std::shared_ptr<Block> Compiler::inputBlock(int id) const
289289
/*! Returns the index of the given variable. */
290290
unsigned int Compiler::variableIndex(std::shared_ptr<Entity> varEntity)
291291
{
292+
assert(varEntity);
292293
auto var = dynamic_cast<Variable *>(varEntity.get());
294+
assert(var);
293295
auto it = std::find(impl->variables.begin(), impl->variables.end(), var);
294296
if (it != impl->variables.end())
295297
return it - impl->variables.begin();

src/engine/internal/engine.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,62 @@ void Engine::resolveIds()
113113
const auto &inputs = block->inputs();
114114
for (const auto &input : inputs) {
115115
input->setValueBlock(getBlock(input->valueBlockId()));
116+
116117
if (container)
117118
input->setInputId(container->resolveInput(input->name()));
118-
input->primaryValue()->setValuePtr(getEntity(input->primaryValue()->valueId()));
119-
input->secondaryValue()->setValuePtr(getEntity(input->primaryValue()->valueId()));
119+
120+
InputValue *value = input->primaryValue();
121+
std::string id = value->valueId(); // no reference!
122+
value->setValuePtr(getEntity(id));
123+
assert(input->secondaryValue()->type() != InputValue::Type::Variable && input->secondaryValue()->type() != InputValue::Type::List); // secondary values never have a variable or list
124+
input->secondaryValue()->setValuePtr(getEntity(input->secondaryValue()->valueId()));
125+
126+
// Add missing variables and lists
127+
if (!value->valuePtr()) {
128+
if (value->type() == InputValue::Type::Variable) {
129+
assert(!id.empty());
130+
std::cout << "warning: variable " << value->value().toString() << " referenced by an input missing, creating in stage" << std::endl;
131+
auto var = std::make_shared<Variable>(id, value->value().toString());
132+
value->setValuePtr(var);
133+
stage()->addVariable(var);
134+
} else if (value->type() == InputValue::Type::List) {
135+
assert(!id.empty());
136+
std::cout << "warning: list " << value->value().toString() << " referenced by an input missing, creating in stage" << std::endl;
137+
std::shared_ptr<List> list = std::make_shared<List>(id, value->value().toString());
138+
value->setValuePtr(list);
139+
stage()->addList(list);
140+
}
141+
}
120142
}
121143

122144
const auto &fields = block->fields();
123145
for (auto field : fields) {
124-
field->setValuePtr(getEntity(field->valueId()));
146+
std::string id = field->valueId(); // no reference!
147+
field->setValuePtr(getEntity(id));
148+
125149
if (container) {
126150
field->setFieldId(container->resolveField(field->name()));
151+
127152
if (!field->valuePtr())
128153
field->setSpecialValueId(container->resolveFieldValue(field->value().toString()));
129154
}
155+
156+
// TODO: Move field information out of Engine
157+
if (!field->valuePtr()) {
158+
if (field->name() == "VARIABLE") {
159+
assert(!id.empty());
160+
std::cout << "warning: variable " << field->value().toString() << " referenced by a field missing, creating in stage" << std::endl;
161+
auto var = std::make_shared<Variable>(id, field->value().toString());
162+
field->setValuePtr(var);
163+
stage()->addVariable(var);
164+
} else if (field->name() == "LIST") {
165+
assert(!id.empty());
166+
std::cout << "warning: list " << field->value().toString() << " referenced by a field missing, creating in stage" << std::endl;
167+
std::shared_ptr<List> list = std::make_shared<List>(id, field->value().toString());
168+
field->setValuePtr(list);
169+
stage()->addList(list);
170+
}
171+
}
130172
}
131173

132174
block->updateInputMap();

src/scratch/inputvalue.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ void InputValue::compile(Compiler *compiler)
3434
break;
3535

3636
case Type::Variable:
37+
assert(impl->valuePtr);
3738
compiler->addInstruction(vm::OP_READ_VAR, { compiler->variableIndex(impl->valuePtr) });
3839
break;
3940

4041
case Type::List:
42+
assert(impl->valuePtr);
4143
compiler->addInstruction(vm::OP_READ_LIST, { compiler->listIndex(impl->valuePtr) });
4244
break;
4345

test/engine/engine_test.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,3 +2201,44 @@ TEST(EngineTest, NoCrashWhenLoadingUndefinedVariableOrListMonitor)
22012201
ASSERT_EQ(list->id(), "7a5rAs|X2_[1APT7@B1V");
22022202
ASSERT_TRUE(list->empty());
22032203
}
2204+
2205+
TEST(EngineTest, NoCrashWithUndefinedVariableOrList)
2206+
{
2207+
// Regtest for #446
2208+
Project p("regtest_projects/446_undefined_variable_or_list_input_crash.sb3");
2209+
ASSERT_TRUE(p.load());
2210+
2211+
auto engine = p.engine();
2212+
2213+
// The undefined variable and list should now be defined
2214+
Stage *stage = engine->stage();
2215+
ASSERT_TRUE(stage);
2216+
ASSERT_VAR(stage, "test var");
2217+
auto var = GET_VAR(stage, "test var");
2218+
ASSERT_EQ(var->id(), "J3iHmLt:dwaR}^-fnGmG");
2219+
ASSERT_EQ(var->value(), Value());
2220+
2221+
ASSERT_LIST(stage, "test list");
2222+
auto list = GET_LIST(stage, "test list");
2223+
ASSERT_EQ(list->id(), "}l+w#Er5y!:*gh~5!3t%");
2224+
ASSERT_TRUE(list->empty());
2225+
2226+
// Test with fields
2227+
p.setFileName("regtest_projects/446_undefined_variable_or_list_field_crash.sb3");
2228+
ASSERT_TRUE(p.load());
2229+
2230+
engine = p.engine();
2231+
2232+
// The undefined variable and list should now be defined
2233+
stage = engine->stage();
2234+
ASSERT_TRUE(stage);
2235+
ASSERT_VAR(stage, "test var");
2236+
var = GET_VAR(stage, "test var");
2237+
ASSERT_EQ(var->id(), "J3iHmLt:dwaR}^-fnGmG");
2238+
ASSERT_EQ(var->value(), Value());
2239+
2240+
ASSERT_LIST(stage, "test list");
2241+
list = GET_LIST(stage, "test list");
2242+
ASSERT_EQ(list->id(), "}l+w#Er5y!:*gh~5!3t%");
2243+
ASSERT_TRUE(list->empty());
2244+
}
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)