Skip to content

Commit 4fa58f5

Browse files
committed
Add initial CppExecutableCode implementation
1 parent 9e8c3e4 commit 4fa58f5

5 files changed

Lines changed: 328 additions & 12 deletions

File tree

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,107 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#include <scratchcpp/valuedata.h>
4+
#include <scratchcpp/promise.h>
5+
#include <scratchcpp/thread.h>
6+
#include <scratchcpp/target.h>
7+
#include <cassert>
48

59
#include "cppexecutablecode.h"
10+
#include "cppcompilercontext.h"
11+
#include "cppexecutioncontext.h"
612

713
using namespace libscratchcpp;
814

915
CppExecutableCode::CppExecutableCode(CppCompilerContext *ctx) :
1016
m_ctx(ctx)
1117
{
18+
assert(m_ctx);
1219
}
1320

1421
void CppExecutableCode::run(ExecutionContext *context)
1522
{
23+
CppExecutionContext *ctx = getContext(context);
24+
25+
if (ctx->finished())
26+
return;
27+
28+
auto promise = ctx->promise();
29+
30+
if (promise) {
31+
if (promise->isResolved())
32+
ctx->setPromise(nullptr);
33+
else
34+
return;
35+
}
36+
37+
// TODO: Add support for coroutines
38+
// TODO: Add support for end thread sentinel value
39+
Target *target = ctx->thread()->target();
40+
runScript(context, target, target->variableData(), target->listData());
41+
ctx->setFinished(true);
1642
}
1743

1844
ValueData CppExecutableCode::runReporter(ExecutionContext *context)
1945
{
20-
return ValueData();
46+
Target *target = context->thread()->target();
47+
return runReporter(context, target, target->variableData(), target->listData());
2148
}
2249

2350
bool CppExecutableCode::runPredicate(ExecutionContext *context)
2451
{
25-
return false;
52+
Target *target = context->thread()->target();
53+
return runPredicate(context, target, target->variableData(), target->listData());
2654
}
2755

2856
void CppExecutableCode::kill(ExecutionContext *context)
2957
{
58+
CppExecutionContext *ctx = getContext(context);
59+
// TODO: Add support for coroutines
60+
// NOTE: Do not destroy coroutine, it will be destroyed later
61+
// (really bad things happen when a coroutine destroys itself...)
62+
// The code is finished and the coroutine won't be used again until reset() is called
63+
ctx->setFinished(true);
64+
ctx->setPromise(nullptr);
3065
}
3166

3267
void CppExecutableCode::reset(ExecutionContext *context)
3368
{
69+
CppExecutionContext *ctx = getContext(context);
70+
71+
// TODO: Add support for coroutines
72+
ctx->setFinished(false);
73+
ctx->setPromise(nullptr);
3474
}
3575

3676
bool CppExecutableCode::isFinished(ExecutionContext *context) const
3777
{
38-
return true;
78+
return getContext(context)->finished();
3979
}
4080

4181
std::shared_ptr<ExecutionContext> CppExecutableCode::createExecutionContext(Thread *thread) const
4282
{
43-
return nullptr;
83+
return std::make_shared<CppExecutionContext>(m_ctx, thread);
84+
}
85+
86+
void CppExecutableCode::runScript(ExecutionContext *ctx, Target *target, ValueData **targetVariables, List **targetLists)
87+
{
88+
// Generated code overrides this
89+
}
90+
91+
ValueData CppExecutableCode::runReporter(ExecutionContext *ctx, Target *target, ValueData **targetVariables, List **targetLists)
92+
{
93+
// Generated code overrides this
94+
return ValueData();
95+
}
96+
97+
bool CppExecutableCode::runPredicate(ExecutionContext *ctx, Target *target, ValueData **targetVariables, List **targetLists)
98+
{
99+
// Generated code overrides this
100+
return false;
101+
}
102+
103+
CppExecutionContext *CppExecutableCode::getContext(ExecutionContext *context)
104+
{
105+
assert(dynamic_cast<CppExecutionContext *>(context));
106+
return static_cast<CppExecutionContext *>(context);
44107
}

‎src/engine/internal/codegen/cpp/cppexecutablecode.h‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,39 @@
44

55
#include <scratchcpp/executablecode.h>
66

7+
#include "test_export.h"
8+
79
namespace libscratchcpp
810
{
911

12+
class Target;
13+
class List;
1014
class CppCompilerContext;
15+
class CppExecutionContext;
1116

12-
class CppExecutableCode : public ExecutableCode
17+
class LIBSCRATCHCPP_TEST_EXPORT CppExecutableCode : public ExecutableCode
1318
{
1419
public:
1520
CppExecutableCode(CppCompilerContext *ctx);
1621

17-
void run(ExecutionContext *context) override;
18-
ValueData runReporter(ExecutionContext *context) override;
19-
bool runPredicate(ExecutionContext *context) override;
20-
void kill(ExecutionContext *context) override;
21-
void reset(ExecutionContext *context) override;
22+
virtual void run(ExecutionContext *context) override final;
23+
virtual ValueData runReporter(ExecutionContext *context) override final;
24+
virtual bool runPredicate(ExecutionContext *context) override final;
25+
virtual void kill(ExecutionContext *context) override final;
26+
virtual void reset(ExecutionContext *context) override final;
27+
28+
virtual bool isFinished(ExecutionContext *context) const override final;
2229

23-
bool isFinished(ExecutionContext *context) const override;
30+
virtual std::shared_ptr<ExecutionContext> createExecutionContext(Thread *thread) const override final;
2431

25-
std::shared_ptr<ExecutionContext> createExecutionContext(Thread *thread) const override;
32+
protected:
33+
virtual void runScript(ExecutionContext *ctx, Target *target, ValueData **targetVariables, List **targetLists);
34+
virtual ValueData runReporter(ExecutionContext *ctx, Target *target, ValueData **targetVariables, List **targetLists);
35+
virtual bool runPredicate(ExecutionContext *ctx, Target *target, ValueData **targetVariables, List **targetLists);
2636

2737
private:
38+
static CppExecutionContext *getContext(ExecutionContext *context);
39+
2840
CppCompilerContext *m_ctx = nullptr;
2941
};
3042

‎test/cpp/CMakeLists.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ add_executable(
22
cpp_test
33
main.cpp
44
cppexecutioncontext_test.cpp
5+
cppexecutablecode_test.cpp
6+
cppexecutablecodemock.h
57
)
68

79
target_link_libraries(
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#include <scratchcpp/target.h>
2+
#include <scratchcpp/value_functions.h>
3+
#include <scratchcpp/variable.h>
4+
#include <scratchcpp/list.h>
5+
#include <scratchcpp/promise.h>
6+
#include <scratchcpp/thread.h>
7+
#include <scratchcpp/script.h>
8+
#include <engine/internal/codegen/cpp/cppexecutablecode.h>
9+
#include <engine/internal/codegen/cpp/cppcompilercontext.h>
10+
#include <engine/internal/codegen/cpp/cppexecutioncontext.h>
11+
#include <enginemock.h>
12+
13+
#include "cppexecutablecodemock.h"
14+
15+
using namespace libscratchcpp;
16+
17+
using ::testing::Return;
18+
19+
class CppExecutableCodeTest : public testing::Test
20+
{
21+
public:
22+
void SetUp() override
23+
{
24+
m_target.setName("test");
25+
m_ctx = std::make_unique<CppCompilerContext>(&m_engine, &m_target);
26+
m_code = std::make_shared<CppExecutableCodeMock>(m_ctx.get());
27+
28+
m_script = std::make_unique<Script>(&m_target, nullptr, &m_engine);
29+
m_script->setCode(m_code);
30+
}
31+
32+
std::unique_ptr<CppCompilerContext> m_ctx;
33+
Target m_target;
34+
EngineMock m_engine;
35+
std::unique_ptr<Script> m_script;
36+
std::shared_ptr<CppExecutableCodeMock> m_code;
37+
};
38+
39+
// TODO: Add string support (see the StringCount test case)
40+
41+
TEST_F(CppExecutableCodeTest, CreateExecutionContext)
42+
{
43+
Thread thread(&m_target, &m_engine, m_script.get());
44+
auto ctx = m_code->createExecutionContext(&thread);
45+
ASSERT_TRUE(ctx);
46+
ASSERT_EQ(ctx->thread(), &thread);
47+
ASSERT_TRUE(dynamic_cast<CppExecutionContext *>(ctx.get()));
48+
}
49+
50+
TEST_F(CppExecutableCodeTest, ScriptFunction)
51+
{
52+
m_target.addVariable(std::make_shared<Variable>("", ""));
53+
m_target.addList(std::make_shared<List>("", ""));
54+
55+
Thread thread(&m_target, &m_engine, m_script.get());
56+
auto ctx = m_code->createExecutionContext(&thread);
57+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
58+
59+
EXPECT_CALL(*m_code, runScript(ctx.get(), &m_target, m_target.variableData(), m_target.listData()));
60+
m_code->run(ctx.get());
61+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
62+
63+
m_code->kill(ctx.get());
64+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
65+
66+
m_code->reset(ctx.get());
67+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
68+
69+
m_code->kill(ctx.get());
70+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
71+
72+
EXPECT_CALL(*m_code, runScript).Times(0);
73+
m_code->run(ctx.get());
74+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
75+
76+
// Test with another context
77+
Target anotherTarget;
78+
anotherTarget.addVariable(std::make_shared<Variable>("", ""));
79+
anotherTarget.addList(std::make_shared<List>("", ""));
80+
Script script(&anotherTarget, nullptr, &m_engine);
81+
script.setCode(m_code);
82+
Thread anotherThread(&anotherTarget, &m_engine, &script);
83+
auto anotherCtx = m_code->createExecutionContext(&anotherThread);
84+
ASSERT_FALSE(m_code->isFinished(anotherCtx.get()));
85+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
86+
87+
EXPECT_CALL(*m_code, runScript(anotherCtx.get(), &anotherTarget, anotherTarget.variableData(), anotherTarget.listData()));
88+
m_code->run(anotherCtx.get());
89+
ASSERT_TRUE(m_code->isFinished(anotherCtx.get()));
90+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
91+
92+
m_code->reset(ctx.get());
93+
ASSERT_TRUE(m_code->isFinished(anotherCtx.get()));
94+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
95+
}
96+
97+
TEST_F(CppExecutableCodeTest, PredicateFunction)
98+
{
99+
m_target.addVariable(std::make_shared<Variable>("", ""));
100+
m_target.addList(std::make_shared<List>("", ""));
101+
102+
Thread thread(&m_target, &m_engine, m_script.get());
103+
auto ctx = m_code->createExecutionContext(&thread);
104+
105+
EXPECT_CALL(*m_code, runPredicate(ctx.get(), &m_target, m_target.variableData(), m_target.listData())).WillOnce(Return(true));
106+
ASSERT_TRUE(std::static_pointer_cast<CppExecutableCode>(m_code)->runPredicate(ctx.get()));
107+
108+
EXPECT_CALL(*m_code, runPredicate(ctx.get(), &m_target, m_target.variableData(), m_target.listData())).WillOnce(Return(true));
109+
ASSERT_TRUE(std::static_pointer_cast<CppExecutableCode>(m_code)->runPredicate(ctx.get()));
110+
111+
EXPECT_CALL(*m_code, runPredicate(ctx.get(), &m_target, m_target.variableData(), m_target.listData())).WillOnce(Return(false));
112+
ASSERT_FALSE(std::static_pointer_cast<CppExecutableCode>(m_code)->runPredicate(ctx.get()));
113+
114+
EXPECT_CALL(*m_code, runPredicate(ctx.get(), &m_target, m_target.variableData(), m_target.listData())).WillOnce(Return(false));
115+
ASSERT_FALSE(std::static_pointer_cast<CppExecutableCode>(m_code)->runPredicate(ctx.get()));
116+
}
117+
118+
TEST_F(CppExecutableCodeTest, ReporterFunction)
119+
{
120+
m_target.addVariable(std::make_shared<Variable>("", ""));
121+
m_target.addList(std::make_shared<List>("", ""));
122+
123+
Thread thread(&m_target, &m_engine, m_script.get());
124+
auto ctx = m_code->createExecutionContext(&thread);
125+
126+
Value v = "test";
127+
EXPECT_CALL(*m_code, runReporter(ctx.get(), &m_target, m_target.variableData(), m_target.listData())).WillOnce(Return(v.data()));
128+
ASSERT_EQ(Value(std::static_pointer_cast<CppExecutableCode>(m_code)->runReporter(ctx.get())).toString(), v.toString());
129+
}
130+
131+
TEST_F(CppExecutableCodeTest, Promise)
132+
{
133+
Thread thread(&m_target, &m_engine, m_script.get());
134+
auto ctx = m_code->createExecutionContext(&thread);
135+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
136+
137+
// run()
138+
auto promise = std::make_shared<Promise>();
139+
ctx->setPromise(promise);
140+
EXPECT_CALL(*m_code, runScript).Times(0);
141+
142+
for (int i = 0; i < 10; i++) {
143+
m_code->run(ctx.get());
144+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
145+
}
146+
147+
promise->resolve();
148+
149+
EXPECT_CALL(*m_code, runScript);
150+
m_code->run(ctx.get());
151+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
152+
ASSERT_EQ(ctx->promise(), nullptr);
153+
m_code->reset(ctx.get());
154+
155+
// kill()
156+
promise = std::make_shared<Promise>();
157+
ctx->setPromise(promise);
158+
EXPECT_CALL(*m_code, runScript).Times(0);
159+
160+
for (int i = 0; i < 10; i++) {
161+
m_code->run(ctx.get());
162+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
163+
}
164+
165+
m_code->kill(ctx.get());
166+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
167+
ASSERT_EQ(ctx->promise(), nullptr);
168+
m_code->reset(ctx.get());
169+
170+
// reset()
171+
promise = std::make_shared<Promise>();
172+
ctx->setPromise(promise);
173+
EXPECT_CALL(*m_code, runScript).Times(0);
174+
175+
for (int i = 0; i < 10; i++) {
176+
m_code->run(ctx.get());
177+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
178+
}
179+
180+
m_code->reset(ctx.get());
181+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
182+
ASSERT_EQ(ctx->promise(), nullptr);
183+
184+
EXPECT_CALL(*m_code, runScript);
185+
m_code->run(ctx.get());
186+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
187+
188+
// Test with another context
189+
Target anotherTarget;
190+
Script script(&anotherTarget, nullptr, &m_engine);
191+
script.setCode(m_code);
192+
Thread anotherThread(&anotherTarget, &m_engine, &script);
193+
auto anotherCtx = m_code->createExecutionContext(&anotherThread);
194+
ASSERT_FALSE(m_code->isFinished(anotherCtx.get()));
195+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
196+
197+
promise = std::make_shared<Promise>();
198+
anotherCtx->setPromise(promise);
199+
EXPECT_CALL(*m_code, runScript).Times(0);
200+
201+
for (int i = 0; i < 10; i++) {
202+
m_code->run(anotherCtx.get());
203+
ASSERT_FALSE(m_code->isFinished(anotherCtx.get()));
204+
}
205+
206+
promise->resolve();
207+
208+
EXPECT_CALL(*m_code, runScript);
209+
m_code->run(anotherCtx.get());
210+
ASSERT_TRUE(m_code->isFinished(anotherCtx.get()));
211+
ASSERT_TRUE(m_code->isFinished(ctx.get()));
212+
213+
m_code->reset(ctx.get());
214+
ASSERT_TRUE(m_code->isFinished(anotherCtx.get()));
215+
ASSERT_FALSE(m_code->isFinished(ctx.get()));
216+
}

0 commit comments

Comments
 (0)