Skip to content

Commit 601bd29

Browse files
committed
Add runReporter() method to ExecutableCode
1 parent dc6ef49 commit 601bd29

File tree

8 files changed

+117
-30
lines changed

8 files changed

+117
-30
lines changed

include/scratchcpp/executablecode.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace libscratchcpp
1111

1212
class ExecutionContext;
1313
class Thread;
14+
struct ValueData;
1415

1516
/*! \brief The ExecutableCode class represents the code of a compiled Scratch script. */
1617
class LIBSCRATCHCPP_EXPORT ExecutableCode
@@ -21,6 +22,12 @@ class LIBSCRATCHCPP_EXPORT ExecutableCode
2122
/*! Runs the script until it finishes or yields. */
2223
virtual void run(ExecutionContext *context) = 0;
2324

25+
/*!
26+
* Runs the reporter and returns its return value.
27+
* \note Make sure to call value_free() to free the value.
28+
*/
29+
virtual ValueData runReporter(ExecutionContext *context) = 0;
30+
2431
/*! Runs the hat predicate and returns its return value. */
2532
virtual bool runPredicate(ExecutionContext *context) = 0;
2633

src/engine/internal/llvm/llvmexecutablecode.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ void LLVMExecutableCode::run(ExecutionContext *context)
6767
}
6868
}
6969

70+
ValueData LLVMExecutableCode::runReporter(ExecutionContext *context)
71+
{
72+
assert(std::holds_alternative<ReporterFunctionType>(m_mainFunction));
73+
Target *target = context->thread()->target();
74+
ReporterFunctionType f = std::get<ReporterFunctionType>(m_mainFunction);
75+
return f(context, target, target->variableData(), target->listData());
76+
}
77+
7078
bool LLVMExecutableCode::runPredicate(ExecutionContext *context)
7179
{
7280
assert(std::holds_alternative<PredicateFunctionType>(m_mainFunction));
@@ -106,7 +114,10 @@ std::shared_ptr<ExecutionContext> LLVMExecutableCode::createExecutionContext(Thr
106114
m_mainFunction = m_ctx->lookupFunction<MainFunctionType>(m_mainFunctionName);
107115
break;
108116

109-
// TODO: Implement reporter code type
117+
case Compiler::CodeType::Reporter:
118+
m_mainFunction = m_ctx->lookupFunction<ReporterFunctionType>(m_mainFunctionName);
119+
break;
120+
110121
case Compiler::CodeType::HatPredicate:
111122
m_mainFunction = m_ctx->lookupFunction<PredicateFunctionType>(m_mainFunctionName);
112123
break;

src/engine/internal/llvm/llvmexecutablecode.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class LLVMExecutableCode : public ExecutableCode
2020
LLVMExecutableCode(LLVMCompilerContext *ctx, const std::string &mainFunctionName, const std::string &resumeFunctionName, Compiler::CodeType codeType);
2121

2222
void run(ExecutionContext *context) override;
23+
ValueData runReporter(ExecutionContext *context) override;
2324
bool runPredicate(ExecutionContext *context) override;
2425
void kill(libscratchcpp::ExecutionContext *context) override;
2526
void reset(ExecutionContext *context) override;
@@ -30,6 +31,7 @@ class LLVMExecutableCode : public ExecutableCode
3031

3132
private:
3233
using MainFunctionType = void *(*)(ExecutionContext *, Target *, ValueData **, List **);
34+
using ReporterFunctionType = ValueData (*)(ExecutionContext *, Target *, ValueData **, List **);
3335
using PredicateFunctionType = bool (*)(ExecutionContext *, Target *, ValueData **, List **);
3436
using ResumeFunctionType = bool (*)(void *);
3537

@@ -41,7 +43,7 @@ class LLVMExecutableCode : public ExecutableCode
4143
std::string m_resumeFunctionName;
4244
Compiler::CodeType m_codeType;
4345

44-
mutable std::variant<MainFunctionType, PredicateFunctionType> m_mainFunction;
46+
mutable std::variant<MainFunctionType, ReporterFunctionType, PredicateFunctionType> m_mainFunction;
4547
mutable ResumeFunctionType m_resumeFunction = nullptr;
4648
};
4749

test/llvm/llvmexecutablecode_test.cpp

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <engine/internal/llvm/llvmexecutablecode.h>
99
#include <engine/internal/llvm/llvmcompilercontext.h>
1010
#include <engine/internal/llvm/llvmexecutioncontext.h>
11+
#include <engine/internal/llvm/llvmtypes.h>
1112
#include <llvm/IR/Module.h>
1213
#include <llvm/IR/IRBuilder.h>
1314
#include <enginemock.h>
@@ -29,19 +30,37 @@ class LLVMExecutableCodeTest : public testing::Test
2930
m_module = m_ctx->module();
3031
m_llvmCtx = m_ctx->llvmCtx();
3132
m_builder = std::make_unique<llvm::IRBuilder<>>(*m_llvmCtx);
33+
m_valueDataType = LLVMTypes::createValueDataType(m_builder.get());
3234
test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency
3335

3436
m_script = std::make_unique<Script>(&m_target, nullptr, &m_engine);
3537
}
3638

3739
inline llvm::Constant *nullPointer() { return llvm::ConstantPointerNull::get(llvm::PointerType::get(llvm::Type::getInt8Ty(*m_llvmCtx), 0)); }
3840

39-
llvm::Function *beginMainFunction(bool predicate = false)
41+
llvm::Function *beginMainFunction(Compiler::CodeType codeType = Compiler::CodeType::Script)
4042
{
4143
// void *f(ExecutionContext *, Target *, ValueData **, List **)
44+
// ValueData f(...) (reporters)
4245
// bool f(...) (hat predicates)
4346
llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_llvmCtx), 0);
44-
llvm::FunctionType *funcType = llvm::FunctionType::get(predicate ? m_builder->getInt1Ty() : pointerType, { pointerType, pointerType, pointerType, pointerType }, false);
47+
llvm::Type *retType = nullptr;
48+
49+
switch (codeType) {
50+
case Compiler::CodeType::Script:
51+
retType = pointerType;
52+
break;
53+
54+
case Compiler::CodeType::Reporter:
55+
retType = m_valueDataType;
56+
break;
57+
58+
case Compiler::CodeType::HatPredicate:
59+
retType = m_builder->getInt1Ty();
60+
break;
61+
}
62+
63+
llvm::FunctionType *funcType = llvm::FunctionType::get(retType, { pointerType, pointerType, pointerType, pointerType }, false);
4564
llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module);
4665

4766
llvm::BasicBlock *entry = llvm::BasicBlock::Create(*m_llvmCtx, "entry", func);
@@ -62,27 +81,22 @@ class LLVMExecutableCodeTest : public testing::Test
6281

6382
void endFunction(llvm::Value *ret) { m_builder->CreateRet(ret); }
6483

65-
void addTestFunction(llvm::Function *mainFunc)
84+
llvm::Value *addMainFunction(llvm::Function *mainFunc, const std::string testFuncName, llvm::Type *retType)
6685
{
6786
auto ptrType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_llvmCtx), 0);
68-
auto func = m_module->getOrInsertFunction("test_function", llvm::FunctionType::get(m_builder->getVoidTy(), { ptrType, ptrType, ptrType, ptrType, ptrType }, false));
87+
auto func = m_module->getOrInsertFunction(testFuncName, llvm::FunctionType::get(retType, { ptrType, ptrType, ptrType, ptrType, ptrType }, false));
6988

7089
llvm::Constant *mockInt = llvm::ConstantInt::get(llvm::Type::getInt64Ty(*m_llvmCtx), (uintptr_t)&m_mock, false);
7190
llvm::Constant *mockPtr = llvm::ConstantExpr::getIntToPtr(mockInt, ptrType);
7291

73-
m_builder->CreateCall(func, { mockPtr, mainFunc->getArg(0), mainFunc->getArg(1), mainFunc->getArg(2), mainFunc->getArg(3) });
92+
return m_builder->CreateCall(func, { mockPtr, mainFunc->getArg(0), mainFunc->getArg(1), mainFunc->getArg(2), mainFunc->getArg(3) });
7493
}
7594

76-
llvm::Value *addPredicateFunction(llvm::Function *mainFunc)
77-
{
78-
auto ptrType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_llvmCtx), 0);
79-
auto func = m_module->getOrInsertFunction("test_predicate", llvm::FunctionType::get(m_builder->getInt1Ty(), { ptrType, ptrType, ptrType, ptrType, ptrType }, false));
95+
void addTestFunction(llvm::Function *mainFunc) { addMainFunction(mainFunc, "test_function", m_builder->getVoidTy()); }
8096

81-
llvm::Constant *mockInt = llvm::ConstantInt::get(llvm::Type::getInt64Ty(*m_llvmCtx), (uintptr_t)&m_mock, false);
82-
llvm::Constant *mockPtr = llvm::ConstantExpr::getIntToPtr(mockInt, ptrType);
97+
llvm::Value *addReporterFunction(llvm::Function *mainFunc) { return addMainFunction(mainFunc, "test_reporter", m_valueDataType); }
8398

84-
return m_builder->CreateCall(func, { mockPtr, mainFunc->getArg(0), mainFunc->getArg(1), mainFunc->getArg(2), mainFunc->getArg(3) });
85-
}
99+
llvm::Value *addPredicateFunction(llvm::Function *mainFunc) { return addMainFunction(mainFunc, "test_predicate", m_builder->getInt1Ty()); }
86100

87101
void addTestPrintFunction(llvm::Value *arg1, llvm::Value *arg2)
88102
{
@@ -95,6 +109,7 @@ class LLVMExecutableCodeTest : public testing::Test
95109
llvm::LLVMContext *m_llvmCtx = nullptr;
96110
llvm::Module *m_module = nullptr;
97111
std::unique_ptr<llvm::IRBuilder<>> m_builder;
112+
llvm::StructType *m_valueDataType = nullptr;
98113
Target m_target;
99114
EngineMock m_engine;
100115
std::unique_ptr<Script> m_script;
@@ -122,7 +137,7 @@ TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
122137

123138
TEST_F(LLVMExecutableCodeTest, CreatePredicateExecutionContext)
124139
{
125-
llvm::Function *mainFunc = beginMainFunction(true);
140+
llvm::Function *mainFunc = beginMainFunction(Compiler::CodeType::HatPredicate);
126141
endFunction(m_builder->getInt1(false));
127142

128143
llvm::Function *resumeFunc = beginResumeFunction();
@@ -139,6 +154,25 @@ TEST_F(LLVMExecutableCodeTest, CreatePredicateExecutionContext)
139154
}
140155
}
141156

157+
TEST_F(LLVMExecutableCodeTest, CreateReporterExecutionContext)
158+
{
159+
llvm::Function *mainFunc = beginMainFunction(Compiler::CodeType::Reporter);
160+
endFunction(addReporterFunction(mainFunc));
161+
162+
llvm::Function *resumeFunc = beginResumeFunction();
163+
endFunction(m_builder->getInt1(true));
164+
165+
{
166+
auto code = std::make_shared<LLVMExecutableCode>(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Reporter);
167+
m_script->setCode(code);
168+
Thread thread(&m_target, &m_engine, m_script.get());
169+
auto ctx = code->createExecutionContext(&thread);
170+
ASSERT_TRUE(ctx);
171+
ASSERT_EQ(ctx->thread(), &thread);
172+
ASSERT_TRUE(dynamic_cast<LLVMExecutionContext *>(ctx.get()));
173+
}
174+
}
175+
142176
TEST_F(LLVMExecutableCodeTest, MainFunction)
143177
{
144178
m_target.addVariable(std::make_shared<Variable>("", ""));
@@ -157,7 +191,7 @@ TEST_F(LLVMExecutableCodeTest, MainFunction)
157191
auto ctx = code->createExecutionContext(&thread);
158192
ASSERT_FALSE(code->isFinished(ctx.get()));
159193

160-
EXPECT_CALL(m_mock, f(ctx.get(), &m_target, m_target.variableData(), m_target.listData()));
194+
EXPECT_CALL(m_mock, script(ctx.get(), &m_target, m_target.variableData(), m_target.listData()));
161195
code->run(ctx.get());
162196
ASSERT_TRUE(code->isFinished(ctx.get()));
163197

@@ -170,7 +204,7 @@ TEST_F(LLVMExecutableCodeTest, MainFunction)
170204
code->kill(ctx.get());
171205
ASSERT_TRUE(code->isFinished(ctx.get()));
172206

173-
EXPECT_CALL(m_mock, f).Times(0);
207+
EXPECT_CALL(m_mock, script).Times(0);
174208
code->run(ctx.get());
175209
ASSERT_TRUE(code->isFinished(ctx.get()));
176210

@@ -185,7 +219,7 @@ TEST_F(LLVMExecutableCodeTest, MainFunction)
185219
ASSERT_FALSE(code->isFinished(anotherCtx.get()));
186220
ASSERT_TRUE(code->isFinished(ctx.get()));
187221

188-
EXPECT_CALL(m_mock, f(anotherCtx.get(), &anotherTarget, anotherTarget.variableData(), anotherTarget.listData()));
222+
EXPECT_CALL(m_mock, script(anotherCtx.get(), &anotherTarget, anotherTarget.variableData(), anotherTarget.listData()));
189223
code->run(anotherCtx.get());
190224
ASSERT_TRUE(code->isFinished(anotherCtx.get()));
191225
ASSERT_TRUE(code->isFinished(ctx.get()));
@@ -200,7 +234,7 @@ TEST_F(LLVMExecutableCodeTest, PredicateFunction)
200234
m_target.addVariable(std::make_shared<Variable>("", ""));
201235
m_target.addList(std::make_shared<List>("", ""));
202236

203-
llvm::Function *mainFunc = beginMainFunction(true);
237+
llvm::Function *mainFunc = beginMainFunction(Compiler::CodeType::HatPredicate);
204238
endFunction(addPredicateFunction(mainFunc));
205239

206240
llvm::Function *resumeFunc = beginResumeFunction();
@@ -224,6 +258,27 @@ TEST_F(LLVMExecutableCodeTest, PredicateFunction)
224258
ASSERT_FALSE(code->runPredicate(ctx.get()));
225259
}
226260

261+
TEST_F(LLVMExecutableCodeTest, ReporterFunction)
262+
{
263+
m_target.addVariable(std::make_shared<Variable>("", ""));
264+
m_target.addList(std::make_shared<List>("", ""));
265+
266+
llvm::Function *mainFunc = beginMainFunction(Compiler::CodeType::Reporter);
267+
endFunction(addReporterFunction(mainFunc));
268+
269+
llvm::Function *resumeFunc = beginResumeFunction();
270+
endFunction(m_builder->getInt1(true));
271+
272+
auto code = std::make_shared<LLVMExecutableCode>(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Reporter);
273+
m_script->setCode(code);
274+
Thread thread(&m_target, &m_engine, m_script.get());
275+
auto ctx = code->createExecutionContext(&thread);
276+
277+
Value v = "test";
278+
EXPECT_CALL(m_mock, reporter(ctx.get(), &m_target, m_target.variableData(), m_target.listData())).WillOnce(Return(v.data()));
279+
ASSERT_EQ(Value(code->runReporter(ctx.get())).toString(), v.toString());
280+
}
281+
227282
TEST_F(LLVMExecutableCodeTest, Promise)
228283
{
229284
llvm::Function *mainFunc = beginMainFunction();
@@ -242,7 +297,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
242297
// run()
243298
auto promise = std::make_shared<Promise>();
244299
ctx->setPromise(promise);
245-
EXPECT_CALL(m_mock, f).Times(0);
300+
EXPECT_CALL(m_mock, script).Times(0);
246301

247302
for (int i = 0; i < 10; i++) {
248303
code->run(ctx.get());
@@ -251,7 +306,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
251306

252307
promise->resolve();
253308

254-
EXPECT_CALL(m_mock, f);
309+
EXPECT_CALL(m_mock, script);
255310
code->run(ctx.get());
256311
ASSERT_TRUE(code->isFinished(ctx.get()));
257312
ASSERT_EQ(ctx->promise(), nullptr);
@@ -260,7 +315,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
260315
// kill()
261316
promise = std::make_shared<Promise>();
262317
ctx->setPromise(promise);
263-
EXPECT_CALL(m_mock, f).Times(0);
318+
EXPECT_CALL(m_mock, script).Times(0);
264319

265320
for (int i = 0; i < 10; i++) {
266321
code->run(ctx.get());
@@ -275,7 +330,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
275330
// reset()
276331
promise = std::make_shared<Promise>();
277332
ctx->setPromise(promise);
278-
EXPECT_CALL(m_mock, f).Times(0);
333+
EXPECT_CALL(m_mock, script).Times(0);
279334

280335
for (int i = 0; i < 10; i++) {
281336
code->run(ctx.get());
@@ -286,7 +341,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
286341
ASSERT_FALSE(code->isFinished(ctx.get()));
287342
ASSERT_EQ(ctx->promise(), nullptr);
288343

289-
EXPECT_CALL(m_mock, f);
344+
EXPECT_CALL(m_mock, script);
290345
code->run(ctx.get());
291346
ASSERT_TRUE(code->isFinished(ctx.get()));
292347

@@ -301,7 +356,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
301356

302357
promise = std::make_shared<Promise>();
303358
anotherCtx->setPromise(promise);
304-
EXPECT_CALL(m_mock, f).Times(0);
359+
EXPECT_CALL(m_mock, script).Times(0);
305360

306361
for (int i = 0; i < 10; i++) {
307362
code->run(anotherCtx.get());
@@ -310,7 +365,7 @@ TEST_F(LLVMExecutableCodeTest, Promise)
310365

311366
promise->resolve();
312367

313-
EXPECT_CALL(m_mock, f);
368+
EXPECT_CALL(m_mock, script);
314369
code->run(anotherCtx.get());
315370
ASSERT_TRUE(code->isFinished(anotherCtx.get()));
316371
ASSERT_TRUE(code->isFinished(ctx.get()));

test/llvm/testfunctions.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@ extern "C"
1818
void test_function(TestMock *mock, ExecutionContext *ctx, Target *target, ValueData **varData, List **listData)
1919
{
2020
if (mock)
21-
mock->f(ctx, target, varData, listData);
21+
mock->script(ctx, target, varData, listData);
22+
}
23+
24+
ValueData test_reporter(TestMock *mock, ExecutionContext *ctx, Target *target, ValueData **varData, List **listData)
25+
{
26+
if (mock)
27+
return mock->reporter(ctx, target, varData, listData);
28+
29+
return ValueData();
2230
}
2331

2432
bool test_predicate(TestMock *mock, ExecutionContext *ctx, Target *target, ValueData **varData, List **listData)

test/llvm/testfunctions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ struct StringPtr;
1313
extern "C"
1414
{
1515
void test_function(TestMock *mock, ExecutionContext *ctx, Target *target, ValueData **varData, List **listData);
16+
ValueData test_reporter(TestMock *mock, ExecutionContext *ctx, Target *target, ValueData **varData, List **listData);
1617
bool test_predicate(TestMock *mock, ExecutionContext *ctx, Target *target, ValueData **varData, List **listData);
1718
void test_print_function(ValueData *arg1, ValueData *arg2);
1819

test/llvm/testmock.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#pragma once
22

3+
#include <scratchcpp/valuedata.h>
34
#include <gmock/gmock.h>
45

56
namespace libscratchcpp
67
{
78

89
class Target;
9-
struct ValueData;
1010
class List;
1111

1212
class TestMock
1313
{
1414
public:
15-
MOCK_METHOD(void, f, (ExecutionContext * ctx, Target *, ValueData **, List **));
15+
MOCK_METHOD(void, script, (ExecutionContext * ctx, Target *, ValueData **, List **));
16+
MOCK_METHOD(ValueData, reporter, (ExecutionContext * ctx, Target *, ValueData **, List **));
1617
MOCK_METHOD(bool, predicate, (ExecutionContext * ctx, Target *, ValueData **, List **));
1718
};
1819

test/mocks/executablecodemock.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <scratchcpp/executablecode.h>
4+
#include <scratchcpp/valuedata.h>
45
#include <gmock/gmock.h>
56

67
using namespace libscratchcpp;
@@ -9,6 +10,7 @@ class ExecutableCodeMock : public ExecutableCode
910
{
1011
public:
1112
MOCK_METHOD(void, run, (ExecutionContext *), (override));
13+
MOCK_METHOD(ValueData, runReporter, (ExecutionContext *), (override));
1214
MOCK_METHOD(bool, runPredicate, (ExecutionContext *), (override));
1315
MOCK_METHOD(void, kill, (ExecutionContext *), (override));
1416
MOCK_METHOD(void, reset, (ExecutionContext *), (override));

0 commit comments

Comments
 (0)