diff --git a/include/cpp/Marshal.h b/include/cpp/Marshal.h new file mode 100644 index 000000000..6f215ece7 --- /dev/null +++ b/include/cpp/Marshal.h @@ -0,0 +1,649 @@ +#ifndef CPP_MARSHAL_H +#define CPP_MARSHAL_H + +#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 + { + // These true and false variants are called based on if T is a pointer + // If T is not a pointer trying to assign a value type to null results in a null pointer exception being thrown. + // But if T is a pointer then the value type holds a null pointer value. + + 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(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 + 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; + }; + } +} + +// Boxed implementation + +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{}); +} + +// Reference implementation + +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(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 +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; +} + +// Pointer implementation + +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 +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; +} + +// + +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; +} + +// ValueType implementation + +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; +} + +// Implement some pointer helpers here + +template +inline cpp::Pointer cpp::Pointer_obj::addressOf(const ::cpp::marshal::ValueReference& ref) +{ + return Pointer(ref.ptr); +} + +#endif \ No newline at end of file diff --git a/include/cpp/Pointer.h b/include/cpp/Pointer.h index 25c28b451..55db33a03 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); } @@ -509,6 +512,8 @@ class Pointer_obj } + template + inline static Pointer addressOf(const ::cpp::marshal::ValueReference&); template inline static Pointer addressOf(T &value) { return Pointer(&value); } 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/hxcpp.h b/include/hxcpp.h index 008f13545..4a1a6eeb5 100755 --- a/include/hxcpp.h +++ b/include/hxcpp.h @@ -259,6 +259,12 @@ 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; } } template class Array_obj; template class Array; namespace hx { @@ -348,6 +354,7 @@ typedef PropertyAccessMode PropertyAccess; #endif #include #include +#include #include #include #include diff --git a/test/RunTests.hx b/test/RunTests.hx index 06f0c79c2..7a3a88c37 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() { @@ -188,6 +195,9 @@ class RunTests //run("opMatrix", opMatrix); run("haxe", runHaxe); run("telemetry", runTelemetry); +#if (haxe_ver >= 5) + run("marshalling", marshalling); +#end run("std32", std32); run("std64", std64); run("native", native); diff --git a/test/native/Native.hx b/test/native/Native.hx index acab7aada..229fc7b6f 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,34 @@ 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() + #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..2ee1965b9 --- /dev/null +++ b/test/native/tests/marshalling/Context.hx @@ -0,0 +1,16 @@ +package tests.marshalling; + +@:semantics(reference) +@: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..e56bc1cc4 --- /dev/null +++ b/test/native/tests/marshalling/Point.hx @@ -0,0 +1,14 @@ +package tests.marshalling; + +@:semantics(reference) +@: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..548f64678 --- /dev/null +++ b/test/native/tests/marshalling/StdVector.hx @@ -0,0 +1,19 @@ +package tests.marshalling; + +import cpp.Reference; + +@:include('vector') +@:semantics(reference) +@: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..8a5bad273 --- /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(reference) +@:include('Base.hpp') +@:cpp.ValueType +private extern class Base { + function foo():Int; +} + +@:semantics(reference) +@: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..43f2596d4 --- /dev/null +++ b/test/native/tests/marshalling/classes/TestValueTypeCollections.hx @@ -0,0 +1,64 @@ +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'); + } + } +} \ 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..cdf2cff6d --- /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(reference) +@: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(reference) +@: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..3059de820 --- /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(reference) +@: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..ed68a9980 --- /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(reference) +@: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..241d470be --- /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..0db1bb22d --- /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(reference) +@:include('Base.hpp') +@:cpp.PointerType +private extern class Base { + function foo():Int; +} + +@:semantics(reference) +@: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..19ad20091 --- /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(reference) +@:include('point.hpp') +@:cpp.PointerType({ type : 'point', namespace : [ 'hx', 'maths' ] }) +private extern class Point { + var x : Float; + var y : Float; +} + +@:semantics(reference) +@: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..6c1948c0a --- /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 == null; + } + + @:unreflective public static function is_raw_ptr_null(ctx : RawPointer) { + return ctx == 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/toolchain/haxe-target.xml b/toolchain/haxe-target.xml index 081c15bc5..b0fb501af 100644 --- a/toolchain/haxe-target.xml +++ b/toolchain/haxe-target.xml @@ -57,6 +57,7 @@ +