Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/scratchcpp/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
Number,
Bool,
String,
Pointer,
Unknown
};

Expand Down
19 changes: 19 additions & 0 deletions include/scratchcpp/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ class LIBSCRATCHCPP_EXPORT Value
value_assign_cstring(&m_data, stringValue);
}

/*! Constructs a pointer Value. */
Value(const void *pointerValue)
{
value_init(&m_data);
value_assign_pointer(&m_data, pointerValue);
}

/*! Constructs value from ValueData. */
Value(const ValueData &v)
{
Expand Down Expand Up @@ -111,6 +118,9 @@ class LIBSCRATCHCPP_EXPORT Value
/*! Returns true if the value is a string. */
bool isString() const { return value_isString(&m_data); }

/*! Returns true if the value is a pointer. */
bool isPointer() const { return value_isPointer(&m_data); }

/*! Returns the int representation of the value. */
int toInt() const { return value_toInt(&m_data); }

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

/*! Returns the pointer stored in the value or nullptr if it isn't a pointer. */
const void *toPointer() const { return value_toPointer(&m_data); };

/*! Adds the given value to the value. */
void add(const Value &v) { value_add(&m_data, &v.m_data, &m_data); }

Expand Down Expand Up @@ -188,6 +201,12 @@ class LIBSCRATCHCPP_EXPORT Value
return *this;
}

const Value &operator=(const void *v)
{
value_assign_pointer(&m_data, v);
return *this;
}

const Value &operator=(const ValueData &v)
{
value_assign_copy(&m_data, &v);
Expand Down
3 changes: 3 additions & 0 deletions include/scratchcpp/value_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ extern "C"
LIBSCRATCHCPP_EXPORT void value_assign_string(ValueData *v, const std::string &stringValue);
LIBSCRATCHCPP_EXPORT void value_assign_cstring(ValueData *v, const char *stringValue);
LIBSCRATCHCPP_EXPORT void value_assign_stringPtr(ValueData *v, const StringPtr *stringValue);
LIBSCRATCHCPP_EXPORT void value_assign_pointer(ValueData *v, const void *pointerValue);
LIBSCRATCHCPP_EXPORT void value_assign_copy(ValueData *v, const ValueData *another);

LIBSCRATCHCPP_EXPORT bool value_isInfinity(const ValueData *v);
Expand All @@ -67,6 +68,7 @@ extern "C"
LIBSCRATCHCPP_EXPORT bool value_isInt(const ValueData *v);
LIBSCRATCHCPP_EXPORT bool value_isBool(const ValueData *v);
LIBSCRATCHCPP_EXPORT bool value_isString(const ValueData *v);
LIBSCRATCHCPP_EXPORT bool value_isPointer(const ValueData *v);

LIBSCRATCHCPP_EXPORT long value_toLong(const ValueData *v);
LIBSCRATCHCPP_EXPORT int value_toInt(const ValueData *v);
Expand All @@ -76,6 +78,7 @@ extern "C"
LIBSCRATCHCPP_EXPORT StringPtr *value_toStringPtr(const ValueData *v);
LIBSCRATCHCPP_EXPORT void value_toUtf16(const ValueData *v, std::u16string *dst);
LIBSCRATCHCPP_EXPORT Rgb value_toRgba(const ValueData *v);
LIBSCRATCHCPP_EXPORT const void *value_toPointer(const ValueData *v);

LIBSCRATCHCPP_EXPORT bool value_doubleIsInt(double v);

Expand Down
4 changes: 3 additions & 1 deletion include/scratchcpp/valuedata.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ enum class LIBSCRATCHCPP_EXPORT ValueType
{
Number = 0,
Bool = 1,
String = 2
String = 2,
Pointer = 3
};

extern "C"
Expand All @@ -29,6 +30,7 @@ extern "C"
double numberValue;
bool boolValue;
StringPtr *stringValue;
const void *pointerValue;
};

ValueType type;
Expand Down
21 changes: 19 additions & 2 deletions src/engine/internal/llvm/llvmcodebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@

using namespace libscratchcpp;

static std::unordered_map<ValueType, Compiler::StaticType>
TYPE_MAP = { { ValueType::Number, Compiler::StaticType::Number }, { ValueType::Bool, Compiler::StaticType::Bool }, { ValueType::String, Compiler::StaticType::String } };
static std::unordered_map<ValueType, Compiler::StaticType> TYPE_MAP = {
{ ValueType::Number, Compiler::StaticType::Number },
{ ValueType::Bool, Compiler::StaticType::Bool },
{ ValueType::String, Compiler::StaticType::String },
{ ValueType::Pointer, Compiler::StaticType::Pointer }
};

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

case Compiler::StaticType::Pointer:
std::cerr << "error: local variables do not support pointer type" << std::endl;
break;

default:
assert(false);
break;
Expand Down Expand Up @@ -2343,6 +2351,11 @@ llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::St
return new llvm::GlobalVariable(*m_module, m_stringPtrType, true, llvm::GlobalValue::PrivateLinkage, stringStruct, "stringPtr");
}

case Compiler::StaticType::Pointer: {
llvm::Constant *addr = m_builder.getInt64((uintptr_t)value.toPointer());
return llvm::ConstantExpr::getIntToPtr(addr, m_builder.getVoidTy()->getPointerTo());
}

default:
assert(false);
return nullptr;
Expand Down Expand Up @@ -2375,6 +2388,9 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type)
case Compiler::StaticType::String:
return m_stringPtrType->getPointerTo();

case Compiler::StaticType::Pointer:
return m_builder.getVoidTy()->getPointerTo();

default:
assert(false);
return nullptr;
Expand Down Expand Up @@ -2883,6 +2899,7 @@ llvm::Value *LLVMCodeBuilder::createValue(LLVMRegister *reg)
break;

case ValueType::String:
case ValueType::Pointer:
value = llvm::ConstantExpr::getPtrToInt(value, m_valueDataType->getElementType(0));
break;

Expand Down
41 changes: 39 additions & 2 deletions src/scratch/value_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ extern "C"
string_assign(v->stringValue, stringValue);
}

/*! Assigns pointer to the given value. */
void value_assign_pointer(ValueData *v, const void *pointerValue)
{
value_free(v);

v->type = ValueType::Pointer;
v->pointerValue = pointerValue;
}

/*! Assigns another value to the given value. */
void value_assign_copy(ValueData *v, const libscratchcpp::ValueData *another)
{
Expand All @@ -98,6 +107,9 @@ extern "C"
string_assign(v->stringValue, another->stringValue);
v->type = ValueType::String;
}
} else if (another->type == ValueType::Pointer) {
value_free(v);
v->pointerValue = another->pointerValue;
}

v->type = another->type;
Expand Down Expand Up @@ -183,11 +195,20 @@ extern "C"

case ValueType::String:
return value_checkString(v->stringValue) == 1;

case ValueType::Pointer:
return false;
}

return false;
}

/*! Returns true if the given value is a pointer. */
bool value_isPointer(const ValueData *v)
{
return v->type == ValueType::Pointer;
}

/*! Returns true if the given value is a boolean. */
bool value_isBool(const libscratchcpp::ValueData *v)
{
Expand Down Expand Up @@ -250,6 +271,8 @@ extern "C"
return v->numberValue != 0 && !std::isnan(v->numberValue);
} else if (v->type == ValueType::String) {
return value_stringToBool(v->stringValue);
} else if (v->type == ValueType::Pointer) {
return v->pointerValue;
} else {
return false;
}
Expand Down Expand Up @@ -285,8 +308,11 @@ extern "C"
string_assign(ret, &FALSE_STR);
return ret;
}
} else
return string_pool_new();
} else {
StringPtr *ret = string_pool_new();
string_assign(ret, &ZERO_STR);
return ret;
}
}

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

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

/*! Returns the pointer stored in the given value or nullptr if it isn't a pointer. */
const void *value_toPointer(const ValueData *v)
{
if (v->type == ValueType::Pointer)
return v->pointerValue;
else
return nullptr;
}

/*! Returns true if the given number represents a round integer. */
bool value_doubleIsInt(double v)
{
Expand Down
4 changes: 4 additions & 0 deletions src/scratch/value_functions_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ extern "C"
return v->numberValue;
} else if (v->type == ValueType::Bool)
return v->boolValue;
else if (v->type == ValueType::Pointer)
return 0;
else {
assert(false); // this should never happen
if (ok)
Expand Down Expand Up @@ -471,6 +473,8 @@ extern "C"
return v1->numberValue - v2->numberValue;
} else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
return v1->boolValue - v2->boolValue;
else if (v1->type == ValueType::Pointer && v2->type == ValueType::Pointer)
return (long long)v1->pointerValue - (long long)v2->pointerValue; // NOTE: We probably don't need GT/LT pointer comparison...

bool ok;
double n1 = value_getNumber(v1, &ok);
Expand Down
85 changes: 68 additions & 17 deletions test/llvm/llvmcodebuilder_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,35 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls)
}
}

TEST_F(LLVMCodeBuilderTest, FunctionCallsWithPointers)
{
createBuilder(true);

int var = 12;
CompilerValue *v = m_builder->addConstValue(&var);
v = m_builder->addTargetFunctionCall("test_function_1_ptr_arg_ret", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v });

m_builder->addFunctionCall("test_print_pointer", Compiler::StaticType::Void, { Compiler::StaticType::Pointer }, { v });

auto code = m_builder->finalize();
Script script(&m_target, nullptr, nullptr);
script.setCode(code);
Thread thread(&m_target, nullptr, &script);
auto ctx = code->createExecutionContext(&thread);

std::stringstream s;
s << &m_target;
std::string ptr = s.str();

const std::string expected = "1_arg_ret 12\n" + ptr + "\n";

EXPECT_CALL(m_target, isStage());
testing::internal::CaptureStdout();
code->run(ctx.get());
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
ASSERT_TRUE(code->isFinished(ctx.get()));
}

TEST_F(LLVMCodeBuilderTest, ConstCasting)
{
createBuilder(true);
Expand Down Expand Up @@ -6093,32 +6122,54 @@ TEST_F(LLVMCodeBuilderTest, Reporters)

auto code3 = m_builder->finalize();

Script script1(&sprite, nullptr, nullptr);
script1.setCode(code1);
Thread thread1(&sprite, nullptr, &script1);
auto ctx = code1->createExecutionContext(&thread1);
// Reporter 4
createReporterBuilder(&sprite);
int pointee;
m_builder->addConstValue(&pointee);

auto code4 = m_builder->finalize();

// Reporter 5
createReporterBuilder(&sprite);
v = m_builder->addConstValue(&pointee);
m_builder->addFunctionCall("test_const_pointer", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v });

auto code5 = m_builder->finalize();

auto runReporter = [&sprite](std::shared_ptr<ExecutableCode> code) {
Script script(&sprite, nullptr, nullptr);
script.setCode(code);
Thread thread1(&sprite, nullptr, &script);
auto ctx = code->createExecutionContext(&thread1);
return code->runReporter(ctx.get());
};

ValueData ret = code1->runReporter(ctx.get());
// 1
ValueData ret = runReporter(code1);
ASSERT_TRUE(value_isNumber(&ret));
ASSERT_EQ(value_toDouble(&ret), -45.23);
value_free(&ret);

Script script2(&sprite, nullptr, nullptr);
script2.setCode(code2);
Thread thread2(&sprite, nullptr, &script2);
ctx = code2->createExecutionContext(&thread2);

ret = code2->runReporter(ctx.get());
// 2
ret = runReporter(code2);
ASSERT_EQ(Value(ret).toString(), "test");
value_free(&ret);

Script script3(&sprite, nullptr, nullptr);
script3.setCode(code3);
Thread thread3(&sprite, nullptr, &script3);
ctx = code3->createExecutionContext(&thread3);

ret = code3->runReporter(ctx.get());
// 3
ret = runReporter(code3);
var->setValue("abc"); // the string should be copied
ASSERT_EQ(Value(ret).toString(), "Hello world!");
value_free(&ret);

// 4
ret = runReporter(code4);
ASSERT_TRUE(value_isPointer(&ret));
ASSERT_EQ(value_toPointer(&ret), &pointee);
value_free(&ret);

// 5
ret = runReporter(code5);
ASSERT_TRUE(value_isPointer(&ret));
ASSERT_EQ(value_toPointer(&ret), &pointee);
value_free(&ret);
}
Loading