Skip to content

Commit e3cb5f9

Browse files
committed
Add deleteClone method to Sprite
1 parent 8c50bad commit e3cb5f9

File tree

8 files changed

+98
-65
lines changed

8 files changed

+98
-65
lines changed

include/scratchcpp/sprite.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
2525

2626
Sprite();
2727
Sprite(const Sprite &) = delete;
28-
~Sprite();
2928

3029
void setInterface(ISpriteHandler *newInterface);
3130

3231
std::shared_ptr<Sprite> clone();
32+
void deleteClone();
3333

3434
bool isClone() const;
3535

src/blocks/controlblocks.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,11 @@ unsigned int ControlBlocks::createCloneOfMyself(VirtualMachine *vm)
286286

287287
unsigned int ControlBlocks::deleteThisClone(VirtualMachine *vm)
288288
{
289-
Target *target = vm->target();
289+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
290290

291-
if (target) {
292-
vm->engine()->stopTarget(target, nullptr);
293-
target->~Target();
291+
if (sprite) {
292+
vm->engine()->stopTarget(sprite, nullptr);
293+
sprite->deleteClone();
294294
}
295295

296296
return 0;

src/engine/internal/engine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1207,7 +1207,7 @@ void Engine::deleteClones()
12071207

12081208
for (auto clone : clones) {
12091209
assert(clone);
1210-
clone->~Sprite();
1210+
clone->deleteClone();
12111211
}
12121212
}
12131213
}

src/scratch/sprite.cpp

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <scratchcpp/costume.h>
99
#include <scratchcpp/rect.h>
1010
#include <cassert>
11+
#include <iostream>
1112

1213
#include "sprite_p.h"
1314

@@ -20,20 +21,6 @@ Sprite::Sprite() :
2021
{
2122
}
2223

23-
/*! Destroys the Sprite object. */
24-
Sprite::~Sprite()
25-
{
26-
if (isClone()) {
27-
IEngine *eng = engine();
28-
29-
if (eng)
30-
eng->deinitClone(this);
31-
32-
assert(impl->cloneSprite);
33-
impl->cloneSprite->impl->removeClone(this);
34-
}
35-
}
36-
3724
/*! Sets the sprite interface. */
3825
void Sprite::setInterface(ISpriteHandler *newInterface)
3926
{
@@ -103,6 +90,23 @@ std::shared_ptr<Sprite> Sprite::clone()
10390
return nullptr;
10491
}
10592

93+
/*! Deletes this clone (if the sprite is a clone). */
94+
void Sprite::deleteClone()
95+
{
96+
assert(isClone());
97+
98+
if (isClone()) {
99+
IEngine *eng = engine();
100+
101+
if (eng)
102+
eng->deinitClone(this);
103+
104+
assert(impl->cloneSprite);
105+
impl->cloneDeleted = true;
106+
impl->cloneSprite->impl->removeClone(this);
107+
}
108+
}
109+
106110
/*! Returns true if this is a clone. */
107111
bool Sprite::isClone() const
108112
{

src/scratch/sprite_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct SpritePrivate
2323
ISpriteHandler *iface = nullptr;
2424
Sprite *cloneSprite = nullptr;
2525
std::vector<std::shared_ptr<Sprite>> clones;
26+
bool cloneDeleted = false;
2627
bool visible = true;
2728
double x = 0;
2829
double y = 0;

test/blocks/control_blocks_test.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,10 @@ TEST_F(ControlBlocksTest, CreateCloneOfImpl)
950950
ASSERT_EQ(sprite.clones().size(), 4);
951951

952952
EXPECT_CALL(m_engineMock, deinitClone(clone1));
953+
clone1->deleteClone();
954+
953955
EXPECT_CALL(m_engineMock, deinitClone(clone3));
956+
clone3->deleteClone();
954957
}
955958

956959
TEST_F(ControlBlocksTest, StartAsClone)
@@ -1004,7 +1007,7 @@ TEST_F(ControlBlocksTest, DeleteThisCloneImpl)
10041007
vm.setFunctions(functions);
10051008

10061009
EXPECT_CALL(m_engineMock, stopTarget(clone, nullptr)).Times(1);
1007-
EXPECT_CALL(m_engineMock, deinitClone(clone)).Times(2); // TODO: Is there a double-free?
1010+
EXPECT_CALL(m_engineMock, deinitClone(clone));
10081011

10091012
vm.setBytecode(bytecode);
10101013
vm.run();

test/scratch_classes/sprite_test.cpp

Lines changed: 68 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,36 @@ TEST(SpriteTest, Visible)
4040

4141
TEST(SpriteTest, Clone)
4242
{
43-
std::unique_ptr<Sprite> sprite = std::make_unique<Sprite>();
44-
sprite->setName("Sprite1");
43+
Sprite sprite;
44+
sprite.setName("Sprite1");
4545
auto var1 = std::make_shared<Variable>("a", "var1", "hello");
4646
auto var2 = std::make_shared<Variable>("b", "var2", "world");
47-
sprite->addVariable(var1);
48-
sprite->addVariable(var2);
47+
sprite.addVariable(var1);
48+
sprite.addVariable(var2);
4949
auto c1 = std::make_shared<Costume>("costume1", "", "svg");
50-
sprite->addCostume(c1);
50+
sprite.addCostume(c1);
5151

5252
auto list1 = std::make_shared<List>("c", "list1");
5353
list1->push_back("item1");
5454
list1->push_back("item2");
5555
auto list2 = std::make_shared<List>("d", "list2");
5656
list2->push_back("test");
57-
sprite->addList(list1);
58-
sprite->addList(list2);
59-
60-
sprite->addCostume(std::make_shared<Costume>("", "", ""));
61-
sprite->addCostume(std::make_shared<Costume>("", "", ""));
62-
sprite->setCostumeIndex(1);
63-
sprite->setLayerOrder(5);
64-
sprite->setVolume(50);
65-
66-
sprite->setVisible(false);
67-
sprite->setX(100.25);
68-
sprite->setY(-45.43);
69-
sprite->setSize(54.121);
70-
sprite->setDirection(179.4);
71-
sprite->setDraggable(true);
72-
sprite->setRotationStyle(Sprite::RotationStyle::DoNotRotate);
57+
sprite.addList(list1);
58+
sprite.addList(list2);
59+
60+
sprite.addCostume(std::make_shared<Costume>("", "", ""));
61+
sprite.addCostume(std::make_shared<Costume>("", "", ""));
62+
sprite.setCostumeIndex(1);
63+
sprite.setLayerOrder(5);
64+
sprite.setVolume(50);
65+
66+
sprite.setVisible(false);
67+
sprite.setX(100.25);
68+
sprite.setY(-45.43);
69+
sprite.setSize(54.121);
70+
sprite.setDirection(179.4);
71+
sprite.setDraggable(true);
72+
sprite.setRotationStyle(Sprite::RotationStyle::DoNotRotate);
7373

7474
auto checkCloneData = [](Sprite *clone) {
7575
ASSERT_TRUE(clone);
@@ -110,34 +110,34 @@ TEST(SpriteTest, Clone)
110110
ASSERT_EQ(clone->rotationStyle(), Sprite::RotationStyle::DoNotRotate);
111111
};
112112

113-
ASSERT_FALSE(sprite->isClone());
114-
ASSERT_EQ(sprite->cloneSprite(), nullptr);
113+
ASSERT_FALSE(sprite.isClone());
114+
ASSERT_EQ(sprite.cloneSprite(), nullptr);
115115

116-
ASSERT_EQ(sprite->clone(), nullptr);
117-
ASSERT_FALSE(sprite->isClone());
118-
ASSERT_EQ(sprite->cloneSprite(), nullptr);
116+
ASSERT_EQ(sprite.clone(), nullptr);
117+
ASSERT_FALSE(sprite.isClone());
118+
ASSERT_EQ(sprite.cloneSprite(), nullptr);
119119

120120
EngineMock engine;
121-
sprite->setEngine(&engine);
121+
sprite.setEngine(&engine);
122122
EXPECT_CALL(engine, requestRedraw()).Times(2);
123123
EXPECT_CALL(engine, cloneLimit()).Times(6).WillRepeatedly(Return(300)); // clone count limit is tested later
124124

125125
Sprite *clone1, *clone1_2;
126126
EXPECT_CALL(engine, cloneCount()).WillOnce(Return(0));
127127
EXPECT_CALL(engine, initClone(_)).WillOnce(SaveArg<0>(&clone1));
128-
EXPECT_CALL(engine, moveSpriteBehindOther(_, sprite.get())).WillOnce(SaveArg<0>(&clone1_2));
129-
ASSERT_EQ(sprite->clone().get(), clone1);
128+
EXPECT_CALL(engine, moveSpriteBehindOther(_, &sprite)).WillOnce(SaveArg<0>(&clone1_2));
129+
ASSERT_EQ(sprite.clone().get(), clone1);
130130
ASSERT_EQ(clone1, clone1_2);
131-
ASSERT_FALSE(sprite->isClone());
132-
ASSERT_EQ(sprite->cloneSprite(), nullptr);
131+
ASSERT_FALSE(sprite.isClone());
132+
ASSERT_EQ(sprite.cloneSprite(), nullptr);
133133

134134
ASSERT_TRUE(clone1->isClone());
135-
ASSERT_EQ(clone1->cloneSprite(), sprite.get());
135+
ASSERT_EQ(clone1->cloneSprite(), &sprite);
136136

137137
checkCloneData(clone1);
138138

139139
// Modify root sprite data to make sure parent is used
140-
sprite->setLayerOrder(3);
140+
sprite.setLayerOrder(3);
141141

142142
Sprite *clone2, *clone2_2;
143143
EXPECT_CALL(engine, cloneCount()).WillOnce(Return(1));
@@ -146,13 +146,13 @@ TEST(SpriteTest, Clone)
146146
ASSERT_EQ(clone1->clone().get(), clone2);
147147
ASSERT_EQ(clone2, clone2_2);
148148
ASSERT_TRUE(clone1->isClone());
149-
ASSERT_EQ(clone1->cloneSprite(), sprite.get());
149+
ASSERT_EQ(clone1->cloneSprite(), &sprite);
150150
ASSERT_TRUE(clone2->isClone());
151-
ASSERT_EQ(clone2->cloneSprite(), sprite.get());
151+
ASSERT_EQ(clone2->cloneSprite(), &sprite);
152152

153153
checkCloneData(clone2);
154154

155-
sprite->setVisible(true);
155+
sprite.setVisible(true);
156156

157157
Sprite *clone3, *clone3_2;
158158
EXPECT_CALL(engine, cloneCount()).WillOnce(Return(2));
@@ -164,20 +164,20 @@ TEST(SpriteTest, Clone)
164164
Sprite *clone4, *clone4_2;
165165
EXPECT_CALL(engine, cloneLimit()).WillOnce(Return(-1));
166166
EXPECT_CALL(engine, initClone(_)).WillOnce(SaveArg<0>(&clone4));
167-
EXPECT_CALL(engine, moveSpriteBehindOther(_, sprite.get())).WillOnce(SaveArg<0>(&clone4_2));
168-
ASSERT_EQ(sprite->clone().get(), clone4);
167+
EXPECT_CALL(engine, moveSpriteBehindOther(_, &sprite)).WillOnce(SaveArg<0>(&clone4_2));
168+
ASSERT_EQ(sprite.clone().get(), clone4);
169169
ASSERT_EQ(clone4, clone4_2);
170170

171171
EXPECT_CALL(engine, cloneLimit()).Times(2).WillRepeatedly(Return(0));
172172
EXPECT_CALL(engine, cloneCount()).WillOnce(Return(0));
173-
ASSERT_EQ(sprite->clone(), nullptr);
173+
ASSERT_EQ(sprite.clone(), nullptr);
174174

175175
EXPECT_CALL(engine, cloneLimit()).Times(2).WillRepeatedly(Return(150));
176176
EXPECT_CALL(engine, cloneCount()).WillOnce(Return(150));
177-
ASSERT_EQ(sprite->clone(), nullptr);
177+
ASSERT_EQ(sprite.clone(), nullptr);
178178

179179
// clones
180-
const auto &clones = sprite->clones();
180+
const auto &clones = sprite.clones();
181181
ASSERT_EQ(clones.size(), 4);
182182
ASSERT_EQ(clones[0].get(), clone1);
183183
ASSERT_EQ(clones[1].get(), clone2);
@@ -189,12 +189,36 @@ TEST(SpriteTest, Clone)
189189
ASSERT_EQ(clone3->clones(), clones);
190190
ASSERT_EQ(clone4->clones(), clones);
191191

192-
ASSERT_EQ(clone2->costumes(), sprite->costumes());
192+
ASSERT_EQ(clone2->costumes(), sprite.costumes());
193193
auto c2 = std::make_shared<Costume>("costume2", "", "png");
194194
clone2->addCostume(c2);
195-
ASSERT_EQ(clone2->costumes(), sprite->costumes());
195+
ASSERT_EQ(clone2->costumes(), sprite.costumes());
196+
197+
// Delete
198+
EXPECT_CALL(engine, deinitClone(clone1));
199+
clone1->deleteClone();
200+
201+
ASSERT_EQ(clones.size(), 3);
202+
ASSERT_EQ(clones[0].get(), clone2);
203+
ASSERT_EQ(clones[1].get(), clone3);
204+
ASSERT_EQ(clones[2].get(), clone4);
205+
206+
EXPECT_CALL(engine, deinitClone(clone3));
207+
clone3->deleteClone();
208+
209+
ASSERT_EQ(clones.size(), 2);
210+
ASSERT_EQ(clones[0].get(), clone2);
211+
ASSERT_EQ(clones[1].get(), clone4);
212+
213+
EXPECT_CALL(engine, deinitClone(clone2));
214+
clone2->deleteClone();
215+
216+
ASSERT_EQ(clones.size(), 1);
217+
ASSERT_EQ(clones[0].get(), clone4);
196218

197-
sprite.reset();
219+
EXPECT_CALL(engine, deinitClone(clone4));
220+
clone4->deleteClone();
221+
ASSERT_TRUE(clones.empty());
198222
}
199223

200224
TEST(SpriteTest, XY)

test/script/script_test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,5 @@ TEST_F(ScriptTest, Start)
142142
ASSERT_EQ(vm->lists()[1], clone->listAt(clone->findListById("d")).get());
143143

144144
EXPECT_CALL(m_engine, deinitClone(clone.get()));
145+
clone->deleteClone();
145146
}

0 commit comments

Comments
 (0)