From 2bca87a1b2865b21d12d6f20d9f59e8f14bb0f47 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 16 Mar 2025 13:45:04 +0100 Subject: [PATCH 1/2] Introduce pointer value type --- include/scratchcpp/value.h | 19 ++ include/scratchcpp/value_functions.h | 3 + include/scratchcpp/valuedata.h | 4 +- src/scratch/value_functions.cpp | 41 +++- src/scratch/value_functions_p.h | 4 + test/scratch_classes/monitor_test.cpp | 1 - test/scratch_classes/value_test.cpp | 282 ++++++++++++++++++++++++++ 7 files changed, 350 insertions(+), 4 deletions(-) diff --git a/include/scratchcpp/value.h b/include/scratchcpp/value.h index eb4a7f0ac..199eebebd 100644 --- a/include/scratchcpp/value.h +++ b/include/scratchcpp/value.h @@ -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) { @@ -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); } @@ -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); } @@ -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); diff --git a/include/scratchcpp/value_functions.h b/include/scratchcpp/value_functions.h index 10061e289..79738ee2c 100644 --- a/include/scratchcpp/value_functions.h +++ b/include/scratchcpp/value_functions.h @@ -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); @@ -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); @@ -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); diff --git a/include/scratchcpp/valuedata.h b/include/scratchcpp/valuedata.h index ccf1ad7ac..adfffa9fe 100644 --- a/include/scratchcpp/valuedata.h +++ b/include/scratchcpp/valuedata.h @@ -15,7 +15,8 @@ enum class LIBSCRATCHCPP_EXPORT ValueType { Number = 0, Bool = 1, - String = 2 + String = 2, + Pointer = 3 }; extern "C" @@ -29,6 +30,7 @@ extern "C" double numberValue; bool boolValue; StringPtr *stringValue; + const void *pointerValue; }; ValueType type; diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp index dde9c74c0..faaf1276b 100644 --- a/src/scratch/value_functions.cpp +++ b/src/scratch/value_functions.cpp @@ -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) { @@ -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; @@ -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) { @@ -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; } @@ -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. */ @@ -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 @@ -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) { diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h index 9a0b2b308..a55673d98 100644 --- a/src/scratch/value_functions_p.h +++ b/src/scratch/value_functions_p.h @@ -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) @@ -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); diff --git a/test/scratch_classes/monitor_test.cpp b/test/scratch_classes/monitor_test.cpp index fbab7b42a..0d4247ac1 100644 --- a/test/scratch_classes/monitor_test.cpp +++ b/test/scratch_classes/monitor_test.cpp @@ -113,7 +113,6 @@ TEST(MonitorTest, Sprite) TEST(MonitorTest, UpdateValue) { Monitor monitor("", ""); - monitor.updateValue(nullptr); monitor.updateValue("test"); MonitorHandlerMock handler; diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index ebfe3e556..12c550f30 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -19,6 +19,7 @@ TEST(ValueTest, DefaultConstructor) { Value v; ASSERT_EQ(v.toDouble(), 0); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Number); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -28,6 +29,7 @@ TEST(ValueTest, DefaultConstructor) ASSERT_TRUE(v.isInt()); ASSERT_FALSE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); } TEST(ValueTest, DoubleConstructor) @@ -35,6 +37,7 @@ TEST(ValueTest, DoubleConstructor) { Value v(static_cast(3.14)); ASSERT_EQ(v.toDouble(), 3.14); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Number); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -44,6 +47,7 @@ TEST(ValueTest, DoubleConstructor) ASSERT_FALSE(v.isInt()); ASSERT_FALSE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { @@ -102,6 +106,7 @@ TEST(ValueTest, BoolConstructor) { Value v(true); ASSERT_EQ(v.toBool(), true); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Bool); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -111,11 +116,13 @@ TEST(ValueTest, BoolConstructor) ASSERT_TRUE(v.isInt()); ASSERT_TRUE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { Value v(false); ASSERT_EQ(v.toBool(), false); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Bool); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -125,6 +132,7 @@ TEST(ValueTest, BoolConstructor) ASSERT_TRUE(v.isInt()); ASSERT_TRUE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); } } @@ -133,6 +141,7 @@ TEST(ValueTest, StdStringConstructor) { Value v(std::string("test")); ASSERT_EQ(v.toString(), "test"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -142,6 +151,7 @@ TEST(ValueTest, StdStringConstructor) ASSERT_FALSE(v.isInt()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { @@ -219,6 +229,7 @@ TEST(ValueTest, StdStringConstructor) { Value v(std::string("Infinity")); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_TRUE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -227,6 +238,7 @@ TEST(ValueTest, StdStringConstructor) ASSERT_TRUE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { @@ -243,6 +255,7 @@ TEST(ValueTest, StdStringConstructor) { Value v(std::string("NaN")); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -251,6 +264,7 @@ TEST(ValueTest, StdStringConstructor) ASSERT_FALSE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } } @@ -259,6 +273,7 @@ TEST(ValueTest, CStringConstructor) { Value v("test"); ASSERT_EQ(v.toString(), "test"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -268,10 +283,12 @@ TEST(ValueTest, CStringConstructor) ASSERT_FALSE(v.isInt()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { Value v("Infinity"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_TRUE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -280,6 +297,7 @@ TEST(ValueTest, CStringConstructor) ASSERT_TRUE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { @@ -296,6 +314,7 @@ TEST(ValueTest, CStringConstructor) { Value v("NaN"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -304,6 +323,42 @@ TEST(ValueTest, CStringConstructor) ASSERT_FALSE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); + } +} + +TEST(ValueTest, PointerConstructor) +{ + int var; + + { + Value v(&var); + ASSERT_EQ(v.toPointer(), &var); + ASSERT_EQ(v.type(), ValueType::Pointer); + ASSERT_FALSE(v.isInfinity()); + ASSERT_FALSE(v.isNegativeInfinity()); + ASSERT_FALSE(v.isNaN()); + ASSERT_FALSE(v.isNumber()); + ASSERT_FALSE(v.isValidNumber()); + ASSERT_FALSE(v.isInt()); + ASSERT_FALSE(v.isBool()); + ASSERT_FALSE(v.isString()); + ASSERT_TRUE(v.isPointer()); + } + + { + Value v((const void *)nullptr); + ASSERT_EQ(v.toPointer(), nullptr); + ASSERT_EQ(v.type(), ValueType::Pointer); + ASSERT_FALSE(v.isInfinity()); + ASSERT_FALSE(v.isNegativeInfinity()); + ASSERT_FALSE(v.isNaN()); + ASSERT_FALSE(v.isNumber()); + ASSERT_FALSE(v.isValidNumber()); + ASSERT_FALSE(v.isInt()); + ASSERT_FALSE(v.isBool()); + ASSERT_FALSE(v.isString()); + ASSERT_TRUE(v.isPointer()); } } @@ -313,6 +368,7 @@ TEST(ValueTest, CopyConstructor) Value v1(50); Value v2(v1); ASSERT_EQ(v1.toInt(), v2.toInt()); + ASSERT_EQ(v2.toPointer(), nullptr); ASSERT_EQ(v1.type(), v2.type()); ASSERT_FALSE(v2.isInfinity()); ASSERT_FALSE(v2.isNegativeInfinity()); @@ -321,6 +377,7 @@ TEST(ValueTest, CopyConstructor) ASSERT_TRUE(v2.isValidNumber()); ASSERT_FALSE(v2.isBool()); ASSERT_FALSE(v2.isString()); + ASSERT_FALSE(v2.isPointer()); } { @@ -341,6 +398,7 @@ TEST(ValueTest, CopyConstructor) Value v1(true); Value v2(v1); ASSERT_EQ(v1.toBool(), v2.toBool()); + ASSERT_EQ(v2.toPointer(), nullptr); ASSERT_EQ(v1.type(), v2.type()); ASSERT_FALSE(v2.isInfinity()); ASSERT_FALSE(v2.isNegativeInfinity()); @@ -349,12 +407,14 @@ TEST(ValueTest, CopyConstructor) ASSERT_TRUE(v2.isValidNumber()); ASSERT_TRUE(v2.isBool()); ASSERT_FALSE(v2.isString()); + ASSERT_FALSE(v2.isPointer()); } { Value v1("test"); Value v2(v1); ASSERT_EQ(v1.toString(), v2.toString()); + ASSERT_EQ(v2.toPointer(), nullptr); ASSERT_EQ(v1.type(), v2.type()); ASSERT_FALSE(v2.isInfinity()); ASSERT_FALSE(v2.isNegativeInfinity()); @@ -363,6 +423,23 @@ TEST(ValueTest, CopyConstructor) ASSERT_FALSE(v2.isValidNumber()); ASSERT_FALSE(v2.isBool()); ASSERT_TRUE(v2.isString()); + ASSERT_FALSE(v2.isPointer()); + } + + { + int var; + Value v1(&var); + Value v2(v1); + ASSERT_EQ(v2.toPointer(), &var); + ASSERT_EQ(v1.type(), v2.type()); + ASSERT_FALSE(v2.isInfinity()); + ASSERT_FALSE(v2.isNegativeInfinity()); + ASSERT_FALSE(v2.isNaN()); + ASSERT_FALSE(v2.isNumber()); + ASSERT_FALSE(v2.isValidNumber()); + ASSERT_FALSE(v2.isBool()); + ASSERT_FALSE(v2.isString()); + ASSERT_TRUE(v2.isPointer()); } } @@ -371,6 +448,7 @@ TEST(ValueTest, DoubleAssignment) Value v; v = static_cast(3.14); ASSERT_EQ(v.toDouble(), 3.14); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Number); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -379,6 +457,7 @@ TEST(ValueTest, DoubleAssignment) ASSERT_TRUE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); v = std::numeric_limits::infinity(); ASSERT_EQ(v.type(), ValueType::Number); @@ -417,6 +496,7 @@ TEST(ValueTest, BoolAssignment) Value v; v = true; ASSERT_EQ(v.toBool(), true); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Bool); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -425,12 +505,14 @@ TEST(ValueTest, BoolAssignment) ASSERT_TRUE(v.isValidNumber()); ASSERT_TRUE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { Value v; v = false; ASSERT_EQ(v.toBool(), false); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::Bool); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -439,6 +521,7 @@ TEST(ValueTest, BoolAssignment) ASSERT_TRUE(v.isValidNumber()); ASSERT_TRUE(v.isBool()); ASSERT_FALSE(v.isString()); + ASSERT_FALSE(v.isPointer()); } } @@ -448,6 +531,7 @@ TEST(ValueTest, StdStringAssignment) Value v; v = std::string("test"); ASSERT_EQ(v.toString(), "test"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -456,12 +540,14 @@ TEST(ValueTest, StdStringAssignment) ASSERT_FALSE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { Value v; v = std::string("Infinity"); ASSERT_EQ(v.toString(), "Infinity"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_TRUE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -470,6 +556,7 @@ TEST(ValueTest, StdStringAssignment) ASSERT_TRUE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { @@ -490,6 +577,7 @@ TEST(ValueTest, StdStringAssignment) Value v; v = std::string("NaN"); ASSERT_EQ(v.toString(), "NaN"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -498,6 +586,7 @@ TEST(ValueTest, StdStringAssignment) ASSERT_FALSE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } } @@ -507,6 +596,7 @@ TEST(ValueTest, CStringAssignment) Value v; v = "test"; ASSERT_EQ(v.toString(), "test"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -515,12 +605,14 @@ TEST(ValueTest, CStringAssignment) ASSERT_FALSE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { Value v; v = "Infinity"; ASSERT_EQ(v.toString(), "Infinity"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_TRUE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -529,6 +621,7 @@ TEST(ValueTest, CStringAssignment) ASSERT_TRUE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } { @@ -549,6 +642,7 @@ TEST(ValueTest, CStringAssignment) Value v; v = "NaN"; ASSERT_EQ(v.toString(), "NaN"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); ASSERT_FALSE(v.isInfinity()); ASSERT_FALSE(v.isNegativeInfinity()); @@ -557,6 +651,7 @@ TEST(ValueTest, CStringAssignment) ASSERT_FALSE(v.isValidNumber()); ASSERT_FALSE(v.isBool()); ASSERT_TRUE(v.isString()); + ASSERT_FALSE(v.isPointer()); } } @@ -568,6 +663,7 @@ TEST(ValueTest, CopyAssignment) v2 = v1; ASSERT_EQ(v2.toInt(), v1.toInt()); ASSERT_EQ(v1.type(), v2.type()); + ASSERT_EQ(v2.toPointer(), nullptr); ASSERT_FALSE(v2.isInfinity()); ASSERT_FALSE(v2.isNegativeInfinity()); ASSERT_FALSE(v2.isNaN()); @@ -575,6 +671,7 @@ TEST(ValueTest, CopyAssignment) ASSERT_TRUE(v2.isValidNumber()); ASSERT_FALSE(v2.isBool()); ASSERT_FALSE(v2.isString()); + ASSERT_FALSE(v2.isPointer()); } { @@ -597,6 +694,7 @@ TEST(ValueTest, CopyAssignment) Value v2; v2 = v1; ASSERT_EQ(v1.toBool(), v2.toBool()); + ASSERT_EQ(v2.toPointer(), nullptr); ASSERT_EQ(v1.type(), v2.type()); ASSERT_FALSE(v2.isInfinity()); ASSERT_FALSE(v2.isNegativeInfinity()); @@ -605,6 +703,7 @@ TEST(ValueTest, CopyAssignment) ASSERT_TRUE(v2.isValidNumber()); ASSERT_TRUE(v2.isBool()); ASSERT_FALSE(v2.isString()); + ASSERT_FALSE(v2.isPointer()); } { @@ -612,6 +711,7 @@ TEST(ValueTest, CopyAssignment) Value v2; v2 = v1; ASSERT_EQ(v1.toString(), v2.toString()); + ASSERT_EQ(v2.toPointer(), nullptr); ASSERT_EQ(v1.type(), v2.type()); ASSERT_FALSE(v2.isInfinity()); ASSERT_FALSE(v2.isNegativeInfinity()); @@ -620,6 +720,7 @@ TEST(ValueTest, CopyAssignment) ASSERT_FALSE(v2.isValidNumber()); ASSERT_FALSE(v2.isBool()); ASSERT_TRUE(v2.isString()); + ASSERT_FALSE(v2.isPointer()); } } @@ -673,6 +774,10 @@ TEST(ValueTest, ToInt) v = "something"; ASSERT_EQ(v.toInt(), 0); + long var; + v = &var; + ASSERT_EQ(v.toInt(), 0); + // Hex v = "0xafe"; ASSERT_TRUE(v.isString()); @@ -822,6 +927,10 @@ TEST(ValueTest, ToLong) v = "something"; ASSERT_EQ(v.toLong(), 0); + long var; + v = &var; + ASSERT_EQ(v.toLong(), 0); + // Hex v = "0xafe"; ASSERT_TRUE(v.isString()); @@ -1020,6 +1129,10 @@ TEST(ValueTest, ToDouble) v = "something"; ASSERT_EQ(v.toDouble(), 0.0); + long var; + v = &var; + ASSERT_EQ(v.toDouble(), 0); + // Hex v = "0xafe"; ASSERT_TRUE(v.isString()); @@ -1242,6 +1355,12 @@ TEST(ValueTest, ToBool) v = ""; ASSERT_EQ(v.toBool(), false); + long var; + v = &var; + ASSERT_TRUE(v.toBool()); + v = (void *)nullptr; + ASSERT_FALSE(v.toBool()); + // Hex v = "0xafe"; ASSERT_TRUE(v.isString()); @@ -1564,6 +1683,12 @@ TEST(ValueTest, ToString) ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + long var; + v = &var; + ASSERT_EQ(v.toString(), "0"); + ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); + ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + v = std::numeric_limits::infinity(); ASSERT_EQ(v.toString(), "Infinity"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); @@ -1632,6 +1757,10 @@ TEST(ValueTest, ToRgba) v = false; ASSERT_EQ(v.toRgba(), 0); + + bool var; + v = (void *)&var; + ASSERT_EQ(v.toRgba(), 0); } TEST(ValueTest, AddFunction) @@ -1685,6 +1814,17 @@ TEST(ValueTest, AddFunction) v = 1; v.add("NaN"); ASSERT_EQ(v.toInt(), 1); + + int var; + v = 50; + v.add(&var); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 50); + + v = (void *)&var; + v.add(50); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 50); } TEST(ValueTest, AddOperator) @@ -1724,6 +1864,17 @@ TEST(ValueTest, AddOperator) v = 1; ASSERT_EQ(v + "NaN", 1); + + int var; + v = 50; + v = v + &var; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 50); + + v = (void *)&var; + v = v + 50; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 50); } TEST(ValueTest, SubtractFunction) @@ -1777,6 +1928,17 @@ TEST(ValueTest, SubtractFunction) v = 1; v.subtract("NaN"); ASSERT_EQ(v.toInt(), 1); + + int var; + v = 50; + v.subtract(&var); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 50); + + v = (void *)&var; + v.subtract(50); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), -50); } TEST(ValueTest, SubtractOperator) @@ -1816,6 +1978,17 @@ TEST(ValueTest, SubtractOperator) v = 1; ASSERT_EQ(v - "NaN", 1); + + int var; + v = 50; + v = v - &var; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 50); + + v = (void *)&var; + v = v - 50; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), -50); } TEST(ValueTest, MultiplyFunction) @@ -1897,6 +2070,17 @@ TEST(ValueTest, MultiplyFunction) v = 1; v.multiply("NaN"); ASSERT_EQ(v.toInt(), 0); + + int var; + v = 50; + v.multiply(&var); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); + + v = (void *)&var; + v.multiply(50); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); } TEST(ValueTest, MultiplyOperator) @@ -1958,6 +2142,17 @@ TEST(ValueTest, MultiplyOperator) v = 1; ASSERT_EQ((v * "NaN"), 0); + + int var; + v = 50; + v = v * &var; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); + + v = (void *)&var; + v = v * 50; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); } TEST(ValueTest, DivideFunction) @@ -2071,6 +2266,17 @@ TEST(ValueTest, DivideFunction) v = 0; v.divide(0); ASSERT_TRUE(v.isNaN()); + + int var; + v = 50; + v.divide(&var); + ASSERT_TRUE(v.isNumber()); + ASSERT_TRUE(v.isInfinity()); + + v = (void *)&var; + v.divide(50); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); } TEST(ValueTest, DivideOperator) @@ -2152,6 +2358,17 @@ TEST(ValueTest, DivideOperator) v = 0; ASSERT_TRUE((v / 0).isNaN()); + + int var; + v = 50; + v = v / &var; + ASSERT_TRUE(v.isNumber()); + ASSERT_TRUE(v.isInfinity()); + + v = (void *)&var; + v = v / 50; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); } TEST(ValueTest, ModFunction) @@ -2227,6 +2444,17 @@ TEST(ValueTest, ModFunction) v = -150; v.mod("-Infinity"); ASSERT_EQ(v.toInt(), -150); + + int var; + v = 50; + v.mod(&var); + ASSERT_TRUE(v.isNumber()); + ASSERT_TRUE(v.isNaN()); + + v = (void *)&var; + v.mod(50); + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); } TEST(ValueTest, ModOperator) @@ -2284,6 +2512,17 @@ TEST(ValueTest, ModOperator) v = -150; ASSERT_EQ(v % "-Infinity", -150); + + int var; + v = 50; + v = v % &var; + ASSERT_TRUE(v.isNumber()); + ASSERT_TRUE(v.isNaN()); + + v = (void *)&var; + v = v % 50; + ASSERT_TRUE(v.isNumber()); + ASSERT_EQ(v.toDouble(), 0); } TEST(ValueTest, ComparisonOperators) @@ -2871,6 +3110,43 @@ TEST(ValueTest, ComparisonOperators) ASSERT_FALSE(v4 == v7); ASSERT_TRUE(v4 != v7); } + + { + int ptr[2]; + Value v1 = ptr; + Value v2 = ptr; + Value v3 = ptr + 1; + + ASSERT_TRUE(v1 == v2); + ASSERT_FALSE(v1 != v2); + + ASSERT_FALSE(v1 == v3); + ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); + + ASSERT_FALSE(v2 == v3); + ASSERT_TRUE(v2 != v3); + ASSERT_FALSE(v2 > v3); + ASSERT_TRUE(v2 < v3); + } + + { + int ptr[2]; + Value v1 = ptr; + Value v2 = 10; + Value v3 = 99999999999999.0; + + ASSERT_FALSE(v1 == v2); + ASSERT_TRUE(v1 != v2); + ASSERT_FALSE(v1 > v2); // pointer and number comparison is not allowed + ASSERT_TRUE(v1 < v2); + + ASSERT_FALSE(v1 == v3); + ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); + } } TEST(ValueTest, AssignStringPtr) @@ -2881,7 +3157,9 @@ TEST(ValueTest, AssignStringPtr) string_assign_cstring(str, "test"); value_assign_stringPtr(&v.data(), str); ASSERT_EQ(v.toString(), "test"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); + ASSERT_FALSE(v.isPointer()); } { @@ -2890,7 +3168,9 @@ TEST(ValueTest, AssignStringPtr) string_assign_cstring(str, "Infinity"); value_assign_stringPtr(&v.data(), str); ASSERT_EQ(v.toString(), "Infinity"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); + ASSERT_FALSE(v.isPointer()); } { @@ -2908,7 +3188,9 @@ TEST(ValueTest, AssignStringPtr) string_assign_cstring(str, "NaN"); value_assign_stringPtr(&v.data(), str); ASSERT_EQ(v.toString(), "NaN"); + ASSERT_EQ(v.toPointer(), nullptr); ASSERT_EQ(v.type(), ValueType::String); + ASSERT_FALSE(v.isPointer()); } } From feef68584f7ef87f1044f4e6cc5a69a2e9ce67df Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:05:52 +0100 Subject: [PATCH 2/2] Add support for pointer parameters and return values --- include/scratchcpp/compiler.h | 1 + src/engine/internal/llvm/llvmcodebuilder.cpp | 21 ++++- test/llvm/llvmcodebuilder_test.cpp | 85 ++++++++++++++++---- test/llvm/testfunctions.cpp | 17 ++++ test/llvm/testfunctions.h | 4 + 5 files changed, 109 insertions(+), 19 deletions(-) diff --git a/include/scratchcpp/compiler.h b/include/scratchcpp/compiler.h index d4d39378c..f718a780b 100644 --- a/include/scratchcpp/compiler.h +++ b/include/scratchcpp/compiler.h @@ -35,6 +35,7 @@ class LIBSCRATCHCPP_EXPORT Compiler Number, Bool, String, + Pointer, Unknown }; diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index e94e0701b..a26546ffa 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -20,8 +20,12 @@ using namespace libscratchcpp; -static std::unordered_map - TYPE_MAP = { { ValueType::Number, Compiler::StaticType::Number }, { ValueType::Bool, Compiler::StaticType::Bool }, { ValueType::String, Compiler::StaticType::String } }; +static std::unordered_map 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 VAR_LIST_READ_INSTRUCTIONS = { LLVMInstruction::Type::ReadVariable, LLVMInstruction::Type::GetListItem, LLVMInstruction::Type::GetListItemIndex, LLVMInstruction::Type::ListContainsItem }; @@ -683,6 +687,10 @@ std::shared_ptr 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; @@ -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; @@ -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; @@ -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; diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index b0a0418ab..a73162fc5 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -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); @@ -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 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); } diff --git a/test/llvm/testfunctions.cpp b/test/llvm/testfunctions.cpp index b592b4b81..7eaf04a0b 100644 --- a/test/llvm/testfunctions.cpp +++ b/test/llvm/testfunctions.cpp @@ -98,6 +98,13 @@ extern "C" return value_toStringPtr(&v.data()); } + const void *test_function_1_ptr_arg_ret(Target *target, const int *arg1) + { + target->isStage(); + std::cout << "1_arg_ret " << *arg1 << std::endl; + return target; + } + bool test_equals(const StringPtr *a, const StringPtr *b) { return string_compare_case_sensitive(a, b) == 0; @@ -131,6 +138,11 @@ extern "C" return ret; } + const void *test_const_pointer(const void *v) + { + return v; + } + bool test_not(bool arg) { return !arg; @@ -165,4 +177,9 @@ extern "C" { std::cout << utf8::utf16to8(std::u16string(v->data)) << std::endl; } + + void test_print_pointer(const void *v) + { + std::cout << v << std::endl; + } } diff --git a/test/llvm/testfunctions.h b/test/llvm/testfunctions.h index 6bd7becf4..94a878d62 100644 --- a/test/llvm/testfunctions.h +++ b/test/llvm/testfunctions.h @@ -27,12 +27,15 @@ extern "C" void test_function_3_args(Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3); StringPtr *test_function_3_args_ret(Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3); + const void *test_function_1_ptr_arg_ret(Target *target, const int *arg1); + bool test_equals(const StringPtr *a, const StringPtr *b); bool test_lower_than(double a, double b); bool test_not(bool arg); double test_const_number(double v); bool test_const_bool(bool v); StringPtr *test_const_string(const StringPtr *v); + const void *test_const_pointer(const void *v); void test_unreachable(); @@ -43,6 +46,7 @@ extern "C" void test_print_number(double v); void test_print_bool(bool v); void test_print_string(const StringPtr *v); + void test_print_pointer(const void *v); } } // namespace libscratchcpp