diff --git a/.gitignore b/.gitignore index 1be4045f..f748effd 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ test/extern-lib/gen-externs test/cffi/project/ndll test/regression/CffiNekoNullBoolWithoutInit/native/ndll test/regression/CffiNekoValueTags/native/ndll +test/regression/CffiPrimeAfterCffi/native/ndll test/regression/CffiPrimeUnicodeFallback/native/ndll test/regression/CffiQuietMissingPrimitive/native/ndll test/snippets/messagebox/cpp diff --git a/Changes.md b/Changes.md index f934df9b..a516fbf5 100644 --- a/Changes.md +++ b/Changes.md @@ -2,6 +2,7 @@ libhxcpp 6.0.0 (unreleased) ------------------------------------------------------------ +* Fixed a native extension that includes hx/CFFI.h before hx/CFFIPrime.h, as SteamWrap does, failing to compile with hxs_encoding, hxs_utf16 and alloc_hxs_utf8 not declared: CFFI.h included first declares the CFFI API without HXCPP_PRIME, so without the Prime string functions that the ordinary wrapper of a DEFINE_PRIME function now uses. CFFIPrime.h declares them then * Fixed a thread that throws while the process exits crashing it on musl, as one with a failing cpp.Lib.load did about once in 800 exits on Alpine: libgcc's unwinder frees the table it finds frames in when the library finalizers run, and other threads still ran Haxe code then. The exit now parks every other thread that runs Haxe code, at its next safe point or as it leaves a GC-free zone, waiting up to 100 ms for one in a loop that does not allocate. A thread let go during the exit stays parked instead of ending (libhxcpp #423) * Fixed programs that use sys.ssl without sockets failing to link on Linux with an undefined reference to hxcpp_alloc_kind: hxcpp_std, linked a second time after hxcpp_ssl, came after the runtime's second copy. The runtime is linked once more after all the libraries linked twice, which keep their order * Fixed closing, reading or polling a socket handle that a library made in stock hxcpp's layout, as CrossByte's accept does, reading past the handle: the runtime took its own wrapper's later fields from it, and a close wrote through what followed it in memory. Only the descriptor is read from such a handle, and the runtime's wrapper no longer shares its name, and with it its vtable, with a library's diff --git a/include/hx/CFFIAPI.h b/include/hx/CFFIAPI.h index 6a05ada7..3951390d 100644 --- a/include/hx/CFFIAPI.h +++ b/include/hx/CFFIAPI.h @@ -2,8 +2,12 @@ This bit of Macro magic is used to define extern function pointers in ndlls, define stub implementations that link back to the hxcpp dll and glue up the implementation in the hxcpp runtime. + + HX_CFFI_PRIME_ONLY declares the Prime string functions alone, for + CFFIPrime.h when CFFI.h was included before it, without them. */ +#ifndef HX_CFFI_PRIME_ONLY DEFFUNC_1(void,val_throw,value) DEFFUNC_0(void,hx_error) DEFFUNC_3(void,hx_fail,const char *,const char *,int) @@ -47,8 +51,10 @@ DEFFUNC_1(const char *,val_string,value) DEFFUNC_1(wchar_t *,val_dup_wstring,value) DEFFUNC_1(char *,val_dup_string,value) DEFFUNC_2(char *,alloc_string_data,const char *,int) +#endif #ifdef HXCPP_PRIME +#define HX_CFFI_PRIME_DECLARED DEFFUNC_2(HxString,alloc_hxs_wchar,const wchar_t *,int) DEFFUNC_2(HxString,alloc_hxs_utf16,const char16_t *,int) DEFFUNC_2(HxString,alloc_hxs_utf8,const char *,int) @@ -60,6 +66,7 @@ DEFFUNC_2(const char16_t *,hxs_utf16,const HxString &,hx::IStringAlloc *) DEFFUNC_1(hx::StringEncoding,hxs_encoding,const HxString &) #endif +#ifndef HX_CFFI_PRIME_ONLY // Array access - generic DEFFUNC_1(value,alloc_array,int) @@ -185,5 +192,6 @@ DEFFUNC_0(bool, gc_try_unblocking) // Used for finding functions in static libraries DEFFUNC_2(int, hx_register_prim, const char *, void*) +#endif diff --git a/include/hx/CFFIPrime.h b/include/hx/CFFIPrime.h index 39153f8f..66516d98 100644 --- a/include/hx/CFFIPrime.h +++ b/include/hx/CFFIPrime.h @@ -166,6 +166,14 @@ struct HxString }; #include "CFFI.h" + +// CFFI.h included before this header declared its API without HXCPP_PRIME, +// so without the string functions that ToValue and ValueToHxString use. +#if !defined(HX_CFFI_PRIME_DECLARED) && !defined(IGNORE_CFFI_API_H) +#define HX_CFFI_PRIME_ONLY +#include "CFFIAPI.h" +#undef HX_CFFI_PRIME_ONLY +#endif #endif #ifndef HXCPP_JS_PRIME diff --git a/test/regression/CffiPrimeAfterCffi/BuildNative.hx b/test/regression/CffiPrimeAfterCffi/BuildNative.hx new file mode 100644 index 00000000..3397f161 --- /dev/null +++ b/test/regression/CffiPrimeAfterCffi/BuildNative.hx @@ -0,0 +1,18 @@ +#if macro +import haxe.macro.Context; +import haxe.macro.Expr; +class BuildNative { + public static function run():Array { + var args=["run","hxcpp","Build.xml"]; + for(define in ["HXCPP_M32","HXCPP_M64","HXCPP_ARM64","HXCPP_DEBUG"]) { + if(Context.defined(define)) args.push("-D"+define); + } + var original=Sys.getCwd(); + Sys.setCwd("native"); + var status=Sys.command("haxelib",args); + Sys.setCwd(original); + if(status!=0) Context.error("Native fixture build failed",Context.currentPos()); + return Context.getBuildFields(); + } +} +#end \ No newline at end of file diff --git a/test/regression/CffiPrimeAfterCffi/Main.hx b/test/regression/CffiPrimeAfterCffi/Main.hx new file mode 100644 index 00000000..8e6d3369 --- /dev/null +++ b/test/regression/CffiPrimeAfterCffi/Main.hx @@ -0,0 +1,18 @@ +// The ndll includes CFFI.h before CFFIPrime.h +@:build(BuildNative.run()) +class Main { + static function main() { + cpp.Lib.pushDllSearchPath(haxe.io.Path.join([haxe.io.Path.directory(Sys.programPath()),"../native/ndll",cpp.Lib.getBinDirectory()])); + var fallback=cpp.Lib.load("afterprime","afterEcho",1); + var fast=cpp.Prime.load("afterprime","afterEcho","ss",false); + for(text in ["plain","café","\u{1f600}"]) { + var ordinary:String=fallback(text); + var direct:String=fast(text); + Sys.println("direct Prime preserves the string: "+(direct==text)); + Sys.println("CFFI fallback preserves the same string: "+(ordinary==text)); + } + var makeFallback=cpp.Lib.load("afterprime","afterMakeText",0); + var made:String=makeFallback(); + Sys.println("CFFI fallback preserves a native Unicode result: "+(made=="日本語")); + } +} diff --git a/test/regression/CffiPrimeAfterCffi/build.hxml b/test/regression/CffiPrimeAfterCffi/build.hxml new file mode 100644 index 00000000..b484e67e --- /dev/null +++ b/test/regression/CffiPrimeAfterCffi/build.hxml @@ -0,0 +1,2 @@ +-m Main +--cpp bin diff --git a/test/regression/CffiPrimeAfterCffi/native/Bridge.cpp b/test/regression/CffiPrimeAfterCffi/native/Bridge.cpp new file mode 100644 index 00000000..a181e725 --- /dev/null +++ b/test/regression/CffiPrimeAfterCffi/native/Bridge.cpp @@ -0,0 +1,7 @@ +#define IMPLEMENT_API +#include +#include +HxString afterEcho(HxString value) { return value; } +DEFINE_PRIME1(afterEcho); +HxString afterMakeText() { return alloc_hxs_utf8("\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e",9); } +DEFINE_PRIME0(afterMakeText); diff --git a/test/regression/CffiPrimeAfterCffi/native/Build.xml b/test/regression/CffiPrimeAfterCffi/native/Build.xml new file mode 100644 index 00000000..9ba51d74 --- /dev/null +++ b/test/regression/CffiPrimeAfterCffi/native/Build.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/test/regression/CffiPrimeAfterCffi/stdout.txt b/test/regression/CffiPrimeAfterCffi/stdout.txt new file mode 100644 index 00000000..38194bb4 --- /dev/null +++ b/test/regression/CffiPrimeAfterCffi/stdout.txt @@ -0,0 +1,7 @@ +direct Prime preserves the string: true +CFFI fallback preserves the same string: true +direct Prime preserves the string: true +CFFI fallback preserves the same string: true +direct Prime preserves the string: true +CFFI fallback preserves the same string: true +CFFI fallback preserves a native Unicode result: true diff --git a/test/regression/emscripten-skip.txt b/test/regression/emscripten-skip.txt index a631e8a5..65524a54 100644 --- a/test/regression/emscripten-skip.txt +++ b/test/regression/emscripten-skip.txt @@ -9,6 +9,7 @@ ArrayConcurrentString: threads AttachedThreadId: threads (std::thread, which needs a pthreads build) CffiNekoNullBoolWithoutInit: runs neko, which loads an ndll the test builds CffiNekoValueTags: runs neko, which loads an ndll the test builds +CffiPrimeAfterCffi: loads an ndll it builds, and Emscripten links statically CffiPrimeUnicodeFallback: loads an ndll it builds, and Emscripten links statically CffiQuietMissingPrimitive: loads an ndll it builds, and Emscripten links statically CppiaArrayAccess: its host is the first --next section, which -D emscripten does not reach