diff --git a/include/Array.h b/include/Array.h index ed6f7386c..e95c433de 100644 --- a/include/Array.h +++ b/include/Array.h @@ -39,6 +39,7 @@ template<> struct ReturnNull { typedef Dynamic type; }; template<> struct ReturnNull { typedef Dynamic type; }; template<> struct ReturnNull { typedef Dynamic type; }; template<> struct ReturnNull { typedef Dynamic type; }; +template<> struct ReturnNull { typedef Dynamic type; }; template<> struct ReturnNull { typedef Dynamic type; }; template<> struct ReturnNull { typedef Dynamic type; }; template<> struct ReturnNull { typedef Dynamic type; }; @@ -431,6 +432,7 @@ template<> struct ArrayClassId { enum { id=hx::clsIdArrayByte }; template<> struct ArrayClassId { enum { id=hx::clsIdArrayByte }; }; template<> struct ArrayClassId { enum { id=hx::clsIdArrayShort }; }; template<> struct ArrayClassId { enum { id=hx::clsIdArrayShort }; }; +template<> struct ArrayClassId { enum { id = hx::clsIdArrayShort }; }; template<> struct ArrayClassId { enum { id=hx::clsIdArrayInt }; }; template<> struct ArrayClassId { enum { id=hx::clsIdArrayInt }; }; template<> struct ArrayClassId { enum { id=hx::clsIdArrayFloat32 }; }; diff --git a/include/Dynamic.h b/include/Dynamic.h index dbf23c836..1d655f47b 100644 --- a/include/Dynamic.h +++ b/include/Dynamic.h @@ -24,6 +24,7 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic : public hx::ObjectPtr Dynamic(unsigned short inVal); Dynamic(unsigned char inVal); Dynamic(signed char inVal); + Dynamic(char16_t inVal); Dynamic(const cpp::CppInt32__ &inVal); Dynamic(bool inVal); Dynamic(double inVal); @@ -71,6 +72,7 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic : public hx::ObjectPtr inline operator unsigned char () const { return mPtr ? mPtr->__ToInt() : 0; } inline operator char () const { return mPtr ? mPtr->__ToInt() : 0; } inline operator signed char () const { return mPtr ? mPtr->__ToInt() : 0; } + inline operator char16_t () const { return mPtr ? mPtr->__ToInt() : 0; } inline operator bool() const { return mPtr && mPtr->__ToInt(); } inline operator cpp::Int64() const { return mPtr ? mPtr->__ToInt64() : 0; } inline operator cpp::UInt64() const { return mPtr ? mPtr->__ToInt64() : 0; } @@ -171,6 +173,7 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic : public hx::ObjectPtr bool operator op (unsigned short inRHS) const { return IsNumeric() && ((double)(*this) op (double)inRHS); } \ bool operator op (signed char inRHS) const { return IsNumeric() && ((double)(*this) op (double)inRHS); } \ bool operator op (unsigned char inRHS) const { return IsNumeric() && ((double)(*this) op (double)inRHS); } \ + bool operator op (char16_t inRHS) const { return IsNumeric() && ((double)(*this) op (double)inRHS); } \ bool operator op (bool inRHS) const { return IsBool() && ((double)(*this) op (double)inRHS); } \ bool operator != (const String &inRHS) const { return !mPtr || ((String)(*this) != inRHS); } @@ -184,6 +187,7 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic : public hx::ObjectPtr bool operator != (unsigned short inRHS) const { return !IsNumeric() || ((double)(*this) != (double)inRHS); } bool operator != (signed char inRHS) const { return !IsNumeric() || ((double)(*this) != (double)inRHS); } bool operator != (unsigned char inRHS) const { return !IsNumeric() || ((double)(*this) != (double)inRHS); } + bool operator != (char16_t inRHS) const { return !IsNumeric() || ((double)(*this) != (double)inRHS); } bool operator != (bool inRHS) const { return !IsBool() || ((double)(*this) != (double)inRHS); } @@ -229,6 +233,7 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic : public hx::ObjectPtr Dynamic operator+(const unsigned short &i) const; Dynamic operator+(const signed char &i) const; Dynamic operator+(const unsigned char &i) const; + Dynamic operator+(const char16_t& i) const; Dynamic operator+(const double &d) const; Dynamic operator+(const float &d) const; Dynamic operator+(const cpp::Variant &d) const; @@ -270,6 +275,8 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic : public hx::ObjectPtr { return mPtr->__GetType()==vtInt ? Dynamic((int)(*this) op inRHS) : Dynamic((double)(*this) op inRHS); } \ Dynamic operator op (const unsigned char &inRHS) const \ { return mPtr->__GetType()==vtInt ? Dynamic((int)(*this) op inRHS) : Dynamic((double)(*this) op inRHS); } \ + Dynamic operator op (const char16_t &inRHS) const \ + { return mPtr->__GetType()==vtInt ? Dynamic((int)(*this) op inRHS) : Dynamic((double)(*this) op inRHS); } \ Dynamic operator op (const cpp::Int64 &inRHS) const \ { return Dynamic((double)(*this) op inRHS); } \ Dynamic operator op (const cpp::UInt64 &inRHS) const \ @@ -457,6 +464,7 @@ COMPARE_DYNAMIC_OP( > ) inline double operator op (const unsigned short &inLHS,const Dynamic &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const signed char &inLHS,const Dynamic &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const unsigned char &inLHS,const Dynamic &inRHS) { return inLHS op (double)inRHS; } \ + inline double operator op (const char16_t &inLHS,const Dynamic &inRHS) { return inLHS op (double)inRHS; } \ ARITH_DYNAMIC( - ) ARITH_DYNAMIC( + ) diff --git a/include/cpp/Pointer.h b/include/cpp/Pointer.h index 25c28b451..48aaa50f7 100644 --- a/include/cpp/Pointer.h +++ b/include/cpp/Pointer.h @@ -131,12 +131,15 @@ class Struct // This allows 'StaticCast' to be used from arrays typedef Dynamic Ptr; - inline Struct( ) { } + inline Struct( ) : value() { } inline Struct( const T &inRHS ) : value(inRHS) { } inline Struct( const null &) { value = T(); } inline Struct( const Reference &); inline Struct( const Dynamic &inRHS) { fromDynamic(inRHS.mPtr); } + template + Struct(TArgs... args) : value(std::forward(args)...) {} + inline Struct &operator=( const T &inRHS ) { value = inRHS; return *this; } inline Struct &operator=( const null & ) { value = T(); return *this; } inline Struct &operator=( const Dynamic &inRHS ) { return *this = Struct(inRHS); } @@ -212,6 +215,7 @@ class Pointer hx::Object *obj = inVariant.asObject(); ptr = obj ? (T*)inVariant.valObject->__GetHandle() : 0; } + inline Pointer(const ::cpp::marshal::PointerReference); template inline Pointer( const O *inValue ) : ptr( (T*) inValue) { } @@ -509,6 +513,11 @@ class Pointer_obj } + template + inline static Pointer addressOf(const ::cpp::marshal::ValueReference&); + + template + inline static Pointer addressOf(const ::cpp::marshal::PointerReference&); template inline static Pointer addressOf(T &value) { return Pointer(&value); } diff --git a/include/cpp/Variant.h b/include/cpp/Variant.h index 842b4338e..7197927a7 100644 --- a/include/cpp/Variant.h +++ b/include/cpp/Variant.h @@ -109,6 +109,7 @@ namespace cpp inline operator unsigned char () const { return asInt(); } inline operator char () const { return asInt(); } inline operator signed char () const { return asInt(); } + inline operator char16_t() const { return asInt(); } inline operator cpp::Int64 () const { return asInt64(); } inline operator cpp::UInt64 () const { return asInt64(); } inline bool operator !() const { return !asInt(); } @@ -206,6 +207,7 @@ namespace cpp inline bool operator op (unsigned short inRHS) const { return isNumeric() && (asDouble() op (double)inRHS); } \ inline bool operator op (signed char inRHS) const { return isNumeric() && (asDouble() op (double)inRHS); } \ inline bool operator op (unsigned char inRHS) const { return isNumeric() && (asDouble() op (double)inRHS); } \ + inline bool operator op (char16_t inRHS) const { return isNumeric() && (asDouble() op (double)inRHS); } \ inline bool operator op (bool inRHS) const { return isBool() && (asDouble() op (double)inRHS); } \ inline bool operator op (const Dynamic &inRHS) const { return Compare(inRHS) op 0; } \ @@ -278,6 +280,7 @@ namespace cpp inline double operator op (const unsigned int &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const signed char &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const unsigned char &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ + inline double operator op (const char16_t &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const signed short &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const unsigned short &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ inline double operator op (const cpp::Int64 &inLHS,const cpp::Variant &inRHS) { return inLHS op (double)inRHS; } \ @@ -604,6 +607,7 @@ HX_VARIANT_OP_ISEQ(short) HX_VARIANT_OP_ISEQ(unsigned short) HX_VARIANT_OP_ISEQ(signed char) HX_VARIANT_OP_ISEQ(unsigned char) +HX_VARIANT_OP_ISEQ(char16_t) HX_VARIANT_OP_ISEQ(bool) inline bool operator < (bool inLHS,const cpp::Variant &inRHS) { return false; } @@ -633,6 +637,8 @@ inline bool operator > (bool inLHS,const cpp::Variant &inRHS) { return false; } { return inRHS.isNumeric() && (inLHS op (double)inRHS); } \ inline bool operator op (unsigned char inLHS,const ::cpp::Variant &inRHS) \ { return inRHS.isNumeric() && (inLHS op (double)inRHS); } \ + inline bool operator op (char16_t inLHS,const ::cpp::Variant &inRHS) \ + { return inRHS.isNumeric() && (inLHS op (double)inRHS); } \ inline bool operator op (const null &,const ::cpp::Variant &inRHS) \ { return false; } \ diff --git a/include/cpp/marshal/Boxed.hpp b/include/cpp/marshal/Boxed.hpp new file mode 100644 index 000000000..b92556609 --- /dev/null +++ b/include/cpp/marshal/Boxed.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "Definitions.inc" + +template +inline void cpp::marshal::Boxed_obj::finalise(::hx::Object* obj) +{ + auto ptr = reinterpret_cast*>(obj); + + ptr->value.~T(); +} + +template +inline void cpp::marshal::Boxed_obj::setFinaliser(std::true_type) +{ + ::hx::GCSetFinalizer(this, finalise); +} + +template +inline void cpp::marshal::Boxed_obj::setFinaliser(std::false_type) {} + +template +inline cpp::marshal::Boxed_obj::Boxed_obj() : value() +{ + setFinaliser(std::is_destructible{}); +} + +template +inline cpp::marshal::Boxed_obj::Boxed_obj(T* ptr) : value(*ptr) +{ + setFinaliser(std::is_destructible{}); +} + +template +template +inline cpp::marshal::Boxed_obj::Boxed_obj(TArgs... args) : value(std::forward(args)...) +{ + setFinaliser(std::is_destructible{}); +} \ No newline at end of file diff --git a/include/cpp/marshal/Definitions.inc b/include/cpp/marshal/Definitions.inc new file mode 100644 index 000000000..7e70e0eb1 --- /dev/null +++ b/include/cpp/marshal/Definitions.inc @@ -0,0 +1,327 @@ +#pragma once + +#include + +namespace cpp +{ + namespace marshal + { + /// + /// Templated class to hold a non GC object on the heap. + /// If T has a destructor a finaliser is added so it is called when this object is collected. + /// + template + class Boxed_obj final : public ::hx::Object + { + static void finalise(::hx::Object* obj); + + void setFinaliser(std::true_type); + void setFinaliser(std::false_type); + + public: + T value; + + Boxed_obj(); + + Boxed_obj(T* ptr); + + template + Boxed_obj(TArgs... args); + }; + + template + class ValueType final + { + static T FromReference(const ValueReference& inRHS); + static T FromBoxed(const Boxed& inRHS); + static T FromDynamic(const Dynamic& inRHS); + + public: + T value; + + // This allows 'StaticCast' to be used from arrays + using Ptr = Dynamic; + + ValueType(); + ValueType(const ValueReference& inRHS); + ValueType(const null& inRHS); + ValueType(const Boxed& inRHS); + ValueType(const Variant& inRHS); + ValueType(const Dynamic& inRHS); + + template + ValueType(TArgs... args); + + ValueType& operator=(const ValueReference& inRHS); + ValueType& operator=(const null& inRHS); + }; + + template + class PointerType final + { + public: + using TPtr = T*; + + private: + static TPtr FromReference(const PointerReference& inRHS); + static TPtr FromBoxed(const Boxed& inRHS); + static TPtr FromDynamic(const Dynamic& inRHS); + + public: + // This allows 'StaticCast' to be used from arrays + using Ptr = Dynamic; + + TPtr value; + + PointerType(); + PointerType(TPtr inRHS); + PointerType(const PointerReference& inRHS); + PointerType(const null& inRHS); + PointerType(const Boxed& inRHS); + PointerType(const Variant& inRHS); + PointerType(const Dynamic& inRHS); + + PointerType& operator=(const PointerReference& inRHS); + PointerType& operator=(const null& inRHS); + }; + + template + class ValueReference final : public ::cpp::Reference + { + using Super = ::cpp::Reference; + + template + static O* FromDynamic(const Dynamic& inRHS); + template + static O* FromBoxed(const Boxed& inRHS); + Boxed ToBoxed() const; + + public: + // This allows 'StaticCast' to be used from arrays + using Ptr = Dynamic; + + ValueReference(); + ValueReference(const null& inRHS); + ValueReference(const ValueType& inRHS); + ValueReference(const Boxed& inRHS); + ValueReference(const T& inRHS); + ValueReference(T& inRHS); + ValueReference(const T* inRHS); + + template + ValueReference(const ValueReference& inRHS); + template + ValueReference(const ValueType& inRHS); + template + ValueReference(const Boxed& inRHS); + + ValueReference(const Variant& inRHS); + ValueReference(const Dynamic& inRHS); + + operator Dynamic() const; + operator Variant() const; + operator Boxed() const; + + T* operator->() const; + T operator*() const; + + bool operator==(const ValueReference& inRHS) const; + bool operator!=(const ValueReference& inRHS) const; + + template + K get(int index); + + template + void set(int index, K value); + }; + + template + class PointerReference final : public ::cpp::Reference + { + public: + using TPtr = T*; + + private: + using Super = ::cpp::Reference; + + template + static O* FromDynamic(const Dynamic& inRHS); + + template + static O* FromBoxed(const Boxed& inRHS); + + Boxed ToBoxed() const; + + public: + PointerReference(const null& inRHS); + PointerReference(const PointerType& inRHS); + PointerReference(const Boxed& inRHS); + + template + PointerReference(const PointerReference& inRHS); + template + PointerReference(const PointerType& inRHS); + template + PointerReference(const Boxed& inRHS); + + PointerReference(const TPtr& inRHS); + PointerReference(TPtr& inRHS); + PointerReference(const TPtr* inRHS); + PointerReference(const Variant& inRHS); + PointerReference(const Dynamic& inRHS); + + operator Dynamic() const; + operator Variant() const; + operator Boxed() const; + operator Pointer() const; + + operator TPtr&(); + operator TPtr*(); + operator void*(); + operator void**(); + + TPtr operator->() const; + + template + K get(int index); + + template + void set(int index, K value); + }; + + template + struct View final + { + ::cpp::Pointer ptr; + size_t length; + + View(::cpp::Pointer _ptr, size_t _length); + + void clear(); + void fill(T value); + bool isEmpty(); + View slice(int64_t index); + View slice(int64_t index, int64_t length); + bool tryCopyTo(const View& destination); + template View reinterpret(); + int compare(const View& inRHS); + + bool operator==(const View& inRHS) const; + bool operator!=(const View& inRHS) const; + + T& operator[] (int64_t index); + + operator void* (); + operator T* (); + operator Pointer(); + }; + + struct Marshal final + { +#ifdef HXCPP_BIG_ENDIAN + static const bool isBigEndian = true; +#else + static const bool isBigEndian = false; +#endif + + static View asView(const char* cstring); + static View asView(const char16_t* cstring); + + static View toCharView(const ::String& string); + static int toCharView(const ::String&, View buffer); + + static View toWideCharView(const ::String& string); + static int toWideCharView(const ::String& string, View buffer); + + static ::String toString(View buffer); + static ::String toString(View buffer); + + template static T read(View view); + template static ::cpp::Pointer readPointer(View view); + static int8_t readInt8(View view); + static int16_t readInt16(View view); + static int32_t readInt32(View view); + static int64_t readInt64(View view); + static uint8_t readUInt8(View view); + static uint16_t readUInt16(View view); + static uint32_t readUInt32(View view); + static uint64_t readUInt64(View view); + static float readFloat32(View view); + static double readFloat64(View view); + + template static ::cpp::Pointer readLittleEndianPointer(View view); + static int16_t readLittleEndianInt16(View view); + static int32_t readLittleEndianInt32(View view); + static int64_t readLittleEndianInt64(View view); + static uint16_t readLittleEndianUInt16(View view); + static uint32_t readLittleEndianUInt32(View view); + static uint64_t readLittleEndianUInt64(View view); + static float readLittleEndianFloat32(View view); + static double readLittleEndianFloat64(View view); + + template static ::cpp::Pointer readBigEndianPointer(View view); + static int16_t readBigEndianInt16(View view); + static int32_t readBigEndianInt32(View view); + static int64_t readBigEndianInt64(View view); + static uint16_t readBigEndianUInt16(View view); + static uint32_t readBigEndianUInt32(View view); + static uint64_t readBigEndianUInt64(View view); + static float readBigEndianFloat32(View view); + static double readBigEndianFloat64(View view); + + template static void write(View view, const T& value); + template static void writePointer(View view, const Pointer& value); + static void writeInt8(View view, const int8_t& value); + static void writeInt16(View view, const int16_t& value); + static void writeInt32(View view, const int32_t& value); + static void writeInt64(View view, const int64_t& value); + static void writeUInt8(View view, const uint8_t& value); + static void writeUInt16(View view, const uint16_t& value); + static void writeUInt32(View view, const uint32_t& value); + static void writeUInt64(View view, const uint64_t& value); + static void writeFloat32(View view, const float& value); + static void writeFloat64(View view, const double& value); + + template static void writeLittleEndianPointer(View view, const Pointer& value); + static void writeLittleEndianInt16(View view, const int16_t& value); + static void writeLittleEndianInt32(View view, const int32_t& value); + static void writeLittleEndianInt64(View view, const int64_t& value); + static void writeLittleEndianUInt16(View view, const uint16_t& value); + static void writeLittleEndianUInt32(View view, const uint32_t& value); + static void writeLittleEndianUInt64(View view, const uint64_t& value); + static void writeLittleEndianFloat32(View view, const float& value); + static void writeLittleEndianFloat64(View view, const double& value); + + template static void writeBigEndianPointer(View view, const Pointer& value); + static void writeBigEndianInt16(View view, const int16_t& value); + static void writeBigEndianInt32(View view, const int32_t& value); + static void writeBigEndianInt64(View view, const int64_t& value); + static void writeBigEndianUInt16(View view, const uint16_t& value); + static void writeBigEndianUInt32(View view, const uint32_t& value); + static void writeBigEndianUInt64(View view, const uint64_t& value); + static void writeBigEndianFloat32(View view, const float& value); + static void writeBigEndianFloat64(View view, const double& value); + }; + } +} + +// Implement some pointer helpers here + +template +inline cpp::Pointer cpp::Pointer_obj::addressOf(const ::cpp::marshal::ValueReference& ref) +{ + return Pointer(ref.ptr); +} + +template +inline cpp::Pointer cpp::Pointer_obj::addressOf(const ::cpp::marshal::PointerReference& ref) +{ + return Pointer(ref.ptr); +} + +// I'm not sure why I need this pointer ctor overload, I'm sure it was working without it at some point +template +inline cpp::Pointer::Pointer(const ::cpp::marshal::PointerReference ref) +{ + ptr = *ref.ptr; +} \ No newline at end of file diff --git a/include/cpp/marshal/Marshal.hpp b/include/cpp/marshal/Marshal.hpp new file mode 100644 index 000000000..997cf5091 --- /dev/null +++ b/include/cpp/marshal/Marshal.hpp @@ -0,0 +1,549 @@ +#pragma once + +#include "Definitions.inc" +#include +#include +#include + +namespace +{ + template + T reverse(T val) + { + auto view = cpp::marshal::View(reinterpret_cast(&val), sizeof(T)); + + std::reverse(view.ptr.ptr, view.ptr.ptr + view.length); + + return val; + } +} + +inline cpp::marshal::View cpp::marshal::Marshal::asView(const char* cstring) +{ + return cpp::marshal::View(const_cast(cstring), static_cast(std::char_traits::length(cstring))); +} + +inline cpp::marshal::View cpp::marshal::Marshal::asView(const char16_t* cstring) +{ + return cpp::marshal::View(const_cast(cstring), static_cast(std::char_traits::length(cstring))); +} + +inline cpp::marshal::View cpp::marshal::Marshal::toCharView(const ::String& string) +{ + auto length = 0; + auto ptr = string.utf8_str(nullptr, true, &length); + + return View(const_cast(ptr), length + 1); +} + +inline int cpp::marshal::Marshal::toCharView(const ::String& string, View buffer) +{ + auto length = 0; + + if (string.utf8_str(buffer, &length)) + { + return length; + } + else + { + hx::Throw(HX_CSTRING("Not enough space in the view to write the string")); + + return 0; + } +} + +inline cpp::marshal::View cpp::marshal::Marshal::toWideCharView(const ::String& string) +{ + auto length = 0; + auto ptr = string.wc_str(nullptr, &length); + + return View(const_cast(ptr), length + 1); +} + +inline int cpp::marshal::Marshal::toWideCharView(const ::String& string, View buffer) +{ + auto length = 0; + + if (string.wc_str(buffer, &length)) + { + return length; + } + else + { + hx::Throw(HX_CSTRING("Not enough space in the view to write the string")); + + return 0; + } +} + +inline ::String cpp::marshal::Marshal::toString(View buffer) +{ + return ::String::create(buffer); +} + +inline ::String cpp::marshal::Marshal::toString(View buffer) +{ + return ::String::create(buffer); +} + +template +inline T cpp::marshal::Marshal::read(View view) +{ + if (view.length < sizeof(T)) + { + hx::Throw(HX_CSTRING("View too small")); + } + + return *(reinterpret_cast(view.ptr.ptr)); +} + +template +inline ::cpp::Pointer cpp::marshal::Marshal::readPointer(View view) +{ + return read(view); +} + +inline int8_t cpp::marshal::Marshal::readInt8(View view) +{ + return read(view); +} + +inline int16_t cpp::marshal::Marshal::readInt16(View view) +{ + return read(view); +} + +inline int32_t cpp::marshal::Marshal::readInt32(View view) +{ + return read(view); +} + +inline int64_t cpp::marshal::Marshal::readInt64(View view) +{ + return read(view); +} + +inline uint8_t cpp::marshal::Marshal::readUInt8(View view) +{ + return read(view); +} + +inline uint16_t cpp::marshal::Marshal::readUInt16(View view) +{ + return read(view); +} + +inline uint32_t cpp::marshal::Marshal::readUInt32(View view) +{ + return read(view); +} + +inline uint64_t cpp::marshal::Marshal::readUInt64(View view) +{ + return read(view); +} + +inline float cpp::marshal::Marshal::readFloat32(View view) +{ + return read(view); +} + +inline double cpp::marshal::Marshal::readFloat64(View view) +{ + return read(view); +} + +template +inline void cpp::marshal::Marshal::write(View view, const T& value) +{ + if (view.length < sizeof(T)) + { + hx::Throw(HX_CSTRING("View too small")); + } + + std::memcpy(view.ptr, reinterpret_cast(&value), sizeof(T)); +} + +template +inline void cpp::marshal::Marshal::writePointer(View view, const ::cpp::Pointer& value) +{ + write(view, value.ptr); +} + +inline void cpp::marshal::Marshal::writeInt8(View view, const int8_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeInt16(View view, const int16_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeInt32(View view, const int32_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeInt64(View view, const int64_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeUInt8(View view, const uint8_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeUInt16(View view, const uint16_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeUInt32(View view, const uint32_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeUInt64(View view, const uint64_t& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeFloat32(View view, const float& value) +{ + write(view, value); +} + +inline void cpp::marshal::Marshal::writeFloat64(View view, const double& value) +{ + write(view, value); +} + +template +inline ::cpp::Pointer cpp::marshal::Marshal::readBigEndianPointer(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readPointer(view); +#else + return reverse(readPointer(view)); +#endif +} + +inline int16_t cpp::marshal::Marshal::readBigEndianInt16(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readInt16(view); +#else + return reverse(readInt16(view)); +#endif +} + +inline int32_t cpp::marshal::Marshal::readBigEndianInt32(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readInt32(view); +#else + return reverse(readInt32(view)); +#endif +} + +inline int64_t cpp::marshal::Marshal::readBigEndianInt64(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readInt64(view); +#else + return reverse(readInt64(view)); +#endif +} + +inline uint16_t cpp::marshal::Marshal::readBigEndianUInt16(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readUInt16(view); +#else + return reverse(readUInt16(view)); +#endif +} + +inline uint32_t cpp::marshal::Marshal::readBigEndianUInt32(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readUInt32(view); +#else + return reverse(readUInt32(view)); +#endif +} + +inline uint64_t cpp::marshal::Marshal::readBigEndianUInt64(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readInt64(view); +#else + return reverse(readUInt64(view)); +#endif +} + +inline float cpp::marshal::Marshal::readBigEndianFloat32(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readFloat32(view); +#else + return reverse(readFloat32(view)); +#endif +} + +inline double cpp::marshal::Marshal::readBigEndianFloat64(View view) +{ +#ifdef HXCPP_BIG_ENDIAN + return readFloat64(view); +#else + return reverse(readFloat64(view)); +#endif +} + +template +inline ::cpp::Pointer cpp::marshal::Marshal::readLittleEndianPointer(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readPointer(view); +#else + return reverse(readPointer(view)); +#endif +} + +inline int16_t cpp::marshal::Marshal::readLittleEndianInt16(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readInt16(view); +#else + return reverse(readInt16(view)); +#endif +} + +inline int32_t cpp::marshal::Marshal::readLittleEndianInt32(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readInt32(view); +#else + return reverse(readInt32(view)); +#endif +} + +inline int64_t cpp::marshal::Marshal::readLittleEndianInt64(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readInt64(view); +#else + return reverse(readInt64(view)); +#endif +} + +inline uint16_t cpp::marshal::Marshal::readLittleEndianUInt16(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readUInt16(view); +#else + return reverse(readUInt16(view)); +#endif +} + +inline uint32_t cpp::marshal::Marshal::readLittleEndianUInt32(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readUInt32(view); +#else + return reverse(readUInt32(view)); +#endif +} + +inline uint64_t cpp::marshal::Marshal::readLittleEndianUInt64(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readInt64(view); +#else + return reverse(readUInt64(view)); +#endif +} + +inline float cpp::marshal::Marshal::readLittleEndianFloat32(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readFloat32(view); +#else + return reverse(readFloat32(view)); +#endif +} + +inline double cpp::marshal::Marshal::readLittleEndianFloat64(View view) +{ +#ifndef HXCPP_BIG_ENDIAN + return readFloat64(view); +#else + return reverse(readFloat64(view)); +#endif +} + +template +inline void cpp::marshal::Marshal::writeLittleEndianPointer(View view, const ::cpp::Pointer& value) +{ +#ifdef HXCPP_BIG_ENDIAN + write(view, reverse(value.ptr)); +#else + write(view, value.ptr); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianInt16(View view, const int16_t& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeInt16(view, reverse(value)); +#else + writeInt16(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianInt32(View view, const int32_t& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeInt32(view, reverse(value)); +#else + writeInt32(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianInt64(View view, const int64_t& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeInt64(view, reverse(value)); +#else + writeInt64(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianUInt16(View view, const uint16_t& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeUInt16(view, reverse(value)); +#else + writeUInt16(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianUInt32(View view, const uint32_t& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeUInt32(view, reverse(value)); +#else + writeUInt32(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianUInt64(View view, const uint64_t& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeUInt16(view, reverse(value)); +#else + writeUInt16(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianFloat32(View view, const float& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeFloat32(view, reverse(value)); +#else + writeFloat32(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeLittleEndianFloat64(View view, const double& value) +{ +#ifdef HXCPP_BIG_ENDIAN + writeFloat64(view, reverse(value)); +#else + writeFloat64(view, value); +#endif +} + +template +inline void cpp::marshal::Marshal::writeBigEndianPointer(View view, const ::cpp::Pointer& value) +{ +#ifndef HXCPP_BIG_ENDIAN + write(view, reverse(value.ptr)); +#else + write(view, value.ptr); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianInt16(View view, const int16_t& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeInt16(view, reverse(value)); +#else + writeInt16(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianInt32(View view, const int32_t& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeInt32(view, reverse(value)); +#else + writeInt32(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianInt64(View view, const int64_t& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeInt64(view, reverse(value)); +#else + writeInt64(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianUInt16(View view, const uint16_t& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeUInt16(view, reverse(value)); +#else + writeUInt16(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianUInt32(View view, const uint32_t& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeUInt32(view, reverse(value)); +#else + writeUInt32(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianUInt64(View view, const uint64_t& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeUInt16(view, reverse(value)); +#else + writeUInt16(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianFloat32(View view, const float& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeFloat32(view, reverse(value)); +#else + writeFloat32(view, value); +#endif +} + +inline void cpp::marshal::Marshal::writeBigEndianFloat64(View view, const double& value) +{ +#ifndef HXCPP_BIG_ENDIAN + writeFloat64(view, reverse(value)); +#else + writeFloat64(view, value); +#endif +} \ No newline at end of file diff --git a/include/cpp/marshal/PointerReference.hpp b/include/cpp/marshal/PointerReference.hpp new file mode 100644 index 000000000..41830620e --- /dev/null +++ b/include/cpp/marshal/PointerReference.hpp @@ -0,0 +1,170 @@ +#pragma once + +#include "Definitions.inc" + +template +inline cpp::marshal::Boxed cpp::marshal::PointerReference::ToBoxed() const +{ + if (nullptr == Super::ptr) + { + return new Boxed_obj(); + } + else + { + return new Boxed_obj(Super::ptr); + } +} + +template +template +inline O* cpp::marshal::PointerReference::FromBoxed(const Boxed& inRHS) +{ + if (nullptr == inRHS.mPtr) + { + return nullptr; + } + + return const_cast(&inRHS->value); +} + +template +template +inline O* cpp::marshal::PointerReference::FromDynamic(const Dynamic& inRHS) +{ + return FromBoxed(inRHS.StaticCast>()); +} + +template +inline cpp::marshal::PointerReference::PointerReference(const null& inRHS) : Super(inRHS) {} + +template +inline cpp::marshal::PointerReference::PointerReference(const PointerType& inRHS) : Super(const_cast(&inRHS.value)) {} + +template +inline cpp::marshal::PointerReference::PointerReference(const Boxed& inRHS) : Super(FromBoxed(inRHS)) {} + +template +template +inline cpp::marshal::PointerReference::PointerReference(const PointerReference& inRHS) : Super(reinterpret_cast(const_cast(inRHS.ptr))) {} + +template +template +inline cpp::marshal::PointerReference::PointerReference(const PointerType& inRHS) : Super(reinterpret_cast(const_cast(&inRHS.value))) {} + +template +template +inline cpp::marshal::PointerReference::PointerReference(const Boxed& inRHS) : Super(reinterpret_cast(FromBoxed(inRHS))) {} + +template +template +inline K cpp::marshal::PointerReference::get(int index) +{ + return (*Super::ptr)[index]; +} + +template +template +inline void cpp::marshal::PointerReference::set(int index, K value) +{ + (*Super::ptr)[index] = value; +} + +template +inline cpp::marshal::PointerReference::PointerReference(const TPtr& inRHS) : Super(inRHS) {} + +template +inline cpp::marshal::PointerReference::PointerReference(TPtr& inRHS) +{ + if (nullptr != inRHS) + { + Super::ptr = &inRHS; + } +} + +template +inline cpp::marshal::PointerReference::PointerReference(const TPtr* inRHS) : Super(inRHS) {} + +template +inline cpp::marshal::PointerReference::PointerReference(const Variant& inRHS) : Super(FromDynamic(inRHS)) {} + +template +inline cpp::marshal::PointerReference::PointerReference(const Dynamic& inRHS) : Super(FromDynamic(inRHS)) {} + +template +inline cpp::marshal::PointerReference::operator ::Dynamic() const +{ + return ToBoxed(); +} + +template +inline cpp::marshal::PointerReference::operator ::cpp::Variant() const +{ + return ToBoxed(); +} + +template +inline cpp::marshal::PointerReference::operator ::cpp::marshal::Boxed() const +{ + return ToBoxed(); +} + +template +inline cpp::marshal::PointerReference::operator ::cpp::Pointer() const +{ + if (nullptr == Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + return ::cpp::Pointer(*Super::ptr); +} + +template +inline cpp::marshal::PointerReference::operator TPtr& () +{ + if (nullptr == Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + return *Super::ptr; +} + +template +inline cpp::marshal::PointerReference::operator TPtr* () +{ + return Super::ptr; +} + +template +inline cpp::marshal::PointerReference::operator void* () +{ + if (nullptr == Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + return *Super::ptr; +} + +template +inline cpp::marshal::PointerReference::operator void** () +{ + return reinterpret_cast(Super::ptr); +} + +template +inline T* cpp::marshal::PointerReference::operator->() const +{ + if (nullptr == Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + if (nullptr == *Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + return *Super::ptr; +} \ No newline at end of file diff --git a/include/cpp/marshal/PointerType.hpp b/include/cpp/marshal/PointerType.hpp new file mode 100644 index 000000000..e2bd26af2 --- /dev/null +++ b/include/cpp/marshal/PointerType.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "Definitions.inc" + +template +inline T* cpp::marshal::PointerType::FromDynamic(const Dynamic& inRHS) +{ + return FromBoxed(inRHS.StaticCast>()); +} + +template +inline T* cpp::marshal::PointerType::FromBoxed(const Boxed& inRHS) +{ + if (nullptr == inRHS.mPtr) + { + return nullptr; + } + + return inRHS->value; +} + +template +inline T* cpp::marshal::PointerType::FromReference(const PointerReference& inRHS) +{ + if (nullptr == inRHS.ptr) + { + return nullptr; + } + + return *inRHS.ptr; +} + +template +inline cpp::marshal::PointerType::PointerType() : value(nullptr) {} + +template +inline cpp::marshal::PointerType::PointerType(TPtr inRHS) : value(inRHS) {} + +template +inline cpp::marshal::PointerType::PointerType(const PointerReference& inRHS) : value(FromReference(inRHS.ptr)) {} + +template +inline cpp::marshal::PointerType::PointerType(const null&) : value(nullptr) {} + +template +inline cpp::marshal::PointerType::PointerType(const Boxed& inRHS) : value(FromBoxed(inRHS)) {} + +template +inline cpp::marshal::PointerType::PointerType(const Variant& inRHS) : value(FromDynamic(inRHS)) {} + +template +inline cpp::marshal::PointerType::PointerType(const Dynamic& inRHS) : value(FromDynamic(inRHS)) {} + +template +inline cpp::marshal::PointerType& cpp::marshal::PointerType::operator=(const PointerReference& inRHS) +{ + value = *inRHS.ptr; + + return *this; +} + +template +inline cpp::marshal::PointerType& cpp::marshal::PointerType::operator=(const null& inRHS) +{ + value = nullptr; + + return *this; +} \ No newline at end of file diff --git a/include/cpp/marshal/ValueReference.hpp b/include/cpp/marshal/ValueReference.hpp new file mode 100644 index 000000000..3123ec1ea --- /dev/null +++ b/include/cpp/marshal/ValueReference.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include "Definitions.inc" + +template +template +inline O* cpp::marshal::ValueReference::FromDynamic(const Dynamic& inRHS) +{ + return FromBoxed(inRHS.StaticCast>()); +} + +template +template +inline O* cpp::marshal::ValueReference::FromBoxed(const Boxed& inRHS) +{ + if (nullptr == inRHS.mPtr) + { + return nullptr; + } + + return const_cast(&inRHS->value); +} + +template +inline cpp::marshal::ValueReference::ValueReference() : Super(nullptr) {} + +template +inline cpp::marshal::ValueReference::ValueReference(const null& inRHS) : Super(inRHS) {} + +template +inline cpp::marshal::ValueReference::ValueReference(const ValueType& inRHS) : Super(inRHS.value) {} + +template +inline cpp::marshal::ValueReference::ValueReference(const Boxed& inRHS) : Super(FromBoxed(inRHS)) {} + +template +inline cpp::marshal::ValueReference::ValueReference(const T& inRHS) : Super(inRHS) {} + +template +inline cpp::marshal::ValueReference::ValueReference(T& inRHS) : Super(inRHS) {} + +template +inline cpp::marshal::ValueReference::ValueReference(const T* inRHS) : Super(inRHS) {} + +template +template +inline cpp::marshal::ValueReference::ValueReference(const ValueReference& inRHS) : Super(reinterpret_cast(const_cast(inRHS.ptr))) {} + +template +template +inline cpp::marshal::ValueReference::ValueReference(const ValueType& inRHS) : Super(reinterpret_cast(const_cast(&inRHS.value))) {} + +template +template +inline cpp::marshal::ValueReference::ValueReference(const Boxed& inRHS) : Super(reinterpret_cast(FromBoxed(inRHS))) {} + +template +template +inline K cpp::marshal::ValueReference::get(int index) +{ + return (*Super::ptr)[index]; +} + +template +template +inline void cpp::marshal::ValueReference::set(int index, K value) +{ + (*Super::ptr)[index] = value; +} + +template +inline cpp::marshal::ValueReference::ValueReference(const Variant& inRHS) : Super(FromDynamic(inRHS)) {} + +template +inline cpp::marshal::ValueReference::ValueReference(const Dynamic& inRHS) : Super(FromDynamic(inRHS)) {} + +template +inline cpp::marshal::Boxed cpp::marshal::ValueReference::ToBoxed() const +{ + if (Super::ptr) + { + return Boxed(new Boxed_obj(Super::ptr)); + } + else + { + return Boxed(); + } +} + +template +inline cpp::marshal::ValueReference::operator ::Dynamic() const +{ + return ToBoxed(); +} + +template +inline cpp::marshal::ValueReference::operator ::cpp::Variant() const +{ + return ToBoxed(); +} + +template +inline cpp::marshal::ValueReference::operator ::cpp::marshal::Boxed() const +{ + return ToBoxed(); +} + +template +inline T* cpp::marshal::ValueReference::operator ->() const +{ + if (nullptr == Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + return Super::ptr; +} + +template +inline bool cpp::marshal::ValueReference::operator==(const ValueReference& inRHS) const +{ + return (*Super::ptr) == (*inRHS.ptr); +} + +template +inline bool cpp::marshal::ValueReference::operator!=(const ValueReference& inRHS) const +{ + return (*Super::ptr) != (*inRHS.ptr); +} + +template +inline T cpp::marshal::ValueReference::operator*() const +{ + if (nullptr == Super::ptr) + { + ::hx::NullReference("ValueType", true); + } + + return *Super::ptr; +} \ No newline at end of file diff --git a/include/cpp/marshal/ValueType.hpp b/include/cpp/marshal/ValueType.hpp new file mode 100644 index 000000000..70e530939 --- /dev/null +++ b/include/cpp/marshal/ValueType.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "Definitions.inc" + +template +inline T cpp::marshal::ValueType::FromDynamic(const Dynamic& inRHS) +{ + return FromBoxed(inRHS.StaticCast>()); +} + +template +inline T cpp::marshal::ValueType::FromBoxed(const Boxed& inRHS) +{ + if (nullptr == inRHS.mPtr) + { + ::hx::NullReference("ValueType", true); + } + + return inRHS->value; +} + +template +inline T cpp::marshal::ValueType::FromReference(const ValueReference& inRHS) +{ + if (nullptr == inRHS.ptr) + { + ::hx::NullReference("ValueType", true); + } + + return *inRHS.ptr; +} + +template +inline cpp::marshal::ValueType::ValueType() : value() {} + +template +inline cpp::marshal::ValueType::ValueType(const ValueReference& inRHS) : value(FromReference(inRHS.ptr)) {} + +template +inline cpp::marshal::ValueType::ValueType(const null& inRHS) : ValueType(::cpp::marshal::ValueReference(inRHS)) {} + +template +inline cpp::marshal::ValueType::ValueType(const Boxed& inRHS) : ValueType(::cpp::marshal::ValueReference(FromBoxed(inRHS))) {} + +template +inline cpp::marshal::ValueType::ValueType(const Variant& inRHS) : ValueType(::cpp::marshal::ValueReference(FromDynamic(inRHS.asDynamic()))) {} + +template +inline cpp::marshal::ValueType::ValueType(const Dynamic& inRHS) : ValueType(::cpp::marshal::ValueReference(FromDynamic(inRHS))) {} + +template +template +inline cpp::marshal::ValueType::ValueType(TArgs... args) : value(std::forward(args)...) {} + +template +inline cpp::marshal::ValueType& cpp::marshal::ValueType::operator=(const ValueReference& inRHS) +{ + if (nullptr == inRHS.ptr) + { + ::hx::NullReference("ValueType", true); + } + + value = *inRHS.ptr; + + return *this; +} + +template +inline cpp::marshal::ValueType& cpp::marshal::ValueType::operator=(const null& inRHS) +{ + ::hx::NullReference("ValueType", true); + + return *this; +} \ No newline at end of file diff --git a/include/cpp/marshal/View.hpp b/include/cpp/marshal/View.hpp new file mode 100644 index 000000000..273da0b97 --- /dev/null +++ b/include/cpp/marshal/View.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include "Definitions.inc" +#include +#include + +template +inline cpp::marshal::View::View(::cpp::Pointer _ptr, size_t _length) : ptr(_ptr), length(_length) {} + +template +inline bool cpp::marshal::View::tryCopyTo(const View& destination) +{ + if (destination.length < length) + { + return false; + } + + std::memcpy(destination.ptr.ptr, ptr.ptr, sizeof(T) * length); + + return true; +} + +template +inline void cpp::marshal::View::clear() +{ + std::memset(ptr, 0, sizeof(T) * length); +} + +template +inline void cpp::marshal::View::fill(T value) +{ + for (auto i = 0; i < length; i++) + { + ptr[i] = value; + } +} + +template +inline bool cpp::marshal::View::isEmpty() +{ + return length == 0; +} + +template +inline cpp::marshal::View cpp::marshal::View::slice(int64_t index) +{ + if (index < 0 || index > length) + { + hx::Throw(HX_CSTRING("View OOB")); + } + + return View(ptr + index, length - index); +} + +template +inline cpp::marshal::View cpp::marshal::View::slice(int64_t inIndex, int64_t inLength) +{ + if (inIndex < 0 || inLength < 0 || inIndex > length || inIndex + inLength > length) + { + hx::Throw(HX_CSTRING("View OOB")); + } + + return View(ptr + inIndex, inLength); +} + +template +template +inline cpp::marshal::View cpp::marshal::View::reinterpret() +{ + auto newPtr = ::cpp::Pointer{ ptr.reinterpret() }; + auto fromSize = sizeof(T); + auto toSize = sizeof(K); + + if (toSize == fromSize) + { + return cpp::marshal::View(newPtr, length); + } + if (toSize < fromSize) + { + return cpp::marshal::View(newPtr, length * (fromSize / toSize)); + } + + auto shrink = static_cast(fromSize) / toSize; + auto newLength = static_cast(std::floor(length * shrink)); + + return cpp::marshal::View(newPtr, newLength); +} + +template +inline int cpp::marshal::View::compare(const View& inRHS) +{ + auto common = length < inRHS.length ? length : inRHS.length; + auto result = std::memcmp(ptr.ptr, inRHS.ptr.ptr, sizeof(T) * common); + + if (result) + { + return result; + } + + return length - inRHS.length; +} + +template +inline bool cpp::marshal::View::operator==(const View& inRHS) const +{ + return length == inRHS.length && ptr.ptr == inRHS.ptr.ptr; +} + +template +inline bool cpp::marshal::View::operator!=(const View& inRHS) const +{ + return length != inRHS.length || ptr.ptr != inRHS.ptr.ptr; +} + +template +inline T& cpp::marshal::View::operator[](int64_t index) +{ + if (index < 0 || index >= length) + { + hx::Throw(HX_CSTRING("View OOB")); + } + + return ptr[index]; +} + +template +inline cpp::marshal::View::operator void* () +{ + return ptr.ptr; +} + +template +inline cpp::marshal::View::operator T* () +{ + return ptr.ptr; +} + +template +inline cpp::marshal::View::operator cpp::Pointer () +{ + return ptr; +} \ No newline at end of file diff --git a/include/hx/LessThanEq.h b/include/hx/LessThanEq.h index e87ebb06c..f5762b2a7 100644 --- a/include/hx/LessThanEq.h +++ b/include/hx/LessThanEq.h @@ -246,6 +246,33 @@ struct CompareTraits< T * > inline static bool isNull(T *inValue) { return !inValue; } }; +template +struct CompareTraits< cpp::marshal::ValueReference > +{ + enum { type = (int)CompareAsDynamic }; + + inline static int toInt(Dynamic inValue) { return inValue; } + inline static double toDouble(Dynamic inValue) { return inValue; } + inline static cpp::Int64 toInt64(Dynamic inValue) { return inValue; } + inline static String toString(Dynamic inValue) { return inValue; } + inline static hx::Object* toObject(Dynamic inValue) { return inValue.mPtr; } + inline static int getDynamicCompareType(const ::Dynamic&) { return type; } + inline static bool isNull(const ::cpp::marshal::ValueReference& ref) { return nullptr == ref.ptr; } +}; + +template +struct CompareTraits< cpp::marshal::PointerReference > +{ + enum { type = (int)CompareAsDynamic }; + + inline static int toInt(Dynamic inValue) { return inValue; } + inline static double toDouble(Dynamic inValue) { return inValue; } + inline static cpp::Int64 toInt64(Dynamic inValue) { return inValue; } + inline static String toString(Dynamic inValue) { return inValue; } + inline static hx::Object* toObject(Dynamic inValue) { return inValue.mPtr; } + inline static int getDynamicCompareType(const ::Dynamic&) { return type; } + inline static bool isNull(const ::cpp::marshal::PointerReference& ref) { return nullptr == ref.ptr || nullptr == *ref.ptr; } +}; template hx::Object *GetExistingObject(const T1 &v1) @@ -410,9 +437,37 @@ inline bool TestLessEq(const T1 &v1, const T2 &v2) template bool IsEq(const T1 &v1, const T2 &v2) { return TestLessEq(v1,v2); } +template +bool IsEq(const ::cpp::marshal::ValueReference& v1, const ::cpp::marshal::ValueReference& v2) { return v1 == v2; } + +template +bool IsEq(const ::cpp::marshal::PointerReference& v1, const ::cpp::marshal::PointerReference& v2) +{ + if (nullptr == v1.ptr && nullptr == v2.ptr) + { + return true; + } + if (nullptr == v1.ptr && nullptr != v2.ptr) + { + return nullptr == *v2.ptr; + } + if (nullptr == v2.ptr && nullptr != v1.ptr) + { + return nullptr == *v1.ptr; + } + + return *v1.ptr == *v2.ptr; +} + template bool IsNotEq(const T1 &v1, const T2 &v2) { return TestLessEq(v1,v2); } +template +bool IsNotEq(const ::cpp::marshal::ValueReference& v1, const ::cpp::marshal::ValueReference& v2) { return v1 != v2; } + +template +bool IsNotEq(const ::cpp::marshal::PointerReference& v1, const ::cpp::marshal::PointerReference& v2) { return IsEq(v1, v2) == false; } + template bool IsLess(const T1 &v1, const T2 &v2) { return TestLessEq(v1,v2); } diff --git a/include/hxString.h b/include/hxString.h index b53ddd4a3..280a8abba 100644 --- a/include/hxString.h +++ b/include/hxString.h @@ -46,6 +46,11 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES String static String create(const char16_t *inPtr,int inLen=-1); static String create(const char *inPtr,int inLen=-1); +#if (HXCPP_API_LEVEL>=500) + static String create(const ::cpp::marshal::View& buffer); + static String create(const ::cpp::marshal::View& buffer); +#endif + // Uses non-gc memory and wont ever be collected static ::String createPermanent(const char *inUtf8, int inLen); const ::String &makePermanent() const; @@ -168,6 +173,11 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES String const wchar_t *wchar_str(hx::IStringAlloc *inBuffer = 0) const; const char16_t *wc_str(hx::IStringAlloc *inBuffer = 0, int *outCharLength = 0) const; +#if (HXCPP_API_LEVEL >= 500) + bool wc_str(::cpp::marshal::View buffer, int* outCharLength = nullptr) const; + bool utf8_str(::cpp::marshal::View buffer, int* outByteLength = nullptr) const; +#endif + const char *__CStr() const { return utf8_str(); }; const wchar_t *__WCStr() const { return wchar_str(0); } inline operator const char *() { return utf8_str(); } diff --git a/include/hxcpp.h b/include/hxcpp.h index e29d62911..053a153b2 100755 --- a/include/hxcpp.h +++ b/include/hxcpp.h @@ -255,6 +255,13 @@ namespace hx { template class ObjectPtr; } namespace cpp { template class Struct; } namespace cpp { template class Pointer; } namespace cpp { template class Function; } +namespace cpp { namespace marshal { template class Boxed_obj; } } +namespace cpp { namespace marshal { template using Boxed =::hx::ObjectPtr>; } } +namespace cpp { namespace marshal { template class ValueType; } } +namespace cpp { namespace marshal { template class ValueReference; } } +namespace cpp { namespace marshal { template class PointerType; } } +namespace cpp { namespace marshal { template class PointerReference; } } +namespace cpp { namespace marshal { template class View; } } template class Array_obj; template class Array; namespace hx { @@ -344,6 +351,15 @@ typedef PropertyAccessMode PropertyAccess; #endif #include #include +#if (HXCPP_API_LEVEL>=500) + #include + #include + #include + #include + #include + #include + #include +#endif #include #include #include diff --git a/include/null.h b/include/null.h index b3a977c36..d01f97ad8 100644 --- a/include/null.h +++ b/include/null.h @@ -90,6 +90,7 @@ class null operator char () { return 0; } operator unsigned char () { return 0; } operator signed char () { return 0; } + operator char16_t () { return 0; } operator short () { return 0; } operator unsigned short () { return 0; } operator cpp::UInt64 () { return 0; } @@ -144,6 +145,7 @@ class null HX_NULL_COMPARE_OPS(unsigned short) HX_NULL_COMPARE_OPS(signed char) HX_NULL_COMPARE_OPS(unsigned char) + HX_NULL_COMPARE_OPS(char16_t) HX_NULL_COMPARE_OPS(cpp::Int64) HX_NULL_COMPARE_OPS(cpp::UInt64) HX_NULL_COMPARE_MOST_OPS(String) @@ -212,6 +214,7 @@ HX_COMPARE_NULL_OPS(short) HX_COMPARE_NULL_OPS(unsigned short) HX_COMPARE_NULL_OPS(signed char) HX_COMPARE_NULL_OPS(unsigned char) +HX_COMPARE_NULL_OPS(char16_t) HX_COMPARE_NULL_OPS(cpp::UInt64) HX_COMPARE_NULL_OPS(cpp::Int64) diff --git a/src/Dynamic.cpp b/src/Dynamic.cpp index 9d52c070e..daf7eae5a 100644 --- a/src/Dynamic.cpp +++ b/src/Dynamic.cpp @@ -397,7 +397,10 @@ Dynamic::Dynamic(signed char inVal) mPtr = fromInt(inVal); } - +Dynamic::Dynamic(char16_t inVal) +{ + mPtr = fromInt(inVal); +} Dynamic::Dynamic(double inVal) { @@ -493,6 +496,7 @@ DYN_OP_ADD(short) DYN_OP_ADD(unsigned short) DYN_OP_ADD(signed char) DYN_OP_ADD(unsigned char) +DYN_OP_ADD(char16_t) DYN_OP_ADD(cpp::Int64) DYN_OP_ADD(cpp::UInt64) diff --git a/src/String.cpp b/src/String.cpp index 0b1b3bef9..280c8cdd4 100644 --- a/src/String.cpp +++ b/src/String.cpp @@ -788,10 +788,43 @@ String String::create(const char *inString,int inLength) return String(s,len); } +#if (HXCPP_API_LEVEL>=500) +String String::create(const::cpp::marshal::View& buffer) +{ + auto start = buffer.ptr.ptr; + auto end = start + buffer.length; + + while (start < end) { + if (*start == false) + { + break; + } + start++; + } + return String::create(buffer.ptr.ptr, buffer.length - (end - start)); +} +String String::create(const cpp::marshal::View& buffer) +{ + auto start = reinterpret_cast(buffer.ptr.ptr); + auto end = start + buffer.length; + auto extra = 0; + while (start < end) { + if (Char16Advance(start) == false) + { + // set extra to 1 so we don't include the null terminating character in the calculated length. + extra = 1; + + break; + } + } + + return String::create(buffer.ptr.ptr, buffer.length - (end - start) - extra); +} +#endif String::String(const Dynamic &inRHS) { @@ -1546,7 +1579,7 @@ void __hxcpp_string_of_bytes(Array &inBytes,String &outString,int else { const unsigned char *p0 = (const unsigned char *)inBytes->GetBase(); - #ifdef HX_SMART_STRINGS +#ifdef HX_SMART_STRINGS bool hasWChar = false; const unsigned char *p = p0 + pos; for(int i=0;i &inBytes,String &outString,int outString = _hx_utf8_to_utf16(p0+pos,len,true); } else - #endif +#endif outString = String( GCStringDup((const char *)p0+pos, len, 0), len); } } @@ -1761,6 +1794,131 @@ const char16_t * String::wc_str(hx::IStringAlloc *inBuffer, int *outCharLength) return str; } +#if (HXCPP_API_LEVEL >= 500) + +bool String::wc_str(::cpp::marshal::View buffer, int* outCharLength) const +{ +#ifdef HX_SMART_STRINGS + if (isUTF16Encoded()) + { + if (buffer.length < length + 1) + { + return false; + } + + if (nullptr != outCharLength) + { + *outCharLength = length + 1; + } + + std::memcpy(buffer.ptr, __w, sizeof(char16_t) * length); + + buffer[int64_t{ length }] = 0; + + return true; + } +#endif + + auto charCount = 0; + auto source = reinterpret_cast(__s); + auto cursor = source; + auto end = source + length; + + while (cursor < end) + { + auto code = DecodeAdvanceUTF8(cursor, end); + + charCount += UTF16BytesCheck(code); + } + + if (buffer.length < charCount + 1) + { + return false; + } + + cursor = source; + auto output = buffer.ptr.ptr; + + while (cursor < end) + { + auto code = DecodeAdvanceUTF8(cursor, end); + + Char16AdvanceSet(output, code); + } + + *output = 0; + + if (nullptr != outCharLength) + { + *outCharLength = length + 1; + } + + return true; +} + +bool String::utf8_str(::cpp::marshal::View buffer, int* outByteLength) const +{ +#ifdef HX_SMART_STRINGS + if (isUTF16Encoded()) + { + auto cursor = __w; + + while (Char16Advance(cursor)) {} + + auto calculated = cursor - __w - 1; + + cursor = __w; + + auto end = cursor + calculated; + auto chars = 0; + + while (cursor < end) + { + chars += UTF8Bytes(Char16Advance(cursor)); + } + + if (buffer.length < chars + 1) + { + return false; + } + + auto output = buffer.ptr.ptr; + cursor = __w; + + while (cursor < end) + { + UTF8EncodeAdvance(output, Char16Advance(cursor)); + } + + *output = 0; + + if (nullptr != outByteLength) + { + *outByteLength = chars + 1; + } + + return true; + } +#endif + + if (buffer.length < length) + { + return false; + } + + if (nullptr != outByteLength) + { + *outByteLength = length + 1; + } + + std::memcpy(buffer.ptr, __s, sizeof(char) * length); + + buffer[int64_t{ length }] = 0; + + return true; +} + +#endif const wchar_t * String::wchar_str(hx::IStringAlloc *inBuffer) const { diff --git a/test/RunTests.hx b/test/RunTests.hx index 06f0c79c2..dd194fff9 100644 --- a/test/RunTests.hx +++ b/test/RunTests.hx @@ -56,6 +56,13 @@ class RunTests } + public static function marshalling() + { + setDir("marshalling"); + + command("haxe", [ "build.hxml" ]); + command("bin" + sep + "Main-debug", []); + } public static function debugger() { diff --git a/test/native/Native.hx b/test/native/Native.hx index acab7aada..0c8cb9506 100644 --- a/test/native/Native.hx +++ b/test/native/Native.hx @@ -1,5 +1,8 @@ package; +#if (haxe_ver>=5) +@:buildXml("") +#end class Native { static function main() @@ -12,7 +15,38 @@ class Native new tests.TestNativeGen(), new tests.TestNonVirtual(), new tests.TestPtr(), - new tests.TestNativeEnum() + new tests.TestNativeEnum(), + + #if (haxe_ver>=5) + new tests.marshalling.classes.TestLocalValueType(), + new tests.marshalling.classes.TestClassValueType(), + new tests.marshalling.classes.TestInterfaceValueType(), + new tests.marshalling.classes.TestEnumValueType(), + new tests.marshalling.classes.TestAbstractValueType(), + new tests.marshalling.classes.TestValueTypeInterop(), + new tests.marshalling.classes.TestValueTypeCollections(), + new tests.marshalling.classes.TestValueTypeFields(), + new tests.marshalling.classes.TestInheritance(), + new tests.marshalling.enums.TestValueTypeEnumAbstract(), + new tests.marshalling.enums.TestValueTypeEnumClassAbstract(), + new tests.marshalling.pointers.TestLocalPointers(), + new tests.marshalling.pointers.TestClassPointers(), + new tests.marshalling.pointers.TestInterfacePointers(), + new tests.marshalling.pointers.TestInheritancePointers(), + new tests.marshalling.pointers.TestEnumPointers(), + new tests.marshalling.pointers.TestPointerFields(), + new tests.marshalling.pointers.TestPointerCollections(), + new tests.marshalling.pointers.TestAbstractPointer(), + new tests.marshalling.pointers.TestPointerInterop(), + new tests.marshalling.managed.TestLocalNonStandardManagedClass(), + new tests.marshalling.managed.TestLocalStandardManagedClass(), + new tests.marshalling.managed.TestClassNonStandardManagedClass(), + new tests.marshalling.managed.TestClassStandardManagedClass(), + + new tests.marshalling.view.TestView(), + new tests.marshalling.view.TestMarshal(), + new tests.marshalling.view.TestViewExtensions() + #end ]); } } diff --git a/test/native/compile.hxml b/test/native/compile.hxml index b2e405bb7..a063c9266 100644 --- a/test/native/compile.hxml +++ b/test/native/compile.hxml @@ -1,4 +1,4 @@ -m Native --D HXCPP_DEBUGGER -L utest +-D HXCPP-DEBUGGER --cpp bin \ No newline at end of file diff --git a/test/native/test.txt b/test/native/test.txt new file mode 100644 index 000000000..f66c9cf4c Binary files /dev/null and b/test/native/test.txt differ diff --git a/test/native/tests/TestValueType.hx b/test/native/tests/TestValueType.hx new file mode 100644 index 000000000..e456c1ca0 --- /dev/null +++ b/test/native/tests/TestValueType.hx @@ -0,0 +1,250 @@ +package tests; + +import utest.Assert; +import utest.Test; +import cpp.Reference; + +@:include('vector') +@:cpp.ValueType({ type : 'vector', namespace : [ 'std' ], flags : [ ImplicitConstruction ] }) +private extern class StdVector implements ArrayAccess> { + @:overload(function ():Void {}) + function new(s:Int):Void; + + function size():Int; + + function resize(s:Int):Void; +} + +private extern class Helpers { + @:native('vec_by_val') + static function vec_by_val(v:StdVector):Void; + + @:native('vec_by_ref') + static function vec_by_ref(v:StdVector):Void; + + @:native('vec_by_ptr') + static function vec_by_ptr(v:StdVector):Void; +} + +@:cppNamespaceCode(' + +void vec_by_val(std::vector v) +{ + v.resize(v.size() * 2); +} + +void vec_by_ref(std::vector& v) +{ + v.resize(v.size() * 2); +} + +void vec_by_ptr(std::vector* v) +{ + v->resize(v->size() * 2); +} + +') +class TestValueType extends Test { + + /** + * Declaring a new variable of a value type will copy the right hand side value. + */ + function test_var_copying() { + final v1 = new StdVector(); + final v2 = v1; + + v1.resize(10); + + Assert.equals(10, v1.size()); + Assert.equals( 0, v2.size()); + } + + function test_var_asignment() { + var v = new StdVector(50); + + v = new StdVector(); + + Assert.equals(0, v.size()); + } + + /** + * Passing a value type into a function will pass it by value. + */ + function test_function_copying() { + final v = new StdVector(); + + by_value(v); + + Assert.equals( 0, v.size()); + } + + function by_value(v:StdVector) { + v.resize(10); + } + + /** + * Variables captured by closures will be wrapped in a type which moves them to the GC heap. + */ + function test_heap_promotion() { + final v = new StdVector(); + final f = () -> { + v.resize(10); + } + + f(); + + Assert.equals(10, v.size()); + } + + function test_nullable() { + var v : Null> = null; + + var thrown = false; + + try { + var _ = v.size(); + } + catch (exn) { + thrown = true; + } + + Assert.isTrue(thrown); + + v = new StdVector(5); + + Assert.equals(5, v.size()); + } + + /** + * Value type variables passed into a function of type dynamic will also be passed a copy. + */ + function test_dynamic() { + final v = new StdVector(5); + + by_value_dynamic(v); + + Assert.equals(5, v.size()); + } + + /** + * Value type variables which have been promoted to the heap will be copied to dynamic functions argumens. + */ + function test_promoted_dynamic() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + by_value_dynamic(v); + + Assert.equals(5, v.size()); + } + + /** + * Value type variables passed put into and accessed via an anonymous object will be promoted to the heap. + */ + function test_anon() { + final v = new StdVector(5); + + by_value_anon({ v : v }); + + Assert.equals(5, v.size()); + } + + + function test_extern_by_val() { + final v = new StdVector(5); + + Helpers.vec_by_val(v); + + Assert.equals(5, v.size()); + } + + function test_extern_by_ref() { + final v = new StdVector(5); + + Helpers.vec_by_ref(v); + + Assert.equals(10, v.size()); + } + + function test_extern_by_ptr() { + final v = new StdVector(5); + + Helpers.vec_by_ptr(v); + + Assert.equals(10, v.size()); + } + + function test_promoted_extern_by_val() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + Helpers.vec_by_val(v); + + Assert.equals(5, v.size()); + } + + function test_promoted_extern_by_ref() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + Helpers.vec_by_ref(v); + + Assert.equals(10, v.size()); + } + + function test_promoted_extern_by_ptr() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + Helpers.vec_by_ptr(v); + + Assert.equals(10, v.size()); + } + + function test_asigning_promoted_variable() { + var v = new StdVector(5); + final f = () -> { + v.resize(10); + } + + f(); + + v = new StdVector(2); + + Assert.equals(2, v.size()); + } + + // + + function by_value_anon(a:{ v : StdVector }) { + Assert.equals(5, a.v.size()); + + a.v.resize(10); + + Assert.equals(10, a.v.size()); + } + + function by_value_dynamic(v:Dynamic) { + Assert.equals(5, (v:StdVector).size()); + + (v:StdVector).resize(10); + + Assert.equals(10, (v:StdVector).size()); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/Build.xml b/test/native/tests/marshalling/Build.xml new file mode 100644 index 000000000..29f8ef243 --- /dev/null +++ b/test/native/tests/marshalling/Build.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/native/tests/marshalling/Context.hx b/test/native/tests/marshalling/Context.hx new file mode 100644 index 000000000..014899872 --- /dev/null +++ b/test/native/tests/marshalling/Context.hx @@ -0,0 +1,16 @@ +package tests.marshalling; + +@:semantics(value) +@:include('ctx.hpp') +@:cpp.PointerType({ type : 'ctx' }) +extern class Context { + public static function create() : Context; + + @:native('create_null') + public static function createNull() : Context; + + var number : Int; + + @:native('Double') + function double() : Int; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/Point.hx b/test/native/tests/marshalling/Point.hx new file mode 100644 index 000000000..dda13f517 --- /dev/null +++ b/test/native/tests/marshalling/Point.hx @@ -0,0 +1,14 @@ +package tests.marshalling; + +@:semantics(value) +@:include('point.hpp') +@:cpp.ValueType({ type : 'point', namespace : [ 'hx', 'maths' ] }) +extern class Point { + var x : Float; + var y : Float; + + @:overload(function(_x : Float, _y : Float) : Void {}) + function new(); + + static function point_vec(v:StdVector):Void; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/StdVector.hx b/test/native/tests/marshalling/StdVector.hx new file mode 100644 index 000000000..0d6780582 --- /dev/null +++ b/test/native/tests/marshalling/StdVector.hx @@ -0,0 +1,19 @@ +package tests.marshalling; + +import cpp.Reference; + +@:include('vector') +@:semantics(value) +@:cpp.ValueType({ type : 'vector', namespace : [ 'std' ] }) +extern class StdVector implements ArrayAccess { + @:overload(function ():Void {}) + function new(s:Int):Void; + + function size():Int; + + function resize(s:Int):Void; + + function at(s:Int):Reference; + + function push_back(v:T):Void; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestAbstractValueType.hx b/test/native/tests/marshalling/classes/TestAbstractValueType.hx new file mode 100644 index 000000000..652f94b3a --- /dev/null +++ b/test/native/tests/marshalling/classes/TestAbstractValueType.hx @@ -0,0 +1,54 @@ +package tests.marshalling.classes; + +import tests.marshalling.StdVector; +import utest.Assert; +import utest.Test; + +abstract MyVector(StdVector) { + public var size (get, never) : Int; + + function get_size() { + return this.size(); + } + + public function new() { + this = new StdVector(50); + } + + public function resize(_size:Int) { + this.resize(_size); + } +} + +class TestAbstractValueType extends Test { + function test_cast_to_underlying() { + final v = new MyVector(); + + Assert.equals(50, (cast v : StdVector).size()); + } + + function test_property_access() { + final v = new MyVector(); + + Assert.equals(50, v.size); + } + + function test_mutating_abstract() { + final v = new MyVector(); + + v.resize(100); + + Assert.equals(100, v.size); + Assert.equals(100, (cast v : StdVector).size()); + } + + function test_casting_copy() { + final v = new MyVector(); + final i = (cast v : StdVector); + + v.resize(100); + + Assert.equals(100, v.size); + Assert.equals( 50, i.size()); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestClassValueType.hx b/test/native/tests/marshalling/classes/TestClassValueType.hx new file mode 100644 index 000000000..dc29e794b --- /dev/null +++ b/test/native/tests/marshalling/classes/TestClassValueType.hx @@ -0,0 +1,208 @@ +package tests.marshalling.classes; + +import utest.Assert; +import utest.Test; + +class Foo { + public static var v_static = new StdVector(50); + + public var v : StdVector; + + public function new() { + v = new StdVector(7); + } + + public function get() { + return v; + } + + public function multiply(v:StdVector) { + v.resize(v.size() * 2); + + return v.size(); + } + + public dynamic function pass_through(v:StdVector) { + return v; + } + + public static function get_random(max:Int) { + return new StdVector(max + Std.random(max)); + } +} + +class WithConstruction { + public final v : StdVector; + + public function new(v) { + this.v = v; + } +} + +class TestClassValueType extends Test { + + // Member Variables + + function test_class_field_access() { + final f = new Foo(); + + Assert.equals(7, f.v.size()); + } + + function test_mutating_class_field() { + final f = new Foo(); + + f.v.resize(100); + + Assert.equals(100, f.v.size()); + } + + function test_class_field_copy() { + final f = new Foo(); + final v = f.v; + + v.resize(100); + + Assert.equals(100, v.size()); + Assert.equals(7, f.v.size()); + } + + function test_class_field_assignment() { + final f = new Foo(); + + f.v = new StdVector(100); + + Assert.equals(100, f.v.size()); + } + + function test_class_reflect_variable() { + final f = new Foo(); + final s = (Reflect.field(f, 'v') : StdVector).size(); + + Assert.equals(7, s); + } + + function test_class_reflect_variable_mutation() { + final f = new Foo(); + + (Reflect.field(f, 'v') : StdVector).resize(100); + + Assert.equals(100, f.v.size()); + } + + function test_class_reflect_variable_assignment() { + final f = new Foo(); + + Reflect.setField(f, 'v', new StdVector(100)); + + Assert.equals(100, f.v.size()); + } + + // Static Variables + + function test_static_variable_mutation() { + Foo.v_static.resize(100); + + Assert.equals(100, Foo.v_static.size()); + } + + function test_static_variable_assignment() { + Foo.v_static = new StdVector(25); + + Assert.equals(25, Foo.v_static.size()); + } + + function test_static_variable_copy() { + final v = Foo.v_static; + + v.resize(75); + + Assert.equals(75, v.size()); + Assert.notEquals(75, Foo.v_static.size()); + } + + function test_static_class_reflect_variable() { + Foo.v_static.resize(11); + + final s = (Reflect.field(Foo, 'v_static') : StdVector).size(); + + Assert.equals(11, s); + } + + function test_static_class_reflect_variable_mutation() { + (Reflect.field(Foo, 'v_static') : StdVector).resize(12); + + Assert.equals(12, Foo.v_static.size()); + } + + function test_static_class_reflect_variable_assignment() { + Reflect.setField(Foo, 'v_static', new StdVector(13)); + + Assert.equals(13, Foo.v_static.size()); + } + + // Member Functions + + function test_class_function_copy() { + final f = new Foo(); + final v = f.get(); + + v.resize(100); + + Assert.equals(100, v.size()); + Assert.equals(7, f.v.size()); + } + + function test_class_function_pass() { + final f = new Foo(); + final v = new StdVector(7); + final s = f.multiply(v); + + Assert.equals(7, v.size()); + Assert.equals(14, s); + } + + function test_class_function_call() { + final f = new Foo(); + final s = f.get().size(); + + Assert.equals(7, s); + } + + function test_member_dynamic_function() { + final f = new Foo(); + final v = f.pass_through(new StdVector(10)); + + Assert.equals(10, v.size()); + } + + // Static Functions + + function test_static_function() { + Assert.equals(true, Foo.get_random(100).size() >= 100); + } + + // Construction + + function test_constructor() { + final v = new StdVector(7); + final o = new WithConstruction(v); + + o.v.resize(100); + + Assert.equals(100, o.v.size()); + Assert.equals(7, v.size()); + } + + function test_reflection_create_empty() { + final f : WithConstruction = Type.createEmptyInstance(WithConstruction); + + Assert.isNull(f.v); + } + + function test_reflection_create() { + final f : WithConstruction = Type.createInstance(WithConstruction, [ new StdVector(7) ]); + + Assert.equals(7, f.v.size()); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestEnumValueType.hx b/test/native/tests/marshalling/classes/TestEnumValueType.hx new file mode 100644 index 000000000..1955b90f1 --- /dev/null +++ b/test/native/tests/marshalling/classes/TestEnumValueType.hx @@ -0,0 +1,80 @@ +package tests.marshalling.classes; + +import haxe.EnumTools; +import utest.Assert; +import utest.Test; + +enum FooEnum { + Bar(v:StdVector); +} + +class TestEnumValueType extends Test { + function test_copy_into_enum() { + final v = new StdVector(50); + final e = FooEnum.Bar(v); + + switch e { + case Bar(v_copy): + v.resize(100); + + Assert.equals(100, v.size()); + Assert.equals( 50, v_copy.size()); + } + } + + function test_mutating_enum() { + final e = FooEnum.Bar(new StdVector(50)); + + switch e { + case Bar(v1): + v1.resize(100); + + Assert.equals(100, v1.size()); + + switch e { + case Bar(v2): + Assert.equals(100, v2.size()); + } + } + } + + function test_copy_from_enum() { + final e = FooEnum.Bar(new StdVector(50)); + + switch e { + case Bar(v): + final v_copy = v; + + v_copy.resize(100); + + Assert.equals(100, v_copy.size()); + Assert.equals( 50, v.size()); + } + } + + function test_create_by_index() { + final v1 = new StdVector(50); + final e = EnumTools.createByIndex(FooEnum, 0, [ v1 ]); + + switch e { + case Bar(v2): + v1.resize(100); + + Assert.equals(100, v1.size()); + Assert.equals( 50, v2.size()); + } + } + + function test_create_by_name() { + final v1 = new StdVector(50); + final e = EnumTools.createByName(FooEnum, 'Bar', [ v1 ]); + + switch e { + case Bar(v2): + v1.resize(100); + + Assert.equals(100, v1.size()); + Assert.equals( 50, v2.size()); + } + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestInheritance.hx b/test/native/tests/marshalling/classes/TestInheritance.hx new file mode 100644 index 000000000..805d2992d --- /dev/null +++ b/test/native/tests/marshalling/classes/TestInheritance.hx @@ -0,0 +1,86 @@ +package tests.marshalling.classes; + +import utest.Assert; +import utest.Test; + +@:semantics(value) +@:include('Base.hpp') +@:cpp.ValueType +private extern class Base { + function foo():Int; +} + +@:semantics(value) +@:include('Child.hpp') +@:cpp.ValueType +private extern class Child extends Base { + function new():Void; + + function bar():Int; +} + +class TestInheritance extends Test { + function test_copying_to_a_more_specific_type() { + final o = create_child(); + final c : Child = cast o; + + Assert.equals(10, c.foo()); + Assert.equals(20, c.bar()); + } + + function test_copying_to_a_more_specific_type_promoted() { + final o = create_child(); + final c : Child = cast o; + final f = () -> { + return c.bar(); + } + + f(); + + Assert.equals(10, c.foo()); + Assert.equals(20, c.bar()); + } + + function test_casting_var_to_base() { + final o = create_child(); + final v = (cast o : Child).bar(); + + Assert.equals(20, v); + } + + function test_passing_child_to_child_function() { + final o = new Child(); + final v = pass_child(o); + + Assert.equals(10, v); + } + + function test_passing_child_to_base_function() { + final o = new Child(); + final v = pass_base(o); + + Assert.equals(7, v); + } + + function test_reassigning() { + var o : Base = new Child(); + + Assert.equals(7, o.foo()); + + o = new Child(); + + Assert.equals(7, o.foo()); + } + + function create_child() : Base { + return new Child(); + } + + function pass_child(c : Child) { + return c.foo(); + } + + function pass_base(b : Base) { + return b.foo(); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestInterfaceValueType.hx b/test/native/tests/marshalling/classes/TestInterfaceValueType.hx new file mode 100644 index 000000000..0819b1267 --- /dev/null +++ b/test/native/tests/marshalling/classes/TestInterfaceValueType.hx @@ -0,0 +1,113 @@ +package tests.marshalling.classes; + +import utest.Assert; +import utest.Test; + +interface IBar { + var v : StdVector; + + function get() : StdVector; + + function multiply(v : StdVector) : Int; +} + +class Bar implements IBar { + public var v : StdVector; + + public function new() { + v = new StdVector(7); + } + + public function get() { + return v; + } + + public function multiply(v:StdVector) { + v.resize(v.size() * 2); + + return v.size(); + } +} + +class TestInterfaceValueType extends Test { + function test_interface_field_access() { + final f : IBar = new Bar(); + + Assert.equals(7, f.v.size()); + } + + function test_mutating_interface_field() { + final f : IBar = new Bar(); + + f.v.resize(100); + + Assert.equals(100, f.v.size()); + } + + function test_interface_field_copy() { + final f : IBar = new Bar(); + final v = f.v; + + v.resize(100); + + Assert.equals(100, v.size()); + Assert.equals(7, f.v.size()); + } + + function test_interface_function_copy() { + final f : IBar = new Bar(); + final v = f.get(); + + v.resize(100); + + Assert.equals(100, v.size()); + Assert.equals(7, f.v.size()); + } + + function test_interface_field_assignment() { + final f : IBar = new Bar(); + + f.v = new StdVector(100); + + Assert.equals(100, f.v.size()); + } + + function test_interface_function_pass() { + final f : IBar = new Bar(); + final v = new StdVector(7); + final s = f.multiply(v); + + Assert.equals(7, v.size()); + Assert.equals(14, s); + } + + function test_interface_function_call() { + final f : IBar = new Bar(); + final s = f.get().size(); + + Assert.equals(7, s); + } + + function test_interface_reflect_variable() { + final f : IBar = new Bar(); + final s = (Reflect.field(f, 'v') : StdVector).size(); + + Assert.equals(7, s); + } + + function test_interface_reflect_variable_mutation() { + final f : IBar = new Bar(); + + (Reflect.field(f, 'v') : StdVector).resize(100); + + Assert.equals(100, f.v.size()); + } + + function test_interface_reflect_variable_assignment() { + final f : IBar = new Bar(); + + Reflect.setField(f, 'v', new StdVector(100)); + + Assert.equals(100, f.v.size()); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestLocalValueType.hx b/test/native/tests/marshalling/classes/TestLocalValueType.hx new file mode 100644 index 000000000..dc694d60f --- /dev/null +++ b/test/native/tests/marshalling/classes/TestLocalValueType.hx @@ -0,0 +1,353 @@ +package tests.marshalling.classes; + +import utest.Assert; +import utest.Test; + +private class StdVectorIterator { + final max : Int; + var current : Int; + + public function new() { + max = 10 + Std.random(10); + current = 0; + } + + public function hasNext() { + return current < max; + } + + public function next() { + return new StdVector(current++); + } +} + +class TestLocalValueType extends Test { + /** + * Declaring a new variable of a value type will copy the right hand side value. + */ + function test_var_copying() { + final v1 = new StdVector(); + final v2 = v1; + + v1.resize(10); + + Assert.equals(10, v1.size()); + Assert.equals( 0, v2.size()); + } + + /** + * Value type variables can be re-assigned and the internal reference is still correct. + */ + function test_var_assignment() { + var v = new StdVector(50); + + v = new StdVector(); + + Assert.equals(0, v.size()); + } + + function test_nullable_var_null() { + final v : Null> = null; + + Assert.isNull(v); + } + + function test_nullable_var_not_null() { + final v : Null> = new StdVector(); + + Assert.notNull(v); + } + + function test_nullable_var_null_copy() { + final v1 : Null> = null; + final v2 = v1; + + Assert.isNull(v1); + Assert.isNull(v2); + } + + function test_nullable_var_to_non_null() { + final v1 : Null> = new StdVector(); + final v2 : StdVector = v1; + + Assert.notNull(v1); + Assert.notNull(v2); + } + + function test_nullable_var_null_to_non_null() { + final v1 : Null> = null; + + Assert.raises(() -> { + final _ : StdVector = v1; + }); + } + + function test_assigning_null_to_non_null_var() { + Assert.raises(() -> { + var v = new StdVector(50); + + v = null; + }); + } + + function test_initialising_non_null_var_to_null() { + Assert.raises(() -> { + var _ : StdVector = null; + }); + } + + function test_initialising_non_null_var_to_dynamic_null() { + function get_null() : Any { + return null; + } + + Assert.raises(() -> { + var _ : StdVector = get_null(); + }); + } + + /** + * Value type function arguments can be re-assigned and the internal reference is still correct. + */ + function test_argument_assignment() { + final v = new StdVector(); + + Assert.equals(10, argument_assign(v)); + Assert.equals(0, v.size()); + } + + function argument_assign(arg) { + arg = new StdVector(10); + + return arg.size(); + } + + /** + * Lamba functions which accept value types as arguments can be correctly re-assigned. + */ + function test_lambda_argument_assignment() { + final v = new StdVector(); + final f = arg -> { + arg = new StdVector(10); + + return arg.size(); + } + + Assert.equals(10, f(v)); + Assert.equals(0, v.size()); + } + + /** + * Passing a value type into a function will pass it by value. + */ + function test_function_copying() { + final v = new StdVector(); + + by_value(v); + + Assert.equals( 0, v.size()); + } + + function by_value(v:StdVector) { + v.resize(10); + } + + /** + * Variables captured by closures will be wrapped in a type which moves them to the GC heap. + */ + function test_heap_promotion() { + final v = new StdVector(); + final f = () -> { + v.resize(10); + } + + f(); + + Assert.equals(10, v.size()); + } + + /** + * Function arguments which are captured are promoted to retain consistent behaviour. + */ + function test_promoting_function_argument() { + final v = new StdVector(); + final s = promote_arg(v); + + Assert.equals(10, s.fst); + Assert.equals(10, s.snd); + } + + function promote_arg(v:StdVector) { + final f = () -> { + v.resize(10); + + return v.size(); + } + final newSize = f(); + final oldSize = v.size(); + + return { fst : oldSize, snd : newSize } + } + + /** + * Lambda arguments which are captured are promoted to retain consistent behaviour. + */ + function test_promoting_lambda_argument() { + final v = new StdVector(); + + function lambda_promote_arg(v:StdVector) { + final f = () -> { + v.resize(10); + + return v.size(); + } + + final newSize = f(); + final oldSize = v.size(); + + return { fst : oldSize, snd : newSize } + } + + final s = lambda_promote_arg(v); + + Assert.equals(10, s.fst); + Assert.equals(10, s.snd); + } + + /** + * Value type variables passed into a function of type dynamic will also be passed a copy. + */ + function test_dynamic() { + final v = new StdVector(5); + + by_value_dynamic(v); + + Assert.equals(5, v.size()); + } + + /** + * Value type variables which have been promoted to the heap will be copied to dynamic functions argumens. + */ + function test_promoted_dynamic() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + by_value_dynamic(v); + + Assert.equals(5, v.size()); + } + + /** + * Value type variables passed put into and accessed via an anonymous object will be promoted to the heap. + */ + function test_anon() { + final v = new StdVector(5); + + by_value_anon({ v : v }); + + Assert.equals(5, v.size()); + } + + function by_value_anon(a:{ v : StdVector }) { + Assert.equals(5, a.v.size()); + + a.v.resize(10); + + Assert.equals(10, a.v.size()); + } + + /** + * Value types which have been promoted can be re-assigned and the internal reference will remain valid. + */ + function test_assigning_promoted_variable() { + var v = new StdVector(5); + final f = () -> { + v.resize(10); + } + + f(); + + v = new StdVector(2); + + Assert.equals(2, v.size()); + } + + function test_lamba_returns() { + final v1 = new StdVector(100); + final f = () -> { + return v1; + } + + final v2 = f(); + + v2.resize(50); + + Assert.equals( 50, v2.size()); + Assert.equals(100, v1.size()); + } + + function test_iterator() { + final iter = new StdVectorIterator(); + + var count = 0; + for (vec in iter) { + Assert.equals(count++, vec.size()); + } + } + + function test_to_string() { + final v = new StdVector(); + final s = Std.string(v); + + Assert.notNull(s); + } + + function test_equality() { + final v1 = new StdVector(); + final v2 = new StdVector(); + final eq = v1 == v2; + + Assert.isTrue(eq); + } + + function test_inequality() { + final v1 = new StdVector(); + final v2 = new StdVector(); + final eq = v1 != v2; + + Assert.isFalse(eq); + } + + function test_just_creation() { + new StdVector(10); + + Assert.pass(); + } + + /** + * Array reading is allowed on value types implementing ArrayAccess. + */ + // function test_array_access() { + // final v = new StdVector(5); + + // // v[1] = 7; + + // final i = v[1]; + + // Assert.equals(0, i); + // } + + // Various helper functions + + function by_value_dynamic(v:Dynamic) { + Assert.equals(5, (v:StdVector).size()); + + (v:StdVector).resize(10); + + Assert.equals(10, (v:StdVector).size()); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestValueTypeCollections.hx b/test/native/tests/marshalling/classes/TestValueTypeCollections.hx new file mode 100644 index 000000000..e24c1d665 --- /dev/null +++ b/test/native/tests/marshalling/classes/TestValueTypeCollections.hx @@ -0,0 +1,82 @@ +package tests.marshalling.classes; + +import utest.Assert; +import utest.Test; + +class TestValueTypeCollections extends Test { + public function test_push_copy_to_array() { + final a = []; + + // prevent haxe optimising the array away + a.resize(10); + a.resize(0); + + a.push(new StdVector()); + + Assert.equals(0, a[0].size()); + } + + public function test_mutate_array_element() { + final a = [ new StdVector() ]; + final s = 100; + + // prevent haxe optimising the array away + a.resize(10); + a.resize(1); + + a[0].resize(s); + + Assert.equals(s, a[0].size()); + } + + public function test_copy_array_element() { + final a = [ new StdVector() ]; + final s = 100; + final c = a[0]; + + c.resize(s); + + // prevent haxe optimising the array away + a.resize(10); + a.resize(1); + + Assert.equals(0, a[0].size()); + Assert.equals(s, c.size()); + } + + public function test_switch_on_array() { + final a = [ new StdVector() ]; + final s = 100; + + // prevent haxe optimising the array away + a.resize(10); + a.resize(1); + + switch a { + case [ elem ]: + elem.resize(s); + + Assert.equals(s, a[0].size()); + default: + Assert.fail('expected array to have one element'); + } + } + + public function test_collection_of_pointers() { + final v = new StdVector(); + + v.push_back(Context.create()); + + Assert.equals(1, v.size()); + + Assert.equals(7, v[0].number); + + v[0].number = v[0].double(); + + Assert.equals(14, v[0].number); + + v[0] = Context.create(); + + Assert.equals(7, v[0].number); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestValueTypeFields.hx b/test/native/tests/marshalling/classes/TestValueTypeFields.hx new file mode 100644 index 000000000..ca1076942 --- /dev/null +++ b/test/native/tests/marshalling/classes/TestValueTypeFields.hx @@ -0,0 +1,147 @@ +package tests.marshalling.classes; + +import utest.Assert; +import utest.Test; + +@:semantics(value) +@:include('point.hpp') +@:cpp.ValueType({ type : 'point', namespace : [ 'hx', 'maths' ] }) +private extern class Point { + var x : Float; + var y : Float; + + @:overload(function(_x : Float, _y : Float) : Void {}) + function new(); +} + +@:semantics(value) +@:include('holder.hpp') +@:cpp.ValueType({ type : 'holder', namespace : [] }) +private extern class Holder { + var p1 : Point; + var p2 : Point; + var pPtr : Point; + + @:overload(function(_p1 : Point, _p2 : Point) : Void {}) + function new(); + + function create() : Point; + + function get_static() : Point; +} + +class TestValueTypeFields extends Test { + function test_struct_with_default_construction() { + final v = new Point(); + Assert.equals(7f64, v.x); + Assert.equals(26f64, v.y); + } + + function test_promoted_struct_with_default_construction() { + final v = new Point(); + final f = () -> { + return v.x; + } + + f(); + + Assert.equals(7f64, v.x); + Assert.equals(26f64, v.y); + } + + function test_accessing_inner_value_types() { + final v = new Holder(); + Assert.equals(7f64, v.p1.x); + Assert.equals(26f64, v.p1.y); + } + + function test_accessing_inner_value_types_promoted() { + final v = new Holder(); + final f = () -> { + return v.p1.x; + } + + f(); + + Assert.equals(7f64, v.p1.x); + Assert.equals(26f64, v.p1.y); + } + + function test_copying_struct_inner_type() { + final v = new Holder(); + final p = v.p1; + + p.x = 33f64; + + Assert.equals(7f64, v.p1.x); + Assert.equals(33f64, p.x); + } + + function test_copying_struct_inner_type_promoted() { + final v = new Holder(); + final p = v.p1; + final f = () -> { + p.x = 33f64; + } + + f(); + + Assert.equals(7f64, v.p1.x); + Assert.equals(33f64, p.x); + } + + function test_assigning_struct_inner_type() { + final p = new Point(33, 66); + final v = new Holder(); + + v.p1 = p; + + Assert.equals(33f64, v.p1.x); + Assert.equals(66f64, v.p1.y); + } + + function test_assigning_struct_inner_promoted() { + final p = new Point(); + final v = new Holder(); + final f = () -> { + p.x = 33; + p.y = 66; + } + + f(); + + v.p1 = p; + + Assert.equals(33f64, v.p1.x); + Assert.equals(66f64, v.p1.y); + } + + function test_extern_function_returning_value() { + final h = new Holder(); + final p = h.create(); + + Assert.equals(h.p1.x + h.p2.x, p.x); + Assert.equals(h.p1.y + h.p2.y, p.y); + } + + // function test_extern_function_returning_pointer() { + // final h = new Holder(); + // final p1 = h.get_static(); + + // Assert.equals( 7f64, p1.x); + // Assert.equals(26f64, p1.y); + + // final p2 = h.get_static(); + + // Assert.equals( 7f64, p2.x); + // Assert.equals(26f64, p2.y); + + // h.get_static().x = 33; + // h.get_static().y = 66; + + // final p3 = h.get_static(); + + // Assert.equals(33f64, p3.x); + // Assert.equals(66f64, p3.y); + // } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/classes/TestValueTypeInterop.hx b/test/native/tests/marshalling/classes/TestValueTypeInterop.hx new file mode 100644 index 000000000..08e96231b --- /dev/null +++ b/test/native/tests/marshalling/classes/TestValueTypeInterop.hx @@ -0,0 +1,227 @@ +package tests.marshalling.classes; + +import cpp.Native; +import cpp.Star; +import cpp.Pointer; +import cpp.Reference; +import cpp.RawPointer; +import utest.Assert; +import utest.Test; + +private extern class NativeFunctions { + @:native('vec_by_val') + static function vec_by_val(v:StdVector):Void; + + @:native('vec_by_ref') + static function vec_by_ref(v:StdVector):Void; + + @:native('vec_by_ptr') + static function vec_by_ptr(v:StdVector):Void; +} + +private class HaxeFunctions { + @:unreflective @:generic public static function get_size_doubled(v : StdVector) { + return v.size() * 2; + } + + @:unreflective @:generic public static function resize_vec_by_ref(v : Reference>, size : Int) { + v.resize(size); + } + + @:unreflective @:generic public static function resize_vec_by_ptr(v : Pointer>, size : Int) { + v[0].resize(size); + } + + @:unreflective @:generic public static function resize_vec_by_raw_ptr(v : RawPointer>, size : Int) { + v[0].resize(size); + } + + @:unreflective @:generic public static function resize_vec_by_star(v : Star>, size : Int) { + v.resize(size); + } +} + +@:cppNamespaceCode(' +void vec_by_val(std::vector v) +{ + v.resize(v.size() * 2); +} + +void vec_by_ref(std::vector& v) +{ + v.resize(v.size() * 2); +} + +void vec_by_ptr(std::vector* v) +{ + v->resize(v->size() * 2); +} + +int* create_int() { + return new int { 7 }; +} + +void int_ptr_ptr(int** ptr) { + *ptr = new int { 14 }; +} +') +class TestValueTypeInterop extends Test { + function test_pass_straight_in() { + Assert.equals(10, HaxeFunctions.get_size_doubled(new StdVector(5))); + } + + /** + * Extern functions which expect a type will be given a copy of the value type. + */ + function test_extern_by_val() { + final v = new StdVector(5); + + NativeFunctions.vec_by_val(v); + + Assert.equals(5, v.size()); + } + + /** + * Extern functions which expect a reference to a type will be given a reference to the value type. + */ + function test_extern_by_ref() { + final v = new StdVector(5); + + NativeFunctions.vec_by_ref(v); + + Assert.equals(10, v.size()); + } + + /** + * Extern functions which expect a pointer to a type will be given a pointer to the value type. + */ + function test_extern_by_ptr() { + final v = new StdVector(5); + + NativeFunctions.vec_by_ptr(v); + + Assert.equals(10, v.size()); + } + + /** + * Extern functions which expect a type will be given a copy of the promoted value type. + */ + function test_promoted_extern_by_val() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + NativeFunctions.vec_by_val(v); + + Assert.equals(5, v.size()); + } + + /** + * Extern functions which expect a reference to a type will be given a reference to the promoted value type. + */ + function test_promoted_extern_by_ref() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + NativeFunctions.vec_by_ref(v); + + Assert.equals(10, v.size()); + } + + /** + * Extern functions which expect a pointer to a type will be given a pointer to the promoted value type. + */ + function test_promoted_extern_by_ptr() { + final v = new StdVector(); + final f = () -> { + v.resize(5); + } + + f(); + + NativeFunctions.vec_by_ptr(v); + + Assert.equals(10, v.size()); + } + + function test_resize_vec_by_ref() { + final v = new StdVector(); + final size = 100; + + HaxeFunctions.resize_vec_by_ref(v, size); + + Assert.equals(size, v.size()); + } + + function test_resize_vec_by_ptr() { + final v = new StdVector(); + final size = 100; + + HaxeFunctions.resize_vec_by_ptr(Pointer.addressOf(v), size); + + Assert.equals(size, v.size()); + } + + function test_resize_vec_by_raw_ptr() { + final v = new StdVector(); + final size = 100; + + HaxeFunctions.resize_vec_by_raw_ptr(Pointer.addressOf(v).raw, size); + + Assert.equals(size, v.size()); + } + + function test_resize_vec_by_star() { + final v = new StdVector(); + final size = 100; + + HaxeFunctions.resize_vec_by_star(Pointer.addressOf(v).ptr, size); + + Assert.equals(size, v.size()); + } + + function test_copying_from_pointer() { + final src = new StdVector(10); + final ptr = Pointer.addressOf(src); + final copy : StdVector = ptr.value; + + copy.resize(100); + + Assert.equals( 10, src.size()); + Assert.equals(100, copy.size()); + } + + function test_native_star() { + final v = new StdVector(); + final ptr = Native.addressOf(v); + + ptr.resize(10); + + Assert.equals(10, v.size()); + } + + function test_native_dereference() { + final v = new StdVector(); + final ptr = Pointer.addressOf(v); + final ref = Native.star(ptr.ptr); + + ref.resize(10); + + Assert.equals(10, v.size()); + } + + function test_vec_of_points() { + final v = new StdVector(5); + + Point.point_vec(v); + + Assert.equals(300f64, v.at(0).x); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/enums/TestValueTypeEnumAbstract.hx b/test/native/tests/marshalling/enums/TestValueTypeEnumAbstract.hx new file mode 100644 index 000000000..6dc5c55d0 --- /dev/null +++ b/test/native/tests/marshalling/enums/TestValueTypeEnumAbstract.hx @@ -0,0 +1,95 @@ +package tests.marshalling.enums; + +import utest.Assert; +import utest.Test; + +@:semantics(value) +@:include('colour.hpp') +@:cpp.ValueType({ type : 'colour' }) +private extern enum abstract Colour(Int) { + @:native('red') + var Red; + + @:native('green') + var Green; + + @:native('blue') + var Blue; +} + +class TestValueTypeEnumAbstract extends Test { + function test_switching_on_uncaptured_enum() { + final e = Colour.Green; + + switch e { + case Green: + Assert.pass(); + default: + Assert.fail('Expected "green"'); + } + } + + function test_switching_on_captured_enum() { + final e = Colour.Green; + final f = () -> { + return e; + } + + f(); + + switch e { + case Red, Blue: + Assert.fail('Expected "green"'); + case Green: + Assert.pass(); + } + } + + function test_uncaptured_equals() { + final e = Colour.Green; + + Assert.isTrue(e == Colour.Green); + } + + function test_uncaptured_not_equals() { + final e = Colour.Green; + + Assert.isFalse(e != Colour.Green); + } + + function test_promoted_equals() { + final e = Colour.Green; + final f = () -> { + return e; + } + + f(); + + Assert.isTrue(e == Colour.Green); + } + + function test_promoted_not_equals() { + final e = Colour.Green; + final f = () -> { + return e; + } + + f(); + + Assert.isFalse(e != Colour.Green); + } + + function test_from_underlying_type() { + final i = 1; + final e : Colour = cast i; + + Assert.isTrue(e == Colour.Green); + } + + function test_to_underlying_type() { + final e = Colour.Green; + final i : Int = cast e; + + Assert.equals(1, i); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/enums/TestValueTypeEnumClassAbstract.hx b/test/native/tests/marshalling/enums/TestValueTypeEnumClassAbstract.hx new file mode 100644 index 000000000..af0f29e7d --- /dev/null +++ b/test/native/tests/marshalling/enums/TestValueTypeEnumClassAbstract.hx @@ -0,0 +1,90 @@ +package tests.marshalling.enums; + +import utest.Assert; +import utest.Test; + +@:semantics(value) +@:include('Numbers.hpp') +@:cpp.ValueType({ namespace : [ 'foo' ] }) +private extern enum abstract Numbers(Int) { + var One; + var Two; + var Three; +} + +class TestValueTypeEnumClassAbstract extends Test { + function test_switching_on_uncaptured_enum() { + final e = Numbers.Two; + + switch e { + case Two: + Assert.pass(); + default: + Assert.fail('Expected "Two"'); + } + } + + function test_switching_on_captured_enum() { + final e = Numbers.Two; + final f = () -> { + return e; + } + + f(); + + switch e { + case One, Three: + Assert.fail('Expected "green"'); + case Two: + Assert.pass(); + } + } + + function test_uncaptured_equals() { + final e = Numbers.Two; + + Assert.isTrue(e == Numbers.Two); + } + + function test_uncaptured_not_equals() { + final e = Numbers.Two; + + Assert.isFalse(e != Numbers.Two); + } + + function test_promoted_equals() { + final e = Numbers.Two; + final f = () -> { + return e; + } + + f(); + + Assert.isTrue(e == Numbers.Two); + } + + function test_promoted_not_equals() { + final e = Numbers.Two; + final f = () -> { + return e; + } + + f(); + + Assert.isFalse(e != Numbers.Two); + } + + function test_from_underlying_type() { + final i = 5; + final e : Numbers = cast i; + + Assert.isTrue(e == Numbers.Two); + } + + function test_to_underlying_type() { + final e = Numbers.Two; + final i : Int = cast e; + + Assert.equals(5, i); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/managed/NonStandardNamingExtern.hx b/test/native/tests/marshalling/managed/NonStandardNamingExtern.hx new file mode 100644 index 000000000..0976448c8 --- /dev/null +++ b/test/native/tests/marshalling/managed/NonStandardNamingExtern.hx @@ -0,0 +1,15 @@ +package tests.marshalling.managed; + +@:include('Managed.hpp') +@:cpp.ManagedType({type: 'standard_naming_obj', namespace: ['foo', 'bar']}) +extern class NonStandardNamingExtern { + static var constNumber:Int; + + var number:Int; + + function new():Void; + + function multiply(input:Int):Int; + + static function create(number:Int):NonStandardNamingExtern; +} diff --git a/test/native/tests/marshalling/managed/StandardNamingExtern.hx b/test/native/tests/marshalling/managed/StandardNamingExtern.hx new file mode 100644 index 000000000..b5550b7f1 --- /dev/null +++ b/test/native/tests/marshalling/managed/StandardNamingExtern.hx @@ -0,0 +1,15 @@ +package tests.marshalling.managed; + +@:include('Managed.hpp') +@:cpp.ManagedType({type: 'standard_naming', namespace: ['foo', 'bar'], flags: [StandardNaming]}) +extern class StandardNamingExtern { + static var constNumber:Int; + + var number:Int; + + function new():Void; + + function multiply(input:Int):Int; + + static function create(number:Int):StandardNamingExtern; +} diff --git a/test/native/tests/marshalling/managed/TestClassHarness.hx b/test/native/tests/marshalling/managed/TestClassHarness.hx new file mode 100644 index 000000000..4e6bf3166 --- /dev/null +++ b/test/native/tests/marshalling/managed/TestClassHarness.hx @@ -0,0 +1,60 @@ +package tests.marshalling.managed; + +import haxe.Constraints; +import utest.Assert; +import utest.Test; + +private typedef TestType = { + public var number : Int; + + public function multiply(input : Int) : Int; +} + +@:generic private class FooVoid> & TestType> { + public var o : T; + + public function new() { + o = new T(); + } +} + +@:generic class TestClassHarnessVoid> & TestType> extends Test { + function test_construction() { + final c = new Foo(); + + Assert.notNull(c.o); + } + + function test_var_access() { + final c = new Foo(); + + Assert.equals(0, c.o.number); + } + + function test_var_mutation() { + final c = new Foo(); + + c.o.number = 7; + + Assert.equals(7, c.o.number); + } + + function test_reassignment() { + final c = new Foo(); + final o = new T(); + + o.number = 100; + + c.o = o; + + Assert.equals(100, c.o.number); + } + + function test_function_call() { + final c = new Foo(); + + c.o.number = 7; + + Assert.equals(14, c.o.multiply(2)); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/managed/TestClassNonStandardManagedClass.hx b/test/native/tests/marshalling/managed/TestClassNonStandardManagedClass.hx new file mode 100644 index 000000000..ca9010571 --- /dev/null +++ b/test/native/tests/marshalling/managed/TestClassNonStandardManagedClass.hx @@ -0,0 +1,5 @@ +package tests.marshalling.managed; + +class TestClassNonStandardManagedClass extends TestClassHarness { + +} \ No newline at end of file diff --git a/test/native/tests/marshalling/managed/TestClassStandardManagedClass.hx b/test/native/tests/marshalling/managed/TestClassStandardManagedClass.hx new file mode 100644 index 000000000..e456edaf3 --- /dev/null +++ b/test/native/tests/marshalling/managed/TestClassStandardManagedClass.hx @@ -0,0 +1,5 @@ +package tests.marshalling.managed; + +class TestClassStandardManagedClass extends TestClassHarness { + // +} \ No newline at end of file diff --git a/test/native/tests/marshalling/managed/TestLocalHarness.hx b/test/native/tests/marshalling/managed/TestLocalHarness.hx new file mode 100644 index 000000000..4cd970853 --- /dev/null +++ b/test/native/tests/marshalling/managed/TestLocalHarness.hx @@ -0,0 +1,111 @@ +package tests.marshalling.managed; + +import haxe.Constraints; +import utest.Assert; +import utest.Test; + +private typedef TestType = { + public var number : Int; + + public function multiply(input : Int) : Int; +} + +@:generic class TestLocalHarnessVoid> & TestType> extends Test { + function test_null_object() { + final o : T = null; + + Assert.isNull(o); + } + + function test_construction() { + final o = new T(); + + Assert.notNull(o); + } + + function test_equality() { + final o1 = new T(); + final o2 = o1; + + Assert.equals(o1, o2); + } + + function test_inequality() { + final o1 = new T(); + final o2 = new T(); + + Assert.notEquals(o1, o2); + } + + function test_var_access() { + final o = new T(); + + Assert.equals(0, o.number); + } + + function test_var_mutation() { + final o = new T(); + + o.number = 7; + + Assert.equals(7, o.number); + } + + function test_function_call() { + final o = new T(); + + o.number = 7; + + Assert.equals(14, o.multiply(2)); + } + + function test_to_string() { + final o = new T(); + + Assert.equals("My Custom Managed Type", Std.string(o)); + } + + function test_null_access() { + final o : T = null; + + Assert.raises(() -> o.number = 7); + } + + function test_casting() { + function create_as_any() : Any { + return new T(); + } + + final a = create_as_any(); + final o = (cast a : T); + + Assert.notNull(o); + } + + function test_anon() { + function create_anon() { + return { o : new T() }; + } + + final a = create_anon(); + + Assert.notNull(a.o); + } + + // function test_type_check() { + // final o : Any = new NonStandardNamingExtern(); + + // Assert.isTrue(o is NonStandardNamingExtern); + // } + + // function test_function_closures() { + // final o = new WithClosure(); + // final f = o.returnSeven; + + // Assert.equals(7, f()); + // } + + // function test_reflection() { + // // + // } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/managed/TestLocalNonStandardManagedClass.hx b/test/native/tests/marshalling/managed/TestLocalNonStandardManagedClass.hx new file mode 100644 index 000000000..fad9840ce --- /dev/null +++ b/test/native/tests/marshalling/managed/TestLocalNonStandardManagedClass.hx @@ -0,0 +1,23 @@ +package tests.marshalling.managed; + +import utest.Assert; + +class TestLocalNonStandardManagedClass extends TestLocalHarness { + function test_static_var_access() { + Assert.equals(300, NonStandardNamingExtern.constNumber); + } + + function test_static_var_mutation() { + NonStandardNamingExtern.constNumber = 200; + + Assert.equals(200, NonStandardNamingExtern.constNumber); + + NonStandardNamingExtern.constNumber = 300; + } + + function test_static_function_call() { + final o = NonStandardNamingExtern.create(7); + + Assert.notNull(o); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/managed/TestLocalStandardManagedClass.hx b/test/native/tests/marshalling/managed/TestLocalStandardManagedClass.hx new file mode 100644 index 000000000..b6244c3ad --- /dev/null +++ b/test/native/tests/marshalling/managed/TestLocalStandardManagedClass.hx @@ -0,0 +1,23 @@ +package tests.marshalling.managed; + +import utest.Assert; + +class TestLocalStandardManagedClass extends TestLocalHarness { + function test_static_var_access() { + Assert.equals(300, StandardNamingExtern.constNumber); + } + + function test_static_var_mutation() { + StandardNamingExtern.constNumber = 200; + + Assert.equals(200, StandardNamingExtern.constNumber); + + StandardNamingExtern.constNumber = 300; + } + + function test_static_function_call() { + final o = StandardNamingExtern.create(7); + + Assert.notNull(o); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Base.cpp b/test/native/tests/marshalling/native/Base.cpp new file mode 100644 index 000000000..a964a889d --- /dev/null +++ b/test/native/tests/marshalling/native/Base.cpp @@ -0,0 +1,7 @@ +#include +#include + +int Base::foo() +{ + return 7; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Base.hpp b/test/native/tests/marshalling/native/Base.hpp new file mode 100644 index 000000000..6aaa091dc --- /dev/null +++ b/test/native/tests/marshalling/native/Base.hpp @@ -0,0 +1,5 @@ +#pragma once + +struct Base { + virtual int foo(); +}; \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Child.cpp b/test/native/tests/marshalling/native/Child.cpp new file mode 100644 index 000000000..4b947ecb6 --- /dev/null +++ b/test/native/tests/marshalling/native/Child.cpp @@ -0,0 +1,12 @@ +#include +#include + +int Child::foo() +{ + return 10; +} + +int Child::bar() +{ + return 20; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Child.hpp b/test/native/tests/marshalling/native/Child.hpp new file mode 100644 index 000000000..8142518b3 --- /dev/null +++ b/test/native/tests/marshalling/native/Child.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct Child : public Base { + int foo() override; + int bar(); +}; \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Managed.cpp b/test/native/tests/marshalling/native/Managed.cpp new file mode 100644 index 000000000..b163e54bb --- /dev/null +++ b/test/native/tests/marshalling/native/Managed.cpp @@ -0,0 +1,22 @@ +#include +#include + +int foo::bar::standard_naming_obj::constNumber = 300; + +foo::bar::standard_naming_obj::standard_naming_obj() : number(0) {} +foo::bar::standard_naming_obj::standard_naming_obj(int inNumber) : number(inNumber) {} + +int foo::bar::standard_naming_obj::multiply(int input) +{ + return number * input; +} + +::String foo::bar::standard_naming_obj::toString() +{ + return ::String::create("My Custom Managed Type"); +} + +foo::bar::standard_naming_obj* foo::bar::standard_naming_obj::create(int inNumber) +{ + return new foo::bar::standard_naming_obj(inNumber); +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Managed.hpp b/test/native/tests/marshalling/native/Managed.hpp new file mode 100644 index 000000000..a7a2886f2 --- /dev/null +++ b/test/native/tests/marshalling/native/Managed.hpp @@ -0,0 +1,30 @@ +#pragma once + +HX_DECLARE_CLASS2(foo,bar,standard_naming) + +namespace foo +{ + namespace bar + { + struct standard_naming_obj : public ::hx::Object { + static int constNumber; + + int number; + + standard_naming_obj(); + standard_naming_obj(int inNumber); + + int multiply(int input); + + ::String toString() override; + + static standard_naming_obj* create(int inNumber); + }; + } + + struct WithClosure : public ::hx::Object { + int ReturnSeven(); + + ::Dynamic ReturnSeven_dyn(); + }; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/Numbers.hpp b/test/native/tests/marshalling/native/Numbers.hpp new file mode 100644 index 000000000..ec2f68d77 --- /dev/null +++ b/test/native/tests/marshalling/native/Numbers.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace foo +{ + enum class Numbers : char { + One, + Two = 5, + Three + }; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/colour.hpp b/test/native/tests/marshalling/native/colour.hpp new file mode 100644 index 000000000..36b175950 --- /dev/null +++ b/test/native/tests/marshalling/native/colour.hpp @@ -0,0 +1,7 @@ +#pragma once + +enum colour { + red, + green, + blue +}; \ No newline at end of file diff --git a/test/native/tests/marshalling/native/ctx.cpp b/test/native/tests/marshalling/native/ctx.cpp new file mode 100644 index 000000000..79a8ad596 --- /dev/null +++ b/test/native/tests/marshalling/native/ctx.cpp @@ -0,0 +1,41 @@ +#include + +ctx::ctx() : number(7) {} + +int ctx::Double() { + return number * 2; +} + +ctx* ctx::create() { + return new ctx(); +} + +ctx* ctx::create_null() { + return nullptr; +} + +void ctx_ptr(ctx* pCtx) { + pCtx->number = 20; +} + +void ctx_ptr_ptr(ctx** ppCtx) { + auto replacement = new ctx(); + + replacement->number = 20; + + *ppCtx = replacement; +} + +void ctx_void_ptr(void* pCtx) { + auto casted = static_cast(pCtx); + + casted->number = 20; +} + +void ctx_void_ptr_ptr(void** ppCtx) { + auto replacement = new ctx(); + + replacement->number = 20; + + *ppCtx = replacement; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/ctx.hpp b/test/native/tests/marshalling/native/ctx.hpp new file mode 100644 index 000000000..089460d37 --- /dev/null +++ b/test/native/tests/marshalling/native/ctx.hpp @@ -0,0 +1,17 @@ +#pragma once + +struct ctx { + int number; + + ctx(); + + int Double(); + + static ctx* create(); + static ctx* create_null(); +}; + +void ctx_ptr(ctx* pCtx); +void ctx_ptr_ptr(ctx** ppCtx); +void ctx_void_ptr(void* pCtx); +void ctx_void_ptr_ptr(void** ppCtx); \ No newline at end of file diff --git a/test/native/tests/marshalling/native/holder.cpp b/test/native/tests/marshalling/native/holder.cpp new file mode 100644 index 000000000..9b4fdef6b --- /dev/null +++ b/test/native/tests/marshalling/native/holder.cpp @@ -0,0 +1,15 @@ +#include +#include + +::hx::maths::point holder::p_static; + +holder::holder(const ::hx::maths::point& _p1, const ::hx::maths::point& _p2) : p1(_p1), p2(_p2), pPtr(new ::hx::maths::point(45, 67)) {} + +::hx::maths::point holder::create() +{ + return ::hx::maths::point(p1.x + p2.x, p1.y + p2.y); +} + +::hx::maths::point* holder::get_static() { + return &p_static; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/holder.hpp b/test/native/tests/marshalling/native/holder.hpp new file mode 100644 index 000000000..60123b757 --- /dev/null +++ b/test/native/tests/marshalling/native/holder.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +class holder { + static ::hx::maths::point p_static; + +public: + ::hx::maths::point p1; + ::hx::maths::point p2; + + ::hx::maths::point* pPtr; + + holder() = default; + holder(const ::hx::maths::point& _p1, const ::hx::maths::point& _p2); + + ::hx::maths::point create(); + ::hx::maths::point* get_static(); +}; \ No newline at end of file diff --git a/test/native/tests/marshalling/native/point.cpp b/test/native/tests/marshalling/native/point.cpp new file mode 100644 index 000000000..fc97b389d --- /dev/null +++ b/test/native/tests/marshalling/native/point.cpp @@ -0,0 +1,8 @@ +#include +#include + +hx::maths::point::point(double _x, double _y) : x(_x), y(_y) {} + +void ::hx::maths::point::point_vec(std::vector<::hx::maths::point>& v) { + v[0].x = 300; +} \ No newline at end of file diff --git a/test/native/tests/marshalling/native/point.hpp b/test/native/tests/marshalling/native/point.hpp new file mode 100644 index 000000000..1feb3366d --- /dev/null +++ b/test/native/tests/marshalling/native/point.hpp @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace hx { + namespace maths { + struct point { + double x = 7; + double y = 26; + + point() = default; + point(double _x, double _y); + + static void point_vec(::std::vector<::hx::maths::point>& v); + }; + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestAbstractPointer.hx b/test/native/tests/marshalling/pointers/TestAbstractPointer.hx new file mode 100644 index 000000000..2409273e1 --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestAbstractPointer.hx @@ -0,0 +1,43 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +abstract MyContext(Context) { + public var number (get, never) : Int; + + function get_number() { + return this.number; + } + + public function new() { + this = Context.create(); + } + + public function double() { + this.number *= 2; + } +} + +class TestAbstractPointer extends Test { + function test_cast_to_underlying() { + final v = new MyContext(); + + Assert.equals(7, (cast v : Context).number); + } + + function test_property_access() { + final v = new MyContext(); + + Assert.equals(7, v.number); + } + + function test_mutating_abstract() { + final v = new MyContext(); + + v.double(); + + Assert.equals(14, v.number); + Assert.equals(14, (cast v : Context).number); + } +} diff --git a/test/native/tests/marshalling/pointers/TestClassPointers.hx b/test/native/tests/marshalling/pointers/TestClassPointers.hx new file mode 100644 index 000000000..e2c403eff --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestClassPointers.hx @@ -0,0 +1,200 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +class Foo { + public static var ptr_static = Context.create(); + + public var v : Context; + + public function new() { + v = Context.create(); + } + + public function get() { + return v; + } + + public function mutate(size : Int) { + return v.number = size; + } + + public dynamic function pass_through(ctx : Context) { + return ctx; + } +} + +class WithConstruction { + public final v : Context; + + public function new(v) { + this.v = v; + } +} + +class TestClassPointers extends Test { + + // Member Variables + + function test_class_field_access() { + final f = new Foo(); + + Assert.equals(7, f.v.number); + } + + function test_mutating_class_field() { + final f = new Foo(); + + f.v.number = 20; + + Assert.equals(20, f.v.number); + } + + function test_class_field_copy() { + final f = new Foo(); + final p = f.v; + + p.number = 20; + + Assert.equals(20, f.v.number); + Assert.equals(20, p.number); + } + + function test_class_field_assignment() { + final f = new Foo(); + final p = Context.create(); + + p.number = 20; + + f.v = p; + + Assert.isTrue(p == f.v); + Assert.equals(20, f.v.number); + } + + function test_class_reflect_variable() { + final f = new Foo(); + final n = (Reflect.field(f, 'v') : Context).number; + + Assert.equals(7, n); + } + + function test_class_reflect_variable_mutation() { + final f = new Foo(); + + (Reflect.field(f, 'v') : Context).number = 20; + + Assert.equals(20, f.v.number); + } + + function test_class_reflect_variable_assignment() { + final f = new Foo(); + final p = Context.create(); + + p.number = 20; + + Reflect.setField(f, 'v', p); + + Assert.isTrue(p == f.v); + Assert.equals(20, f.v.number); + } + + // Static Variables + + function test_static_variable_mutation() { + Foo.ptr_static.number = 20; + + Assert.equals(20, Foo.ptr_static.number); + } + + function test_static_variable_assignment() { + final ptr = Context.create(); + final old = Foo.ptr_static; + + Foo.ptr_static = ptr; + + Assert.isTrue(Foo.ptr_static == ptr); + Assert.isTrue(Foo.ptr_static != old); + } + + function test_static_variable_copy() { + final c = Foo.ptr_static; + + c.number = 75; + + Assert.equals(75, c.number); + Assert.equals(75, Foo.ptr_static.number); + } + + function test_static_class_reflect_variable() { + Foo.ptr_static.number = 11; + + final s = (Reflect.field(Foo, 'ptr_static') : Context).number; + + Assert.equals(11, s); + } + + function test_static_class_reflect_variable_mutation() { + (Reflect.field(Foo, 'ptr_static') : Context).number = 12; + + Assert.equals(12, Foo.ptr_static.number); + } + + function test_static_class_reflect_variable_assignment() { + final ptr = Context.create(); + final old = Foo.ptr_static; + + Reflect.setField(Foo, 'ptr_static', ptr); + + Assert.isTrue(Foo.ptr_static == ptr); + Assert.isTrue(Foo.ptr_static != old); + } + + // Member Functions + + function test_class_function_call() { + final f = new Foo(); + + Assert.equals(14, f.v.double()); + } + + function test_class_function_pass() { + final f = new Foo(); + final s = f.mutate(20); + + Assert.equals(20, f.v.number); + } + + function test_member_dynamic_function() { + final f = new Foo(); + final p = Context.create(); + final v = f.pass_through(p); + + Assert.isTrue(p == v); + } + + // Construction + + function test_constructor() { + final v = Context.create(); + final o = new WithConstruction(v); + + o.v.number = 20; + + Assert.equals(20, o.v.number); + Assert.equals(20, v.number); + } + + function test_reflection_create_empty() { + final f : WithConstruction = Type.createEmptyInstance(WithConstruction); + + Assert.isTrue(f.v == null); + } + + function test_reflection_create() { + final f : WithConstruction = Type.createInstance(WithConstruction, [ Context.create() ]); + + Assert.equals(7, f.v.number); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestEnumPointers.hx b/test/native/tests/marshalling/pointers/TestEnumPointers.hx new file mode 100644 index 000000000..0ba65d2ef --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestEnumPointers.hx @@ -0,0 +1,78 @@ +package tests.marshalling.pointers; + +import haxe.EnumTools; +import utest.Assert; +import utest.Test; + +enum FooEnum { + Bar(c:Context); +} + +class TestEnumPointers extends Test { + function test_into_enum() { + final c = Context.create(); + final e = FooEnum.Bar(c); + + c.number = 20; + + switch e { + case Bar(v): + + Assert.equals(20, c.number); + Assert.equals(20, v.number); + } + } + + function test_mutating_enum() { + final c = Context.create(); + final e = FooEnum.Bar(c); + + switch e { + case Bar(v1): + v1.number = 20; + + Assert.equals(20, v1.number); + Assert.equals(20, c.number); + + switch e { + case Bar(v2): + Assert.equals(20, v2.number); + } + } + } + + function test_copy_from_enum() { + final e = FooEnum.Bar(Context.create()); + + switch e { + case Bar(v): + final v_copy = v; + + v_copy.number = 20; + + Assert.equals(20, v_copy.number); + Assert.equals(20, v.number); + } + } + + function test_create_by_index() { + final c = Context.create(); + final e = EnumTools.createByIndex(FooEnum, 0, [ c ]); + + switch e { + case Bar(v): + Assert.equals(7, v.number); + } + } + + function test_create_by_name() { + final c = Context.create(); + final e = EnumTools.createByName(FooEnum, 'Bar', [ c ]); + + switch e { + case Bar(v): + + Assert.equals(7, v.number); + } + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestInheritancePointers.hx b/test/native/tests/marshalling/pointers/TestInheritancePointers.hx new file mode 100644 index 000000000..91c83a6d5 --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestInheritancePointers.hx @@ -0,0 +1,88 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +@:semantics(value) +@:include('Base.hpp') +@:cpp.PointerType +private extern class Base { + function foo():Int; +} + +@:semantics(value) +@:include('Child.hpp') +@:cpp.PointerType +private extern class Child extends Base { + function bar():Int; +} + +class TestInheritancePointers extends Test { + function test_assigning_to_a_more_specific_type() { + final o = create_child(); + final c : Child = cast o; + + Assert.equals(10, c.foo()); + Assert.equals(20, c.bar()); + } + + function test_assigning_to_a_more_specific_promoted() { + final o = create_child(); + final c : Child = cast o; + final f = () -> { + return c.bar(); + } + + f(); + + Assert.equals(10, c.foo()); + Assert.equals(20, c.bar()); + } + + function test_casting_var_to_base() { + final o = create_child(); + final v = (cast o : Child).bar(); + + Assert.equals(20, v); + } + + function test_passing_child_to_child_function() { + final o = create_child(); + final v = pass_child(cast o); + + Assert.equals(10, v); + } + + function test_passing_child_to_base_function() { + final o = create_child(); + final v = pass_base(o); + + Assert.equals(10, v); + } + + function test_reassigning() { + var o : Base = create_child(); + + Assert.equals(10, o.foo()); + + o = create_child_as_child(); + + Assert.equals(10, o.foo()); + } + + function create_child() : Base { + return untyped __cpp__('new Child()'); + } + + function create_child_as_child() : Child { + return untyped __cpp__('new Child()'); + } + + function pass_child(c : Child) { + return c.foo(); + } + + function pass_base(b : Base) { + return b.foo(); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestInterfacePointers.hx b/test/native/tests/marshalling/pointers/TestInterfacePointers.hx new file mode 100644 index 000000000..092c4508b --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestInterfacePointers.hx @@ -0,0 +1,104 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +interface IBar { + var c : Context; + + function get() : Context; + + function assign(c : Context) : Void; +} + +class Bar implements IBar { + public var c : Context; + + public function new() { + c = Context.create(); + } + + public function assign(c:Context) { + c.number = 20; + } + + public function get():Context { + return c; + } +} + +class TestInterfacePointers extends Test { + function test_interface_field_access() { + final f : IBar = new Bar(); + + Assert.equals(7, f.c.number); + } + + function test_mutating_interface_field() { + final f : IBar = new Bar(); + + f.c.number = 20; + + Assert.equals(20, f.c.number); + } + + function test_interface_field_copy() { + final f : IBar = new Bar(); + final c = f.c; + + c.number = 20; + + Assert.isTrue(c == f.c); + Assert.equals(20, c.number); + Assert.equals(20, f.c.number); + } + + function test_interface_field_assignment() { + final f : IBar = new Bar(); + final old = f.c; + + f.c = Context.create(); + + Assert.isTrue(old != f.c); + } + + function test_interface_function_return() { + final f : IBar = new Bar(); + final c = f.get(); + + Assert.isTrue(f.c == c); + } + + function test_interface_function_call() { + final f : IBar = new Bar(); + final c = Context.create(); + + f.assign(c); + + Assert.equals(20, c.number); + } + + function test_interface_reflect_variable() { + final f : IBar = new Bar(); + final s = (Reflect.field(f, 'c') : Context).number; + + Assert.equals(7, s); + } + + function test_interface_reflect_variable_mutation() { + final f : IBar = new Bar(); + + (Reflect.field(f, 'c') : Context).number = 20; + + Assert.equals(20, f.c.number); + } + + function test_interface_reflect_variable_assignment() { + final f : IBar = new Bar(); + final old = f.c; + + Reflect.setField(f, 'c', Context.create()); + + Assert.isTrue(old != f.c); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestLocalPointers.hx b/test/native/tests/marshalling/pointers/TestLocalPointers.hx new file mode 100644 index 000000000..dcca7ff85 --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestLocalPointers.hx @@ -0,0 +1,269 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +class TestLocalPointers extends Test { + function test_null_ptr() { + final ptr : Context = null; + + Assert.isTrue(ptr == null); + } + + function test_null_captured_ptr() { + final ptr : Context = null; + final f = () -> { + return ptr; + } + + f(); + + Assert.isTrue(ptr == null); + } + + function test_non_null_ptr() { + final ptr = Context.create(); + + Assert.isTrue(ptr != null); + } + + function test_non_null_captured_ptr() { + final ptr = Context.create(); + final f = () -> { + return ptr; + } + + f(); + + Assert.isTrue(ptr != null); + } + + function test_assinging_ptr() { + var ptr : Context = null; + + Assert.isTrue(ptr == null); + + ptr = Context.create(); + + Assert.isTrue(ptr != null); + } + + function test_assinging_captured_ptr() { + var ptr : Context = null; + final f = () -> { + return ptr; + } + + f(); + + Assert.isTrue(ptr == null); + + ptr = Context.create(); + + Assert.isTrue(ptr != null); + } + + function test_assinging_captured_ptr_in_closure() { + var ptr : Context = null; + final f = () -> { + ptr = Context.create(); + } + + f(); + + Assert.isTrue(ptr != null); + } + + function test_pointer_equality() { + final ptr = Context.create(); + final copy = ptr; + + Assert.isTrue(ptr == copy); + } + + function test_captured_pointer_equality() { + final ptr = Context.create(); + final f = () -> { + return ptr; + } + final copy = f(); + + Assert.isTrue(ptr == copy); + } + + function test_captured_pointer_inequality() { + final ptr = Context.create(); + final f = () -> { + return Context.create(); + } + final copy = f(); + + Assert.isTrue(ptr != copy); + } + + function test_pointer_field_access() { + final ptr = Context.create(); + + Assert.equals(7, ptr.number); + } + + function test_captured_pointer_field_access() { + final ptr = Context.create(); + final f = () -> { + return ptr.number; + } + + Assert.equals(7, f()); + } + + function test_pointer_field_mutation() { + final ptr = Context.create(); + + ptr.number = 14; + + Assert.equals(14, ptr.number); + } + + function test_capturd_pointer_field_mutation() { + final ptr = Context.create(); + final f = () -> { + ptr.number = 14; + } + + f(); + + Assert.equals(14, ptr.number); + } + + function test_pointer_function_call() { + final ptr = Context.create(); + + Assert.equals(14, ptr.double()); + } + + function test_captured_pointer_function_call() { + final ptr = Context.create(); + final f = () -> { + return ptr.double(); + } + + Assert.equals(14, f()); + } + + function test_dynamic() { + final ptr = Context.create(); + + by_dynamic(ptr); + + Assert.equals(20, ptr.number); + } + + function test_promoted_dynamic() { + var ptr = null; + + final f = () -> { + ptr = Context.create(); + } + + f(); + + by_dynamic(ptr); + + Assert.equals(20, ptr.number); + } + + function test_anon() { + final ptr = Context.create(); + + by_anon({ v : ptr }); + + Assert.equals(20, ptr.number); + } + + function test_lambda_return() { + final f = () -> { + return Context.create(); + } + + final ptr = f(); + + Assert.notNull(ptr); + } + + function test_to_string() { + final ptr = Context.create(); + final str = Std.string(ptr); + + Assert.notNull(str); + } + + function test_just_creation() { + Context.create(); + + Assert.pass(); + } + + function test_reassignment() { + var ptr = Context.create(); + + ptr.number = 20; + + ptr = Context.create(); + + Assert.equals(7, ptr.number); + } + + function test_null_access_exception() { + final ptr : Context = null; + + Assert.raises(() -> ptr.number = 7); + } + + function test_promoted_null_access_exception() { + final ptr : Context = null; + final f = () -> { + return ptr; + } + + f(); + + Assert.raises(() -> ptr.number = 7); + } + + // function test_weird_nullness() { + // function isAnyNull(a:Any) { + // return a == null; + // } + + // final ptr = Context.createNull(); + + // Assert.isTrue(ptr == null); + // Assert.isTrue(isAnyNull(ptr)); + // } + + // function test_weird_promoted_nullness() { + // function isAnyNull(a:Any) { + // return a == null; + // } + + // final ptr = Context.createNull(); + // final f = () -> { + // return ptr; + // } + + // f(); + + // Assert.isTrue(ptr == null); + // Assert.isTrue(isAnyNull(ptr)); + // } + + // + + function by_anon(a : { v : Context }) { + a.v.number = 20; + } + + function by_dynamic(v:Dynamic) { + (v:Context).number = 20; + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestPointerCollections.hx b/test/native/tests/marshalling/pointers/TestPointerCollections.hx new file mode 100644 index 000000000..3611fe971 --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestPointerCollections.hx @@ -0,0 +1,62 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +class TestPointerCollections extends Test { + function test_pushing_to_array() { + final a = []; + + a.push(Context.create()); + + Assert.equals(7, a[0].number); + } + + function test_mutate_element() { + final c = Context.create(); + final a = [ c ]; + + a[0].number = 20; + + Assert.equals(20, c.number); + Assert.equals(20, a[0].number); + } + + function test_null_default() { + final a = new Array(); + + a.resize(1); + + Assert.isTrue(a[0] == null); + } + + function test_setting_array_element() { + final c = Context.create(); + final a = [ null ]; + + a[0] = c; + + if (Assert.isTrue(a[0] != null)) { + Assert.equals(7, a[0].number); + } + } + + function test_switch_on_array() { + final c = Context.create(); + final a = [ c ]; + + // prevent haxe optimising the array away + a.resize(10); + a.resize(1); + + switch a { + case [ elem ]: + elem.number = 20; + + Assert.equals(20, c.number); + Assert.equals(20, a[0].number); + default: + Assert.fail('expected array to have one element'); + } + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestPointerFields.hx b/test/native/tests/marshalling/pointers/TestPointerFields.hx new file mode 100644 index 000000000..3b0fde545 --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestPointerFields.hx @@ -0,0 +1,87 @@ +package tests.marshalling.pointers; + +import utest.Assert; +import utest.Test; + +@:semantics(value) +@:include('point.hpp') +@:cpp.PointerType({ type : 'point', namespace : [ 'hx', 'maths' ] }) +private extern class Point { + var x : Float; + var y : Float; +} + +@:semantics(value) +@:include('holder.hpp') +@:cpp.ValueType({ type : 'holder', namespace : [] }) +private extern class Holder { + var pPtr : Point; +} + +class TestPointerFields extends Test { + + function test_field_access() { + final h = create_holder(); + + if (Assert.isTrue(h.pPtr != null)) { + Assert.equals(45f64, h.pPtr.x); + Assert.equals(67f64, h.pPtr.y); + } + } + + function test_field_null_access() { + final h = create_holder_with_null(); + + if (Assert.isTrue(h.pPtr == null)) { + Assert.exception(() -> h.pPtr.x = 7); + } + } + + function test_copying_to_var() { + final h = create_holder(); + final p = h.pPtr; + + p.x = 100; + p.y = 200; + + Assert.equals(100f64, h.pPtr.x); + Assert.equals(200f64, h.pPtr.y); + } + + function test_assignment() { + final h = create_holder(); + final p = create_point(); + + h.pPtr = p; + + Assert.equals(100f64, h.pPtr.x); + Assert.equals(200f64, h.pPtr.y); + } + + function test_assignment_from_promoted() { + final h = create_holder(); + final p = create_point(); + final f = () -> { + return p.x; + } + + f(); + + h.pPtr = p; + + Assert.equals(100f64, h.pPtr.x); + Assert.equals(200f64, h.pPtr.y); + } + + function create_holder() : Holder { + return untyped __cpp__('::holder(::hx::maths::point(), ::hx::maths::point())'); + } + + function create_holder_with_null() : Holder { + return untyped __cpp__('::holder()'); + } + + function create_point() : Point { + return untyped __cpp__('new ::hx::maths::point(100, 200)'); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/pointers/TestPointerInterop.hx b/test/native/tests/marshalling/pointers/TestPointerInterop.hx new file mode 100644 index 000000000..d02f507bf --- /dev/null +++ b/test/native/tests/marshalling/pointers/TestPointerInterop.hx @@ -0,0 +1,168 @@ +package tests.marshalling.pointers; + +import cpp.RawPointer; +import cpp.Star; +import cpp.Pointer; +import utest.Test; +import utest.Assert; + +@:include('ctx.hpp') +private extern class NativeFunction { + @:native('ctx_ptr') + static function ctx_ptr(ctx:Context):Void; + + @:native('ctx_ptr_ptr') + static function ctx_ptr_ptr(ctx:Context):Void; + + @:native('ctx_void_ptr') + static function ctx_void_ptr(ctx:Context):Void; + + @:native('ctx_void_ptr_ptr') + static function ctx_void_ptr_ptr(ctx:Context):Void; +} + +private class HaxeFunctions { + @:unreflective public static function set_number_ptr(ctx : Pointer) { + ctx[0].number = 20; + } + + @:unreflective public static function set_number_raw_ptr(ctx : RawPointer) { + ctx[0].number = 20; + } + + // @:unreflective public static function set_number_star(ctx : Star) { + // ctx.number = 20; + // } + + @:unreflective public static function is_ptr_null(ctx : Pointer) { + return ctx[0] == null; + } + + @:unreflective public static function is_raw_ptr_null(ctx : RawPointer) { + return ctx[0] == null; + } + + // @:unreflective public static function is_star_null(ctx : Star) { + // return ctx == null; + // } +} + +class TestPointerInterop extends Test { + function test_implicit_to_ptr() { + final ctx = Context.create(); + + Assert.equals(7, ctx.number); + + NativeFunction.ctx_ptr(ctx); + + Assert.equals(20, ctx.number); + } + + function test_implicit_to_ptr_ptr() { + final ctx = Context.create(); + + Assert.equals(7, ctx.number); + + NativeFunction.ctx_ptr_ptr(ctx); + + Assert.equals(20, ctx.number); + } + + function test_implicit_to_ptr_ptr_null() { + final ctx : Context = null; + + Assert.isTrue(ctx == null); + + NativeFunction.ctx_ptr_ptr(ctx); + + if (Assert.isTrue(ctx != null)) { + Assert.equals(20, ctx.number); + } + } + + function test_implicit_to_ptr_ptr_copy() { + final ctx = Context.create(); + final copy = ctx; + + Assert.equals(7, ctx.number); + + NativeFunction.ctx_ptr_ptr(ctx); + + Assert.equals(20, ctx.number); + Assert.equals(7, copy.number); + } + + function test_implicit_to_void_ptr() { + final ctx = Context.create(); + + Assert.equals(7, ctx.number); + + NativeFunction.ctx_void_ptr(ctx); + + Assert.equals(20, ctx.number); + } + + function test_implicit_to_void_ptr_ptr() { + final ctx = Context.create(); + + Assert.equals(7, ctx.number); + + NativeFunction.ctx_void_ptr_ptr(ctx); + + Assert.equals(20, ctx.number); + } + + function test_implicit_to_void_ptr_ptr_null() { + final ctx : Context = null; + + Assert.isTrue(ctx == null); + + NativeFunction.ctx_void_ptr_ptr(ctx); + + if (Assert.isTrue(ctx != null)) { + Assert.equals(20, ctx.number); + } + } + + function test_to_cpp_pointer() { + final ctx = Context.create(); + + HaxeFunctions.set_number_ptr(cast ctx); + + Assert.equals(20, ctx.number); + } + + function test_null_to_cpp_pointer_throws() { + final ctx : Context = null; + + Assert.isTrue(HaxeFunctions.is_ptr_null(cast ctx)); + } + + function test_to_cpp_raw_pointer() { + final ctx = Context.create(); + + HaxeFunctions.set_number_raw_ptr(cast ctx); + + Assert.equals(20, ctx.number); + } + + function test_null_to_cpp_raw_pointer_throws() { + final ctx : Context = null; + + Assert.isTrue(HaxeFunctions.is_raw_ptr_null(cast ctx)); + } + + // function test_to_cpp_star() { + // final ctx = Context.create(); + + // HaxeFunctions.set_number_star(cast ctx); + + // Assert.equals(20, ctx.number); + // } + + // function test_null_to_cpp_star_throws() { + // final ctx : Context = null; + + // Assert.isTrue(HaxeFunctions.is_star_null(cast ctx)); + // } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/view/TestMarshal.hx b/test/native/tests/marshalling/view/TestMarshal.hx new file mode 100644 index 000000000..60199b5be --- /dev/null +++ b/test/native/tests/marshalling/view/TestMarshal.hx @@ -0,0 +1,347 @@ +package tests.marshalling.view; + +import haxe.ds.Vector; +import cpp.Char; +import cpp.Char16; +import cpp.Pointer; +import cpp.marshal.View; +import haxe.io.Bytes; +import utest.Test; +import utest.Assert; +import tests.marshalling.Point; + +using cpp.marshal.Marshal; +using cpp.marshal.ViewExtensions; + +class TestMarshal extends Test { + function test_write_int() { + final storage = 0; + final source = new View(Pointer.addressOf(storage), 1); + final value = 200; + + source.asBytesView().write(value); + + Assert.equals(value, storage); + } + + function test_write_not_enough_space() { + final storage = Bytes.alloc(1); + + Assert.raises(() -> storage.asView().writeInt32(0)); + } + + function test_read_int() { + final storage = 200; + final source = new View(Pointer.addressOf(storage), 1); + + Assert.isTrue(storage == source.asBytesView().read()); + } + + function test_read_not_enough_space() { + final storage = Bytes.alloc(1); + + Assert.raises(() -> storage.asView().readInt32()); + } + + function test_write_float() { + final storage = 0f64; + final source = new View(Pointer.addressOf(storage), 1); + final value = 200.81; + + source.asBytesView().write(value); + + Assert.equals(value, storage); + } + + function test_read_float() { + final storage = 200.81; + final source = new View(Pointer.addressOf(storage), 1); + + Assert.isTrue(storage == source.asBytesView().read()); + } + + function test_write_bool() { + final storage = false; + final source = new View(Pointer.addressOf(storage), 1); + final value = true; + + source.asBytesView().write(value); + + Assert.equals(value, storage); + } + + function test_read_bool() { + final storage = true; + final source = new View(Pointer.addressOf(storage), 1); + + Assert.isTrue(storage == source.asBytesView().read()); + } + + function test_write_value_type() { + final storage = new Point(0, 0); + final source = new View(Pointer.addressOf(storage), 1); + final value = new Point(); + + source.asBytesView().write(value); + + Assert.equals(value.x, storage.x); + Assert.equals(value.y, storage.y); + } + + function test_read_value_type() { + final storage = new Point(); + final source = new View(Pointer.addressOf(storage), 1); + final value = (source.asBytesView().read() : Point); + + Assert.equals(value.x, storage.x); + Assert.equals(value.y, storage.y); + } + + function test_write_pointer_type() { + final storage = Context.createNull(); + final source = new View(Pointer.addressOf(storage), 1); + final value = Context.create(); + + source.asBytesView().write(value); + + Assert.isTrue(storage == value); + } + + function test_read_pointer_type() { + final storage = Context.create(); + final source = new View(Pointer.addressOf(storage), 1); + final value = (source.asBytesView().read() : Context); + + Assert.isTrue(storage == value); + } + + function test_ascii_string_to_utf8() { + final source = "Hello, World!"; + final view = source.toCharView(); + + if (Assert.equals(source.length + 1, view.length)) { + Assert.equals(view[ 0], "H".code); + Assert.equals(view[ 1], "e".code); + Assert.equals(view[ 2], "l".code); + Assert.equals(view[ 3], "l".code); + Assert.equals(view[ 4], "o".code); + Assert.equals(view[ 5], ",".code); + Assert.equals(view[ 6], " ".code); + Assert.equals(view[ 7], "W".code); + Assert.equals(view[ 8], "o".code); + Assert.equals(view[ 9], "r".code); + Assert.equals(view[10], "l".code); + Assert.equals(view[11], "d".code); + Assert.equals(view[12], "!".code); + Assert.equals(view[13], 0); + } + } + + function test_ascii_string_to_utf8_buffer() { + final source = "Hello, World!"; + final buffer = Bytes.ofHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + final view = buffer.asView().reinterpret(); + final count = Marshal.toCharView(source, view); + + if (Assert.equals(source.length + 1, count)) { + Assert.equals(view[ 0], "H".code); + Assert.equals(view[ 1], "e".code); + Assert.equals(view[ 2], "l".code); + Assert.equals(view[ 3], "l".code); + Assert.equals(view[ 4], "o".code); + Assert.equals(view[ 5], ",".code); + Assert.equals(view[ 6], " ".code); + Assert.equals(view[ 7], "W".code); + Assert.equals(view[ 8], "o".code); + Assert.equals(view[ 9], "r".code); + Assert.equals(view[10], "l".code); + Assert.equals(view[11], "d".code); + Assert.equals(view[12], "!".code); + Assert.equals(view[13], 0); + } + } + + function test_emoji_string_to_utf8() { + final source = "😂"; + final view = source.toCharView(); + + if (Assert.equals(5, view.length)) { + Assert.equals((0xf0:Char), view[0]); + Assert.equals((0x9f:Char), view[1]); + Assert.equals((0x98:Char), view[2]); + Assert.equals((0x82:Char), view[3]); + Assert.equals(0, view[4]); + } + } + + function test_emoji_string_to_utf8_buffer() { + final source = "😂"; + final buffer = Bytes.ofHex("FFFFFFFFFF"); + final view = buffer.asView().reinterpret(); + final count = Marshal.toCharView(source, view); + + if (Assert.equals(5, count)) { + Assert.equals((0xf0:Char), view[0]); + Assert.equals((0x9f:Char), view[1]); + Assert.equals((0x98:Char), view[2]); + Assert.equals((0x82:Char), view[3]); + Assert.equals(0, view[4]); + } + } + + function test_ascii_string_to_utf16() { + final source = "Hello, World!"; + final view = source.toWideCharView(); + + if (Assert.equals(source.length + 1, view.length)) { + Assert.equals(view[ 0], "H".code); + Assert.equals(view[ 1], "e".code); + Assert.equals(view[ 2], "l".code); + Assert.equals(view[ 3], "l".code); + Assert.equals(view[ 4], "o".code); + Assert.equals(view[ 5], ",".code); + Assert.equals(view[ 6], " ".code); + Assert.equals(view[ 7], "W".code); + Assert.equals(view[ 8], "o".code); + Assert.equals(view[ 9], "r".code); + Assert.equals(view[10], "l".code); + Assert.equals(view[11], "d".code); + Assert.equals(view[12], "!".code); + Assert.equals(view[13], 0); + } + } + + function test_ascii_string_to_utf16_buffer() { + final source = "Hello, World!"; + final buffer = Bytes.ofHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + final view = buffer.asView().reinterpret(); + final count = Marshal.toWideCharView(source, view); + + if (Assert.equals(count, view.length)) { + Assert.equals(view[ 0], "H".code); + Assert.equals(view[ 1], "e".code); + Assert.equals(view[ 2], "l".code); + Assert.equals(view[ 3], "l".code); + Assert.equals(view[ 4], "o".code); + Assert.equals(view[ 5], ",".code); + Assert.equals(view[ 6], " ".code); + Assert.equals(view[ 7], "W".code); + Assert.equals(view[ 8], "o".code); + Assert.equals(view[ 9], "r".code); + Assert.equals(view[10], "l".code); + Assert.equals(view[11], "d".code); + Assert.equals(view[12], "!".code); + Assert.equals(view[13], 0); + } + } + + function test_emoji_string_to_utf16() { + final source = "😂"; + final view = source.toWideCharView(); + + if (Assert.equals(3, view.length)) { + Assert.equals((0xD83D:Char16), view[0]); + Assert.equals((0xDE02:Char16), view[1]); + Assert.equals(0, view[2]); + } + } + + function test_emoji_string_to_utf16_buffer() { + final source = "😂"; + final buffer = Bytes.ofHex("FFFFFFFFFFFFFFFF"); + final view = buffer.asView().slice(0, 3 * 2).reinterpret(); + final count = Marshal.toWideCharView(source, view); + + if (Assert.equals(count, view.length)) { + Assert.equals((0xD83D:Char16), view[0]); + Assert.equals((0xDE02:Char16), view[1]); + Assert.equals(0, view[2]); + } + } + + function test_ascii_chars_to_string() { + final buffer = new Vector(5); + buffer[0] = 'H'.code; + buffer[1] = 'e'.code; + buffer[2] = 'l'.code; + buffer[3] = 'l'.code; + buffer[4] = 'o'.code; + final view = buffer.asView(); + final string = view.toString(); + + Assert.equals('Hello', string); + } + + function test_ascii_wide_chars_to_string() { + final buffer = new Vector(5); + buffer[0] = 'H'.code; + buffer[1] = 'e'.code; + buffer[2] = 'l'.code; + buffer[3] = 'l'.code; + buffer[4] = 'o'.code; + final view = buffer.asView(); + final string = view.toString(); + + Assert.equals('Hello', string); + } + + function test_null_terminated_ascii_chars_to_string() { + final buffer = new Vector(5); + buffer[0] = 'H'.code; + buffer[1] = 'e'.code; + buffer[2] = 'l'.code; + buffer[3] = 'l'.code; + buffer[4] = 'o'.code; + buffer[5] = 0; + final view = buffer.asView(); + final string = view.toString(); + + Assert.equals('Hello', string); + } + + function test_null_terminated_ascii_wide_chars_to_string() { + final buffer = new Vector(5); + buffer[0] = 'H'.code; + buffer[1] = 'e'.code; + buffer[2] = 'l'.code; + buffer[3] = 'l'.code; + buffer[4] = 'o'.code; + buffer[5] = 0; + final view = buffer.asView(); + final string = view.toString(); + + Assert.equals('Hello', string); + } + + function test_utf8_bytes_to_string() { + final buffer = Bytes.ofHex("f09f9882"); + final view = (buffer.asView().reinterpret() : View); + final string = view.toString(); + + Assert.equals('😂', string); + } + + function test_null_terminated_utf8_bytes_to_string() { + final buffer = Bytes.ofHex("f09f98820000"); + final view = (buffer.asView().reinterpret() : View); + final string = view.toString(); + + Assert.equals('😂', string); + } + + function test_utf16_bytes_to_string() { + final buffer = Bytes.ofHex("3DD802De"); + final view = (buffer.asView().reinterpret() : View); + final string = view.toString(); + + Assert.equals('😂', string); + } + + function test_null_terminated_utf16_bytes_to_string() { + final buffer = Bytes.ofHex("3DD802De00000000"); + final view = (buffer.asView().reinterpret() : View); + final string = view.toString(); + + Assert.equals('😂', string); + } +} \ No newline at end of file diff --git a/test/native/tests/marshalling/view/TestView.hx b/test/native/tests/marshalling/view/TestView.hx new file mode 100644 index 000000000..8eb6c5555 --- /dev/null +++ b/test/native/tests/marshalling/view/TestView.hx @@ -0,0 +1,295 @@ +package tests.marshalling.view; + +import cpp.Int64; +import cpp.UInt32; +import cpp.Int16; +import cpp.Pointer; +import cpp.marshal.View; +import utest.Test; +import utest.Assert; +import tests.marshalling.Point; + +using cpp.marshal.ViewExtensions; + +class TestView extends Test { + function test_reading_ints() { + final buffer = [ for (i in 0...10) i + 1 ]; + final view = buffer.asView(); + + for (i in 0...10) { + Assert.equals(i + 1, view[i]); + } + } + + function test_writing_ints() { + final buffer = [ for (_ in 0...10) 0 ]; + final view = buffer.asView(); + + for (i in 0...10) { + view[i] = i + 1; + } + + Assert.same([ for (i in 0...buffer.length) i + 1 ], buffer); + } + + function test_reading_value_types() { + final buffer = new Point(0, 0); + final view = new View(Pointer.addressOf(buffer), 1); + final point = view[0]; + + Assert.equals(0f64, point.x); + Assert.equals(0f64, point.y); + + buffer.x = 200; + buffer.y = 300; + + Assert.notEquals(buffer.x, point.x); + Assert.notEquals(buffer.y, point.y); + } + + function test_writing_value_types() { + final buffer = new Point(0, 0); + final view = new View(Pointer.addressOf(buffer), 1); + final point = new Point(); + + view[0] = point; + + Assert.equals(buffer.x, point.x); + Assert.equals(buffer.y, point.y); + + point.x = 200; + point.y = 300; + + Assert.notEquals(buffer.x, point.x); + Assert.notEquals(buffer.y, point.y); + } + + function test_reading_value_type_fields() { + final point = new Point(); + final view = new View(Pointer.addressOf(point), 1); + + Assert.equals( 7f64, view[0].x); + Assert.equals(26f64, view[0].y); + } + + function test_writing_value_type_fields() { + final point = new Point(); + final view = new View(Pointer.addressOf(point), 1); + + view[0].x = 8; + view[0].y = 40; + + Assert.equals( 8f64, point.x); + Assert.equals(40f64, point.y); + } + + function test_reading_pointer_type() { + final obj = Context.create(); + final view = new View(Pointer.addressOf(obj), 1); + + Assert.isTrue(obj == view[0]); + } + + function test_writing_pointer_type() { + final obj = Context.createNull(); + final view = new View(Pointer.addressOf(obj), 1); + final next = Context.create(); + + view[0] = next; + + Assert.isTrue(next == view[0]); + Assert.isTrue(obj == view[0]); + } + + function test_reading_pointer_type_field() { + final obj = Context.create(); + final view = new View(Pointer.addressOf(obj), 1); + + Assert.equals(7, view[0].number); + } + + function test_writing_pointer_type_field() { + final obj = Context.create(); + final view = new View(Pointer.addressOf(obj), 1); + + view[0].number = 200; + + Assert.equals(200, obj.number); + } + + function test_reading_error() { + final buffer = [ for (i in 0...10) i + 1 ]; + + Assert.raises(() -> final _ = buffer.asView()[-1], String); + Assert.raises(() -> final _ = buffer.asView()[10], String); + Assert.raises(() -> final _ = buffer.asView()[20], String); + } + + function test_writing_error() { + final buffer = [ for (i in 0...10) i + 1 ]; + + Assert.raises(() -> buffer.asView()[-1] = 100, String); + Assert.raises(() -> buffer.asView()[10] = 100, String); + Assert.raises(() -> buffer.asView()[20] = 100, String); + } + + function test_slice() { + final buffer = [ for (i in 0...10) i + 1 ]; + final view = buffer.asView(); + final index = 3; + final slice = view.slice(index); + + if (Assert.equals(7, slice.length)) { + for (i in 0...slice.length) { + Assert.equals(i + index + 1, slice[i]); + } + } + } + + function test_slice_errors() { + final buffer = [ for (i in 0...10) i + 1 ]; + + Assert.raises(() -> buffer.asView().slice(-1), String); + Assert.raises(() -> buffer.asView().slice(100), String); + Assert.isTrue(buffer.asView().slice(10).isEmpty()); + } + + function test_slice_with_length() { + final buffer = [ for (i in 0...10) i + 1 ]; + final view = buffer.asView(); + final index = 3; + final length = 4; + final slice = view.slice(index, length); + + if (Assert.equals(length, slice.length)) { + for (i in 0...slice.length) { + Assert.equals(i + index + 1, slice[i]); + } + } + } + + function test_slice_with_length_errors() { + final buffer = [ for (i in 0...10) i + 1 ]; + + Assert.raises(() -> buffer.asView().slice(-1, 5), String); + Assert.raises(() -> buffer.asView().slice(5, -1), String); + Assert.raises(() -> buffer.asView().slice(1, 100), String); + Assert.raises(() -> buffer.asView().slice(100, 5), String); + Assert.raises(() -> buffer.asView().slice(10, 5), String); + Assert.isTrue(buffer.asView().slice(10, 0).isEmpty()); + } + + function test_clear() { + final buffer = [ for (_ in 0...10) 8 ]; + final view = buffer.asView(); + + view.slice(2, 6).clear(); + + Assert.same([ 8, 8, 0, 0, 0, 0, 0, 0, 8, 8 ], buffer); + } + + function test_fill() { + final buffer = [ for (_ in 0...10) 0 ]; + final view = buffer.asView(); + final value = 8; + + view.slice(2, 6).fill(value); + + Assert.same([ 0, 0, value, value, value, value, value, value, 0, 0 ], buffer); + } + + function test_isEmpty() { + Assert.isTrue(new View(null, 0).isEmpty()); + + final buffer = [ for (_ in 0...10) 0 ]; + + Assert.isFalse(buffer.asView().isEmpty()); + } + + function test_equality() { + final buffer = [ for (_ in 0...10) 0 ]; + final view = buffer.asView(); + + final fst = view.slice(0); + final snd = view.slice(0); + + Assert.isTrue(fst == snd); + Assert.isFalse(fst != snd); + + final trd = view.slice(2); + + Assert.isFalse(fst == trd); + Assert.isTrue(fst != trd); + } + + function test_reinterpret_equal_size() { + final buffer = [ for (_ in 0...10) 0 ]; + final view = buffer.asView(); + final second : View = view.reinterpret(); + + Assert.equals(view.length, second.length); + } + + function test_reinterpret_to_smaller_type() { + final buffer = [ for (_ in 0...10) 0 ]; + final view = buffer.asView(); + final second : View = view.reinterpret(); + + Assert.equals(view.length * 2, second.length); + } + + function test_reinterpret_to_larger_type() { + final buffer = [ for (_ in 0...3) 0 ]; + final view = buffer.asView(); + final second : View = view.reinterpret(); + + Assert.equals(1, second.length); + } + + function test_reinterpret_to_larger_type_not_enough_length() { + final buffer = [ 0 ]; + final view = buffer.asView(); + final second : View = view.reinterpret(); + + Assert.equals(0, second.length); + } + + function test_reinterpret_to_value_type() { + final buffer = [ 0f64, 0f64, 0f64, 0f64 ]; + final view = buffer.asView(); + final points = (view.reinterpret() : View); + + Assert.equals(2, points.length); + Assert.equals(0f64, points[0].x); + Assert.equals(0f64, points[0].y); + + points[0].x = 200; + points[0].y = 300; + + Assert.equals(200f64, buffer[0]); + Assert.equals(300f64, buffer[1]); + + points[0] = new Point(); + + Assert.equals(7f64, buffer[0]); + Assert.equals(26f64, buffer[1]); + } + + function test_tryCopyTo() { + final buffer = [ for (i in 0...10) i ]; + final biggerDst = buffer.copy(); + final matchingDst = buffer.copy(); + final smallerDst = buffer.copy(); + + biggerDst.resize(20); + smallerDst.resize(5); + + Assert.isTrue(buffer.asView().tryCopyTo(biggerDst.asView())); + Assert.isTrue(buffer.asView().tryCopyTo(matchingDst.asView())); + Assert.isFalse(buffer.asView().tryCopyTo(smallerDst.asView())); + + Assert.same(buffer, biggerDst.slice(0, 10)); + Assert.same(buffer, matchingDst); + Assert.same(buffer.slice(0, smallerDst.length), smallerDst); + } +} diff --git a/test/native/tests/marshalling/view/TestViewExtensions.hx b/test/native/tests/marshalling/view/TestViewExtensions.hx new file mode 100644 index 000000000..271b1e103 --- /dev/null +++ b/test/native/tests/marshalling/view/TestViewExtensions.hx @@ -0,0 +1,164 @@ +package tests.marshalling.view; + +import haxe.io.UInt8Array; +import haxe.io.UInt16Array; +import haxe.io.UInt32Array; +import haxe.io.Int32Array; +import haxe.io.Float64Array; +import haxe.io.Float32Array; +import haxe.io.ArrayBufferView; +import haxe.io.Bytes; +import haxe.ds.Vector; +import cpp.Pointer; +import cpp.marshal.View; +import utest.Test; +import utest.Assert; + +using cpp.marshal.ViewExtensions; + +class TestViewExtensions extends Test { + function test_toArray() { + final source = 200; + final view = new View(Pointer.addressOf(source), 1); + final array = view.toArray(); + + Assert.same([ source ], array); + } + + function test_toVector() { + final source = 200; + final view = new View(Pointer.addressOf(source), 1); + final vector = view.toVector(); + + if (Assert.equals(1, vector.length)) { + Assert.equals(source, vector[0]); + } + } + + function test_toBytes() { + final source = 200; + final view = new View(Pointer.addressOf(source), 1); + final bytes = view.toBytes(); + + if (Assert.equals(4, bytes.length)) { + Assert.equals(source, bytes.getInt32(0)); + } + } + + function test_array_as_view() { + final array = [ 100, 200, 300, 400 ]; + final view = array.asView(); + + if (Assert.equals(array.length, view.length)) { + for (i in 0...array.length) { + Assert.equals(array[i], view[i]); + } + } + } + + function test_vector_as_view() { + final vector = Vector.fromData([ 100, 200, 300, 400 ]); + final view = vector.asView(); + + if (Assert.equals(vector.length, view.length)) { + for (i in 0...vector.length) { + Assert.equals(vector[i], view[i]); + } + } + } + + function test_bytes_as_view() { + final bytes = Bytes.ofData([ 10, 20, 30, 40 ]); + final view = bytes.asView(); + + if (Assert.equals(bytes.length, view.length)) { + for (i in 0...bytes.length) { + Assert.equals(bytes.get(i), view[i]); + } + } + } + + function test_array_buffer_view_as_view() { + final index = 7; + final buffer = ArrayBufferView.fromBytes(Bytes.ofData([ for (i in 0...100) i ])).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.byteLength, view.length)) { + for (i in 0...buffer.byteLength) { + Assert.equals(buffer.buffer.get(index + i), view[i]); + } + } + } + + function test_float32_array_as_view() { + final index = 7; + final buffer = Float32Array.fromArray([ for (i in 0...100) i ]).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.length, view.length)) { + for (i in 0...buffer.length) { + Assert.equals(buffer[i], view[i]); + } + } + } + + function test_float64_array_as_view() { + final index = 7; + final buffer = Float64Array.fromArray([ for (i in 0...100) i ]).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.length, view.length)) { + for (i in 0...buffer.length) { + Assert.equals(buffer[i], view[i]); + } + } + } + + function test_int32_array_as_view() { + final index = 7; + final buffer = Int32Array.fromArray([ for (i in 0...100) i ]).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.length, view.length)) { + for (i in 0...buffer.length) { + Assert.equals(buffer[i], view[i]); + } + } + } + + function test_uint32_array_as_view() { + final index = 7; + final buffer = UInt32Array.fromArray([ for (i in 0...100) i ]).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.length, view.length)) { + for (i in 0...buffer.length) { + Assert.equals(buffer[i], view[i]); + } + } + } + + function test_uint16_array_as_view() { + final index = 7; + final buffer = UInt16Array.fromArray([ for (i in 0...100) i ]).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.length, view.length)) { + for (i in 0...buffer.length) { + Assert.equals(buffer[i], view[i]); + } + } + } + + function test_uint8_array_as_view() { + final index = 7; + final buffer = UInt8Array.fromArray([ for (i in 0...100) i ]).sub(index, 10); + final view = buffer.asView(); + + if (Assert.equals(buffer.length, view.length)) { + for (i in 0...buffer.length) { + Assert.equals(buffer[i], view[i]); + } + } + } +} \ No newline at end of file diff --git a/toolchain/haxe-target.xml b/toolchain/haxe-target.xml index 2cbd0d05a..a5aa5ea5d 100644 --- a/toolchain/haxe-target.xml +++ b/toolchain/haxe-target.xml @@ -60,6 +60,14 @@ + + + + + + + +