Skip to content

Commit 93e330e

Browse files
authored
Merge pull request #635 from scratchcpp/llvm_pointers
Introduce pointer value type
2 parents 3bfd0dd + feef685 commit 93e330e

File tree

12 files changed

+459
-23
lines changed

12 files changed

+459
-23
lines changed

include/scratchcpp/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
3535
Number,
3636
Bool,
3737
String,
38+
Pointer,
3839
Unknown
3940
};
4041

include/scratchcpp/value.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ class LIBSCRATCHCPP_EXPORT Value
5757
value_assign_cstring(&m_data, stringValue);
5858
}
5959

60+
/*! Constructs a pointer Value. */
61+
Value(const void *pointerValue)
62+
{
63+
value_init(&m_data);
64+
value_assign_pointer(&m_data, pointerValue);
65+
}
66+
6067
/*! Constructs value from ValueData. */
6168
Value(const ValueData &v)
6269
{
@@ -111,6 +118,9 @@ class LIBSCRATCHCPP_EXPORT Value
111118
/*! Returns true if the value is a string. */
112119
bool isString() const { return value_isString(&m_data); }
113120

121+
/*! Returns true if the value is a pointer. */
122+
bool isPointer() const { return value_isPointer(&m_data); }
123+
114124
/*! Returns the int representation of the value. */
115125
int toInt() const { return value_toInt(&m_data); }
116126

@@ -142,6 +152,9 @@ class LIBSCRATCHCPP_EXPORT Value
142152
/*! Converts the value to an RGBA quadruplet. */
143153
Rgb toRgba() const { return value_toRgba(&m_data); }
144154

155+
/*! Returns the pointer stored in the value or nullptr if it isn't a pointer. */
156+
const void *toPointer() const { return value_toPointer(&m_data); };
157+
145158
/*! Adds the given value to the value. */
146159
void add(const Value &v) { value_add(&m_data, &v.m_data, &m_data); }
147160

@@ -188,6 +201,12 @@ class LIBSCRATCHCPP_EXPORT Value
188201
return *this;
189202
}
190203

204+
const Value &operator=(const void *v)
205+
{
206+
value_assign_pointer(&m_data, v);
207+
return *this;
208+
}
209+
191210
const Value &operator=(const ValueData &v)
192211
{
193212
value_assign_copy(&m_data, &v);

include/scratchcpp/value_functions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ extern "C"
5757
LIBSCRATCHCPP_EXPORT void value_assign_string(ValueData *v, const std::string &stringValue);
5858
LIBSCRATCHCPP_EXPORT void value_assign_cstring(ValueData *v, const char *stringValue);
5959
LIBSCRATCHCPP_EXPORT void value_assign_stringPtr(ValueData *v, const StringPtr *stringValue);
60+
LIBSCRATCHCPP_EXPORT void value_assign_pointer(ValueData *v, const void *pointerValue);
6061
LIBSCRATCHCPP_EXPORT void value_assign_copy(ValueData *v, const ValueData *another);
6162

6263
LIBSCRATCHCPP_EXPORT bool value_isInfinity(const ValueData *v);
@@ -67,6 +68,7 @@ extern "C"
6768
LIBSCRATCHCPP_EXPORT bool value_isInt(const ValueData *v);
6869
LIBSCRATCHCPP_EXPORT bool value_isBool(const ValueData *v);
6970
LIBSCRATCHCPP_EXPORT bool value_isString(const ValueData *v);
71+
LIBSCRATCHCPP_EXPORT bool value_isPointer(const ValueData *v);
7072

7173
LIBSCRATCHCPP_EXPORT long value_toLong(const ValueData *v);
7274
LIBSCRATCHCPP_EXPORT int value_toInt(const ValueData *v);
@@ -76,6 +78,7 @@ extern "C"
7678
LIBSCRATCHCPP_EXPORT StringPtr *value_toStringPtr(const ValueData *v);
7779
LIBSCRATCHCPP_EXPORT void value_toUtf16(const ValueData *v, std::u16string *dst);
7880
LIBSCRATCHCPP_EXPORT Rgb value_toRgba(const ValueData *v);
81+
LIBSCRATCHCPP_EXPORT const void *value_toPointer(const ValueData *v);
7982

8083
LIBSCRATCHCPP_EXPORT bool value_doubleIsInt(double v);
8184

include/scratchcpp/valuedata.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ enum class LIBSCRATCHCPP_EXPORT ValueType
1515
{
1616
Number = 0,
1717
Bool = 1,
18-
String = 2
18+
String = 2,
19+
Pointer = 3
1920
};
2021

2122
extern "C"
@@ -29,6 +30,7 @@ extern "C"
2930
double numberValue;
3031
bool boolValue;
3132
StringPtr *stringValue;
33+
const void *pointerValue;
3234
};
3335

3436
ValueType type;

src/engine/internal/llvm/llvmcodebuilder.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020

2121
using namespace libscratchcpp;
2222

23-
static std::unordered_map<ValueType, Compiler::StaticType>
24-
TYPE_MAP = { { ValueType::Number, Compiler::StaticType::Number }, { ValueType::Bool, Compiler::StaticType::Bool }, { ValueType::String, Compiler::StaticType::String } };
23+
static std::unordered_map<ValueType, Compiler::StaticType> TYPE_MAP = {
24+
{ ValueType::Number, Compiler::StaticType::Number },
25+
{ ValueType::Bool, Compiler::StaticType::Bool },
26+
{ ValueType::String, Compiler::StaticType::String },
27+
{ ValueType::Pointer, Compiler::StaticType::Pointer }
28+
};
2529

2630
static const std::unordered_set<LLVMInstruction::Type>
2731
VAR_LIST_READ_INSTRUCTIONS = { LLVMInstruction::Type::ReadVariable, LLVMInstruction::Type::GetListItem, LLVMInstruction::Type::GetListItemIndex, LLVMInstruction::Type::ListContainsItem };
@@ -683,6 +687,10 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
683687
std::cerr << "error: local variables do not support string type" << std::endl;
684688
break;
685689

690+
case Compiler::StaticType::Pointer:
691+
std::cerr << "error: local variables do not support pointer type" << std::endl;
692+
break;
693+
686694
default:
687695
assert(false);
688696
break;
@@ -2343,6 +2351,11 @@ llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::St
23432351
return new llvm::GlobalVariable(*m_module, m_stringPtrType, true, llvm::GlobalValue::PrivateLinkage, stringStruct, "stringPtr");
23442352
}
23452353

2354+
case Compiler::StaticType::Pointer: {
2355+
llvm::Constant *addr = m_builder.getInt64((uintptr_t)value.toPointer());
2356+
return llvm::ConstantExpr::getIntToPtr(addr, m_builder.getVoidTy()->getPointerTo());
2357+
}
2358+
23462359
default:
23472360
assert(false);
23482361
return nullptr;
@@ -2375,6 +2388,9 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type)
23752388
case Compiler::StaticType::String:
23762389
return m_stringPtrType->getPointerTo();
23772390

2391+
case Compiler::StaticType::Pointer:
2392+
return m_builder.getVoidTy()->getPointerTo();
2393+
23782394
default:
23792395
assert(false);
23802396
return nullptr;
@@ -2883,6 +2899,7 @@ llvm::Value *LLVMCodeBuilder::createValue(LLVMRegister *reg)
28832899
break;
28842900

28852901
case ValueType::String:
2902+
case ValueType::Pointer:
28862903
value = llvm::ConstantExpr::getPtrToInt(value, m_valueDataType->getElementType(0));
28872904
break;
28882905

src/scratch/value_functions.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ extern "C"
8181
string_assign(v->stringValue, stringValue);
8282
}
8383

84+
/*! Assigns pointer to the given value. */
85+
void value_assign_pointer(ValueData *v, const void *pointerValue)
86+
{
87+
value_free(v);
88+
89+
v->type = ValueType::Pointer;
90+
v->pointerValue = pointerValue;
91+
}
92+
8493
/*! Assigns another value to the given value. */
8594
void value_assign_copy(ValueData *v, const libscratchcpp::ValueData *another)
8695
{
@@ -98,6 +107,9 @@ extern "C"
98107
string_assign(v->stringValue, another->stringValue);
99108
v->type = ValueType::String;
100109
}
110+
} else if (another->type == ValueType::Pointer) {
111+
value_free(v);
112+
v->pointerValue = another->pointerValue;
101113
}
102114

103115
v->type = another->type;
@@ -183,11 +195,20 @@ extern "C"
183195

184196
case ValueType::String:
185197
return value_checkString(v->stringValue) == 1;
198+
199+
case ValueType::Pointer:
200+
return false;
186201
}
187202

188203
return false;
189204
}
190205

206+
/*! Returns true if the given value is a pointer. */
207+
bool value_isPointer(const ValueData *v)
208+
{
209+
return v->type == ValueType::Pointer;
210+
}
211+
191212
/*! Returns true if the given value is a boolean. */
192213
bool value_isBool(const libscratchcpp::ValueData *v)
193214
{
@@ -250,6 +271,8 @@ extern "C"
250271
return v->numberValue != 0 && !std::isnan(v->numberValue);
251272
} else if (v->type == ValueType::String) {
252273
return value_stringToBool(v->stringValue);
274+
} else if (v->type == ValueType::Pointer) {
275+
return v->pointerValue;
253276
} else {
254277
return false;
255278
}
@@ -285,8 +308,11 @@ extern "C"
285308
string_assign(ret, &FALSE_STR);
286309
return ret;
287310
}
288-
} else
289-
return string_pool_new();
311+
} else {
312+
StringPtr *ret = string_pool_new();
313+
string_assign(ret, &ZERO_STR);
314+
return ret;
315+
}
290316
}
291317

292318
/*! Writes the UTF-16 representation of the given value to dst. */
@@ -309,6 +335,8 @@ extern "C"
309335
string = v->stringValue;
310336
else if (v->type == ValueType::Bool)
311337
return v->boolValue;
338+
else
339+
return 0;
312340

313341
if (string->size > 0 && string->data[0] == u'#') {
314342
// https://github.com/scratchfoundation/scratch-vm/blob/a4f095db5e03e072ba222fe721eeeb543c9b9c15/src/util/color.js#L60-L69
@@ -347,6 +375,15 @@ extern "C"
347375
return rgb(0, 0, 0);
348376
}
349377

378+
/*! Returns the pointer stored in the given value or nullptr if it isn't a pointer. */
379+
const void *value_toPointer(const ValueData *v)
380+
{
381+
if (v->type == ValueType::Pointer)
382+
return v->pointerValue;
383+
else
384+
return nullptr;
385+
}
386+
350387
/*! Returns true if the given number represents a round integer. */
351388
bool value_doubleIsInt(double v)
352389
{

src/scratch/value_functions_p.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ extern "C"
411411
return v->numberValue;
412412
} else if (v->type == ValueType::Bool)
413413
return v->boolValue;
414+
else if (v->type == ValueType::Pointer)
415+
return 0;
414416
else {
415417
assert(false); // this should never happen
416418
if (ok)
@@ -471,6 +473,8 @@ extern "C"
471473
return v1->numberValue - v2->numberValue;
472474
} else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
473475
return v1->boolValue - v2->boolValue;
476+
else if (v1->type == ValueType::Pointer && v2->type == ValueType::Pointer)
477+
return (long long)v1->pointerValue - (long long)v2->pointerValue; // NOTE: We probably don't need GT/LT pointer comparison...
474478

475479
bool ok;
476480
double n1 = value_getNumber(v1, &ok);

test/llvm/llvmcodebuilder_test.cpp

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,35 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls)
507507
}
508508
}
509509

510+
TEST_F(LLVMCodeBuilderTest, FunctionCallsWithPointers)
511+
{
512+
createBuilder(true);
513+
514+
int var = 12;
515+
CompilerValue *v = m_builder->addConstValue(&var);
516+
v = m_builder->addTargetFunctionCall("test_function_1_ptr_arg_ret", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v });
517+
518+
m_builder->addFunctionCall("test_print_pointer", Compiler::StaticType::Void, { Compiler::StaticType::Pointer }, { v });
519+
520+
auto code = m_builder->finalize();
521+
Script script(&m_target, nullptr, nullptr);
522+
script.setCode(code);
523+
Thread thread(&m_target, nullptr, &script);
524+
auto ctx = code->createExecutionContext(&thread);
525+
526+
std::stringstream s;
527+
s << &m_target;
528+
std::string ptr = s.str();
529+
530+
const std::string expected = "1_arg_ret 12\n" + ptr + "\n";
531+
532+
EXPECT_CALL(m_target, isStage());
533+
testing::internal::CaptureStdout();
534+
code->run(ctx.get());
535+
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
536+
ASSERT_TRUE(code->isFinished(ctx.get()));
537+
}
538+
510539
TEST_F(LLVMCodeBuilderTest, ConstCasting)
511540
{
512541
createBuilder(true);
@@ -6093,32 +6122,54 @@ TEST_F(LLVMCodeBuilderTest, Reporters)
60936122

60946123
auto code3 = m_builder->finalize();
60956124

6096-
Script script1(&sprite, nullptr, nullptr);
6097-
script1.setCode(code1);
6098-
Thread thread1(&sprite, nullptr, &script1);
6099-
auto ctx = code1->createExecutionContext(&thread1);
6125+
// Reporter 4
6126+
createReporterBuilder(&sprite);
6127+
int pointee;
6128+
m_builder->addConstValue(&pointee);
6129+
6130+
auto code4 = m_builder->finalize();
6131+
6132+
// Reporter 5
6133+
createReporterBuilder(&sprite);
6134+
v = m_builder->addConstValue(&pointee);
6135+
m_builder->addFunctionCall("test_const_pointer", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v });
6136+
6137+
auto code5 = m_builder->finalize();
6138+
6139+
auto runReporter = [&sprite](std::shared_ptr<ExecutableCode> code) {
6140+
Script script(&sprite, nullptr, nullptr);
6141+
script.setCode(code);
6142+
Thread thread1(&sprite, nullptr, &script);
6143+
auto ctx = code->createExecutionContext(&thread1);
6144+
return code->runReporter(ctx.get());
6145+
};
61006146

6101-
ValueData ret = code1->runReporter(ctx.get());
6147+
// 1
6148+
ValueData ret = runReporter(code1);
61026149
ASSERT_TRUE(value_isNumber(&ret));
61036150
ASSERT_EQ(value_toDouble(&ret), -45.23);
61046151
value_free(&ret);
61056152

6106-
Script script2(&sprite, nullptr, nullptr);
6107-
script2.setCode(code2);
6108-
Thread thread2(&sprite, nullptr, &script2);
6109-
ctx = code2->createExecutionContext(&thread2);
6110-
6111-
ret = code2->runReporter(ctx.get());
6153+
// 2
6154+
ret = runReporter(code2);
61126155
ASSERT_EQ(Value(ret).toString(), "test");
61136156
value_free(&ret);
61146157

6115-
Script script3(&sprite, nullptr, nullptr);
6116-
script3.setCode(code3);
6117-
Thread thread3(&sprite, nullptr, &script3);
6118-
ctx = code3->createExecutionContext(&thread3);
6119-
6120-
ret = code3->runReporter(ctx.get());
6158+
// 3
6159+
ret = runReporter(code3);
61216160
var->setValue("abc"); // the string should be copied
61226161
ASSERT_EQ(Value(ret).toString(), "Hello world!");
61236162
value_free(&ret);
6163+
6164+
// 4
6165+
ret = runReporter(code4);
6166+
ASSERT_TRUE(value_isPointer(&ret));
6167+
ASSERT_EQ(value_toPointer(&ret), &pointee);
6168+
value_free(&ret);
6169+
6170+
// 5
6171+
ret = runReporter(code5);
6172+
ASSERT_TRUE(value_isPointer(&ret));
6173+
ASSERT_EQ(value_toPointer(&ret), &pointee);
6174+
value_free(&ret);
61246175
}

0 commit comments

Comments
 (0)