From c5ce5b39f940e10a2d821bb4ab0f87e52f91978d Mon Sep 17 00:00:00 2001 From: Aurel Date: Sat, 29 Jul 2023 16:38:25 +0200 Subject: [PATCH] `ammer-core` rewrite (#53) * delete v0 * new version --- .github/workflows/build-linux.yml | 188 ---- .github/workflows/build-osx.yml | 139 --- .gitignore | 3 +- README.md | 7 - haxelib.json | 7 +- samples/poc/Adder.hx | 10 - samples/poc/Main.hx | 8 - samples/poc/README.md | 175 ---- samples/poc/build-common.hxml | 4 - samples/poc/build-cpp.hxml | 5 - samples/poc/build-eval.hxml | 4 - samples/poc/build-hl-win.cmd | 2 - samples/poc/build-hl.hxml | 2 - samples/poc/build-lua.hxml | 3 - samples/poc/build-native.cmd | 2 - samples/poc/dummy.txt | 2 - samples/poc/native/Makefile.linux | 9 - samples/poc/native/Makefile.osx | 12 - samples/poc/native/Makefile.win | 9 - samples/poc/native/adder.c | 50 - samples/poc/native/adder.h | 20 - samples/poc/native/tmp.adder.h | 20 - samples/poc/run-hl-win.cmd | 4 - src/ammer/Ammer.hx | 931 ------------------ src/ammer/AmmerContext.hx | 41 - src/ammer/AmmerMethodPatchContext.hx | 11 - src/ammer/AmmerTypeContext.hx | 22 - src/ammer/Config.hx | 201 ---- src/ammer/Debug.hx | 28 - src/ammer/FFIArrayType.hx | 10 - src/ammer/FFIClosureSignature.hx | 8 - src/ammer/FFIConstant.hx | 14 - src/ammer/FFIMethod.hx | 18 - src/ammer/FFITools.hx | 575 ----------- src/ammer/FFIType.hx | 71 -- src/ammer/FFIVariable.hx | 13 - src/ammer/IntEnum.hx | 9 - src/ammer/IntEnum.macro.hx | 21 - src/ammer/Lib.hx | 23 + src/ammer/Lib.macro.baked.hx | 22 + src/ammer/Lib.macro.hx | 251 +++++ src/ammer/Library.hx | 9 - src/ammer/Library.macro.hx | 20 - src/ammer/LineBuf.hx | 35 - src/ammer/Pointer.hx | 9 - src/ammer/Pointer.macro.hx | 21 - src/ammer/PointerNoStar.hx | 7 - src/ammer/Sublibrary.hx | 4 - src/ammer/SubtypeKind.hx | 7 - src/ammer/Syntax.hx | 4 + src/ammer/Syntax.macro.hx | 90 ++ src/ammer/Utils.hx | 130 --- src/ammer/build/BuildEval.hx | 7 - src/ammer/build/BuildHl.hx | 41 - src/ammer/build/BuildLua.hx | 42 - src/ammer/build/BuildTools.hx | 247 ----- src/ammer/conv/ArrayTools.hx | 6 - src/ammer/conv/ArrayTools.macro.hx | 68 -- src/ammer/conv/ArrayWrapper.hx | 49 - src/ammer/conv/Bytes.cpp.hx | 14 - src/ammer/conv/Bytes.eval.hx | 14 - src/ammer/conv/Bytes.hl.hx | 14 - src/ammer/conv/Bytes.hx | 14 - src/ammer/conv/Bytes.lua.hx | 27 - src/ammer/conv/CArray.cpp.hx | 27 - src/ammer/conv/CArray.hx | 14 - src/ammer/conv/CDirectArray.hx | 11 - src/ammer/conv/CString.cpp.hx | 11 - src/ammer/conv/CString.eval.hx | 11 - src/ammer/conv/CString.hl.hx | 11 - src/ammer/conv/CString.hx | 11 - src/ammer/conv/CString.lua.hx | 11 - src/ammer/def/Enum.hx | 13 + src/ammer/def/Library.hx | 8 + src/ammer/def/Opaque.hx | 8 + src/ammer/def/Struct.hx | 8 + src/ammer/def/Sublibrary.hx | 8 + src/ammer/ffi/Alloc.hx | 4 + src/ammer/ffi/Array.hx | 8 + src/ammer/ffi/ArrayDynamic.hx | 3 - src/ammer/ffi/ArrayFixed.hx | 3 - src/ammer/ffi/Bool.hx | 7 + src/ammer/ffi/Box.hx | 8 + src/ammer/ffi/Bytes.hx | 8 + src/ammer/ffi/Callback.hx | 25 + src/ammer/ffi/Closure.hx | 3 - src/ammer/ffi/ClosureData.hx | 3 - src/ammer/ffi/ClosureDataUse.hx | 3 - src/ammer/ffi/Deref.hx | 7 + src/ammer/ffi/FilePtr.hx | 77 ++ src/ammer/ffi/Float32.hx | 4 + src/ammer/ffi/Float64.hx | 4 + src/ammer/ffi/Haxe.hx | 8 + src/ammer/ffi/Int16.hx | 4 + src/ammer/ffi/Int32.hx | 4 + src/ammer/ffi/Int64.hx | 4 + src/ammer/ffi/Int8.hx | 4 + src/ammer/ffi/NativeHl.hx | 3 - src/ammer/ffi/Nested.hx | 3 - src/ammer/ffi/NoSize.hx | 3 - src/ammer/ffi/OutPointer.hx | 3 - src/ammer/ffi/SameSizeAs.hx | 3 - src/ammer/ffi/Size.hx | 4 + src/ammer/ffi/SizeOf.hx | 3 - src/ammer/ffi/SizeOfReturn.hx | 3 - src/ammer/ffi/String.hx | 7 + src/ammer/ffi/This.hx | 4 + src/ammer/ffi/UInt16.hx | 4 + src/ammer/ffi/UInt32.hx | 4 + src/ammer/ffi/UInt64.hx | 4 + src/ammer/ffi/UInt8.hx | 4 + src/ammer/ffi/Unsupported.hx | 6 +- src/ammer/ffi/Void.hx | 7 + src/ammer/internal/Ammer.hx | 527 ++++++++++ src/ammer/internal/Bakery.hx | 556 +++++++++++ src/ammer/internal/Config.hx | 85 ++ src/ammer/internal/Entrypoint.hx | 922 +++++++++++++++++ src/ammer/internal/Fields.hx | 561 +++++++++++ src/ammer/internal/FilePtrOutput.hx | 23 + src/ammer/internal/LibContext.hx | 185 ++++ src/ammer/internal/LibTypes.hx | 67 ++ src/ammer/internal/Meta.hx | 276 ++++++ src/ammer/internal/Reporting.hx | 110 +++ src/ammer/internal/Types.hx | 365 +++++++ src/ammer/internal/Utils.hx | 191 ++++ src/ammer/internal/v1/AmmerBaked.hx | 41 + src/ammer/internal/v1/AmmerSetup.baked.hx | 28 + src/ammer/internal/v1/LibInfo.hx | 179 ++++ src/ammer/internal/v1/OsInfo.hx | 76 ++ src/ammer/internal/v1/RelativePathsHelper.hx | 35 + src/ammer/patch/PatchCpp.hx | 141 --- src/ammer/patch/PatchCross.hx | 9 - src/ammer/patch/PatchEval.hx | 13 - src/ammer/patch/PatchHl.hx | 90 -- src/ammer/patch/PatchLua.hx | 75 -- src/ammer/patch/PatchMethod.hx | 175 ---- src/ammer/stub/StubBaseC.hx | 48 - src/ammer/stub/StubCpp.hx | 166 ---- src/ammer/stub/StubEval.hx | 7 - src/ammer/stub/StubHl.hx | 220 ----- src/ammer/stub/StubLua.hx | 216 ---- test/native-gen/NativeGen.hx | 127 +++ test/native-gen/ammer/def/Enum.hx | 13 + test/native-gen/common-footer/native.h | 3 + test/native-gen/common-header/native.c | 7 + test/native-gen/common-header/native.h | 15 + test/native-gen/common-header/templates.cpp | 1 + test/native-gen/common-header/templates.hpp | 10 + test/native-gen/make.hxml | 4 + test/native-gen/test/Test.hx | 16 + .../native => test/native-src}/Makefile.linux | 0 .../native => test/native-src}/Makefile.osx | 0 .../native => test/native-src}/Makefile.win | 0 test/native-src/utf8.c | 26 + {tests/native => test/native-src}/utf8.h | 4 +- {tests => test/src}/Main.hx | 0 test/src/def/Native.hx | 15 + test/src/def/Templates.hx | 7 + test/src/test/Test.hx | 38 + test/src/test/TestArrays.hx | 70 ++ test/src/test/TestBytes.hx | 65 ++ test/src/test/TestCInjection.hx | 34 + test/src/test/TestCallback.hx | 232 +++++ test/src/test/TestConstants.hx | 38 + test/src/test/TestCpp.hx | 71 ++ test/src/test/TestDatatypes.hx | 234 +++++ test/src/test/TestEnums.hx | 46 + test/src/test/TestHaxe.hx | 22 + test/src/test/TestHaxeRef.hx | 39 + test/src/test/TestMaths.hx | 157 +++ test/src/test/TestSignature.hx | 135 +++ test/src/test/TestStrings.hx | 53 + tests/Native.hx | 151 --- tests/Templates.hx | 22 - tests/build-cpp.hxml | 11 - tests/build-eval.hxml | 12 - tests/build-hl.hxml | 10 - tests/build-lua.hxml | 11 - tests/native/native.c | 268 ----- tests/native/native.h | 132 --- tests/native/templates.cpp | 15 - tests/native/templates.hpp | 22 - tests/native/utf8.c | 26 - tests/test-linux.sh | 15 - tests/test-osx.sh | 15 - tests/test-windows.bat | 41 - tests/test/Test.hx | 81 -- tests/test/TestArrays.hx | 20 - tests/test/TestBytes.hx | 30 - tests/test/TestCInjection.hx | 12 - tests/test/TestCallback.hx | 70 -- tests/test/TestCpp.hx | 27 - tests/test/TestEnums.hx | 8 - tests/test/TestMaths.hx | 62 -- tests/test/TestOpaque.hx | 102 -- tests/test/TestSignature.hx | 36 - tests/test/TestStrings.hx | 15 - tests/test/TestVariables.hx | 18 - 198 files changed, 6367 insertions(+), 5975 deletions(-) delete mode 100644 .github/workflows/build-linux.yml delete mode 100644 .github/workflows/build-osx.yml delete mode 100644 README.md delete mode 100644 samples/poc/Adder.hx delete mode 100644 samples/poc/Main.hx delete mode 100644 samples/poc/README.md delete mode 100644 samples/poc/build-common.hxml delete mode 100644 samples/poc/build-cpp.hxml delete mode 100644 samples/poc/build-eval.hxml delete mode 100644 samples/poc/build-hl-win.cmd delete mode 100644 samples/poc/build-hl.hxml delete mode 100644 samples/poc/build-lua.hxml delete mode 100644 samples/poc/build-native.cmd delete mode 100644 samples/poc/dummy.txt delete mode 100644 samples/poc/native/Makefile.linux delete mode 100644 samples/poc/native/Makefile.osx delete mode 100644 samples/poc/native/Makefile.win delete mode 100644 samples/poc/native/adder.c delete mode 100644 samples/poc/native/adder.h delete mode 100644 samples/poc/native/tmp.adder.h delete mode 100644 samples/poc/run-hl-win.cmd delete mode 100644 src/ammer/Ammer.hx delete mode 100644 src/ammer/AmmerContext.hx delete mode 100644 src/ammer/AmmerMethodPatchContext.hx delete mode 100644 src/ammer/AmmerTypeContext.hx delete mode 100644 src/ammer/Config.hx delete mode 100644 src/ammer/Debug.hx delete mode 100644 src/ammer/FFIArrayType.hx delete mode 100644 src/ammer/FFIClosureSignature.hx delete mode 100644 src/ammer/FFIConstant.hx delete mode 100644 src/ammer/FFIMethod.hx delete mode 100644 src/ammer/FFITools.hx delete mode 100644 src/ammer/FFIType.hx delete mode 100644 src/ammer/FFIVariable.hx delete mode 100644 src/ammer/IntEnum.hx delete mode 100644 src/ammer/IntEnum.macro.hx create mode 100644 src/ammer/Lib.hx create mode 100644 src/ammer/Lib.macro.baked.hx create mode 100644 src/ammer/Lib.macro.hx delete mode 100644 src/ammer/Library.hx delete mode 100644 src/ammer/Library.macro.hx delete mode 100644 src/ammer/LineBuf.hx delete mode 100644 src/ammer/Pointer.hx delete mode 100644 src/ammer/Pointer.macro.hx delete mode 100644 src/ammer/PointerNoStar.hx delete mode 100644 src/ammer/Sublibrary.hx delete mode 100644 src/ammer/SubtypeKind.hx create mode 100644 src/ammer/Syntax.hx create mode 100644 src/ammer/Syntax.macro.hx delete mode 100644 src/ammer/Utils.hx delete mode 100644 src/ammer/build/BuildEval.hx delete mode 100644 src/ammer/build/BuildHl.hx delete mode 100644 src/ammer/build/BuildLua.hx delete mode 100644 src/ammer/build/BuildTools.hx delete mode 100644 src/ammer/conv/ArrayTools.hx delete mode 100644 src/ammer/conv/ArrayTools.macro.hx delete mode 100644 src/ammer/conv/ArrayWrapper.hx delete mode 100644 src/ammer/conv/Bytes.cpp.hx delete mode 100644 src/ammer/conv/Bytes.eval.hx delete mode 100644 src/ammer/conv/Bytes.hl.hx delete mode 100644 src/ammer/conv/Bytes.hx delete mode 100644 src/ammer/conv/Bytes.lua.hx delete mode 100644 src/ammer/conv/CArray.cpp.hx delete mode 100644 src/ammer/conv/CArray.hx delete mode 100644 src/ammer/conv/CDirectArray.hx delete mode 100644 src/ammer/conv/CString.cpp.hx delete mode 100644 src/ammer/conv/CString.eval.hx delete mode 100644 src/ammer/conv/CString.hl.hx delete mode 100644 src/ammer/conv/CString.hx delete mode 100644 src/ammer/conv/CString.lua.hx create mode 100644 src/ammer/def/Enum.hx create mode 100644 src/ammer/def/Library.hx create mode 100644 src/ammer/def/Opaque.hx create mode 100644 src/ammer/def/Struct.hx create mode 100644 src/ammer/def/Sublibrary.hx create mode 100644 src/ammer/ffi/Array.hx delete mode 100644 src/ammer/ffi/ArrayDynamic.hx delete mode 100644 src/ammer/ffi/ArrayFixed.hx create mode 100644 src/ammer/ffi/Bool.hx create mode 100644 src/ammer/ffi/Box.hx create mode 100644 src/ammer/ffi/Bytes.hx create mode 100644 src/ammer/ffi/Callback.hx delete mode 100644 src/ammer/ffi/Closure.hx delete mode 100644 src/ammer/ffi/ClosureData.hx delete mode 100644 src/ammer/ffi/ClosureDataUse.hx create mode 100644 src/ammer/ffi/Deref.hx create mode 100644 src/ammer/ffi/FilePtr.hx create mode 100644 src/ammer/ffi/Haxe.hx delete mode 100644 src/ammer/ffi/NativeHl.hx delete mode 100644 src/ammer/ffi/Nested.hx delete mode 100644 src/ammer/ffi/NoSize.hx delete mode 100644 src/ammer/ffi/OutPointer.hx delete mode 100644 src/ammer/ffi/SameSizeAs.hx create mode 100644 src/ammer/ffi/Size.hx delete mode 100644 src/ammer/ffi/SizeOf.hx delete mode 100644 src/ammer/ffi/SizeOfReturn.hx create mode 100644 src/ammer/ffi/String.hx create mode 100644 src/ammer/ffi/Void.hx create mode 100644 src/ammer/internal/Ammer.hx create mode 100644 src/ammer/internal/Bakery.hx create mode 100644 src/ammer/internal/Config.hx create mode 100644 src/ammer/internal/Entrypoint.hx create mode 100644 src/ammer/internal/Fields.hx create mode 100644 src/ammer/internal/FilePtrOutput.hx create mode 100644 src/ammer/internal/LibContext.hx create mode 100644 src/ammer/internal/LibTypes.hx create mode 100644 src/ammer/internal/Meta.hx create mode 100644 src/ammer/internal/Reporting.hx create mode 100644 src/ammer/internal/Types.hx create mode 100644 src/ammer/internal/Utils.hx create mode 100644 src/ammer/internal/v1/AmmerBaked.hx create mode 100644 src/ammer/internal/v1/AmmerSetup.baked.hx create mode 100644 src/ammer/internal/v1/LibInfo.hx create mode 100644 src/ammer/internal/v1/OsInfo.hx create mode 100644 src/ammer/internal/v1/RelativePathsHelper.hx delete mode 100644 src/ammer/patch/PatchCpp.hx delete mode 100644 src/ammer/patch/PatchCross.hx delete mode 100644 src/ammer/patch/PatchEval.hx delete mode 100644 src/ammer/patch/PatchHl.hx delete mode 100644 src/ammer/patch/PatchLua.hx delete mode 100644 src/ammer/patch/PatchMethod.hx delete mode 100644 src/ammer/stub/StubBaseC.hx delete mode 100644 src/ammer/stub/StubCpp.hx delete mode 100644 src/ammer/stub/StubEval.hx delete mode 100644 src/ammer/stub/StubHl.hx delete mode 100644 src/ammer/stub/StubLua.hx create mode 100644 test/native-gen/NativeGen.hx create mode 100644 test/native-gen/ammer/def/Enum.hx create mode 100644 test/native-gen/common-footer/native.h create mode 100644 test/native-gen/common-header/native.c create mode 100644 test/native-gen/common-header/native.h create mode 100644 test/native-gen/common-header/templates.cpp create mode 100644 test/native-gen/common-header/templates.hpp create mode 100644 test/native-gen/make.hxml create mode 100644 test/native-gen/test/Test.hx rename {tests/native => test/native-src}/Makefile.linux (100%) rename {tests/native => test/native-src}/Makefile.osx (100%) rename {tests/native => test/native-src}/Makefile.win (100%) create mode 100644 test/native-src/utf8.c rename {tests/native => test/native-src}/utf8.h (73%) rename {tests => test/src}/Main.hx (100%) create mode 100644 test/src/def/Native.hx create mode 100644 test/src/def/Templates.hx create mode 100644 test/src/test/Test.hx create mode 100644 test/src/test/TestArrays.hx create mode 100644 test/src/test/TestBytes.hx create mode 100644 test/src/test/TestCInjection.hx create mode 100644 test/src/test/TestCallback.hx create mode 100644 test/src/test/TestConstants.hx create mode 100644 test/src/test/TestCpp.hx create mode 100644 test/src/test/TestDatatypes.hx create mode 100644 test/src/test/TestEnums.hx create mode 100644 test/src/test/TestHaxe.hx create mode 100644 test/src/test/TestHaxeRef.hx create mode 100644 test/src/test/TestMaths.hx create mode 100644 test/src/test/TestSignature.hx create mode 100644 test/src/test/TestStrings.hx delete mode 100644 tests/Native.hx delete mode 100644 tests/Templates.hx delete mode 100644 tests/build-cpp.hxml delete mode 100644 tests/build-eval.hxml delete mode 100644 tests/build-hl.hxml delete mode 100644 tests/build-lua.hxml delete mode 100644 tests/native/native.c delete mode 100644 tests/native/native.h delete mode 100644 tests/native/templates.cpp delete mode 100644 tests/native/templates.hpp delete mode 100644 tests/native/utf8.c delete mode 100755 tests/test-linux.sh delete mode 100755 tests/test-osx.sh delete mode 100755 tests/test-windows.bat delete mode 100644 tests/test/Test.hx delete mode 100644 tests/test/TestArrays.hx delete mode 100644 tests/test/TestBytes.hx delete mode 100644 tests/test/TestCInjection.hx delete mode 100644 tests/test/TestCallback.hx delete mode 100644 tests/test/TestCpp.hx delete mode 100644 tests/test/TestEnums.hx delete mode 100644 tests/test/TestMaths.hx delete mode 100644 tests/test/TestOpaque.hx delete mode 100644 tests/test/TestSignature.hx delete mode 100644 tests/test/TestStrings.hx delete mode 100644 tests/test/TestVariables.hx diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml deleted file mode 100644 index 3ec979f..0000000 --- a/.github/workflows/build-linux.yml +++ /dev/null @@ -1,188 +0,0 @@ -name: Build Linux - -on: [push, pull_request] - -jobs: - test-hashlink: - runs-on: ubuntu-latest - steps: - - name : Adjust path - run : | - if [[ -z "$GITHUB_PATH" ]]; then - echo "${HOME}/.local/bin" >> $GITHUB_PATH - fi - - - name: Update apt and install necessary packages - run : | - if ! command -v sudo &> /dev/null; then - apt install sudo - fi - sudo apt update - sudo apt-get install -y software-properties-common - - - name: Install Haxe (4.2.3) - uses: krdlab/setup-haxe@v1 - with: - haxe-version: 4.2.3 - - - name: Checkout ammer - uses: actions/checkout@v2 - with: - path: "ammer" - - - name: Set up haxelibs - run: | - haxelib dev ammer ammer - haxelib install utest - - - name: Build native library - run: | - cd ammer/tests/native - make -f Makefile.linux - - - name: Checkout HashLink - uses: actions/checkout@v2 - with: - repository: "HaxeFoundation/hashlink" - path: "hashlink" - - - name: Build and install HashLink - run: | - cd hashlink - sudo apt-get install libpng-dev - sudo apt-get install libturbojpeg-dev - sudo apt-get install libvorbis-dev - sudo apt-get install libopenal-dev - sudo apt-get install libsdl2-dev - sudo apt-get install libmbedtls-dev - sudo apt-get install libuv1-dev - make - sudo make install - - - name: Compile tests - run: | - cd ammer/tests - haxe build-hl.hxml - cd bin/hl - cp $GITHUB_WORKSPACE/hashlink/libhl.so . - - - name: Run tests - run: | - cd ammer/tests/bin/hl - LD_LIBRARY_PATH=../../native:. hl test.hl - - test-hxcpp: - runs-on: ubuntu-latest - steps: - - name : Adjust path - run : | - if [[ ! -z "$GITHUB_PATH" ]]; then - echo "${HOME}/.local/bin" >> $GITHUB_PATH - fi - - - name: Update apt and install necessary packages - run : | - if ! command -v sudo &> /dev/null; then - apt install sudo - fi - sudo apt update - sudo apt-get install -y software-properties-common - - - name: Install Haxe (4.2.3) - uses: krdlab/setup-haxe@v1 - with: - haxe-version: 4.2.3 - - - name: Checkout ammer - uses: actions/checkout@v2 - with: - path: "ammer" - - - name: Set up haxelibs - run: | - haxelib dev ammer ammer - haxelib install utest - - - name: Build native library - run: | - cd ammer/tests/native - make -f Makefile.linux - - - name: Install hxcpp - run: | - haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git - cd `haxelib path hxcpp | head -n 1` - cd tools/hxcpp - haxe compile.hxml - - - name: Compile tests - run: | - cd ammer/tests - haxe build-cpp.hxml - - - name: Run tests - run: | - cd ammer/tests/bin/cpp - LD_LIBRARY_PATH=../../native:. ./Main - - test-lua: - runs-on: ubuntu-latest - steps: - - name : Adjust path - run : | - if [[ ! -z "$GITHUB_PATH" ]]; then - echo "${HOME}/.local/bin" >> $GITHUB_PATH - fi - - - name: Install sudo package - run : | - if ! command -v sudo &> /dev/null; then - apt install sudo - fi - sudo apt update - sudo apt-get install -y software-properties-common - - - name: Install Haxe (4.2.3) - uses: krdlab/setup-haxe@v1 - with: - haxe-version: 4.2.3 - - - name: Install Lua and dependencies - run: | - pip3 install hererocks - hererocks ~/lua5.3 -l5.3 -rlatest --cflags="-fPIC" - source ~/lua5.3/bin/activate - ln -s ~/lua5.3/lib/liblua53.a ~/lua5.3/lib/liblua.a - luarocks install lrexlib-pcre 2.9.1-1 - luarocks install luv 1.41.1-0 - luarocks install luasocket 3.0rc1-2 - luarocks install luautf8 0.1.3-1 - luarocks install bit32 5.3.5.1-1 - luarocks install hx-lua-simdjson 0.0.1-1 - - - name: Checkout ammer - uses: actions/checkout@v2 - with: - path: "ammer" - - - name: Set up haxelibs - run: | - haxelib dev ammer ammer - haxelib install utest - - - name: Build native library - run: | - cd ammer/tests/native - make -f Makefile.linux - - - name: Compile tests - run: | - source ~/lua5.3/bin/activate - cd ammer/tests - haxe -D ammer.lua.luaInclude=`luarocks config --lua-incdir` -D ammer.lua.luaLibrary=`luarocks config --lua-libdir` build-lua.hxml - - - name: Run tests - run: | - source ~/lua5.3/bin/activate - cd ammer/tests/bin/lua - LD_LIBRARY_PATH=../../native:. lua test.lua diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml deleted file mode 100644 index 4a33dd4..0000000 --- a/.github/workflows/build-osx.yml +++ /dev/null @@ -1,139 +0,0 @@ -name: Build OS X - -on: [push, pull_request] - -jobs: - test-hashlink: - runs-on: macos-latest - steps: - - name: Install Haxe (4.2.3) - uses: krdlab/setup-haxe@v1 - with: - haxe-version: 4.2.3 - - - name: Checkout ammer - uses: actions/checkout@v2 - with: - path: "ammer" - - - name: Set up haxelibs - run: | - haxelib dev ammer ammer - haxelib install utest - - - name: Build native library - run: | - cd ammer/tests/native - make -f Makefile.osx - - - name: Checkout HashLink - uses: actions/checkout@v2 - with: - repository: "HaxeFoundation/hashlink" - path: "hashlink" - - - name: Build and install HashLink - run: | - cd hashlink - - # https://github.com/HaxeFoundation/hashlink/pull/468 - sed -i '' 's/brew "mbedtls"/brew "mbedtls@2"/g' Brewfile - brew bundle - brew link mbedtls@2 - - make - sudo make install - - - name: Compile tests - run: | - cd ammer/tests - haxe build-hl.hxml - cd bin/hl - - - name: Run tests - run: | - cd ammer/tests/bin/hl - DYLD_LIBRARY_PATH=../../native hl test.hl - - test-hxcpp: - runs-on: macos-latest - steps: - - name: Install Haxe (4.2.3) - uses: krdlab/setup-haxe@v1 - with: - haxe-version: 4.2.3 - - - name: Checkout ammer - uses: actions/checkout@v2 - with: - path: "ammer" - - - name: Set up haxelibs - run: | - haxelib dev ammer ammer - haxelib install utest - - - name: Build native library - run: | - cd ammer/tests/native - make -f Makefile.osx - - - name: Install hxcpp - run: | - haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git - cd `haxelib path hxcpp | head -n 1` - cd tools/hxcpp - haxe compile.hxml - - - name: Compile tests - run: | - cd ammer/tests - haxe build-cpp.hxml - - - name: Run tests - run: | - cd ammer/tests/bin/cpp - DYLD_LIBRARY_PATH=../../native ./Main - - test-lua: - runs-on: macos-latest - steps: - - name: Install Haxe (4.2.3) - uses: krdlab/setup-haxe@v1 - with: - haxe-version: 4.2.3 - - - name: Install Lua and dependencies - run: | - brew install lua luarocks - luarocks install lrexlib-pcre 2.9.1-1 - luarocks install luv 1.41.1-0 - luarocks install luasocket 3.0rc1-2 - luarocks install luautf8 0.1.3-1 - luarocks install bit32 5.3.5.1-1 - luarocks install hx-lua-simdjson 0.0.1-1 - - - name: Checkout ammer - uses: actions/checkout@v2 - with: - path: "ammer" - - - name: Set up haxelibs - run: | - haxelib dev ammer ammer - haxelib install utest - - - name: Build native library - run: | - cd ammer/tests/native - make -f Makefile.osx - - - name: Compile tests - run: | - cd ammer/tests - haxe -D ammer.lua.luaInclude=`luarocks config --lua-incdir` -D ammer.lua.luaLibrary=`luarocks config --lua-libdir` build-lua.hxml - - - name: Run tests - run: | - cd ammer/tests/bin/lua - DYLD_LIBRARY_PATH=../../native lua test.lua diff --git a/.gitignore b/.gitignore index d58a2cc..eb89364 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,4 @@ *.o *.so -samples/poc/bin -tests/bin +/test/bin/** diff --git a/README.md b/README.md deleted file mode 100644 index 7183b01..0000000 --- a/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# `ammer` - -![Build Linux](https://github.com/Aurel300/ammer/workflows/Build%20Linux/badge.svg) ![Build OS X](https://github.com/Aurel300/ammer/workflows/Build%20OS%20X/badge.svg) - -Unified FFI for native extensions for [Haxe](https://haxe.org/). - -See the [`ammer` manual](https://aurel300.github.io/ammer/) for documentation. diff --git a/haxelib.json b/haxelib.json index 393e5b3..4c2eadc 100644 --- a/haxelib.json +++ b/haxelib.json @@ -5,9 +5,12 @@ "tags": [ ], "description": "", - "version": "0.0.1", + "version": "0.1.0", "classPath": "src/", "contributors": [ "Aurel300" - ] + ], + "dependencies": { + "ammer-core": "" + } } \ No newline at end of file diff --git a/samples/poc/Adder.hx b/samples/poc/Adder.hx deleted file mode 100644 index e63802b..0000000 --- a/samples/poc/Adder.hx +++ /dev/null @@ -1,10 +0,0 @@ -import ammer.Library; -import ammer.ffi.*; -import haxe.io.Bytes; - -class Adder extends Library<"adder"> { - public static function add_numbers(a:Int, b:Int):Int; - public static function load_file(filename:String, loaded:SizeOfReturn):Bytes; - public static function concat_strings(a:String, b:String):String; - public static function reverse_bytes(data:Bytes, dataLen:SizeOf<"data">):SameSizeAs; -} diff --git a/samples/poc/Main.hx b/samples/poc/Main.hx deleted file mode 100644 index 98fe17d..0000000 --- a/samples/poc/Main.hx +++ /dev/null @@ -1,8 +0,0 @@ -class Main { - public static function main():Void { - trace('3 + 9 = ${Adder.add_numbers(3, 9)}'); - trace('foo + bar = ${Adder.concat_strings("foo", "bar")}'); - trace('read("dummy.txt") = ${Adder.load_file("dummy.txt")}'); - trace('reverse_bytes("hello") = ${Adder.reverse_bytes(haxe.io.Bytes.ofString("hello"))}'); - } -} diff --git a/samples/poc/README.md b/samples/poc/README.md deleted file mode 100644 index 8c66520..0000000 --- a/samples/poc/README.md +++ /dev/null @@ -1,175 +0,0 @@ -# Sample `ammer` project - -This is a sample project for the `ammer` library, based on the requirements of [the bounty by Lars Doucet](https://github.com/larsiusprime/larsBounties/issues/2). It contains the sources to a simple library called `adder`, its `ammer` library definition, and a trivial Haxe program using the library. - ---- - - - [Directory structure](#directory-structure) - - [Building the native library](#building-the-native-library) - - [On Windows](#on-windows) - - [On OS X](#on-os-x) - - [On Linux](#on-linux) - - [Building the Haxe project](#building-the-haxe-project) - - [Running](#running) - - [On Windows](#on-windows-1) - - [On OS X](#on-os-x-1) - - [On Linux](#on-linux-1) - - [Troubleshooting](#troubleshooting) - -## Directory structure - - - [`Adder.hx`](Adder.hx) - `ammer` library definition for the `adder` library. This class essentially maps Haxe types to the C library. - - [`Main.hx`](Main.hx) - main program using the library in regular Haxe code. - - [`build-common.hxml`](build-common.hxml) - build configuration common to all targets. - - [`build-cpp.hxml`](build-cpp.hxml) - build configuration for hxcpp. - - [`build-eval.hxml`](build-eval.hxml) - build configuration for Eval. - - [`build-hl.hxml`](build-hl.hxml) - build configuration for HashLink. - - [`dummy.txt`](dummy.txt) - file containing some UTF-8 text, loaded by `Main` to demonstrate one function of the `adder` library. - - [`native`](native) - contains sources to the `adder` library. - - [`adder.c`](native/adder.c) - - [`adder.h`](native/adder.h) - - [`Makefile.win`](native/Makefile.win) - build script for building the library on Windows (using MSVC). - - [`Makefile.osx`](native/Makefile.osx) - build script for building the library on OS X. - - [`Makefile.linux`](native/Makefile.linux) - build script for building the library on Linux. - -## Building the native library - -Many popular libraries provide pre-compiled releases, but in this example, the native library needs to be compiled to a binary format before it can be used. In practice, `ammer` only needs the compiled binary of a library (`.dll`, `.dylib`, `.so`) and its header files (`.h` files) to be able to use it. - -### On Windows - -Assuming [MSVC](https://visualstudio.microsoft.com/downloads/) is set up on the local machine, navigate to the `native` directory in a Visual Studio Developer Command Prompt (or a regular command prompt initialised by running `vcvars32`), then use the provided `Makefile.win`: - -```bash -$ cd /native -$ nmake /F Makefile.win -``` - -This should create (among others) the files `adder.dll` and `adder.lib` in the `native` directory. - -### On OS X - -Assuming any reasonably modern C compiler (`gcc` or `clang`) is set up on the local machine, navigate to the `native` directory in a terminal, then use the provided `Makefile.osx`: - -```bash -$ cd /native -$ make -f Makefile.osx -``` - -This should create (among others) the file `libadder.dylib` in the `native` directory. - -### On Linux - -Assuming any reasonably modern C compiler (`gcc` or `clang`) is set up on the local machine, navigate to the `native` directory in a terminal, then use the provided `Makefile.linux`: - -```bash -$ cd /native -$ make -f Makefile.linux -``` - -This should create (among others) the file `libadder.so` in the `native` directory. - -## Building the Haxe project - -Once the native library is built, the Haxe project itself can be compiled. The necessary configuration is already provided in the HXML files. Some targets require additional configuration, which needs to be provided on the command line via a define. See the [target-specific configuration](https://github.com/Aurel300/ammer#target-specifics) section in the main README for more information about these defines. - -```bash -$ haxe -D ammer.hl.hlInclude= -D ammer.hl.hlLibrary= build-hl.hxml -$ haxe build-cpp.hxml -$ haxe -D ammer.eval.haxeDir= build-eval.hxml -``` - -## Running - -`ammer` works with dynamic libraries (except on hxcpp on Windows), which must either be distributed alongside with the program or already be present on the system. Since the sample `adder` library is specific to this project, it needs to be placed next to the binary. - -One of the test functions in the Haxe project tries to load the file `dummy.txt`. It should be copied into each directory in `bin`. - -### On Windows - -```bash -$ cp native/adder.dll bin/hl -$ cd bin/hl -$ hl sample.hl -``` - -```bash -$ cd bin/cpp -$ ./Main -``` - -### On OS X - -When running the project, the current working directory will not be searched for dynamic libraries by default, so the `DYLD_LIBRARY_PATH` variable must be used. - -```bash -$ cd bin/hl -$ DYLD_LIBRARY_PATH=../../native hl sample.hl -``` - -```bash -cd bin/cpp -$ DYLD_LIBRARY_PATH=../../native ./Main -```` - -### On Linux - -When running the project, the current working directory will not be searched for dynamic libraries by default, so the `LD_LIBRARY_PATH` variable must be used. - -```bash -$ cd bin/hl -$ LD_LIBRARY_PATH=../../native hl sample.hl -``` - -```bash -cd bin/cpp -$ LD_LIBRARY_PATH=../../native ./Main -```` - -## Troubleshooting - -This section lists some problems you may come across while following the steps described above. - -#### "hl.h": no such file or directory - -This error can occur during the build phase when targeting HashLink. It indicates that the native compiler cannot find the `hl.h` header file. Use the [`ammer.hl.hlInclude`](https://github.com/Aurel300/ammer#ammerhlhlinclude-ammerhlhllibrary-optional) option to point the compiler to a directory containing the `hl.h` file. - - - When using HashLink binary distributions, the directory is `include` in the downloaded folder. - - When using HashLink built from source, the directory is `src` in the repository root. - -#### "stddef.h": no such file or directory - -This error can occur during the build phase when using MSVC on Windows. It indicates that the native compiler cannot find header files of the C standard library. To solve this: - - - Either use the "Visual Studio Developer Command Prompt"; or - - Use a regular command-line prompt, but use the [`vcvars32` script](https://stackoverflow.com/questions/42805662/vsvars32-bat-in-visual-studio-2017) to initialise the environment. - -#### Failed to load library ammer_adder.hdll - -This error can occur when testing HashLink for two reasons (possibly both at the same time): - -##### 1. `ammer_adder.hdll` is not in the CWD - -The `hl` command will look for `hdll` files in the current working directory as well as the system library directories. Therefore, if `ammer_adder.hdll` is in the `bin/hl` directory (which is the case if the default configuration of this project is used), invoking `hl` from the `samples/poc` directory will NOT work. - - - Either `cd bin/hl` before invoking `hl`; or - - Copy `ammer_adder.hdll` from `bin/hl` into the current working directory. - -##### 2. The dynamic library cannot be found - -Refer to the next section. - -#### Image not found / Library not loaded - -This error can occur when the system cannot find the compiled native library (`.dll`, `.dylib`, or `.so` file). - -On Windows, the system will look for `dll` files in the current working directory. - - - Copy `adder.dll` from the `native` directory into the current working directory. - -On OS X and Linux, the dynamic linker needs to be told where to look for additional dynamic libraries with an environment variable. - - - On OS X, prepend `DYLD_LIBRARY_PATH=` to the command, e.g. `DYLD_LIBRARY_PATH=../../native hl sample.hl`. - - On Linux, prepend `LD_LIBRARY_PATH=` to the command, e.g. `LD_LIBRARY_PATH=../../native hl sample.hl`. - -See also [general notes about dynamic libraries](https://github.com/Aurel300/ammer#general-notes-about-dynamic-libraries) in the main README. diff --git a/samples/poc/build-common.hxml b/samples/poc/build-common.hxml deleted file mode 100644 index 63cdf4d..0000000 --- a/samples/poc/build-common.hxml +++ /dev/null @@ -1,4 +0,0 @@ ---library ammer ---main Main --D ammer.lib.adder.include=native --D ammer.lib.adder.library=native diff --git a/samples/poc/build-cpp.hxml b/samples/poc/build-cpp.hxml deleted file mode 100644 index 6f95b38..0000000 --- a/samples/poc/build-cpp.hxml +++ /dev/null @@ -1,5 +0,0 @@ -build-common.hxml ---library hxcpp -# otherwise Adder.h (generated by hxcpp) is included --D ammer.lib.adder.headers=tmp.adder.h ---cpp bin/cpp diff --git a/samples/poc/build-eval.hxml b/samples/poc/build-eval.hxml deleted file mode 100644 index 642d726..0000000 --- a/samples/poc/build-eval.hxml +++ /dev/null @@ -1,4 +0,0 @@ -build-common.hxml --D ammer.eval.build=bin/eval --D ammer.eval.output=. ---interp diff --git a/samples/poc/build-hl-win.cmd b/samples/poc/build-hl-win.cmd deleted file mode 100644 index 05662dc..0000000 --- a/samples/poc/build-hl-win.cmd +++ /dev/null @@ -1,2 +0,0 @@ -haxe -D ammer.hl.hlInclude=%HLPATH%/include -D ammer.hl.hlLibrary=%HLPATH% build-hl.hxml -copy native\adder.dll bin\hl\ \ No newline at end of file diff --git a/samples/poc/build-hl.hxml b/samples/poc/build-hl.hxml deleted file mode 100644 index c5fba6c..0000000 --- a/samples/poc/build-hl.hxml +++ /dev/null @@ -1,2 +0,0 @@ -build-common.hxml ---hl bin/hl/sample.hl diff --git a/samples/poc/build-lua.hxml b/samples/poc/build-lua.hxml deleted file mode 100644 index 7e3d3d0..0000000 --- a/samples/poc/build-lua.hxml +++ /dev/null @@ -1,3 +0,0 @@ -build-common.hxml --D ammer.lua.luaInclude=/usr/local/include/lua ---lua bin/lua/sample.lua diff --git a/samples/poc/build-native.cmd b/samples/poc/build-native.cmd deleted file mode 100644 index 2296c48..0000000 --- a/samples/poc/build-native.cmd +++ /dev/null @@ -1,2 +0,0 @@ -cd native -nmake Makefile.win \ No newline at end of file diff --git a/samples/poc/dummy.txt b/samples/poc/dummy.txt deleted file mode 100644 index 1088345..0000000 --- a/samples/poc/dummy.txt +++ /dev/null @@ -1,2 +0,0 @@ -hello world! -and a Unicode cow: 🐄 diff --git a/samples/poc/native/Makefile.linux b/samples/poc/native/Makefile.linux deleted file mode 100644 index 370593d..0000000 --- a/samples/poc/native/Makefile.linux +++ /dev/null @@ -1,9 +0,0 @@ -all: libadder.so adder.o - -libadder.so: adder.o - gcc -shared -fPIC -o libadder.so adder.o -lc - -adder.o: adder.c - gcc -c -fPIC -o adder.o adder.c - -.PHONY: all diff --git a/samples/poc/native/Makefile.osx b/samples/poc/native/Makefile.osx deleted file mode 100644 index 92b9cd0..0000000 --- a/samples/poc/native/Makefile.osx +++ /dev/null @@ -1,12 +0,0 @@ -all: libadder.dylib adder.o - -libadder.dylib: adder.c - gcc -dynamiclib -o libadder.dylib adder.c - -adder.so: adder.o - gcc -shared -fPIC -o adder.so adder.o -lc - -adder.o: adder.c - gcc -c -fPIC -o adder.o adder.c - -.PHONY: all diff --git a/samples/poc/native/Makefile.win b/samples/poc/native/Makefile.win deleted file mode 100644 index 9fb4752..0000000 --- a/samples/poc/native/Makefile.win +++ /dev/null @@ -1,9 +0,0 @@ -all: adder.dll - -adder.dll: adder.obj - cl /LD adder.obj - -adder.obj: adder.c - cl /c adder.c - -.PHONY: all diff --git a/samples/poc/native/adder.c b/samples/poc/native/adder.c deleted file mode 100644 index ad79969..0000000 --- a/samples/poc/native/adder.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -#include "adder.h" - -LIB_EXPORT int add_numbers(int a, int b) { - return a + b; -} - -LIB_EXPORT unsigned char *load_file(const char *filename, size_t *loaded) { - FILE *f = fopen(filename, "rb"); - if (f == NULL) { - puts("cannot open file"); - return NULL; - } - fseek(f, 0, SEEK_END); - size_t s = ftell(f); - rewind(f); - void *buf = malloc(s); - if (buf == NULL) { - puts("cannot malloc"); - return NULL; - } - if (fread(buf, 1, s, f) != s) { - puts("cannot read file"); - return NULL; - } - fclose(f); - *loaded = s; - return buf; -} - -LIB_EXPORT char *concat_strings(const char *a, const char *b) { - int lenA = strlen(a); - int lenB = strlen(b); - char *ret = malloc(lenA + lenB + 1); - memcpy(ret, a, lenA); - memcpy(ret + lenB, b, lenB); - ret[lenA + lenB] = 0; - return ret; -} - -LIB_EXPORT unsigned char *reverse_bytes(unsigned char *data, int len) { - unsigned char *rev = malloc(len); - for (int i = 0; i < len; i++) { - rev[i] = data[len - i - 1]; - } - return rev; -} diff --git a/samples/poc/native/adder.h b/samples/poc/native/adder.h deleted file mode 100644 index 7954cd9..0000000 --- a/samples/poc/native/adder.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef _WIN32 - #define LIB_EXPORT __declspec(dllexport) -#else - #define LIB_EXPORT -#endif - -#include - -LIB_EXPORT int add_numbers(int a, int b); -LIB_EXPORT unsigned char *load_file(const char *filename, size_t *loaded); -LIB_EXPORT char *concat_strings(const char *a, const char *b); -LIB_EXPORT unsigned char *reverse_bytes(unsigned char *data, int len); - -#ifdef __cplusplus -} -#endif diff --git a/samples/poc/native/tmp.adder.h b/samples/poc/native/tmp.adder.h deleted file mode 100644 index 7954cd9..0000000 --- a/samples/poc/native/tmp.adder.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef _WIN32 - #define LIB_EXPORT __declspec(dllexport) -#else - #define LIB_EXPORT -#endif - -#include - -LIB_EXPORT int add_numbers(int a, int b); -LIB_EXPORT unsigned char *load_file(const char *filename, size_t *loaded); -LIB_EXPORT char *concat_strings(const char *a, const char *b); -LIB_EXPORT unsigned char *reverse_bytes(unsigned char *data, int len); - -#ifdef __cplusplus -} -#endif diff --git a/samples/poc/run-hl-win.cmd b/samples/poc/run-hl-win.cmd deleted file mode 100644 index 36cabd8..0000000 --- a/samples/poc/run-hl-win.cmd +++ /dev/null @@ -1,4 +0,0 @@ -copy native/adder.dll bin/hl -copy dummy.txt bin\hl -cd bin/hl -hl sample.hl \ No newline at end of file diff --git a/src/ammer/Ammer.hx b/src/ammer/Ammer.hx deleted file mode 100644 index c6e9a98..0000000 --- a/src/ammer/Ammer.hx +++ /dev/null @@ -1,931 +0,0 @@ -package ammer; - -import haxe.macro.Context; -import haxe.macro.Expr; -import haxe.macro.Type; -import sys.FileSystem; -import ammer.Config.AmmerLibraryConfig; -import ammer.patch.PatchMethod; - -using StringTools; - -/** - Main class for `ammer`. Handles common tasks and dispatches calls to - target-specific stages. -**/ -class Ammer { - public static var config(default, null):Config; - public static var typeMap:Map = []; - public static var typeCache:Map, library:ComplexType, kind:SubtypeKind}> = []; - public static var ctx:AmmerContext; - static var libraries:Array = []; - static var libraryMap:Map = []; - static var libraryContextMap:Map = []; - static var types:Array = []; - static var typeCtr = 0; - static var ctxStack:Array = []; - static var definedTypes:Array; - static var modifiedTypes:Array<{t:ClassType, fields:Array}>; - - /** - Creates `config` object, runs some project-global tasks. - **/ - static function configure():Void { - // run only once - if (config != null) - return; - - // create config from defines - config = new Config(); - - // create build directories - switch (config.platform) { - case Cpp: - Utils.ensureDirectory(config.output + "/ammer"); - case Eval: - Context.fatalError("eval target is currently not supported by ammer", Context.currentPos()); - Utils.ensureDirectory(config.eval.build); - Utils.ensureDirectory(config.eval.output); - case Hl: - Utils.ensureDirectory(config.hl.build); - Utils.ensureDirectory(config.hl.output); - case Lua: - Utils.ensureDirectory(config.lua.build); - Utils.ensureDirectory(config.lua.output); - case _: - } - - // register the build stage - Context.onAfterTyping(runBuild); - } - - public static function defineType(c:TypeDefinition):Void { - if (definedTypes != null) - definedTypes.push(c); - Context.defineType(c); - } - - static function modifyType(t:ClassType, fields:Array):Void { - if (modifiedTypes != null) - modifiedTypes.push({t: t, fields: fields}); - } - - static function registerType(t:FFIType):Void { - switch (t) { - case LibType(t, _) | LibIntEnum(t, _) | LibSub(t): - if (ctx == null || t == null) { - Context.fatalError("context loss (make sure classes are linked properly with @:ammer.sub)", Context.currentPos()); - } - ctx.types[t.id] = t; - case Closure(_, args, ret, _): - for (a in args) - registerType(a); - registerType(ret); - case _: - } - } - - /** - Registers the types of a library. - **/ - static function registerTypes(field:Field, f:Function, ?typeThis:String):Void { - var ffi = FFITools.toFFITypeFunctionF(field, f, typeThis); - for (arg in ffi.args) - registerType(arg); - registerType(ffi.ret); - } - - /** - Creates the `FFIMethod` corresponding to the given class method. Raises an - error if the FFI types are incorrectly specified. - **/ - static function createFFIMethod(field:Field, f:Function, nativePrefix:String, ?typeThis:String):FFIMethod { - var ffiFunc = FFITools.toFFITypeFunctionF(field, f, typeThis); - - var ffi:FFIMethod = { - name: field.name, - uniqueName: field.name, - native: nativePrefix + field.name, - cPrereturn: null, - cReturn: null, - isMacro: false, - isCppConstructor: false, - isCppMemberCall: false, - args: ffiFunc.args, - ret: ffiFunc.ret, - field: field - } - - // handle metadata - // TODO: make sure there are no duplicates - for (meta in Utils.meta(field.meta, Utils.META_LIBRARY_METHOD)) { - switch (meta) { - case {id: "native", params: [{expr: EConst(CString(n))}]}: - ffi.native = n; - case {id: "c.prereturn", params: [{expr: EConst(CString(n))}]}: - ffi.cPrereturn = n; - case {id: "c.return", params: [{expr: EConst(CString(n))}]}: - ffi.cReturn = n; - case {id: "macroCall", params: []}: - ffi.isMacro = true; - case {id: "cpp.constructor", params: []}: - ffi.isCppConstructor = true; - case {id: "cpp.member", params: []}: - ffi.isCppMemberCall = true; - f.args.push({ - name: "_", - type: (macro : ammer.ffi.This), - }); - ffi.args.push(LibType(typeMap[typeThis], true)); - case _: - } - } - - return ffi; - } - - static function parseMetadata():Void { - for (meta in Utils.meta(ctx.implType.meta.get(), Utils.META_LIBRARY_CLASS)) { - switch (meta) { - case {id: "nativePrefix", params: [{expr: EConst(CString(n))}]}: - ctx.nativePrefix = n; - case {id: "sub", params: [e]}: - var ct = Utils.extractComplexType(e); - ctx.subtypes.push(ct); - case _: - } - } - } - - /** - Creates the `FFIConstant` corresponding to the given `static var`. - **/ - static function createFFIConstant(field:Field, t:ComplexType, nativePrefix:String):FFIConstant { - var type = FFITools.toFFITypeVariable(field, t); - - if (!type.isVariableType()) - Context.fatalError('invalid type for ${field.name}', field.pos); - - var ffi = { - name: field.name, - uniqueName: null, - index: -1, - native: nativePrefix + field.name, - type: type, - nativeType: (switch (type) { - case LibIntEnum(_, _): FFIType.Integer(Signed32); - // TODO: support 64-bit enums - case Integer(_): FFIType.Integer(Signed32); - case Float(_): FFIType.Float(Float32); // TODO - case Bool | String: type; - case _: throw "!"; - }), - field: field, - target: null - }; - - // handle metadata - for (meta in Utils.meta(field.meta, Utils.META_LIBRARY_VARIABLE)) { - switch (meta) { - case {id: "native", params: [{expr: EConst(CString(n))}]}: - ffi.native = n; - case _: - } - } - - // register constant index - if (!ctx.ffiConstants.exists(ffi.nativeType)) - ctx.ffiConstants[ffi.nativeType] = []; - ffi.index = ctx.ffiConstants[ffi.nativeType].length; - ctx.ffiConstants[ffi.nativeType].push(ffi); - - var t = FFITools.CONSTANT_TYPES_MAP[ffi.nativeType]; - var values = macro @:privateAccess $p{Utils.access(ctx.implType).concat(['ammer_g_${t.name}_values'])}(); - - // create read-only field - ffi.field.kind = (switch [ffi.field.kind, ffi.type] { - case [FVar(vt, _), LibIntEnum(t, _)]: - var implTypePath = t.implTypePath; - FProp("default", "never", vt, macro @:privateAccess new $implTypePath($values[$v{ffi.index}])); - case [FVar(vt, _), String]: - FProp("default", "never", vt, macro ammer.conv.CString.fromNative($values[$v{ffi.index}])); - case [FVar(vt, _), _]: - FProp("default", "never", vt, macro $values[$v{ffi.index}]); - case _: throw "!"; - }); - - return ffi; - } - - /** - Creates the `FFIVariable` corresponding to the given `var`. - **/ - static function createFFIVariable(field:Field, t:ComplexType, nativePrefix:String):FFIVariable { - var type = FFITools.toFFIType(t, { - pos: field.pos, - parent: null, - type: LibType - }); - - // TODO: check annotations make sense - - var ffi = { - name: field.name, - native: nativePrefix + field.name, - type: type, - complexType: type.toComplexType(), - field: field, - getField: null, - setField: null - }; - - // handle metadata - for (meta in Utils.meta(field.meta, Utils.META_LIBRARY_VARIABLE)) { - switch (meta) { - case {id: "native", params: [{expr: EConst(CString(n))}]}: - ffi.native = n; - case _: - } - } - - return ffi; - } - - /** - Creates FFI-mapped fields for the library class. - **/ - static function createFFI():Void { - for (field in ctx.implFields) { - switch (field) { - case {kind: FFun(f)}: - registerTypes(field, f); - case {kind: FVar(t, null)}: - if (t == null) - Context.fatalError('type annotation required for ${field.name}', field.pos); - case _: - Context.fatalError("properties are not supported in ammer library definitions", field.pos); - } - } - for (type in ctx.subtypes) { - registerType(FFITools.toFFIType(type, { - pos: ctx.implType.pos, - parent: null, - type: None - })); - } - for (field in ctx.implFields) { - switch (field) { - case {kind: FFun(f)}: - ctx.ffiMethods.push(createFFIMethod(field, f, ctx.nativePrefix)); - case {kind: FVar(t, null)}: - var const = createFFIConstant(field, t, ctx.nativePrefix); - const.target = { - pack: ctx.implType.pack, - module: ctx.implType.name, - cls: ctx.implType.name, - field: field.name - }; - case _: - } - } - for (id => type in ctx.types) { - Debug.log('type $id for library ${ctx.libraryConfig.name}', "msg"); - for (method in type.ffiMethods) { - method.uniqueName = Utils.typeIdField(type.implType) + method.name; - Debug.log(' -> field: ${method.field.name} (${method.uniqueName})', "msg"); - ctx.ffiMethods.push(method); - var libraryField:Field = { - access: [APrivate, AStatic], - kind: FFun({ret: null, expr: null, args: null}), - name: Utils.typeIdField(type.implType) + method.field.name, - pos: method.field.pos - }; - method.field = libraryField; - ctx.implFields.push(libraryField); - } - for (const in type.ffiConstants) { - const.uniqueName = Utils.typeIdField(type.implType) + const.name; - Debug.log(' -> field: ${const.field.name} (${const.uniqueName})', "msg"); - } - } - Debug.log(ctx.ffiMethods, "gen-library"); - Debug.log(ctx.ffiConstants, "gen-library"); - } - - /** - Patches extern calls. - **/ - static function patchImpl():Void { - var externPath = ["ammer", "externs", ctx.externName]; - for (t in FFITools.CONSTANT_TYPES) { - var consts = ctx.ffiConstants[t.ffi]; - if (consts == null || consts.length == 0) - continue; - ctx.implFields.push({ - access: [AStatic], - kind: FVar(null, macro null), - name: 'ammer_g_${t.name}_cache', - pos: ctx.implType.pos - }); - var cache = macro $p{Utils.access(ctx.implType).concat(['ammer_g_${t.name}_cache'])}; - ctx.implFields.push({ - access: [AStatic], - kind: FFun({ - ret: null, - args: [], - expr: macro @:privateAccess { - if ($cache == null) - $cache = $p{externPath.concat(['ammer_g_${t.name}'])}(); - return $cache; - } - }), - name: 'ammer_g_${t.name}_values', - pos: ctx.implType.pos - }); - } - switch (config.platform) { - case Eval: ammer.patch.PatchEval.patch(ctx); - case Cpp: ammer.patch.PatchCpp.patch(ctx); - case Hl: ammer.patch.PatchHl.patch(ctx); - case Lua: ammer.patch.PatchLua.patch(ctx); - case Cross: ammer.patch.PatchCross.patch(ctx); - case _: throw "!"; - } - for (method in ctx.ffiMethods) Utils.withPos(() -> { - Debug.log('patching ${method.name} (${method.native})', "msg"); - var f = (switch (method.field.kind) { - case FFun(f): f; - case _: throw "!"; - }); - - // normalise derivable arguments - var norm = method.args.map(FFITools.normalise); - - // generate signature for calls from Haxe code - f.args = [ for (i in 0...method.args.length) switch (norm[i]) { - case Derived(_, _) | SizeOfReturn: continue; - case ClosureDataUse: continue; - case ClosureData(_): continue; - case Unsupported(_): continue; - case t: { - name: '_arg$i', - type: t.toComplexType() - }; - } ]; - f.ret = method.ret.toComplexType(); - - // apply common patches - var mctx:AmmerMethodPatchContext = { - top: ctx, - ffi: method, - callArgs: [ for (i in 0...method.args.length) switch (norm[i]) { - case Derived(e, _): e; - case SizeOfReturn: Utils.id("_retSize"); - case _: Utils.arg(i); - } ], - callExpr: null, - wrapExpr: null - }; - ctx.methodContexts.push(mctx); - mctx.callExpr = macro $p{externPath.concat([method.uniqueName])}($a{mctx.callArgs}); - mctx.wrapExpr = mctx.callExpr; - - // common patches - mctx.wrapExpr = PatchMethod.commonPatchReturn(mctx.wrapExpr, method.ret); - for (i in 0...method.args.length) { - mctx.callArgs[i] = PatchMethod.commonPatchArgument(mctx.callArgs[i], method.args[i]); - } - - // apply platform-specific patches - var methodPatcher = (switch (config.platform) { - case Eval: new ammer.patch.PatchEval.PatchEvalMethod(mctx); - case Cpp: new ammer.patch.PatchCpp.PatchCppMethod(mctx); - case Hl: new ammer.patch.PatchHl.PatchHlMethod(mctx); - case Lua: new ammer.patch.PatchLua.PatchLuaMethod(mctx); - case Cross: new ammer.patch.PatchCross.PatchCrossMethod(mctx); - case _: throw "!"; - }); - for (i in 0...method.args.length) { - methodPatcher.visitArgument(i, method.args[i]); - } - methodPatcher.finish(); - - // wrap up - if (config.platform == Cross) { - f.expr = macro throw ""; - } else { - if (method.ret == Void) - f.expr = macro ${mctx.wrapExpr}; - else - f.expr = macro return ${mctx.wrapExpr}; - } - }, method.field.pos); - } - - /** - Creates an extern type for the library. - **/ - static function createExtern():Void { - var c = macro class AmmerExtern {}; - c.isExtern = ctx.externIsExtern; - c.meta = ctx.externMeta; - c.name = ctx.externName; - c.pack = ["ammer", "externs"]; - c.fields = ctx.externFields; - Debug.logP(() -> Debug.typeDefinition(c), "gen-library"); - defineType(c); - } - - /** - Runs target-specific build actions after all libraries are processed. - Callback to this is registered in `configure`. - **/ - static function runBuild(_):Void { - switch (config.platform) { - case Cpp: - for (library in libraries) - ammer.stub.StubCpp.generate(config, library); - // no build process - case Eval: - for (library in libraries) - ammer.stub.StubEval.generate(config, library); - ammer.build.BuildEval.build(config, libraries); - case Hl: - for (library in libraries) - ammer.stub.StubHl.generate(config, library); - ammer.build.BuildHl.build(config, libraries); - case Lua: - for (library in libraries) - ammer.stub.StubLua.generate(config, library); - ammer.build.BuildLua.build(config, libraries); - case _: - } - } - - static function createLibraryConfig(libname:String):AmmerLibraryConfig { - if (libraryMap.exists(libname)) - return libraryMap[libname]; - var libConfig = config.createLibraryConfig(libname); - libraries.push(libConfig); - return libraryMap[libname] = libConfig; - } - - /** - Main entry point for each library. - **/ - public static function build():Array { - configure(); - var implType = Context.getLocalClass().get(); - var implComplexType = TPath({ - name: implType.module.split(".").pop(), - pack: implType.pack, - sub: implType.name, - }); - var libname = (switch (implType.superClass.params[0]) { - case TInst(_.get() => {kind: KExpr({expr: EConst(CString(libname))})}, []): - libname; - case _: - throw Context.fatalError("ammer.Library type parameter should be a string", implType.pos); - }); - Debug.log('started ${implType.name} (library $libname)', "stage"); - var libraryConfig = createLibraryConfig(libname); - var ctxIndex = libraryConfig.contexts.length; - ctx = { - index: ctxIndex, - config: config, - subtypes: [], - libraryConfig: libraryConfig, - implType: implType, - implComplexType: implComplexType, - implFields: Context.getBuildFields(), - externName: 'AmmerExtern_${libname}_${ctxIndex}', - externFields: [], - externIsExtern: true, - externMeta: [], - ffiMethods: [], - ffiConstants: [], - closureTypes: [], - arrayTypes: [], - nativePrefix: "", - types: [], - methodContexts: [] - }; - ctxStack.push(ctx); - libraryConfig.contexts.push(ctx); - libraryContextMap[Utils.typeId(implType)] = ctx; - Utils.posStack.push(implType.pos); - parseMetadata(); - createFFI(); - patchImpl(); - createExtern(); - var ret = ctx.implFields; - for (f in ret) - Debug.logP(() -> Debug.field(f), "gen-library"); - Debug.log('finished ${implType.name} (library $libname)', "stage"); - modifyType(ctx.implType, ret); - ctxStack.pop(); - ctx = ctxStack.length > 0 ? ctxStack[ctxStack.length - 1] : null; - Utils.posStack.pop(); - return ret; - } - - public static function delayedBuildType(id:String, implType:ClassType, subtypeKind:SubtypeKind):AmmerTypeContext { - var moduleName = implType.module.split(".").pop(); - var implTypePath:TypePath = (if (implType.name != moduleName) - {pack: implType.pack, name: moduleName, sub: implType.name} - else - {pack: implType.pack, name: implType.name}); - - if (!typeMap.exists(id)) { - if (!typeCache.exists(id)) - throw "!"; - - var nativeName = typeCache[id].native; - var nativePrefix = ""; - var isStruct = false; - for (meta in Utils.meta(implType.meta.get(), Utils.META_TYPE_CLASS)) { - switch (meta) { - case {id: "nativePrefix", params: [{expr: EConst(CString(n))}]}: - nativePrefix = n; - case {id: "struct", params: []}: - if (!subtypeKind.match(Pointer(_))) - Context.fatalError("ammer.struct is only valid on library data types", implType.pos); - isStruct = true; - case _: - } - } - - types.push(typeMap[id] = { - id: id, - implType: implType, - implTypePath: implTypePath, - nativeName: nativeName, - nativePrefix: nativePrefix, - nativeType: (switch [subtypeKind, config.platform] { - case [Pointer(_), Hl]: - TPath({name: "Abstract", pack: ["hl"], params: [TPExpr({expr: EConst(CString(nativeName)), pos: implType.pos})]}); - case [Pointer(star), Cpp]: - var c = macro class LibTypeExtern {}; - c.isExtern = true; - c.meta = [{name: ":native", params: [macro $v{nativeName}], pos: implType.pos}]; - c.name = 'AmmerExternType_${typeCtr++}'; - c.pack = ["ammer", "externs"]; - defineType(c); - var externType:ComplexType = TPath({name: c.name, pack: c.pack}); - star - ? TPath({name: "Pointer", pack: ["cpp"], params: [TPType(externType)]}) - : TPath({name: "Struct", pack: ["cpp"], params: [TPType(externType)]}); - case [Pointer(_), Lua]: - TPath({name: "UserData", pack: ["lua"], params: []}); - case [Pointer(_), _]: - throw "!"; - case [IntEnum, _]: - TPath({name: "Int", pack: [], params: []}); - case [Sublibrary, _]: - (macro : Void); - }), - originalFields: typeCache[id].fields, - library: typeCache[id].library, - processed: null, - isStruct: isStruct, - kind: subtypeKind, - ffiMethods: [], - ffiConstants: [], - ffiVariables: [], - libraryCtx: null - }); - typeCache.remove(id); - } - var typeCtx = typeMap[id]; - if (typeCtx.processed != null) - return typeCtx; - Debug.log('finalising type $id', "stage"); - var native = typeCtx.nativeType; - var retFields:Array = []; - switch (subtypeKind) { - case Pointer(_): - retFields = retFields.concat((macro class LibType { - private var ammerNative:$native; - - private function new(native:$native) { - this.ammerNative = native; - } - - public static function nullPointer() { - return new $implTypePath(null); - } - }).fields); - case IntEnum: - var impl = TPath(typeCtx.implTypePath); - retFields = retFields.concat((macro class LibType { - private static var ammerNativeInstances:Map; - - private static function ammerFromNative(native:$native) { - if (ammerNativeInstances == null) { - ammerNativeInstances = []; - } - return ammerNativeInstances.exists(native) - ? ammerNativeInstances[native] - : new $implTypePath(native); - } - - private var ammerNative:$native; - - private function new(native:$native) { - this.ammerNative = native; - if (ammerNativeInstances == null) { - ammerNativeInstances = []; - } - ammerNativeInstances[native] = this; - } - - public function asInt():Int { - return ammerNative; - } - }).fields); - case Sublibrary: - // nothing to add - } - var library = (switch (typeCtx.library) { - case TPath(tp): tp; - case _: throw "!"; - }); - var libraryParts = library.pack.concat([library.name]).concat(library.sub != null ? [library.sub] : []); - var idField = Utils.typeIdField(typeCtx.implType); - Utils.posStack.push(typeCtx.implType.pos); - function accessLibrary(field:String, ?pos:Position):Expr { - if (pos == null) - pos = typeCtx.implType.pos; - var fieldParts = libraryParts.concat([idField + field]); - var fieldAccess = {expr: EConst(CIdent(fieldParts[0])), pos: pos}; - for (i in 1...fieldParts.length) { - fieldAccess = {expr: EField(fieldAccess, fieldParts[i]), pos: pos}; - } - return fieldAccess; - } - if (subtypeKind.match(Pointer(_)) && typeCtx.isStruct) { - // generate alloc and free if the type is marked with ammer.struct - typeCtx.ffiMethods.push({ - name: "alloc", - uniqueName: "alloc", - native: "", - cPrereturn: null, - cReturn: '(${typeCtx.nativeName} *)calloc(sizeof(${typeCtx.nativeName}), 1)', - isMacro: false, - isCppConstructor: false, - isCppMemberCall: false, - args: [], - ret: LibType(typeCtx, false), - field: { - access: [], - kind: FFun({args: [], ret: null, expr: null}), - name: "alloc", - pos: typeCtx.implType.pos - } - }); - retFields.push({ - access: [APublic, AStatic], - kind: FFun({ - args: [], - ret: TPath(implTypePath), - expr: macro return @:privateAccess $e{accessLibrary("alloc")}() - }), - name: "alloc", - pos: typeCtx.implType.pos - }); - typeCtx.ffiMethods.push({ - name: "free", - uniqueName: "free", - native: "", - cPrereturn: null, - cReturn: "free(arg_0)", - isMacro: false, - isCppConstructor: false, - isCppMemberCall: false, - args: [LibType(typeCtx, true)], - ret: Void, - field: { - access: [], - kind: FFun({args: [], ret: null, expr: null}), - name: "free", - pos: typeCtx.implType.pos - } - }); - retFields.push({ - access: [APublic], - kind: FFun({ - args: [], - ret: (macro : Void), - expr: macro @:privateAccess $e{accessLibrary("free")}(this) - }), - name: "free", - pos: typeCtx.implType.pos - }); - } - // process "virtual" fields - var fieldSizes = new Map(); - for (field in typeCtx.originalFields) { - switch (field) { - case {kind: FVar(ct, null), access: [APublic]}: - var ffi = createFFIVariable(field, ct, typeCtx.nativePrefix); - switch (ffi.type) { - case SizeOfField(target): - fieldSizes[target] = macro $p{["_arg0", field.name]}; - case _: - } - case _: - } - } - // process remaining fields - for (field in typeCtx.originalFields) { - switch (field) { - case {kind: FFun(f), access: access}: - if (access.indexOf(APublic) == -1) - Context.fatalError("type methods must be public", field.pos); - var isInstance = access.indexOf(AStatic) == -1; - // TODO: allow overloads - registerTypes(field, f, id); - var ffi = createFFIMethod(field, f, typeCtx.nativePrefix, id); - var thisArgs = ffi.args.filter(arg -> arg.match( - LibType(_, true) | Nested(LibType(_, true)) | LibIntEnum(_, true) - )).length; - if (ffi.isCppConstructor && isInstance) { - Context.fatalError("constructors must be static", field.pos); - } - if (isInstance) { - if (thisArgs != 1) - Context.fatalError("non-static type methods must have exactly one ammer.ffi.This argument", field.pos); - } else { - if (thisArgs != 0) - Context.fatalError("static type methods must have no ammer.ffi.This arguments", field.pos); - } - var norm = ffi.args.map(FFITools.normalise); - var signArgs = [ for (i in 0...f.args.length) { - name: '_arg$i', - type: (switch (norm[i]) { - case LibType(_, true): continue; - case Nested(LibType(_, true)): continue; - case LibIntEnum(_, true): continue; - case Derived(_) | SizeOfReturn: continue; - case ClosureData(_): continue; - case Unsupported(_): continue; - case t: t.toComplexType(); - }) - } ]; - var callArgs = [ for (i in 0...f.args.length) switch (norm[i]) { - case LibType(_, true): macro this; - case Nested(LibType(_, true)): macro this; - case LibIntEnum(_, true): macro this; - case Derived(_) | SizeOfReturn: continue; - case ClosureData(_): continue; - case Unsupported(_): continue; - case _: Utils.arg(i); - } ]; - typeCtx.ffiMethods.push(ffi); - retFields.push({ - access: isInstance ? [APublic] : [APublic, AStatic], - kind: FFun({ - args: signArgs, - ret: ffi.ret.toComplexType(), - expr: macro return @:privateAccess $e{accessLibrary(field.name, field.pos)}($a{callArgs}) - }), - name: field.name, - pos: field.pos - }); - case {kind: FVar(ct, null), access: [APublic]}: - var ffi = createFFIVariable(field, ct, typeCtx.nativePrefix); - typeCtx.ffiVariables.push(ffi); - switch (ffi.type) { - case ClosureDataUse: - // ClosureDataUse is only visible in ffiVariables (for stub access) - continue; - case _: - } - var isNested = ffi.type.match(Nested(LibType(_, _))); - var isReadOnly = ffi.type.match(ArrayDynamic(_, _) | ArrayFixed(_, _, _)); - if (ffi.type.needsSize()) { - if (!fieldSizes.exists(field.name)) - Context.fatalError("field requires size", field.pos); - ffi.type = WithSize(fieldSizes[field.name], ffi.type); - } - var ffiGet:FFIMethod = { - name: 'get_${field.name}', - uniqueName: 'get_${field.name}', - native: "", - cPrereturn: null, - cReturn: (isNested ? "&" : "") + 'arg_0->${ffi.native}', - isMacro: false, - isCppConstructor: false, - isCppMemberCall: false, - args: [LibType(typeCtx, true)], - ret: ffi.type, - field: null - }; - typeCtx.ffiMethods.push(ffiGet); - retFields.push(ffi.getField = ffiGet.field = { - access: [AInline], - kind: FFun({ - args: [], - ret: ffi.complexType, - expr: macro return @:privateAccess $e{accessLibrary('get_${field.name}', field.pos)}(this) - }), - name: 'get_${field.name}', - pos: field.pos - }); - if (!isReadOnly) { - var ffiSet:FFIMethod = { - name: 'set_${field.name}', - uniqueName: 'set_${field.name}', - native: "", - cPrereturn: null, - cReturn: 'arg_0->${ffi.native} = ${isNested ? "*" : ""}arg_1', - isMacro: false, - isCppConstructor: false, - isCppMemberCall: false, - args: [LibType(typeCtx, true), ffi.type], - ret: Void, - field: null - }; - typeCtx.ffiMethods.push(ffiSet); - retFields.push(ffi.setField = ffiSet.field = { - access: [AInline], - kind: FFun({ - args: [{name: "val", type: ffi.complexType}], - ret: ffi.complexType, - expr: macro { - @:privateAccess $e{accessLibrary('set_${field.name}', field.pos)}(this, val); - return val; - } - }), - name: 'set_${field.name}', - pos: field.pos - }); - } - retFields.push({ - access: [APublic], - kind: FProp("get", isReadOnly ? "never" : "set", ffi.complexType, null), - name: field.name, - pos: field.pos - }); - case {kind: FVar(ct, null), access: [APublic, AStatic]}: - var ffi = createFFIConstant(field, ct, typeCtx.nativePrefix); - ffi.target = { - pack: implType.pack, - module: implType.module.split(".").pop(), - cls: implType.name, - field: field.name - }; - typeCtx.ffiConstants.push(ffi); - retFields.push(field); - case {kind: FVar(_, _)}: - Context.fatalError("only public variables are supported in ammer type definitions", field.pos); - case _: - Context.fatalError("invalid field in ammer type definition", field.pos); - } - } - Utils.posStack.pop(); - Debug.log('finished type $id', "stage"); - typeCtx.processed = retFields; - return typeCtx; - } - - public static function buildType():Array { - configure(); - var implType = Context.getLocalClass().get(); - var id = Utils.typeId(implType); - var subtypeKind = SubtypeKind.Pointer(true); - Debug.log('started type $id', "stage"); - // add type into cache - // ensure base library is typed - var libraryCT = (switch (implType.superClass) { - case {t: _.get() => {name: "PointerProcessed", pack: ["ammer"]}, params: [TInst(_.get() => {kind: KExpr({expr: EConst(CString(native))})}, []), libType = TInst(lib, [])]}: - typeCache[id] = {native: native, fields: Context.getBuildFields(), library: Context.toComplexType(libType), kind: subtypeKind = Pointer(true)}; - lib.get(); - case {t: _.get() => {name: "PointerNoStarProcessed", pack: ["ammer"]}, params: [TInst(_.get() => {kind: KExpr({expr: EConst(CString(native))})}, []), libType = TInst(lib, [])]}: - typeCache[id] = {native: native, fields: Context.getBuildFields(), library: Context.toComplexType(libType), kind: subtypeKind = Pointer(false)}; - lib.get(); - case {t: _.get() => {name: "IntEnumProcessed", pack: ["ammer"]}, params: [TInst(_.get() => {kind: KExpr({expr: EConst(CString(native))})}, []), libType = TInst(lib, [])]}: - typeCache[id] = {native: native, fields: Context.getBuildFields(), library: Context.toComplexType(libType), kind: subtypeKind = IntEnum}; - lib.get(); - case {t: _.get() => {name: "Sublibrary", pack: ["ammer"]}, params: [libType = TInst(lib, [])]}: - typeCache[id] = {native: null, fields: Context.getBuildFields(), library: Context.toComplexType(libType), kind: subtypeKind = Sublibrary}; - lib.get(); - case _: - throw "!"; - }); - var ctx = delayedBuildType(id, implType, subtypeKind); - ctx.libraryCtx = libraryContextMap[Utils.typeId(libraryCT)]; - switch (config.platform) { - // case Eval: ammer.patch.PatchEval.patchType(ctx); - case Cpp: ammer.patch.PatchCpp.patchType(ctx); - // case Hl: ammer.patch.PatchHl.patchType(ctx); - // case Cross: ammer.patch.PatchCross.patchType(ctx); - case _: - } - for (f in ctx.processed) { - Debug.logP(() -> Debug.field(f), "gen-type"); - } - modifyType(ctx.implType, ctx.processed); - return ctx.processed; - } -} diff --git a/src/ammer/AmmerContext.hx b/src/ammer/AmmerContext.hx deleted file mode 100644 index e7f0c43..0000000 --- a/src/ammer/AmmerContext.hx +++ /dev/null @@ -1,41 +0,0 @@ -package ammer; - -import haxe.macro.Expr; -import haxe.macro.Type; -import ammer.Config.AmmerLibraryConfig; - -/** - This object is created once per `ammer` library. It is updated during the - various processing stages. -**/ -typedef AmmerContext = { - /** - Configuration stage. - **/ - index:Int, - config:Config, - libraryConfig:AmmerLibraryConfig, - subtypes:Array, - /** - FFI mapping stage. - **/ - ffiMethods:Array, - ffiConstants:Map>, - closureTypes:Array, - arrayTypes:Array, - nativePrefix:String, - types:Map, - methodContexts:Array, - /** - Patching stage. - **/ - // the original class - implType:ClassType, - implComplexType:ComplexType, - implFields:Array, - // class with `extern` functions, `@:hlNative` ... - externName:String, - externFields:Array, - externIsExtern:Bool, - externMeta:Array -}; diff --git a/src/ammer/AmmerMethodPatchContext.hx b/src/ammer/AmmerMethodPatchContext.hx deleted file mode 100644 index c2e06ba..0000000 --- a/src/ammer/AmmerMethodPatchContext.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer; - -import haxe.macro.Expr; - -typedef AmmerMethodPatchContext = { - top:AmmerContext, - ffi:FFIMethod, - callArgs:Array, - callExpr:Expr, - wrapExpr:Expr -}; diff --git a/src/ammer/AmmerTypeContext.hx b/src/ammer/AmmerTypeContext.hx deleted file mode 100644 index 69ab448..0000000 --- a/src/ammer/AmmerTypeContext.hx +++ /dev/null @@ -1,22 +0,0 @@ -package ammer; - -import haxe.macro.Expr; -import haxe.macro.Type; - -typedef AmmerTypeContext = { - id:String, - implType:ClassType, - implTypePath:TypePath, - nativeName:String, - nativePrefix:String, - nativeType:ComplexType, - originalFields:Array, - library:ComplexType, - processed:Array, - isStruct:Bool, - kind:SubtypeKind, - ffiMethods:Array, - ffiConstants:Array, - ffiVariables:Array, - libraryCtx:AmmerContext // only set when patching -}; diff --git a/src/ammer/Config.hx b/src/ammer/Config.hx deleted file mode 100644 index 82625c4..0000000 --- a/src/ammer/Config.hx +++ /dev/null @@ -1,201 +0,0 @@ -package ammer; - -import haxe.io.Path; -import haxe.macro.Compiler; -import haxe.macro.Context; - -class Config { - static final BOOL_YES = ["yes", "y", "true", "1", "on"]; - static final BOOL_NO = ["no", "n", "false", "0", "off"]; - - public final eval:Null = null; - public final hl:Null = null; - public final lua:Null = null; - public final output:String; - public final debug:Array; - public final platform:AmmerPlatform; - public final useMSVC:Bool; - public final pathMSVC:String; - public final useMakefiles:Bool; - - public function new() { - output = Compiler.getOutput(); - debug = (switch (getDefine("ammer.debug")) { - case null: []; - case "all": ["stage", "gen-library", "gen-type", "msg"]; - case s: s.split(","); - }); - platform = (switch (Context.definedValue("target.name")) { - case "hl": AmmerPlatform.Hl; - case "cpp": AmmerPlatform.Cpp; - case "eval": AmmerPlatform.Eval; - case "lua": AmmerPlatform.Lua; - case "cross": AmmerPlatform.Cross; - case _: - Context.fatalError("unsupported ammer platform", Context.currentPos()); - null; - }); - useMSVC = getBool("ammer.msvc", Sys.systemName() == "Windows"); - pathMSVC = getPath("ammer.msvcPath"); - if (pathMSVC == null) { - pathMSVC = ""; - } else if (pathMSVC != "" && pathMSVC.substr(-1) != "/") { - pathMSVC += "/"; - } - useMakefiles = getBool("ammer.makefiles", true); - - // create platform-specific config - switch (platform) { - case Eval: - eval = { - build: getPath("ammer.eval.build", Sys.getCwd()), - output: getPath("ammer.eval.output", Sys.getCwd()), - haxeDir: getPath("ammer.eval.haxeDir", true), - bytecode: getBool("ammer.eval.bytecode", false) - }; - case Hl: - var outputDir = Path.directory(output); - hl = { - build: getPath("ammer.hl.build", outputDir), - output: getPath("ammer.hl.output", outputDir), - hlIncludePath: getPath("ammer.hl.hlInclude", null), - hlLibraryPath: getPath("ammer.hl.hlLibrary", null) - }; - case Lua: - var outputDir = Path.directory(output); - lua = { - build: getPath("ammer.lua.build", outputDir), - output: getPath("ammer.lua.output", outputDir), - luaIncludePath: getPath("ammer.lua.luaInclude", null), - luaLibraryPath: getPath("ammer.lua.luaLibrary", null) - }; - case _: - } - } - - /** - Gets a compile-time define by `key`. If the specified key is not defined, - return the value `dv`, or throw an error if `doThrow` is `true`. - **/ - public function getDefine(key:String, ?dv:String, ?doThrow:Bool = false):String { - if (Context.defined(key)) - return Context.definedValue(key); - if (doThrow) - Context.fatalError('required define: $key', Context.currentPos()); - return dv; - } - - /** - Gets a boolean from the compile-time define `key`. - **/ - public function getBool(key:String, ?dv:Bool, ?doThrow:Bool = false):Bool { - if (Context.defined(key)) { - if (BOOL_YES.indexOf(Context.definedValue(key)) != -1) - return true; - if (BOOL_NO.indexOf(Context.definedValue(key)) != -1) - return false; - Context.fatalError('invalid define (should be yes or no): $key', Context.currentPos()); - } - if (doThrow) - Context.fatalError('required define: $key', Context.currentPos()); - return dv; - } - - /** - Gets a path from the compile-time define `key`. If the path is relative, - resolve it relative to the current working directory. - **/ - public function getPath(key:String, ?dv:String, ?doThrow:Bool = false):String { - var p = getDefine(key, dv, doThrow); - if (p != null && !Path.isAbsolute(p)) - p = Path.join([Sys.getCwd(), p]); - return p; - } - - public function getEnum(key:String, map:Map, ?dv:T, ?doThrow:Bool = false):T { - var p = getDefine(key, null, doThrow); - if (p == null) - return dv; - if (!map.exists(p)) { - var keys = [for (k in map.keys()) k]; - keys.sort(Reflect.compare); - Context.fatalError('invalid define (should be one of ${keys.join(", ")})', Context.currentPos()); - } - return map[p]; - } - - public function createLibraryConfig(libname:String):AmmerLibraryConfig { - return { - name: libname, - linkName: getDefine('ammer.lib.${libname}.linkName', libname).split(","), - includePath: getPath('ammer.lib.${libname}.include').split(","), - libraryPath: getPath('ammer.lib.${libname}.library').split(","), - headers: getDefine('ammer.lib.${libname}.headers', '${libname}.h').split(","), - abi: getEnum('ammer.lib.${libname}.abi', [ - "c" => AmmerAbi.C, - "cpp" => Cpp, - "objc" => ObjectiveC, - "objcpp" => ObjectiveCpp, - ], C), - contexts: [], - }; - } -} - -enum AmmerPlatform { - Cpp; - Eval; - Hl; - Lua; - Cross; -} - -typedef AmmerConfigEval = { - build:String, - output:String, - haxeDir:String, - bytecode:Bool -}; - -typedef AmmerConfigHl = { - build:String, - output:String, - hlIncludePath:String, - hlLibraryPath:String -}; - -typedef AmmerConfigLua = { - build:String, - output:String, - luaIncludePath:String, - luaLibraryPath:String -}; - -typedef AmmerLibraryConfig = { - name:String, - linkName:Array, - includePath:Array, - libraryPath:Array, - headers:Array, - abi:AmmerAbi, - contexts:Array -}; - -@:using(ammer.Config.AmmerAbiTools) -enum AmmerAbi { - C; - Cpp; - ObjectiveC; - ObjectiveCpp; -} - -class AmmerAbiTools { - public static function fileExtension(abi:AmmerAbi):String { - return (switch (abi) { - case C: "c"; - case Cpp: "cpp"; - case ObjectiveC: "m"; - case ObjectiveCpp: "mm"; - }); - } -} diff --git a/src/ammer/Debug.hx b/src/ammer/Debug.hx deleted file mode 100644 index e178136..0000000 --- a/src/ammer/Debug.hx +++ /dev/null @@ -1,28 +0,0 @@ -package ammer; - -import haxe.macro.Expr; -import haxe.macro.Printer; - -class Debug { - static var printer:Printer = new Printer(); - - public static function logP(message:() -> String, stream:String, ?pos:haxe.PosInfos):Void { - if (Ammer.config.debug.indexOf(stream) == -1) - return; - Sys.println('[ammer:$stream] ${message()} (${pos.fileName}:${pos.lineNumber})'); - } - - public static function log(message:Dynamic, stream:String, ?pos:haxe.PosInfos):Void { - if (Ammer.config.debug.indexOf(stream) == -1) - return; - Sys.println('[ammer:$stream] $message (${pos.fileName}:${pos.lineNumber})'); - } - - public static function typeDefinition(t:TypeDefinition):String { - return printer.printTypeDefinition(t); - } - - public static function field(f:Field):String { - return printer.printField(f); - } -} diff --git a/src/ammer/FFIArrayType.hx b/src/ammer/FFIArrayType.hx deleted file mode 100644 index 45ad921..0000000 --- a/src/ammer/FFIArrayType.hx +++ /dev/null @@ -1,10 +0,0 @@ -package ammer; - -import haxe.macro.Expr; - -typedef FFIArrayType = { - index:Int, - ffi:FFIType, - implTypePath:TypePath, - wrapperTypePath:TypePath, -}; diff --git a/src/ammer/FFIClosureSignature.hx b/src/ammer/FFIClosureSignature.hx deleted file mode 100644 index 16beced..0000000 --- a/src/ammer/FFIClosureSignature.hx +++ /dev/null @@ -1,8 +0,0 @@ -package ammer; - -typedef FFIClosureSignature = { - index:Int, - args:Array, - ret:FFIType, - dataAccess:Array -}; diff --git a/src/ammer/FFIConstant.hx b/src/ammer/FFIConstant.hx deleted file mode 100644 index a87b424..0000000 --- a/src/ammer/FFIConstant.hx +++ /dev/null @@ -1,14 +0,0 @@ -package ammer; - -import haxe.macro.Expr; - -typedef FFIConstant = { - name:String, - uniqueName:String, - index:Int, - native:String, - type:FFIType, - nativeType:FFIType, - field:Field, - target:{pack:Array, module:String, cls:String, field:String} -}; diff --git a/src/ammer/FFIMethod.hx b/src/ammer/FFIMethod.hx deleted file mode 100644 index 274a329..0000000 --- a/src/ammer/FFIMethod.hx +++ /dev/null @@ -1,18 +0,0 @@ -package ammer; - -import haxe.macro.Expr; - -typedef FFIMethod = { - name:String, - uniqueName:String, // prefixed by type id if needed - native:String, - cPrereturn:Null, - cReturn:Null, - // TODO: instead of is* fields have an enum - isMacro:Bool, - isCppConstructor:Bool, - isCppMemberCall:Bool, - args:Array, - ret:FFIType, - field:Field -}; diff --git a/src/ammer/FFITools.hx b/src/ammer/FFITools.hx deleted file mode 100644 index cf39a47..0000000 --- a/src/ammer/FFITools.hx +++ /dev/null @@ -1,575 +0,0 @@ -package ammer; - -import haxe.macro.Context; -import haxe.macro.Expr; -import haxe.macro.Type; - -using Lambda; - -class FFITools { - static var herePos = (macro null).pos; - - public static var CONSTANT_TYPES:Array<{ffi:FFIType, haxe:ComplexType, name:String}> = [ - {ffi: Integer(Signed32), haxe: (macro : Int), name: "int"}, - // TODO: other integer sizes? - {ffi: String, haxe: (macro : String), name: "string"}, - {ffi: Bool, haxe: (macro : Bool), name: "bool"}, - {ffi: Float(Float32), haxe: (macro : Float), name: "float"}, - // TODO: other float sizes? default to double? - ]; - public static var CONSTANT_TYPES_MAP = [ for (t in CONSTANT_TYPES) t.ffi => t ]; - - public static function isArgumentType(t:FFIType):Bool { - return (switch (t) { - case SameSizeAs(_, _) | Void | Alloc(_): false; - case _: true; - }); - } - - public static function isReturnType(t:FFIType):Bool { - return (switch (t) { - case SizeOf(_) | SizeOfReturn | Nested(_): false; - case _: true; - }); - } - - public static function isVariableType(t:FFIType):Bool { - return (switch (t) { - case Integer(_): true; - case String: true; - case Bool: true; - case Float(_): true; - case LibIntEnum(_, _): true; - case _: false; - }); - } - - public static function needsSize(t:FFIType):Bool { - return (switch (t) { - case SameSizeAs(_, _): false; - case /*String | */ Bytes: true; - case ArrayDynamic(_, _): true; - case _: false; - }); - } - - /** - Maps an FFI type to its syntactic Haxe equivalent. - **/ - public static function toComplexType(t:FFIType):ComplexType { - // TODO: this match on platform is not great - return (switch [t, Ammer.config.platform] { - case [Void, _]: (macro:Void); - case [Bool, _]: (macro:Bool); - case [Integer(Signed8 | Unsigned8), Hl]: (macro:hl.UI8); - case [Integer(Signed16 | Unsigned16), Hl]: (macro:hl.UI16); - case [Integer(Signed8), Cpp]: (macro:cpp.Int8); - case [Integer(Signed16), Cpp]: (macro:cpp.Int16); - case [Integer(Signed32), Cpp]: (macro:cpp.Int32); - //case [Integer(Signed64), Cpp]: (macro:cpp.Int64); - case [Integer(Unsigned8), Cpp]: (macro:cpp.UInt8); - case [Integer(Unsigned16), Cpp]: (macro:cpp.UInt16); - case [Integer(Unsigned32), Cpp]: (macro:cpp.UInt32); - //case [Integer(Unsigned64), Cpp]: (macro:cpp.UInt64); - case [Integer(Signed64 | Unsigned64), _]: (macro:haxe.Int64); - case [Integer(_), _]: (macro:Int); - case [Float(Float64), _]: (macro:Float); - case [Float(Float32), _]: (macro:Single); - case [Bytes, _]: (macro:haxe.io.Bytes); - case [ArrayDynamic(idx, _) | ArrayFixed(idx, _, _), _]: TPath(Ammer.ctx.arrayTypes[idx].wrapperTypePath); - case [String, _]: (macro:String); - case [Derived(_, t), _]: toComplexType(t); - case [WithSize(_, t), _]: toComplexType(t); - case [Closure(_, args, ret, _), _]: TFunction(args.filter(a -> !a.match(ClosureDataUse)).map(toComplexType), toComplexType(ret)); - case [ClosureDataUse, _]: (macro:Int); - case [ClosureData(_), _]: (macro:Int); // pass dummy 0 - case [LibType(t, _), _]: TPath(t.implTypePath); - case [LibIntEnum(t, _), _]: TPath(t.implTypePath); - case [OutPointer(LibType(t, _)), _]: TPath(t.implTypePath); - case [Nested(LibType(t, _)), _]: TPath(t.implTypePath); - case [Alloc(LibType(t, _)), _]: TPath(t.implTypePath); - case [NoSize(t), _]: toComplexType(t); - case [SameSizeAs(t, _), _]: toComplexType(t); - case [SizeOf(_), _]: (macro:Int); - case [SizeOfReturn, _]: (macro:Int); - case [SizeOfField(_), _]: (macro:Int); - case [NativeHl(ct, _, _), _]: ct; - case [Unsupported(_), _]: (macro:Int); // pass dummy 0 - case _: throw "!"; - }); - } - - public static function toClosureDataUse(t:FFIType, prefix:Array):Array> { - return (switch (t) { - case ClosureDataUse: [prefix.copy()]; - case LibType(t, _): - t.ffiVariables.map(f -> toClosureDataUse(f.type, prefix.concat([f.name]))).flatten(); - case _: []; - }); - } - - static function defineArrayType(inner:FFIType):Int { - var idx = -1; - for (i in 0...Ammer.ctx.arrayTypes.length) { - if (equal(Ammer.ctx.arrayTypes[i].ffi, inner)) { - idx = i; - break; - } - } - if (idx == -1) { - idx = Ammer.ctx.arrayTypes.length; - var at:ComplexType = TPath({name: 'AmmerArray_$idx', pack: ["ammer", "externs"]}); - var t = toComplexType(inner); - var impl = macro class AmmerArray { - @:ammer.c.return("(%RET_TYPE%)calloc(sizeof(%RET_ELEM_TYPE%), arg_0)") - public static function alloc(size:Int):$at; - @:ammer.c.return("arg_0[arg_1]") - public function get(_:ammer.ffi.This, idx:Int):$t; - @:ammer.c.return("arg_0[arg_1] = arg_2") - public function set(_:ammer.ffi.This, idx:Int, val:$t):Void; - }; - switch (Ammer.config.platform) { - case Hl: - impl.fields = impl.fields.concat((macro class { - @:ammer.c.return("(%RET_TYPE%)(((void **)arg_0)[2])") - // "(%RET_TYPE%)hl_aptr(arg_0, %RET_ELEM_TYPE%)") - public static function ofNativeInt(arr:ammer.ffi.NativeHl, "_OBJ(_I32 _BYTES _I32)", "vobj *">):$at; - }).fields); - case _: - } - impl.pack = ["ammer", "externs"]; - impl.name = 'AmmerArray_$idx'; - impl.kind = TDClass({ - name: "Pointer", - pack: ["ammer"], - params: [ - TPExpr(macro $v{'wt_array_${idx}_${Ammer.ctx.index}'}), - TPType(Ammer.ctx.implComplexType), - ], - }, []); - Ammer.defineType(impl); - var implTypePath = {name: impl.name, pack: impl.pack}; - Ammer.ctx.arrayTypes.push({ - index: idx, - ffi: inner, - implTypePath: implTypePath, - wrapperTypePath: { - name: "ArrayWrapper", - pack: ["ammer", "conv"], - params: [ - TPType(t), - TPType(TPath(implTypePath)), - ], - }, - }); - Context.resolveType(TPath({name: impl.name, pack: impl.pack}), herePos); - } - return idx; - } - - /** - Maps a Haxe type (including the special `ammer.ffi.*` types) to its FFI - type equivalent. - **/ - // argNames:Array, pos:Position, arg:Null, ?annotated:Bool = false - public static function toFFITypeResolved(resolved:Type, ctx:FFIContext):FFIType { - var ret = null; - function c(type:ComplexType, ffi:FFIType):Bool { - if (type == null) { - return false; - } - if (Context.unify(Context.resolveType(type, herePos), resolved)) { - ret = ffi; - return true; - } - return false; - } - c((macro:Void), Void) - || c((macro:Bool), Bool) // order matters for Float and Int! - || c((macro:Float), Float(Float64)) - // TODO: disallowing Single completely for Lua is not an ideal solution - || c(Ammer.config.platform.match(Hl | Cpp) ? (macro:Single) : null, Float(Float32)) - || c((macro:Int), Integer(Signed32)) // also matches UInt - || c((macro:ammer.ffi.Int8), Integer(Signed8)) - || c((macro:ammer.ffi.Int16), Integer(Signed16)) - || c((macro:ammer.ffi.Int32), Integer(Signed32)) - || c((macro:ammer.ffi.Int64), Integer(Signed64)) - || c((macro:ammer.ffi.UInt8), Integer(Unsigned8)) - || c((macro:ammer.ffi.UInt16), Integer(Unsigned16)) - || c((macro:ammer.ffi.UInt32), Integer(Unsigned32)) - || c((macro:ammer.ffi.UInt64), Integer(Unsigned64)) - || c((macro:ammer.ffi.Float32), Float(Float32)) - || c((macro:ammer.ffi.Float64), Float(Float64)) - || c((macro:String), String) - || c((macro:haxe.io.Bytes), Bytes) - || c((macro:ammer.ffi.SizeOfReturn), SizeOfReturn) - || c((macro:ammer.ffi.This), This) - || { - ret = (switch [resolved, ctx] { - // context dependent (function signatures) - case [ - TInst(_.get() => {name: "SameSizeAs", pack: ["ammer", "ffi"]}, [inner, TInst(_.get() => {kind: KExpr({expr: EConst(CString(argName))})}, [])]), - {type: FunctionArgument(_, _, {argNames: argNames}) | FunctionReturn({argNames: argNames})} - ]: - SameSizeAs(toFFITypeResolved(inner, ctx), argNames.indexOf(argName)); - case [ - TInst(_.get() => {name: "SizeOf", pack: ["ammer", "ffi"]}, [TInst(_.get() => {kind: KExpr({expr: EConst(CString(argName))})}, [])]), - {type: FunctionArgument(_, _, {argNames: argNames}) | FunctionReturn({argNames: argNames})} - ]: - SizeOf(argNames.indexOf(argName)); - case [ - TInst(_.get() => {name: "ClosureData", pack: ["ammer", "ffi"]}, [TInst(_.get() => {kind: KExpr({expr: EConst(CString(argName))})}, [])]), - {type: FunctionArgument(_, _, {argNames: argNames}) | FunctionReturn({argNames: argNames})} - ]: - ClosureData(argNames.indexOf(argName)); - // context dependent (struct members) - case [ - TInst(_.get() => {name: "SizeOf", pack: ["ammer", "ffi"]}, [TInst(_.get() => {kind: KExpr({expr: EConst(CString(fieldName))})}, [])]), - {type: LibType} - ]: - SizeOfField(fieldName); - // context independent - case [TInst(_.get() => {name: "NativeHl", pack: ["ammer", "ffi"]}, [ - inner, - TInst(_.get() => {kind: KExpr({expr: EConst(CString(ffiName))})}, []), - TInst(_.get() => {kind: KExpr({expr: EConst(CString(cName))})}, []), - ]), _]: - NativeHl(Context.toComplexType(inner), ffiName, cName); - case [TInst(_.get() => {name: "ArrayDynamic", pack: ["ammer", "ffi"]}, [inner]), _]: - var inner = toFFITypeResolved(inner, ctx); - var idx = defineArrayType(inner); - ArrayDynamic(idx, inner); - case [TInst(_.get() => {name: "ArrayFixed", pack: ["ammer", "ffi"]}, [inner, TInst(_.get() => {kind: KExpr({expr: EConst(CInt(Std.parseInt(_) => size))})}, [])]), _]: - var inner = toFFITypeResolved(inner, ctx); - var idx = defineArrayType(inner); - ArrayFixed(idx, inner, size); - case [TInst(_.get() => {name: "NoSize", pack: ["ammer", "ffi"]}, [inner]), _]: - NoSize(toFFITypeResolved(inner, ctx)); - case [ - TInst(_.get() => {name: "Closure", pack: ["ammer", "ffi"]}, [ - Context.follow(_) => TFun(args, ret), - TInst(_.get() => {kind: KExpr({expr: EConst(CString(mode = ("none" | "once" | "forever")))})}, []) - ]), - _ - ]: - var ffi = toFFITypeFunction(args.map(a -> {name: a.name, type: Context.toComplexType(a.t)}), Context.toComplexType(ret), ctx.pos, ctx.typeThis); - // check if the closure type exists already - var idx = -1; - for (i in 0...Ammer.ctx.closureTypes.length) { - var closureType = Ammer.ctx.closureTypes[i]; - if (ffi.args.length != closureType.args.length) - continue; - var argsMatch = true; - for (i in 0...ffi.args.length) { - if (!equal(ffi.args[i], closureType.args[i])) { - argsMatch = false; - break; - } - } - if (!argsMatch) - continue; - if (!equal(ffi.ret, closureType.ret)) - continue; - idx = i; - break; - } - if (idx == -1) { - var data = ffi.args.mapi((i, a) -> toClosureDataUse(a, ['arg_$i'])).flatten(); - if (data.length != 1) { - trace(args, ret, data); - Context.fatalError('closure type must have exactly one occurrence of ClosureDataUse', ctx.pos); - } - idx = Ammer.ctx.closureTypes.length; - Ammer.ctx.closureTypes.push({ - index: idx, - args: ffi.args, - ret: ffi.ret, - dataAccess: data[0] - }); - } - Closure(idx, ffi.args, ffi.ret, switch (mode) { - case "none": None; - case "once": Once; - case "forever": Forever; - case _: throw "!"; - }); - case [TInst(_.get() => {name: "ClosureDataUse", pack: ["ammer", "ffi"]}, []), _]: - ClosureDataUse; - case [TInst(_.get() => {name: "OutPointer", pack: ["ammer", "ffi"]}, [inner]), _]: - var inner = toFFITypeResolved(inner, ctx); - if (!inner.match(LibType(_, _))) - Context.fatalError("OutPointer must wrap a pointer type", ctx.pos); - OutPointer(inner); - case [TInst(_.get() => {name: "Nested", pack: ["ammer", "ffi"]}, [inner]), _]: - var inner = toFFITypeResolved(inner, ctx); - if (!inner.match(LibType(_, _) | This)) - Context.fatalError("Nested must wrap a pointer type", ctx.pos); - Nested(inner); - case [TInst(_.get() => {name: "Alloc", pack: ["ammer", "ffi"]}, [inner]), _]: - var inner = toFFITypeResolved(inner, ctx); - if (!inner.match(LibType(_, _))) - Context.fatalError("Alloc must wrap a pointer type", ctx.pos); - Alloc(inner); - case [TInst(_.get() => {name: "Unsupported", pack: ["ammer", "ffi"]}, [ - TInst(_.get() => {kind: KExpr({expr: EConst(CString(cName))})}, []), - ]), _]: - Unsupported(cName); - case [TInst(_.get() => type, []), _] if (type.superClass != null): - switch (type.superClass.t.get()) { - case {name: "PointerProcessed", module: "ammer.Pointer"}: - var id = Utils.typeId(type); - if (!Ammer.typeMap.exists(id)) - Ammer.delayedBuildType(id, type, Pointer(true)); - LibType(Ammer.typeMap[id], false); - case {name: "PointerNoStarProcessed", module: "ammer.PointerNoStar"}: - var id = Utils.typeId(type); - if (!Ammer.typeMap.exists(id)) - Ammer.delayedBuildType(id, type, Pointer(false)); - LibType(Ammer.typeMap[id], false); - case {name: "IntEnumProcessed", module: "ammer.IntEnum"}: - var id = Utils.typeId(type); - if (!Ammer.typeMap.exists(id)) - Ammer.delayedBuildType(id, type, IntEnum); - LibIntEnum(Ammer.typeMap[id], false); - case {name: "Sublibrary", pack: ["ammer"]}: - var id = Utils.typeId(type); - if (!Ammer.typeMap.exists(id)) - Ammer.delayedBuildType(id, type, Sublibrary); - LibSub(Ammer.typeMap[id]); - case _: - null; - } - case [TType(_, []), _]: - // TODO: get rid of this case; - // handle resolution failure errors in a method wrapping this - // so that toFFIType can be properly used in Ammer.registerType() - // even if it returns an "invalid" type - // also get rid of LibSub - LibSub(null); - case _: - null; - }); - true; - }; - - // TODO: validate annotations if final return - - if (ret == null) { - Context.fatalError(switch (ctx.type) { - case None: "invalid FFI type"; - case FunctionReturn(_): "invalid FFI type for return"; - case FunctionArgument(arg, _, _): 'invalid FFI type for argument $arg'; - case Function(_): "invalid FFI type in function"; - case LibType: "invalid FFI type in library data type"; - }, ctx.pos); - } - - return ret; - } - - /** - Resolves a Haxe syntactic type at the given position, then maps it to its - FFI type equivalent. - **/ - public static function toFFIType(t:ComplexType, ctx:FFIContext):FFIType { - return toFFITypeResolved(Context.resolveType(t, ctx.pos), ctx); // argNames, pos, arg); - } - - public static function toFFITypeFunction( - args:Array<{name:String, type:ComplexType}>, - ret:ComplexType, - pos:Position, - ?typeThis:String - ):{args:Array, ret:FFIType} { - var argNames:Array = args.map(a -> a.name); - var needsSizes = []; - var hasSizes = []; - var sizeArgs = new Map(); - var ffiCtxSub:FFIContextFunction = { - args: args, - ret: ret, - argNames: argNames, - needsSizes: needsSizes, - hasSizes: hasSizes, - }; - var ffiCtx:FFIContext = { - pos: pos, - parent: null, - typeThis: typeThis, - type: Function(ffiCtxSub) - }; - - var ffiThis:FFIType = null; - if (typeThis != null) { - ffiThis = (switch (Ammer.typeMap[typeThis].kind) { - case IntEnum: FFIType.LibIntEnum(Ammer.typeMap[typeThis], true); - case _: FFIType.LibType(Ammer.typeMap[typeThis], true); - }); - } - - // map arguments - var ffiArgs = [ - for (i in 0...args.length) { - var arg = args[i]; - if (arg.type == null) - Context.fatalError('type required for argument ${arg.name}', pos); - var type = FFITools.toFFIType(arg.type, { - pos: pos, - parent: ffiCtx, - typeThis: typeThis, - type: FunctionArgument(arg.name, i, ffiCtxSub) - }); - if (!type.isArgumentType()) - Context.fatalError('FFI type not allowed for argument ${arg.name}', pos); - if (type.needsSize()) { - // a size specification would be ambiguous - var prev = argNames.indexOf(arg.name); - if (prev != -1 && prev < i) - Context.fatalError('argument ${arg.name} should have a unique identifier', pos); - needsSizes.push(i); - } - switch (type) { - case NoSize(_): - if (hasSizes.indexOf(i) != -1) - Context.fatalError('size of ${arg.name} is already specified in a prior argument', pos); - hasSizes.push(i); - case SizeOf(j): - if (hasSizes.indexOf(j) != -1) - Context.fatalError('size of ${args[j].name} is already specified in a prior argument', pos); - hasSizes.push(j); - sizeArgs[j] = Utils.arg(i); - case SizeOfReturn: - if (hasSizes.indexOf(-1) != -1) - Context.fatalError('size of return is already specified in a prior argument', pos); - hasSizes.push(-1); - sizeArgs[-1] = macro _retSize; - case _: - } - // resolve ammer.ffi.This - if (type.match(Nested(This))) { - if (typeThis == null) - Context.fatalError('ammer.ffi.This can only be used in library type methods', pos); - FFIType.Nested(ffiThis); - } else if (type == This) { - if (typeThis == null) - Context.fatalError('ammer.ffi.This can only be used in library type methods', pos); - ffiThis; - } else - type; - } - ]; - - // map return type - if (ret == null) - Context.fatalError('return type required', pos); - var ffiRet = FFITools.toFFIType(ret, { - pos: pos, - parent: ffiCtx, - typeThis: typeThis, - type: FunctionReturn(ffiCtxSub) - }); - if (!ffiRet.isReturnType()) - Context.fatalError('FFI type not allowed for return', pos); - if (ffiRet.needsSize()) - needsSizes.push(-1); - if (ffiRet == This) { - if (typeThis == null) - Context.fatalError('ammer.ffi.This can only be used in library type methods', pos); - // TODO: does This as return type make sense? - ffiRet = ffiThis; - } - - // ensure all size requirements are satisfied - for (need in needsSizes) { - if (hasSizes.indexOf(need) == -1) - if (need == -1) - Context.fatalError('size specification required for return', pos); - else - Context.fatalError('size specification required for argument ${args[need].name}', pos); - hasSizes.remove(need); - } - // if (hasSizes.length > 0) - // Context.fatalError('superfluous sizes specified', pos); - - // map size requirements to WithSize - for (i in 0...args.length) { - if (ffiArgs[i].needsSize()) { - ffiArgs[i] = WithSize(sizeArgs[i], ffiArgs[i]); - } - } - if (ffiRet.needsSize()) { - ffiRet = WithSize(sizeArgs[-1], ffiRet); - } - - return {args: ffiArgs, ret: ffiRet}; - } - - public static function toFFITypeFunctionF(field:Field, f:Function, ?typeThis:String):{args:Array, ret:FFIType} { - return toFFITypeFunction(f.args, f.ret, field.pos, typeThis); - } - - public static function toFFITypeVariable(field:Field, ct:ComplexType):FFIType { - return FFITools.toFFIType(ct, { - pos: field.pos, - parent: null, - type: None - }); - } - - public static function normalise(t:FFIType):FFIType { - return (switch (t) { - case This: throw "!"; - // TODO: eventually support size_t/64-bit - case SizeOf(arg): Derived(macro ($e{Utils.arg(arg)}.length:Int), Integer(Signed32)); - case _: t; - }); - } - - public static function equal(a:FFIType, b:FFIType):Bool { - return (switch [a, b] { - case [Void, Void]: true; - case [Bool, Bool]: true; - case [Integer(a), Integer(b)]: a == b; - case [Float(a), Float(b)]: a == b; - case [Bytes, Bytes]: true; - case [String, String]: true; - case [This, This]: true; - case [LibType(a, at), LibType(b, bt)]: a == b && at == bt; - case [LibIntEnum(a, at), LibIntEnum(b, bt)]: a == b && at == bt; - case [Derived(_, a), Derived(_, b)]: equal(a, b); - case [Closure(a, _, _, am), Closure(b, _, _, bm)]: am == bm && a == b; - case [ClosureDataUse, ClosureDataUse]: true; - case [ClosureData(a), ClosureData(b)]: a == b; - case [NoSize(a), NoSize(b)]: equal(a, b); - case [SameSizeAs(a, ai), SameSizeAs(b, bi)]: ai == bi && equal(a, b); - case [SizeOf(a), SizeOf(b)]: a == b; - case [SizeOfReturn, SizeOfReturn]: true; - case [SizeOfField(a), SizeOfField(b)]: a == b; - case _: false; - }); - } -} - -typedef FFIContext = { - pos:Position, - parent:FFIContext, - ?typeThis:String, - type:FFIContextType, -}; - -enum FFIContextType { - None; - FunctionReturn(ctx:FFIContextFunction); - FunctionArgument(arg:String, argIdx:Int, ctx:FFIContextFunction); - Function(ctx:FFIContextFunction); - LibType; -} - -typedef FFIContextFunction = { - args:Array<{name:String, type:ComplexType}>, - ret:ComplexType, - argNames:Array, - // -1 in the needsSizes and hasSizes arrays signifies the return - needsSizes:Array, - hasSizes:Array, -}; diff --git a/src/ammer/FFIType.hx b/src/ammer/FFIType.hx deleted file mode 100644 index 3bd47da..0000000 --- a/src/ammer/FFIType.hx +++ /dev/null @@ -1,71 +0,0 @@ -package ammer; - -import haxe.macro.Expr; - -@:using(ammer.FFITools) -enum FFIType { - Void; - - // numeric types - Bool; // == Integer(Bool) ? - Integer(kind:IntegerKind); - Float(kind:FloatKind); - - // pointer types - Bytes; - String; - ArrayDynamic(typeIdx:Int, type:FFIType); - ArrayFixed(typeIdx:Int, type:FFIType, size:Int); // TODO: change index to instance, same in closure - - // library types - This; - LibType(_:AmmerTypeContext, argThis:Bool); - LibIntEnum(_:AmmerTypeContext, argThis:Bool); - LibSub(_:AmmerTypeContext); - OutPointer(_:FFIType); - Nested(_:FFIType); - Alloc(_:FFIType); - - // special types - Derived(e:Expr, t:FFIType); - WithSize(e:Expr, t:FFIType); - - Closure(typeIdx:Int, args:Array, ret:FFIType, mode:RootMode); - ClosureDataUse; - ClosureData(arg:Int); - - NoSize(t:FFIType); - SameSizeAs(t:FFIType, arg:Int); - SizeOf(arg:Int); - SizeOfReturn; - SizeOfField(name:String); - - Unsupported(cName:String); - - // target specific - NativeHl(t:ComplexType, ffiName:String, cName:String); -} - -enum IntegerKind { - Signed8; - Signed16; - Signed32; - Signed64; - Unsigned8; - Unsigned16; - Unsigned32; - Unsigned64; - //Custom(cName:String, signed:Bool, bits:Int); -} - -enum FloatKind { - Float32; - Float64; - //Custom(cName:String, bits:Int); -} - -enum RootMode { - None; - Forever; - Once; -} diff --git a/src/ammer/FFIVariable.hx b/src/ammer/FFIVariable.hx deleted file mode 100644 index e9769ed..0000000 --- a/src/ammer/FFIVariable.hx +++ /dev/null @@ -1,13 +0,0 @@ -package ammer; - -import haxe.macro.Expr; - -typedef FFIVariable = { - name:String, - native:String, - type:FFIType, - complexType:ComplexType, - field:Field, - getField:Field, - setField:Field -}; diff --git a/src/ammer/IntEnum.hx b/src/ammer/IntEnum.hx deleted file mode 100644 index 02d1640..0000000 --- a/src/ammer/IntEnum.hx +++ /dev/null @@ -1,9 +0,0 @@ -package ammer; - -@:genericBuild(ammer.IntEnum.initType()) -class IntEnum { - public static macro function initType(); -} - -@:autoBuild(ammer.Ammer.buildType()) -class IntEnumProcessed {} diff --git a/src/ammer/IntEnum.macro.hx b/src/ammer/IntEnum.macro.hx deleted file mode 100644 index f1013db..0000000 --- a/src/ammer/IntEnum.macro.hx +++ /dev/null @@ -1,21 +0,0 @@ -package ammer; - -import haxe.macro.Context; -import haxe.macro.Expr; -import haxe.macro.TypeTools; - -class IntEnum { - public static function initType():ComplexType { - switch (Context.getLocalType()) { - case TInst(_, [TInst(_.get() => {kind: KExpr(e = {expr: EConst(CString(_))})}, []), lib]): - return TPath({ - name: "IntEnum", - pack: ["ammer"], - sub: "IntEnumProcessed", - params: [TPExpr(e), TPType(TypeTools.toComplexType(lib))] - }); - case _: - throw Context.fatalError("ammer.IntEnum first type parameter should be a string", Context.currentPos()); - } - } -} diff --git a/src/ammer/Lib.hx b/src/ammer/Lib.hx new file mode 100644 index 0000000..3d5c7aa --- /dev/null +++ b/src/ammer/Lib.hx @@ -0,0 +1,23 @@ +// ammer-bake: ammer Lib true +package ammer; + +class Lib { + // struct methods + public static macro function allocStruct(cls:Class, ?initVals:{}):T; + public static macro function nullPtrStruct(cls:Class):T; + + // box methods + public static macro function allocBox(cls:Class, ?initVal:T):ammer.ffi.Box; + public static macro function nullPtrBox(cls:Class):ammer.ffi.Box; + + // array methods + public static macro function allocArray(cls:Class, size:Int, ?initVal:T):ammer.ffi.Array; + public static macro function nullPtrArray(cls:Class):ammer.ffi.Array; + + public static macro function vecToArrayCopy(vec:haxe.ds.Vector):ammer.ffi.Array; + public static macro function vecToArrayRef(vec:haxe.ds.Vector):ammer.ffi.ArrayRef; + public static macro function vecToArrayRefForce(vec:haxe.ds.Vector):ammer.ffi.ArrayRef; + + // Haxe ref methods + public static macro function createHaxeRef(cls:Class, e:T):ammer.ffi.Haxe; +} diff --git a/src/ammer/Lib.macro.baked.hx b/src/ammer/Lib.macro.baked.hx new file mode 100644 index 0000000..b76fcfa --- /dev/null +++ b/src/ammer/Lib.macro.baked.hx @@ -0,0 +1,22 @@ +// ammer-bake: ammer Lib.macro macro +package ammer; + +import haxe.macro.Context; +import haxe.macro.Context.currentPos; +import haxe.macro.Context.fatalError as fail; +import haxe.macro.Context.resolveType; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.TypeTools; +import ammer.internal.*; +import ammer.internal.v1.AmmerBaked.mergedInfo as info; + +using Lambda; + +class Lib { + static function withPos(pos:Position, f:()->T):T { + return f(); + } +// ammer-include: internal/Utils.hx lib-baked +// ammer-include: Lib.macro.hx lib-baked +} diff --git a/src/ammer/Lib.macro.hx b/src/ammer/Lib.macro.hx new file mode 100644 index 0000000..1da396a --- /dev/null +++ b/src/ammer/Lib.macro.hx @@ -0,0 +1,251 @@ +package ammer; + +import haxe.macro.Context; +import haxe.macro.Context.fatalError as fail; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.TypeTools; +import ammer.internal.*; +import ammer.internal.Ammer.mergedInfo as info; +import ammer.internal.Utils.access; +import ammer.internal.Utils.accessTp; +import ammer.internal.Utils.complexTypeExpr; +import ammer.internal.Utils.expectTypePath; +import ammer.internal.Utils.isNull; +import ammer.internal.Utils.triggerTyping; +import ammer.internal.Utils.typeId; +import ammer.internal.Utils.typeId2; + +using Lambda; + +class Lib { + static function withPos(pos:Position, f:()->T):T { + return Reporting.withPosition(pos, f); + } + + static function resolveType(ct:ComplexType, pos:Position):Type { + return Reporting.withPosition(pos, () -> Reporting.resolveType(ct, pos)); + } + + // These methods are inserted into `Lib.macro.hx` when baking a library. + // This avoids code duplication/synchronisation issues. Importantly, the code + // is just string-pasted, so it is important that the `import`s that are + // in `Lib.macro.baked.hx` are sufficient for the code to work. + +// ammer-fragment-begin: lib-baked + public static function allocStruct(cls:Expr, ?initVals:Expr):Expr { + var ct = complexTypeExpr(cls); + var clsType = withPos(cls.pos, () -> triggerTyping(ct)); + clsType != null || throw fail("invalid type in allocStruct call", cls.pos); + var struct = info.structs[typeId(clsType)]; + struct != null || throw fail("not a struct type in allocStruct call", cls.pos); + struct.gen.alloc != null || throw fail("struct type was not marked with @:ammer.alloc", cls.pos); + var alloc = struct.gen.alloc; + if (isNull(initVals)) { + return macro @:privateAccess $p{access(clsType)}.$alloc(); + } + var assigns = (switch (initVals) { + case {expr: EObjectDecl(fields)}: + [ for (field in fields) macro @:pos(field.expr.pos) $p{["_allocated", field.field]} = $e{field.expr} ]; + case _: throw fail("expected initial values (e.g. {a: 1, b: 2, ...}) as second argument of allocStruct call", initVals.pos); + }); + return macro { + var _allocated = @:privateAccess $p{access(clsType)}.$alloc(); + $b{assigns}; + _allocated; + }; + } + + public static function nullPtrStruct(cls:Expr):Expr { + var ct = complexTypeExpr(cls); + var clsType = withPos(cls.pos, () -> triggerTyping(ct)); + clsType != null || throw fail("invalid type in nullPtrStruct call", cls.pos); + var typeId = typeId(clsType); + var struct = info.structs[typeId]; + //var opaque = info.opaques[typeId]; + //(struct != null || opaque != null) || throw fail("not a struct type or opaque type in nullPtrStruct call", cls.pos); + struct != null || throw fail("not a struct type in nullPtrStruct call", cls.pos); + struct.gen.nullPtr != null || throw fail("struct type was not marked with @:ammer.alloc", cls.pos); + var nullPtr = struct.gen.nullPtr; + return macro @:privateAccess $p{access(clsType)}.$nullPtr(); + } + + public static function allocBox(cls:Expr, ?initVal:Expr):Expr { + var elCt = complexTypeExpr(cls); + #if ammer + var elType = resolveType(elCt, cls.pos); + resolveType((macro : ammer.ffi.Box<$elCt>), cls.pos); + var box = info.boxes.byElementTypeId[typeId2(elType)]; + #else + // if baked, ammer.ffi.Box does not exist, only its monomorphisations + var box = info.boxes.byElementTypeId[typeIdCt(elCt)]; + #end + box != null || throw fail("not a known box type in allocBox call", cls.pos); + var tp = expectTypePath(box.boxCt); + if (isNull(initVal)) { + return macro @:privateAccess new $tp($e{box.alloc}); + } + return macro { + var _allocated = @:privateAccess new $tp($e{box.alloc}); + _allocated.set($initVal); + _allocated; + }; + } + + public static function nullPtrBox(cls:Expr):Expr { + var elCt = complexTypeExpr(cls); + #if ammer + var elType = resolveType(elCt, cls.pos); + resolveType((macro : ammer.ffi.Box<$elCt>), cls.pos); + var box = info.boxes.byElementTypeId[typeId2(elType)]; + #else + // if baked, ammer.ffi.Box does not exist, only its monomorphisations + var box = info.boxes.byElementTypeId[typeIdCt(elCt)]; + #end + box != null || throw fail("not a known box type in nullPtrBox call", cls.pos); + var tp = expectTypePath(box.boxCt); + return macro @:privateAccess $p{accessTp(tp)}.nullPtr(); + } + + public static function allocArray(cls:Expr, size:Expr, ?initVal:Expr):Expr { + var elCt = complexTypeExpr(cls); + #if ammer + var elType = resolveType(elCt, cls.pos); + resolveType((macro : ammer.ffi.Array<$elCt>), cls.pos); + var array = info.arrays.byElementTypeId[typeId2(elType)]; + #else + // if baked, ammer.ffi.Array does not exist, only its monomorphisations + var array = info.arrays.byElementTypeId[typeIdCt(elCt)]; + #end + array != null || throw fail("not a known array type in allocArray call", cls.pos); + var tp = expectTypePath(array.arrayCt); + if (isNull(initVal)) { + return macro { + var _size = $size; + @:privateAccess new $tp($e{array.alloc}); + } + } + return macro { + var _size = $size; + var _val = $initVal; + var _allocated = @:privateAccess new $tp($e{array.alloc}); + for (i in 0..._size) { + _allocated.set(i, _val); + } + _allocated; + }; + } + + public static function nullPtrArray(cls:Expr):Expr { + var elCt = complexTypeExpr(cls); + #if ammer + var elType = resolveType(elCt, cls.pos); + resolveType((macro : ammer.ffi.Array<$elCt>), cls.pos); + var array = info.arrays.byElementTypeId[typeId2(elType)]; + #else + // if baked, ammer.ffi.Array does not exist, only its monomorphisations + var array = info.arrays.byElementTypeId[typeIdCt(elCt)]; + #end + array != null || throw fail("not a known array type in allocArray call", cls.pos); + var tp = expectTypePath(array.arrayCt); + return macro @:privateAccess $p{accessTp(tp)}.nullPtr(); + } + + public static function vecToArrayCopy(vec:Expr):Expr { + var typed = withPos(vec.pos, () -> Context.typeExpr(vec)); + var elType = (switch (typed.t) { + case TInst(typeId(_.get()) => "haxe.ds.Vector.Vector", [el]): el; + case TAbstract(typeId(_.get()) => "haxe.ds.Vector.Vector", [el]): el; + case _: throw fail("argument should be a haxe.ds.Vector", vec.pos); + }); + var elCt = TypeTools.toComplexType(elType); + var stored = Context.storeTypedExpr(typed); + #if ammer + // if baked, ammer.ffi.Array does not exist, only its monomorphisations + resolveType((macro : ammer.ffi.Array<$elCt>), vec.pos); + #end + var array = info.arrays.byElementTypeId[typeId2(elType)]; + array != null || throw fail("not a known array type in vecToArrayCopy call", vec.pos); + var tp = expectTypePath(array.arrayCt); + return macro { + var _vec = $vec; + @:privateAccess new $tp($e{array.fromHaxeCopy}); + }; + } + + public static function vecToArrayRef(vec:Expr):Expr { + var typed = withPos(vec.pos, () -> Context.typeExpr(vec)); + var elType = (switch (typed.t) { + case TInst(typeId(_.get()) => "haxe.ds.Vector.Vector", [el]): el; + case TAbstract(typeId(_.get()) => "haxe.ds.Vector.Vector", [el]): el; + case _: throw fail("argument should be a haxe.ds.Vector", vec.pos); + }); + var elCt = TypeTools.toComplexType(elType); + var stored = Context.storeTypedExpr(typed); + #if ammer + // if baked, ammer.ffi.Array does not exist, only its monomorphisations + resolveType((macro : ammer.ffi.Array<$elCt>), vec.pos); + #end + var array = info.arrays.byElementTypeId[typeId2(elType)]; + array != null || throw fail("not a known array type in vecToArrayRef call", vec.pos); + var tp = expectTypePath(array.arrayRefCt); + if (array.fromHaxeRef != null) { + // if references are supported, create an array ref + return macro { + var _vec = $vec; + @:privateAccess new $tp($e{array.fromHaxeRef}, _vec); + }; + } + // if not, create a fake ref with the same API + return macro { + var _vec = $vec; + @:privateAccess new $tp($e{array.fromHaxeCopy}, _vec); + }; + } + + public static function vecToArrayRefForce(vec:Expr):Expr { + var typed = withPos(vec.pos, () -> Context.typeExpr(vec)); + var elType = (switch (typed.t) { + case TInst(typeId(_.get()) => "haxe.ds.Vector.Vector", [el]): el; + case TAbstract(typeId(_.get()) => "haxe.ds.Vector.Vector", [el]): el; + case _: throw fail("argument should be a haxe.ds.Vector", vec.pos); + }); + var elCt = TypeTools.toComplexType(elType); + var stored = Context.storeTypedExpr(typed); + #if ammer + // if baked, ammer.ffi.Array does not exist, only its monomorphisations + resolveType((macro : ammer.ffi.Array<$elCt>), vec.pos); + #end + var array = info.arrays.byElementTypeId[typeId2(elType)]; + array != null || throw fail("not a known array type in vecToArrayRefForce call", vec.pos); + var tp = expectTypePath(array.arrayRefCt); + array.fromHaxeRef != null || throw fail("platform does not support non-copy references to Vector", vec.pos); + return macro { + var _vec = $vec; + @:privateAccess new $tp($e{array.fromHaxeRef}, _vec); + }; + } + + public static function createHaxeRef(cls:Expr, e:Expr):Expr { + var elCt = complexTypeExpr(cls); + var elType = resolveType(elCt, cls.pos); + #if ammer + // if baked, ammer.ffi.Haxe does not exist, only its monomorphisations + resolveType((macro : ammer.ffi.Haxe<$elCt>), cls.pos); + #end + var elId = typeId2(elType); + if (info.haxeRefs.byElementTypeId.exists(elId)) { + var haxeRef = info.haxeRefs.byElementTypeId[elId]; + return macro { + var _hxval = $e; + $e{haxeRef.create}; + }; + } + info.haxeRefs.byElementTypeId.exists(".Any.Any") || throw 0; + return macro { + var _hxval = $e; + new ammer.internal.LibTypes.HaxeAnyRef<$elCt>($e{info.haxeRefs.byElementTypeId[".Any.Any"].create}); + }; + } +// ammer-fragment-end: lib-baked +} diff --git a/src/ammer/Library.hx b/src/ammer/Library.hx deleted file mode 100644 index 18731ed..0000000 --- a/src/ammer/Library.hx +++ /dev/null @@ -1,9 +0,0 @@ -package ammer; - -@:genericBuild(ammer.Library.initLibrary()) -class Library { - public static macro function initLibrary(); -} - -@:autoBuild(ammer.Ammer.build()) -class LibraryProcessed {} diff --git a/src/ammer/Library.macro.hx b/src/ammer/Library.macro.hx deleted file mode 100644 index b6fa107..0000000 --- a/src/ammer/Library.macro.hx +++ /dev/null @@ -1,20 +0,0 @@ -package ammer; - -import haxe.macro.Context; -import haxe.macro.Expr; - -class Library { - public static function initLibrary():ComplexType { - switch (Context.getLocalType()) { - case TInst(_, [TInst(_.get() => {kind: KExpr(e)}, [])]): - return TPath({ - name: "Library", - pack: ["ammer"], - sub: "LibraryProcessed", - params: [TPExpr(e)] - }); - case _: - throw Context.fatalError("ammer.Library type parameter should be a string", Context.currentPos()); - } - } -} diff --git a/src/ammer/LineBuf.hx b/src/ammer/LineBuf.hx deleted file mode 100644 index 940e689..0000000 --- a/src/ammer/LineBuf.hx +++ /dev/null @@ -1,35 +0,0 @@ -package ammer; - -class LineBuf { - var currentIndent:String = ""; - var buf = new StringBuf(); - var tmpCounter = 0; - - public function new() {} - - public inline function ai(data:String):Void { - buf.add('$currentIndent$data'); - } - - public inline function a(data:String):Void { - buf.add(data); - } - - public inline function fresh():Int { - return tmpCounter++; - } - - public function indent(f:() -> Void, ?with:String = " "):Void { - var prev = currentIndent; - currentIndent += with; - f(); - currentIndent = prev; - } - - public function dump():String { - var ret = buf.toString(); - buf = new StringBuf(); - tmpCounter = 0; - return ret; - } -} diff --git a/src/ammer/Pointer.hx b/src/ammer/Pointer.hx deleted file mode 100644 index 5611ce6..0000000 --- a/src/ammer/Pointer.hx +++ /dev/null @@ -1,9 +0,0 @@ -package ammer; - -@:genericBuild(ammer.Pointer.initType(true)) -class Pointer { - public static macro function initType(star:Bool); -} - -@:autoBuild(ammer.Ammer.buildType()) -class PointerProcessed {} diff --git a/src/ammer/Pointer.macro.hx b/src/ammer/Pointer.macro.hx deleted file mode 100644 index 8e96ff6..0000000 --- a/src/ammer/Pointer.macro.hx +++ /dev/null @@ -1,21 +0,0 @@ -package ammer; - -import haxe.macro.Context; -import haxe.macro.Expr; -import haxe.macro.TypeTools; - -class Pointer { - public static function initType(star:Bool):ComplexType { - switch (Context.getLocalType()) { - case TInst(_, [TInst(_.get() => {kind: KExpr(e = {expr: EConst(CString(_))})}, []), lib]): - return TPath({ - name: star ? "Pointer" : "PointerNoStar", - pack: ["ammer"], - sub: star ? "PointerProcessed" : "PointerNoStarProcessed", - params: [TPExpr(e), TPType(TypeTools.toComplexType(lib))] - }); - case _: - throw Context.fatalError("ammer.Pointer first type parameter should be a string", Context.currentPos()); - } - } -} diff --git a/src/ammer/PointerNoStar.hx b/src/ammer/PointerNoStar.hx deleted file mode 100644 index f86886d..0000000 --- a/src/ammer/PointerNoStar.hx +++ /dev/null @@ -1,7 +0,0 @@ -package ammer; - -@:genericBuild(ammer.Pointer.initType(false)) -class PointerNoStar {} - -@:autoBuild(ammer.Ammer.buildType()) -class PointerNoStarProcessed {} diff --git a/src/ammer/Sublibrary.hx b/src/ammer/Sublibrary.hx deleted file mode 100644 index b45d8ce..0000000 --- a/src/ammer/Sublibrary.hx +++ /dev/null @@ -1,4 +0,0 @@ -package ammer; - -@:autoBuild(ammer.Ammer.buildType()) -class Sublibrary {} diff --git a/src/ammer/SubtypeKind.hx b/src/ammer/SubtypeKind.hx deleted file mode 100644 index 7884c61..0000000 --- a/src/ammer/SubtypeKind.hx +++ /dev/null @@ -1,7 +0,0 @@ -package ammer; - -enum SubtypeKind { - Pointer(star:Bool); - IntEnum; - Sublibrary; -} diff --git a/src/ammer/Syntax.hx b/src/ammer/Syntax.hx new file mode 100644 index 0000000..083ef67 --- /dev/null +++ b/src/ammer/Syntax.hx @@ -0,0 +1,4 @@ +package ammer; + +@:autoBuild(ammer.Syntax.build()) +interface Syntax {} diff --git a/src/ammer/Syntax.macro.hx b/src/ammer/Syntax.macro.hx new file mode 100644 index 0000000..0cd2f60 --- /dev/null +++ b/src/ammer/Syntax.macro.hx @@ -0,0 +1,90 @@ +package ammer; + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.ExprTools; + +class Syntax { + public static function build():Array { + return [ for (field in Context.getBuildFields()) { + pos: field.pos, + name: field.name, + meta: field.meta, + kind: (switch (field.kind) { + case FVar(t, e): FVar(t, e != null ? process(e) : null); + case FFun(f): FFun({ + ret: f.ret, + params: f.params, + expr: f.expr != null ? process(f.expr) : null, + args: f.args, + }); + case FProp(get, set, t, e): FProp(get, set, t, e != null ? process(e) : null); + }), + doc: field.doc, + access: field.access, + } ]; + } + + static function process(e:Expr):Expr { + return (switch (e) { + // TODO: support bytes for ref/copy as well + case macro @copy $expr: macro ammer.Lib.vecToArrayCopy($expr); + case macro @leak $expr: macro { var _ref = new ammer.ffi.Haxe($expr); _ref.incref(); _ref; } + // TODO: this @ret solution is not good; process typed fields instead? + // TODO: reduce duplication + // TODO: better tempvar names + case macro @ret $e{{expr: ECall(f, args)}}: + var block = []; + var frees = []; + args = [ for (idx => arg in args) switch (arg) { + case macro @ref $expr: + var tmp = '_syntax_arg$idx'; + block.push(macro var $tmp = ammer.Lib.vecToArrayRef($e{process(expr)})); + frees.push(macro $i{tmp}.unref()); + macro $i{tmp}.array; + case macro @copyfree $expr: + var tmp = '_syntax_arg$idx'; + block.push(macro var $tmp = ammer.Lib.vecToArrayCopy($e{process(expr)})); + frees.push(macro $i{tmp}.free()); + macro $i{tmp}; + case _: process(arg); + } ]; + if (block.length > 0) { + var call = {expr: ECall(process(f), args), pos: e.pos}; + block.push(macro var _ret = $call); + block.push(macro $b{frees}); + block.push(macro _ret); + macro $b{block}; + } else { + ExprTools.map(e, process); + } + case {expr: ECall(f, args)}: + var block = []; + var frees = []; + args = [ for (idx => arg in args) switch (arg) { + case macro @ref $expr: + var tmp = '_syntax_arg$idx'; + block.push(macro var $tmp = ammer.Lib.vecToArrayRef($e{process(expr)})); + frees.push(macro $i{tmp}.unref()); + macro $i{tmp}.array; + case macro @copyfree $expr: + var tmp = '_syntax_arg$idx'; + block.push(macro var $tmp = ammer.Lib.vecToArrayCopy($e{process(expr)})); + frees.push(macro $i{tmp}.free()); + macro $i{tmp}; + case _: process(arg); + } ]; + if (block.length > 0) { + var call = {expr: ECall(process(f), args), pos: e.pos}; + block.push(macro $call); + block.push(macro if (true) $b{frees}); + macro $b{block}; + } else { + ExprTools.map(e, process); + } + case macro @ref $expr: throw Context.error("@ref can only be used on function call arguments", e.pos); + case macro @copyfree $expr: throw Context.error("@copyfree can only be used on function call arguments", e.pos); + case _: ExprTools.map(e, process); + }); + } +} diff --git a/src/ammer/Utils.hx b/src/ammer/Utils.hx deleted file mode 100644 index 80ba28b..0000000 --- a/src/ammer/Utils.hx +++ /dev/null @@ -1,130 +0,0 @@ -package ammer; - -#if macro - -import haxe.macro.Context; -import haxe.macro.Expr; -import haxe.macro.Type; -import sys.FileSystem; -import sys.io.File; - -using StringTools; - -class Utils { - /** - Metadata allowed for the class defining a library. - **/ - public static final META_LIBRARY_CLASS = [ - "nativePrefix", - "sub" - ]; - - /** - Metadata allowed for a method of a library. - **/ - public static final META_LIBRARY_METHOD = [ - "native", - "macroCall", - "c.prereturn", - "c.return", - "cpp.constructor", - "cpp.member" - ]; - - /** - Metadata allowed for a variable of a library. - **/ - public static final META_LIBRARY_VARIABLE = [ - "native" - ]; - - /** - Metadata allowed for the class defining a library type. - **/ - public static final META_TYPE_CLASS = [ - "nativePrefix", - "struct" - ]; - - public static var posStack = []; - - public static function withPos(f:()->Void, p:Position):Void { - posStack.push(p); - f(); - posStack.pop(); - } - - public static inline function e(e:ExprDef):Expr { - return {expr: e, pos: posStack[posStack.length - 1]}; - } - - public static inline function id(s:String):Expr { - return e(EConst(CIdent(s))); - } - - public static inline function arg(n:Int):Expr { - return id('_arg$n'); - } - - /** - Iterate through the given `metas`. Any entries that do not start with - `:ammer` will be ignored. All other entries must be present in the `ids` - whitelist to be accepted. For example, `ids` must contain `"native"` to - allow the metadata `:ammer.native`. Metadata that are not in the whitelist - but start with `:ammer` will cause a compile-time error. - **/ - public static function meta(metas:Metadata, ids:Array):Array<{id:String, params:Array}> { - return [ for (meta in metas) { - if (!meta.name.startsWith(":ammer")) - continue; - var id = meta.name.substr(":ammer.".length); - if (ids.indexOf(id) == -1) - Context.fatalError('unsupported or incorrectly specified ammer metadata ${meta.name} (should be one of ${ids.join(", ")})', meta.pos); - {id: id, params: meta.params}; - } ]; - } - - /** - Save `content` into `path`. Do not rewrite the file if it already exists - and has the same content. - **/ - public static function update(path:String, content:String):Void { - if (!FileSystem.exists(path) || File.getContent(path) != content) - File.saveContent(path, content); - } - - /** - Ensure `path` exists and is a directory. Create it if it does not exist. - **/ - public static function ensureDirectory(path:String):Void { - if (!FileSystem.exists(path)) - FileSystem.createDirectory(path); - if (!FileSystem.isDirectory(path)) - Context.fatalError('$path should be a directory', Context.currentPos()); - } - - public static function access(t:{pack:Array, module:String, name:String}, ?field:String):Array { - return t.pack.concat([t.module.split(".").pop(), t.name]).concat(field != null ? [field] : []); - } - - public static function typeId(t:{pack:Array, module:String, name:String}):String { - return '${t.pack.join(".")}.${t.module.split(".").pop()}.${t.name}'; - } - - public static function typeIdField(t:{pack:Array, module:String, name:String}):String { - return '_${t.pack.join("_")}_${t.module.split(".").pop()}_${t.name}_'; - } - - /** - Extracts a `ComplexType` from an expression like `(_:SomeType)`. - **/ - public static function extractComplexType(e:Expr):ComplexType { - return (switch (e.expr) { - case EParenthesis(e): extractComplexType(e); - case ECheckType(_, ct): ct; - case _: Context.fatalError("type annotation expected", e.pos); - }); - } -} - -#end diff --git a/src/ammer/build/BuildEval.hx b/src/ammer/build/BuildEval.hx deleted file mode 100644 index 67f76c7..0000000 --- a/src/ammer/build/BuildEval.hx +++ /dev/null @@ -1,7 +0,0 @@ -package ammer.build; - -import ammer.Config.AmmerLibraryConfig; - -class BuildEval { - public static function build(config:Config, libraries:Array):Void {} -} diff --git a/src/ammer/build/BuildHl.hx b/src/ammer/build/BuildHl.hx deleted file mode 100644 index 82fd9e6..0000000 --- a/src/ammer/build/BuildHl.hx +++ /dev/null @@ -1,41 +0,0 @@ -package ammer.build; - -import ammer.Config.AmmerLibraryConfig; -import ammer.build.BuildTools.MakeCommand; - -using Lambda; - -class BuildHl { - public static function build(config:Config, libraries:Array):Void { - BuildTools.make([ - {target: "all", requires: libraries.map(l -> 'ammer_${l.name}.hdll'), command: Phony} - ].concat([ - for (library in libraries) { - var sourceExt = library.abi.fileExtension(); - [ - { - target: 'ammer_${library.name}.hdll', - requires: [BuildTools.extensions('ammer_${library.name}.hl.%OBJ%')], - command: LinkLibrary(library.abi, { - defines: ["LIBHL_EXPORTS"], - libraryPaths: (config.hl.hlLibraryPath != null ? [config.hl.hlLibraryPath] : []).concat(library.libraryPath), - libraries: [config.useMSVC ? "libhl" : "hl"].concat(library.linkName), - }) - }, - { - target: BuildTools.extensions('ammer_${library.name}.hl.%OBJ%'), - requires: ['ammer_${library.name}.hl.${sourceExt}'], - command: CompileObject(library.abi, { - includePaths: (config.hl.hlIncludePath != null ? [config.hl.hlIncludePath] : []).concat(library.includePath) - }) - } - ]; - } - ].flatten()), config.hl.build, "Makefile.hl.ammer"); - if (config.hl.build != config.hl.output) { - for (library in libraries) { - sys.io.File.copy('${config.hl.build}/ammer_${library.name}.hdll', '${config.hl.output}/ammer_${library.name}.hdll'); - } - } - } -} diff --git a/src/ammer/build/BuildLua.hx b/src/ammer/build/BuildLua.hx deleted file mode 100644 index 5082ceb..0000000 --- a/src/ammer/build/BuildLua.hx +++ /dev/null @@ -1,42 +0,0 @@ -package ammer.build; - -import ammer.Config.AmmerLibraryConfig; -import ammer.build.BuildTools.MakeCommand; - -using Lambda; - -class BuildLua { - public static function build(config:Config, libraries:Array):Void { - BuildTools.make([ - {target: "all", requires: libraries.map(l -> BuildTools.extensions('ammer_${l.name}.%DLL%')), command: Phony} - ].concat([ - for (library in libraries) { - var sourceExt = library.abi == Cpp ? "cpp" : "c"; - [ - { - target: BuildTools.extensions('ammer_${library.name}.%DLL%'), - requires: [BuildTools.extensions('ammer_${library.name}.lua.%OBJ%')], - command: LinkLibrary(library.abi, { - defines: [], - libraryPaths: (config.lua.luaLibraryPath != null ? [config.lua.luaLibraryPath] : []).concat(library.libraryPath), - libraries: library.linkName, - staticLibraries: [config.useMSVC ? "liblua" : "lua"] - }) - }, - { - target: BuildTools.extensions('ammer_${library.name}.lua.%OBJ%'), - requires: ['ammer_${library.name}.lua.${sourceExt}'], - command: CompileObject(library.abi, { - includePaths: (config.lua.luaIncludePath != null ? [config.lua.luaIncludePath] : []).concat(library.includePath) - }) - } - ]; - } - ].flatten()), config.lua.build, "Makefile.lua.ammer"); - if (config.lua.build != config.lua.output) { - for (library in libraries) { - sys.io.File.copy('${config.lua.build}/${BuildTools.extensions('ammer_${library.name}.%DLL%')}', '${config.lua.output}/${BuildTools.extensions('ammer_${library.name}.%DLL%')}'); - } - } - } -} diff --git a/src/ammer/build/BuildTools.hx b/src/ammer/build/BuildTools.hx deleted file mode 100644 index 204cf44..0000000 --- a/src/ammer/build/BuildTools.hx +++ /dev/null @@ -1,247 +0,0 @@ -package ammer.build; - -import haxe.macro.Context; -import sys.FileSystem; -import sys.io.File; - -using StringTools; - -class BuildTools { - public static function inDir(path:String, f:() -> Void):Void { - var cwd = Sys.getCwd(); - Sys.setCwd(path); - f(); - Sys.setCwd(cwd); - } - - public static function extensions(s:String):String { - return s - .replace("%OBJ%", Ammer.config.useMSVC ? "obj" : "o") - .replace("%DLL%", switch (Sys.systemName()) { - case "Windows": "dll"; - case "Mac": "dylib"; - case _: "so"; - }); - } - - public static function run(cmd:String, args:Array):Bool { - Sys.println('$cmd $args'); - return Sys.command(cmd, args) == 0; - } - - public static function make(data:Array, dir:String, name:String):Void { - // TODO: finer-grained configuration, e.g. choose compiler binary - if (Ammer.config.useMakefiles) { - var lb:LineBuf = new LineBuf(); - var phony = []; - for (e in data) { - lb.ai('${e.target}:'); - for (req in e.requires) - lb.a(' $req'); - lb.a("\n"); - lb.indent(() -> { - switch (e.command) { - case Phony: - lb.ai("@:"); - phony.push(e.target); - case Copy: - lb.ai('cp ${e.requires[0]} ${e.target}'); - case CompileObject(abi, opt): - if (Ammer.config.useMSVC) { - lb.ai('${Ammer.config.pathMSVC}cl /Fe:${e.target} /c ${e.requires.join(" ")}'); - for (path in opt.includePaths) - lb.a(' /I "$path"'); - } else { - if (abi.match(Cpp | ObjectiveCpp)) lb.ai("g++"); - else lb.ai("cc"); - lb.a(' -fPIC -o ${e.target} -c ${e.requires.join(" ")}'); - if (abi.match(Cpp | ObjectiveCpp)) - lb.a(" -std=c++11"); - for (path in opt.includePaths) - lb.a(' -I "$path"'); - } - case LinkLibrary(abi, opt): - if (Ammer.config.useMSVC) { - lb.ai('${Ammer.config.pathMSVC}cl /Fe:${e.target} /LD ${e.requires.join(" ")}'); - for (d in opt.defines) - lb.a(' /D$d'); - lb.a(' /link'); - for (path in opt.libraryPaths) - lb.a(' /LIBPATH:"$path"'); - for (lib in opt.libraries.concat(opt.staticLibraries != null ? opt.staticLibraries : [])) // TODO: static/dynamic linking on Windows - lb.a(' $lib.lib'); - } else { - if (abi.match(Cpp | ObjectiveCpp)) lb.ai("g++"); - else lb.ai("cc"); - lb.a(' -m64 ${Sys.systemName() == "Mac" ? "-dynamiclib" : "-fPIC -shared"} -o ${e.target} ${e.requires.join(" ")}'); - for (d in opt.defines) - lb.a(' -D $d'); - for (path in opt.libraryPaths) - lb.a(' -L"$path"'); - if (opt.staticLibraries != null) { - if (Sys.systemName() == "Mac") { - // TODO: mixing dynamic and static linking on Mac - // https://stackoverflow.com/questions/4576235/mixed-static-and-dynamic-link-on-mac-os - for (lib in opt.staticLibraries) - lb.a(' -l$lib'); - } else { - lb.a(" -Wl,-Bstatic"); - for (lib in opt.staticLibraries) - lb.a(' -l$lib'); - lb.a(" -Wl,-Bdynamic"); - } - } - for (lib in opt.libraries) - lb.a(' -l$lib'); - } - } - }, "\t"); - lb.a("\n\n"); - } - lb.ai(".PHONY:"); - for (e in phony) - lb.a(' $e'); - lb.a("\n"); - Utils.update('$dir/$name', lb.dump()); - if (Ammer.config.useMSVC) { - BuildTools.inDir(dir, () -> { - if (!run(Ammer.config.pathMSVC + "nmake", ["/f", name])) - Context.fatalError("native compilation failed", Context.currentPos()); - }); - } else { - if (!run("make", ["-C", dir, "-f", name])) - Context.fatalError("native compilation failed", Context.currentPos()); - } - } else { - var targetMap = [ for (e in data) e.target => e ]; - BuildTools.inDir(dir, () -> { - function build(name:String):Bool { - if (!targetMap.exists(name)) { - return FileSystem.exists(name); - } - var e = targetMap[name]; - var target = e.target; - var requires = e.requires; - for (req in requires) - if (!build(req)) - return false; - var needsUpdate = false; - if (!FileSystem.exists(target)) { - needsUpdate = true; - } else { - var mtime = FileSystem.stat(target).mtime.getTime(); - for (req in requires) { - if (FileSystem.exists(req) && FileSystem.stat(req).mtime.getTime() > mtime) { - needsUpdate = true; - break; - } - } - } - if (!needsUpdate) - return true; - switch (e.command) { - case Phony: - case Copy: - File.copy(e.requires[0], e.target); - case CompileObject(abi, opt): - if (Ammer.config.useMSVC) { - var args = ['/Fe:${e.target}', "/c"]; - for (req in e.requires) - args.push(req); - for (path in opt.includePaths) { - args.push("/I"); - args.push(path); - } - return run('${Ammer.config.pathMSVC}cl', args); - } else { - var args = ["-fPIC", "-o", e.target, "-c"]; - for (req in e.requires) - args.push(req); - if (abi == Cpp || abi == ObjectiveCpp) { - args.push("-std=c++11"); - } - for (path in opt.includePaths) { - args.push("-I"); - args.push(path); - } - return run(abi.match(Cpp | ObjectiveCpp) ? "g++" : "cc", args); - } - case LinkLibrary(abi, opt): - if (Ammer.config.useMSVC) { - var args = ['/Fe:${e.target}', "/LD"]; - for (req in e.requires) - args.push(req); - for (d in opt.defines) - args.push(' /D$d'); - args.push("/link"); - for (path in opt.libraryPaths) - args.push('/LIBPATH:$path'); - for (lib in opt.libraries.concat(opt.staticLibraries != null ? opt.staticLibraries : [])) // TODO: static/dynamic linking on Windows - args.push('$lib.lib'); - return run('${Ammer.config.pathMSVC}cl', args); - } else { - var args = ["-m64", "-o", e.target]; - if (Sys.systemName() == "Mac") { - args.push("-dynamiclib"); - } else { - args.push("-shared"); - args.push("-fPIC"); - } - for (req in e.requires) - args.push(req); - for (d in opt.defines) { - args.push("-D"); - args.push(d); - } - for (path in opt.libraryPaths) - args.push('-L$path'); - if (opt.staticLibraries != null) { - if (Sys.systemName() == "Mac") { - // TODO: mixing dynamic and static linking on Mac - // https://stackoverflow.com/questions/4576235/mixed-static-and-dynamic-link-on-mac-os - for (lib in opt.staticLibraries) - args.push('-l$lib'); - } else { - args.push("-Wl,-Bstatic"); - for (lib in opt.staticLibraries) - args.push('-l$lib'); - args.push("-Wl,-Bdynamic"); - } - } - for (lib in opt.libraries) - args.push('-l$lib'); - return run(abi.match(Cpp | ObjectiveCpp) ? "g++" : "cc", args); - } - } - return true; - } - if (!build("all")) - Context.fatalError("native compilation failed", Context.currentPos()); - }); - } - } -} - -typedef MakeEntry = { - target:String, - requires:Array, - command:MakeCommand -}; - -typedef MakeCompileOptions = { - includePaths:Array -}; - -typedef MakeLinkOptions = { - defines:Array, - libraryPaths:Array, - libraries:Array, - ?staticLibraries:Array -}; - -enum MakeCommand { - Phony; - Copy; - CompileObject(abi:ammer.Config.AmmerAbi, opt:MakeCompileOptions); - LinkLibrary(abi:ammer.Config.AmmerAbi, opt:MakeLinkOptions); -} diff --git a/src/ammer/conv/ArrayTools.hx b/src/ammer/conv/ArrayTools.hx deleted file mode 100644 index 53f7da8..0000000 --- a/src/ammer/conv/ArrayTools.hx +++ /dev/null @@ -1,6 +0,0 @@ -package ammer.conv; - -class ArrayTools { - public static macro function asShared(e); - public static macro function asCopy(e); -} diff --git a/src/ammer/conv/ArrayTools.macro.hx b/src/ammer/conv/ArrayTools.macro.hx deleted file mode 100644 index 19561b3..0000000 --- a/src/ammer/conv/ArrayTools.macro.hx +++ /dev/null @@ -1,68 +0,0 @@ -package ammer.conv; - -import haxe.macro.Context; -import haxe.macro.Expr; - -class ArrayTools { - public static function asShared(e:Expr):Expr { - var expected = Context.getExpectedType(); - if (expected == null) { - Context.fatalError("target type is not known", e.pos); - } - switch (expected) { - case TAbstract(_.get().name => "ArrayWrapper", [ - TAbstract(_.get() => elemType, []), - TInst(_.get() => arrayType, []), - ]): - var arrayTypeTp = {name: arrayType.name, pack: arrayType.pack, params: []}; - return (switch [Ammer.config.platform, elemType] { - case [Cpp, {name: "Int", module: "StdTypes", pack: []}] - | [Cpp, {name: "Int32", module: "cpp.Int32", pack: ["cpp"]}]: - macro { - var _ammer_vec = $e; - @:privateAccess new ammer.conv.ArrayWrapper( - new $arrayTypeTp(cast cpp.NativeArray.address(_ammer_vec.toData(), 0)), - _ammer_vec.length - ); - }; - case [Hl, {name: "Int", module: "StdTypes", pack: []}]: - macro { - var _ammer_vec = $e; - @:privateAccess new ammer.conv.ArrayWrapper( - $p{Utils.access(arrayType)}.ofNativeInt(_ammer_vec.toData()), - _ammer_vec.length - ); - }; - case _: Context.fatalError("asShared can only be applied to arrays of primitive types", e.pos); - }); - case _: - Context.fatalError("target type is not an array", e.pos); - } - throw "!"; - } - - public static function asCopy(e:ExprOf>):Expr { - var expected = Context.getExpectedType(); - if (expected == null) { - Context.fatalError("target type is not known", e.pos); - } - switch (expected) { - case TAbstract(_.get().name => "ArrayWrapper", [_, TInst(_.get() => arrayType, [])]): - return macro { - var _ammer_vec = $e; - var _ammer_wrapper = @:privateAccess new ammer.conv.ArrayWrapper( - $p{Utils.access(arrayType)}.alloc(_ammer_vec.length), - _ammer_vec.length - ); - // TODO: use faster platform-specific copy methods - for (i in 0..._ammer_vec.length) { - _ammer_wrapper[i] = _ammer_vec[i]; - } - _ammer_wrapper; - }; - case _: - Context.fatalError("target type is not an array", e.pos); - } - throw "!"; - } -} diff --git a/src/ammer/conv/ArrayWrapper.hx b/src/ammer/conv/ArrayWrapper.hx deleted file mode 100644 index 446061a..0000000 --- a/src/ammer/conv/ArrayWrapper.hx +++ /dev/null @@ -1,49 +0,0 @@ -package ammer.conv; - -abstract ArrayWrapper({ - array: U, - length: Int, -}) { - public var length(get, never):Int; - private inline function get_length():Int { - return this.length; - } - - private inline function new(array:U, length:Int) { - this = { - array: array, - length: length, - }; - } - - public inline function toNative():U { - return this.array; - } - - @:arrayAccess - private inline function wrapperGet(idx:Int):T { - return this.array.get(idx); - } - - @:arrayAccess - private inline function wrapperSet(idx:Int, val:T):Void { - this.array.set(idx, val); - } - - public function toVector():haxe.ds.Vector { - var ret = new haxe.ds.Vector(this.length); - for (i in 0...this.length) { - ret[i] = this.array.get(i); - } - return ret; - } - - public function toArray():Array { - return [ for (i in 0...this.length) this.array.get(i) ]; - } - - // TODO: add (optional) exceptions when writing outside bounds -} diff --git a/src/ammer/conv/Bytes.cpp.hx b/src/ammer/conv/Bytes.cpp.hx deleted file mode 100644 index 1b4f3e7..0000000 --- a/src/ammer/conv/Bytes.cpp.hx +++ /dev/null @@ -1,14 +0,0 @@ -package ammer.conv; - -import haxe.io.Bytes as HaxeBytes; - -abstract Bytes(HaxeBytes) from HaxeBytes to HaxeBytes { - public static inline function fromNative(ptr:cpp.Pointer, size:Int):HaxeBytes - return @:privateAccess new HaxeBytes(size, ptr.toUnmanagedArray(size)); - - public inline function toNative1():cpp.Pointer - return cpp.Pointer.ofArray(@:privateAccess this.b); - - public inline function toNative2():Int - return this.length; -} diff --git a/src/ammer/conv/Bytes.eval.hx b/src/ammer/conv/Bytes.eval.hx deleted file mode 100644 index 60b72f7..0000000 --- a/src/ammer/conv/Bytes.eval.hx +++ /dev/null @@ -1,14 +0,0 @@ -package ammer.conv; - -import haxe.io.Bytes as HaxeBytes; - -abstract Bytes(HaxeBytes) from HaxeBytes to HaxeBytes { - public static inline function fromNative(ptr:HaxeBytes, size:Int):HaxeBytes - return ptr; - - public inline function toNative1():HaxeBytes - return this; - - public inline function toNative2():Int - return this.length; -} diff --git a/src/ammer/conv/Bytes.hl.hx b/src/ammer/conv/Bytes.hl.hx deleted file mode 100644 index e2cb474..0000000 --- a/src/ammer/conv/Bytes.hl.hx +++ /dev/null @@ -1,14 +0,0 @@ -package ammer.conv; - -import haxe.io.Bytes as HaxeBytes; - -abstract Bytes(HaxeBytes) from HaxeBytes to HaxeBytes { - public static inline function fromNative(ptr:hl.Bytes, size:Int):HaxeBytes - return ptr != null ? ptr.toBytes(size) : null; - - public inline function toNative1():hl.Bytes - return @:privateAccess this.b; - - public inline function toNative2():Int - return this.length; -} diff --git a/src/ammer/conv/Bytes.hx b/src/ammer/conv/Bytes.hx deleted file mode 100644 index a7859c0..0000000 --- a/src/ammer/conv/Bytes.hx +++ /dev/null @@ -1,14 +0,0 @@ -package ammer.conv; - -import haxe.io.Bytes as HaxeBytes; - -abstract Bytes(HaxeBytes) from HaxeBytes to HaxeBytes { - public static inline function fromNative(_, _) - throw "not implemented"; - - public inline function toNative1() - throw "not implemented"; - - public inline function toNative2() - throw "not implemented"; -} diff --git a/src/ammer/conv/Bytes.lua.hx b/src/ammer/conv/Bytes.lua.hx deleted file mode 100644 index 3f1d036..0000000 --- a/src/ammer/conv/Bytes.lua.hx +++ /dev/null @@ -1,27 +0,0 @@ -package ammer.conv; - -import haxe.io.Bytes as HaxeBytes; -import lua.NativeStringTools; -import lua.Table; - -abstract Bytes(HaxeBytes) from HaxeBytes to HaxeBytes { - public static function fromNative(ptr:String, size:Int):HaxeBytes { - var data = []; - for (i in 0...ptr.length) { - data.push(NativeStringTools.byte(ptr, i + 1)); - } - return @:privateAccess new HaxeBytes(ptr.length, data); - } - - public function toNative1():String { - var b = this.getData(); - var t:Table = Table.create(); - for (i in 0...b.length) { - Table.insert(t, NativeStringTools.char(b[i])); - } - return Table.concat(t); - } - - public inline function toNative2():Int - return this.length; -} diff --git a/src/ammer/conv/CArray.cpp.hx b/src/ammer/conv/CArray.cpp.hx deleted file mode 100644 index 7f20f73..0000000 --- a/src/ammer/conv/CArray.cpp.hx +++ /dev/null @@ -1,27 +0,0 @@ -package ammer.conv; - -import haxe.ds.Vector; - -abstract CArray(Vector) from Vector to Vector { - public static inline function fromNative(ptr:cpp.Star, size:Int):Vector - return cpp.Pointer.fromStar(ptr).toUnmanagedVector(size); - - public inline function toNative1():cpp.Star - return cpp.Pointer.ofArray(this.toData()).ptr; - - public inline function toNative2():Int - return this.length; -} - -/* -abstract CArray(Vector) from Vector to Vector { - public static inline function fromNative(ptr:Array, size:Int):Vector - return cpp.Pointer.ofArray(ptr).toUnmanagedVector(size); - - public inline function toNative1():Array - return this.toData(); - - public inline function toNative2():Int - return this.length; -} -*/ \ No newline at end of file diff --git a/src/ammer/conv/CArray.hx b/src/ammer/conv/CArray.hx deleted file mode 100644 index e0d63fa..0000000 --- a/src/ammer/conv/CArray.hx +++ /dev/null @@ -1,14 +0,0 @@ -package ammer.conv; - -import haxe.ds.Vector; - -abstract CArray(Vector) from Vector to Vector { - public static inline function fromNative(_, _) - throw "not implemented"; - - public inline function toNative1() - throw "not implemented"; - - public inline function toNative2() - throw "not implemented"; -} diff --git a/src/ammer/conv/CDirectArray.hx b/src/ammer/conv/CDirectArray.hx deleted file mode 100644 index 4252826..0000000 --- a/src/ammer/conv/CDirectArray.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer.conv; - -abstract CDirectArray(CDirectArrayImpl) { - @:arrayAccess public function get(idx:Int):T { - return cpp.Pointer.arrayElem(this, idx); - } -} - -class CDirectArrayImpl { - -} diff --git a/src/ammer/conv/CString.cpp.hx b/src/ammer/conv/CString.cpp.hx deleted file mode 100644 index 9cbac64..0000000 --- a/src/ammer/conv/CString.cpp.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer.conv; - -import String as HaxeString; - -abstract CString(HaxeString) from HaxeString to HaxeString { - public static inline function fromNative(ptr:cpp.ConstPointer):HaxeString - return cpp.NativeString.fromPointer(ptr); - - public inline function toNative():cpp.ConstPointer - return cpp.NativeString.c_str(this); -} diff --git a/src/ammer/conv/CString.eval.hx b/src/ammer/conv/CString.eval.hx deleted file mode 100644 index cb5276c..0000000 --- a/src/ammer/conv/CString.eval.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer.conv; - -import String as HaxeString; - -abstract CString(HaxeString) from HaxeString to HaxeString { - public static inline function fromNative(str:HaxeString):HaxeString - return str; - - public inline function toNative():HaxeString - return this; -} diff --git a/src/ammer/conv/CString.hl.hx b/src/ammer/conv/CString.hl.hx deleted file mode 100644 index 5240a31..0000000 --- a/src/ammer/conv/CString.hl.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer.conv; - -import String as HaxeString; - -abstract CString(HaxeString) from HaxeString to HaxeString { - public static inline function fromNative(ptr:hl.Bytes):HaxeString - return ptr != null ? @:privateAccess String.fromUTF8(ptr) : null; - - public inline function toNative():hl.Bytes - return this != null ? @:privateAccess this.toUtf8() : null; -} diff --git a/src/ammer/conv/CString.hx b/src/ammer/conv/CString.hx deleted file mode 100644 index 9408fe4..0000000 --- a/src/ammer/conv/CString.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer.conv; - -import String as HaxeString; - -abstract CString(HaxeString) from HaxeString to HaxeString { - public static inline function fromNative(_) - throw "not implemented"; - - public inline function toNative() - throw "not implemented"; -} diff --git a/src/ammer/conv/CString.lua.hx b/src/ammer/conv/CString.lua.hx deleted file mode 100644 index cb5276c..0000000 --- a/src/ammer/conv/CString.lua.hx +++ /dev/null @@ -1,11 +0,0 @@ -package ammer.conv; - -import String as HaxeString; - -abstract CString(HaxeString) from HaxeString to HaxeString { - public static inline function fromNative(str:HaxeString):HaxeString - return str; - - public inline function toNative():HaxeString - return this; -} diff --git a/src/ammer/def/Enum.hx b/src/ammer/def/Enum.hx new file mode 100644 index 0000000..b952aac --- /dev/null +++ b/src/ammer/def/Enum.hx @@ -0,0 +1,13 @@ +package ammer.def; + +#if macro + +import haxe.macro.Expr; + +class Enum { + public static function build(name:String, ffi:Expr, lib:Expr):Array { + return ammer.internal.Entrypoint.buildEnum(name, ffi, lib); + } +} + +#end diff --git a/src/ammer/def/Library.hx b/src/ammer/def/Library.hx new file mode 100644 index 0000000..6ed82b1 --- /dev/null +++ b/src/ammer/def/Library.hx @@ -0,0 +1,8 @@ +package ammer.def; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildLibrary()) +class Library<@:const Name> {} + +#end diff --git a/src/ammer/def/Opaque.hx b/src/ammer/def/Opaque.hx new file mode 100644 index 0000000..7c5a530 --- /dev/null +++ b/src/ammer/def/Opaque.hx @@ -0,0 +1,8 @@ +package ammer.def; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildOpaque()) +class Opaque<@:const Name, Lib> {} + +#end diff --git a/src/ammer/def/Struct.hx b/src/ammer/def/Struct.hx new file mode 100644 index 0000000..c72b797 --- /dev/null +++ b/src/ammer/def/Struct.hx @@ -0,0 +1,8 @@ +package ammer.def; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildStruct()) +class Struct<@:const Name, Lib> {} + +#end diff --git a/src/ammer/def/Sublibrary.hx b/src/ammer/def/Sublibrary.hx new file mode 100644 index 0000000..a87662d --- /dev/null +++ b/src/ammer/def/Sublibrary.hx @@ -0,0 +1,8 @@ +package ammer.def; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildSublibrary()) +class Sublibrary {} + +#end diff --git a/src/ammer/ffi/Alloc.hx b/src/ammer/ffi/Alloc.hx index 5d36fa9..91a87f1 100644 --- a/src/ammer/ffi/Alloc.hx +++ b/src/ammer/ffi/Alloc.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Alloc {} + +#end diff --git a/src/ammer/ffi/Array.hx b/src/ammer/ffi/Array.hx new file mode 100644 index 0000000..52e2795 --- /dev/null +++ b/src/ammer/ffi/Array.hx @@ -0,0 +1,8 @@ +package ammer.ffi; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildArray()) +class Array {} + +#end diff --git a/src/ammer/ffi/ArrayDynamic.hx b/src/ammer/ffi/ArrayDynamic.hx deleted file mode 100644 index 071e34f..0000000 --- a/src/ammer/ffi/ArrayDynamic.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class ArrayDynamic {} diff --git a/src/ammer/ffi/ArrayFixed.hx b/src/ammer/ffi/ArrayFixed.hx deleted file mode 100644 index de8db99..0000000 --- a/src/ammer/ffi/ArrayFixed.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class ArrayFixed {} diff --git a/src/ammer/ffi/Bool.hx b/src/ammer/ffi/Bool.hx new file mode 100644 index 0000000..3d7ca98 --- /dev/null +++ b/src/ammer/ffi/Bool.hx @@ -0,0 +1,7 @@ +package ammer.ffi; + +#if !macro + +class Bool {} + +#end diff --git a/src/ammer/ffi/Box.hx b/src/ammer/ffi/Box.hx new file mode 100644 index 0000000..601897e --- /dev/null +++ b/src/ammer/ffi/Box.hx @@ -0,0 +1,8 @@ +package ammer.ffi; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildBox()) +class Box {} + +#end diff --git a/src/ammer/ffi/Bytes.hx b/src/ammer/ffi/Bytes.hx new file mode 100644 index 0000000..6cce86b --- /dev/null +++ b/src/ammer/ffi/Bytes.hx @@ -0,0 +1,8 @@ +package ammer.ffi; + +#if !macro + +@:build(ammer.internal.Entrypoint.buildBytes()) +class Bytes {} + +#end diff --git a/src/ammer/ffi/Callback.hx b/src/ammer/ffi/Callback.hx new file mode 100644 index 0000000..154c482 --- /dev/null +++ b/src/ammer/ffi/Callback.hx @@ -0,0 +1,25 @@ +package ammer.ffi; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildCallback()) +class Callback< + // function type as seen by the native library + // e.g. (Int32, Haxe<(Int) -> Int>) -> Int32 + CallbackType, + + // function type as seen by Haxe + // e.g. (Int) -> Int + FunctionType, + + // where to find the function in CallbackType + @:const CallTarget, + + // which arguments to pass through to the Haxe function + @:const CallArgs, + + // parent library + Lib +> {} + +#end diff --git a/src/ammer/ffi/Closure.hx b/src/ammer/ffi/Closure.hx deleted file mode 100644 index 7ca531d..0000000 --- a/src/ammer/ffi/Closure.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class Closure {} diff --git a/src/ammer/ffi/ClosureData.hx b/src/ammer/ffi/ClosureData.hx deleted file mode 100644 index 008120b..0000000 --- a/src/ammer/ffi/ClosureData.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class ClosureData {} diff --git a/src/ammer/ffi/ClosureDataUse.hx b/src/ammer/ffi/ClosureDataUse.hx deleted file mode 100644 index 7ab1481..0000000 --- a/src/ammer/ffi/ClosureDataUse.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class ClosureDataUse {} diff --git a/src/ammer/ffi/Deref.hx b/src/ammer/ffi/Deref.hx new file mode 100644 index 0000000..c2a62f4 --- /dev/null +++ b/src/ammer/ffi/Deref.hx @@ -0,0 +1,7 @@ +package ammer.ffi; + +#if !macro + +class Deref {} + +#end diff --git a/src/ammer/ffi/FilePtr.hx b/src/ammer/ffi/FilePtr.hx new file mode 100644 index 0000000..53f18b0 --- /dev/null +++ b/src/ammer/ffi/FilePtr.hx @@ -0,0 +1,77 @@ +package ammer.ffi; + +#if !macro + +class FilePtr extends ammer.def.Opaque<"FILE*", ammer.internal.LibTypes> { + public static final SEEK_SET:Int; + public static final SEEK_CUR:Int; + public static final SEEK_END:Int; + public static final BUFSIZ:Int; + public static final EOF:Int; + public static final _IOFBF:Int; + public static final _IOLBF:Int; + public static final _IONBF:Int; + public static final FILENAME_MAX:Int; + public static final FOPEN_MAX:Int; + public static final TMP_MAX:Int; + public static final L_tmpnam:Int; + + public static final stderr:FilePtr; + public static final stdin:FilePtr; + public static final stdout:FilePtr; + + public static function fopen(filename:String, mode:String):FilePtr; + public static function getchar():Int; + public static function perror(str:String):Void; + public static function putchar(char:Int):Int; + public static function puts(str:String):Int; + public static function remove(filename:String):Int; + public static function rename(oldname:String, newname:String):Int; + public static function tmpfile():FilePtr; + // public static function tmpnam(str:String):String; // deprecated + + public function fclose(_:This):Void; + public function feof(_:This):Bool; + public function ferror(_:This):Int; + public function fflush(_:This):Int; + public function fgetc(_:This):Int; + public function fgetpos(_:This, pos:FilePos):Int; + //fgets + //fprintf? + public function fputc(char:Int, _:This):Int; + public function fputs(str:String, _:This):Int; + public function fread(ptr:Bytes, size:Size, count:Size, _:This):Size; + public function freopen(filename:String, mode:String, _:This):FilePtr; + // public function fscanf(_:This, format: ...) + public function fseek(_:This, offset:Int64, origin:Int):Int; + public function fsetpos(_:This, pos:FilePos):Int; + public function ftell(_:This):Int64; + public function fwrite(ptr:Bytes, size:Size, count:Size, _:This):Size; + // getc -> fgetc + // gets // removed + // public static function printf(...) + public function putc(char:Int, _:This):Int; + public function rewind(_:This):Void; + // public static function scanf(...) + public function setbuf(_:This, @:ammer.c.cast("char*") buffer:Bytes):Void; + public function setvbuf(_:This, @:ammer.c.cast("char*") buffer:Bytes, mode:Int, size:Size):Int; + // public static function snprintf(...) + // public static function sprintf(...) + // public static function sscanf(...) + public function ungetc(char:Int, _:This):Int; + // public function vfprintf(...) + // public function vfscanf(...) + // public function vprintf(...) + // public function vscanf(...) + // public function vsnprintf(...) + // public function vsprintf(...) + // public function vsscanf(...) + + @:ammer.haxe public function output():haxe.io.Output return @:privateAccess new ammer.internal.FilePtrOutput(this); + //public function input():haxe.io.Input return @:privateAccess new ammer.internal.FilePtrInput(this); +} + +@:ammer.alloc +class FilePos extends ammer.def.Struct<"fpos_t", ammer.internal.LibTypes> {} + +#end diff --git a/src/ammer/ffi/Float32.hx b/src/ammer/ffi/Float32.hx index fe7c238..19393db 100644 --- a/src/ammer/ffi/Float32.hx +++ b/src/ammer/ffi/Float32.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Float32 {} + +#end diff --git a/src/ammer/ffi/Float64.hx b/src/ammer/ffi/Float64.hx index 7a74c24..e4df009 100644 --- a/src/ammer/ffi/Float64.hx +++ b/src/ammer/ffi/Float64.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Float64 {} + +#end diff --git a/src/ammer/ffi/Haxe.hx b/src/ammer/ffi/Haxe.hx new file mode 100644 index 0000000..3142f08 --- /dev/null +++ b/src/ammer/ffi/Haxe.hx @@ -0,0 +1,8 @@ +package ammer.ffi; + +#if !macro + +@:genericBuild(ammer.internal.Entrypoint.genericBuildHaxeRef()) +class Haxe {} + +#end diff --git a/src/ammer/ffi/Int16.hx b/src/ammer/ffi/Int16.hx index 85a91d9..b375ecb 100644 --- a/src/ammer/ffi/Int16.hx +++ b/src/ammer/ffi/Int16.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Int16 {} + +#end diff --git a/src/ammer/ffi/Int32.hx b/src/ammer/ffi/Int32.hx index 4405ffe..0f289bb 100644 --- a/src/ammer/ffi/Int32.hx +++ b/src/ammer/ffi/Int32.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Int32 {} + +#end diff --git a/src/ammer/ffi/Int64.hx b/src/ammer/ffi/Int64.hx index 34875a1..ca0e644 100644 --- a/src/ammer/ffi/Int64.hx +++ b/src/ammer/ffi/Int64.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Int64 {} + +#end diff --git a/src/ammer/ffi/Int8.hx b/src/ammer/ffi/Int8.hx index b404531..5abc94c 100644 --- a/src/ammer/ffi/Int8.hx +++ b/src/ammer/ffi/Int8.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class Int8 {} + +#end diff --git a/src/ammer/ffi/NativeHl.hx b/src/ammer/ffi/NativeHl.hx deleted file mode 100644 index f238d08..0000000 --- a/src/ammer/ffi/NativeHl.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class NativeHl {} diff --git a/src/ammer/ffi/Nested.hx b/src/ammer/ffi/Nested.hx deleted file mode 100644 index 4ebec26..0000000 --- a/src/ammer/ffi/Nested.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class Nested {} diff --git a/src/ammer/ffi/NoSize.hx b/src/ammer/ffi/NoSize.hx deleted file mode 100644 index 81a973d..0000000 --- a/src/ammer/ffi/NoSize.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class NoSize {} diff --git a/src/ammer/ffi/OutPointer.hx b/src/ammer/ffi/OutPointer.hx deleted file mode 100644 index bfcc349..0000000 --- a/src/ammer/ffi/OutPointer.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class OutPointer {} diff --git a/src/ammer/ffi/SameSizeAs.hx b/src/ammer/ffi/SameSizeAs.hx deleted file mode 100644 index 51e3686..0000000 --- a/src/ammer/ffi/SameSizeAs.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class SameSizeAs {} diff --git a/src/ammer/ffi/Size.hx b/src/ammer/ffi/Size.hx new file mode 100644 index 0000000..7e020a0 --- /dev/null +++ b/src/ammer/ffi/Size.hx @@ -0,0 +1,4 @@ +package ammer.ffi; + +// TODO: deal with this ... +typedef Size = ammer.ffi.Int32; diff --git a/src/ammer/ffi/SizeOf.hx b/src/ammer/ffi/SizeOf.hx deleted file mode 100644 index fd1c911..0000000 --- a/src/ammer/ffi/SizeOf.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class SizeOf {} diff --git a/src/ammer/ffi/SizeOfReturn.hx b/src/ammer/ffi/SizeOfReturn.hx deleted file mode 100644 index 7478592..0000000 --- a/src/ammer/ffi/SizeOfReturn.hx +++ /dev/null @@ -1,3 +0,0 @@ -package ammer.ffi; - -class SizeOfReturn {} diff --git a/src/ammer/ffi/String.hx b/src/ammer/ffi/String.hx new file mode 100644 index 0000000..481d020 --- /dev/null +++ b/src/ammer/ffi/String.hx @@ -0,0 +1,7 @@ +package ammer.ffi; + +#if !macro + +class String {} + +#end diff --git a/src/ammer/ffi/This.hx b/src/ammer/ffi/This.hx index 39bf6df..2195a7b 100644 --- a/src/ammer/ffi/This.hx +++ b/src/ammer/ffi/This.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class This {} + +#end diff --git a/src/ammer/ffi/UInt16.hx b/src/ammer/ffi/UInt16.hx index 36feed9..8583c10 100644 --- a/src/ammer/ffi/UInt16.hx +++ b/src/ammer/ffi/UInt16.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class UInt16 {} + +#end diff --git a/src/ammer/ffi/UInt32.hx b/src/ammer/ffi/UInt32.hx index d25302a..4011d8c 100644 --- a/src/ammer/ffi/UInt32.hx +++ b/src/ammer/ffi/UInt32.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class UInt32 {} + +#end diff --git a/src/ammer/ffi/UInt64.hx b/src/ammer/ffi/UInt64.hx index c79b40e..c6357e3 100644 --- a/src/ammer/ffi/UInt64.hx +++ b/src/ammer/ffi/UInt64.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class UInt64 {} + +#end diff --git a/src/ammer/ffi/UInt8.hx b/src/ammer/ffi/UInt8.hx index af67bd1..652f1ad 100644 --- a/src/ammer/ffi/UInt8.hx +++ b/src/ammer/ffi/UInt8.hx @@ -1,3 +1,7 @@ package ammer.ffi; +#if !macro + class UInt8 {} + +#end diff --git a/src/ammer/ffi/Unsupported.hx b/src/ammer/ffi/Unsupported.hx index 7017ca3..c90c854 100644 --- a/src/ammer/ffi/Unsupported.hx +++ b/src/ammer/ffi/Unsupported.hx @@ -1,3 +1,7 @@ package ammer.ffi; -class Unsupported {} +#if !macro + +class Unsupported<@:const Expr> {} + +#end diff --git a/src/ammer/ffi/Void.hx b/src/ammer/ffi/Void.hx new file mode 100644 index 0000000..3578ac4 --- /dev/null +++ b/src/ammer/ffi/Void.hx @@ -0,0 +1,7 @@ +package ammer.ffi; + +#if !macro + +class Void {} + +#end diff --git a/src/ammer/internal/Ammer.hx b/src/ammer/internal/Ammer.hx new file mode 100644 index 0000000..621f7f5 --- /dev/null +++ b/src/ammer/internal/Ammer.hx @@ -0,0 +1,527 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Compiler; +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.PositionTools; +import haxe.macro.Type; +import haxe.io.Path; +import sys.io.File; +import sys.FileSystem; + +using StringTools; + +typedef AmmerConfig = { + buildPath:String, + outputPath:String, +}; + +typedef QueuedSubtype = { + subId:String, + lib:Type, + pos:Position, + process:(ctx:LibContext)->Any, + result:Null, + done:Bool, + prev:Null, + next:Null, +}; + +class Ammer { + public static var baseConfig(get, never):AmmerConfig; + public static var builder(get, never):ammer.core.Builder; + public static var platform(get, never):ammer.core.Platform; + public static var platformConfig(default, null):ammer.core.plat.BaseConfig; + + public static var mergedInfo = new ammer.internal.v1.LibInfo(); + public static var libraries:{ + byLibraryName:Map, + byTypeId:Map, + active:Array, + } = { + byLibraryName: [], + byTypeId: [], + active: [], + }; + + static var queuedSubtypes:{ + first:QueuedSubtype, + last:QueuedSubtype, + } = { + first: null, + last: null, + }; + static function enqueueSubtype(s:QueuedSubtype):Void { + s.prev == null || throw 0; + s.next == null || throw 0; + if (queuedSubtypes.last == null) { + queuedSubtypes.first == null || throw 0; + queuedSubtypes.first = s; + queuedSubtypes.last = s; + return; + } + queuedSubtypes.first != null || throw 0; + queuedSubtypes.last.next == null || throw 0; + s.prev = queuedSubtypes.last; + queuedSubtypes.last.next = s; + queuedSubtypes.last = s; + } + static function dequeueSubtype(s:QueuedSubtype):Void { + if (s.prev == null) { + queuedSubtypes.first = s.next; + } else { + s.prev.next = s.next; + } + if (s.next == null) { + queuedSubtypes.last = s.prev; + } else { + s.next.prev = s.prev; + } + } + + static var baseConfigL:AmmerConfig; + static var builderL:ammer.core.Builder; + static var platformL:ammer.core.Platform; + + static function get_baseConfig():AmmerConfig { + if (baseConfigL != null) + return baseConfigL; + return baseConfigL = { + buildPath: Config.getPath("ammer.buildPath", null, true), + outputPath: Config.getPath("ammer.outputPath", null, true), + }; + } + + static function get_builder():ammer.core.Builder { + if (builderL != null) + return builderL; + + // TODO: allow selection of toolchain, configuration, etc + return ammer.core.Builder.createCurrentBuilder(({} : ammer.core.build.BaseBuilderConfig)); + } + + static function get_platform():ammer.core.Platform { + if (platformL != null) + return platformL; + + Bakery.init(); + + function getPaths(key:String):Array { + var paths = Config.getStringArray(key, ";"); + if (paths == null) return []; + return paths.filter(v -> v != ""); + } + platformConfig = (switch (Context.definedValue("target.name")) { + case "cpp": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + staticLink: Config.getBool("ammer.cpp.staticLink", false), + } : ammer.core.plat.Cpp.CppConfig); + case "cs": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + } : ammer.core.plat.Cs.CsConfig); + // TODO: eval? + case "hl": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + hlc: Config.getBool("ammer.hl.hlc", !Compiler.getOutput().endsWith(".hl")), + hlIncludePaths: getPaths("ammer.hl.includePaths"), + hlLibraryPaths: getPaths("ammer.hl.libraryPaths"), + } : ammer.core.plat.Hashlink.HashlinkConfig); + case "java": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + javaIncludePaths: getPaths("ammer.java.includePaths"), + javaLibraryPaths: getPaths("ammer.java.libraryPaths"), + } : ammer.core.plat.Java.JavaConfig); + case "lua": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + luaIncludePaths: getPaths("ammer.lua.includePaths"), + luaLibraryPaths: getPaths("ammer.lua.libraryPaths"), + } : ammer.core.plat.Lua.LuaConfig); + case "neko": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + nekoIncludePaths: getPaths("ammer.neko.includePaths"), + nekoLibraryPaths: getPaths("ammer.neko.libraryPaths"), + } : ammer.core.plat.Neko.NekoConfig); + case "js": ({ + // TODO: (once implemented,) choose JS build system + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + nodeGypBinary: Config.getString("ammer.js.nodeGypBinary", "node-gyp"), + } : ammer.core.plat.Nodejs.NodejsConfig); + case "python": ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + pythonVersionMinor: Config.getInt("ammer.python.version", 8), + pythonIncludePaths: getPaths("ammer.python.includePaths"), + pythonLibraryPaths: getPaths("ammer.python.libraryPaths"), + } : ammer.core.plat.Python.PythonConfig); + + case _: ({ + buildPath: baseConfig.buildPath, + outputPath: baseConfig.outputPath, + } : ammer.core.plat.None.NoneConfig); + }); + + platformL = ammer.core.Platform.createCurrentPlatform(platformConfig); + Context.onAfterTyping(_ -> { + var program = platformL.finalise(); + builder.build(program); + }); + return platformL; + } + + public static function registerBakedLibraryV1(info:ammer.internal.v1.LibInfo):Void { + // TODO: also detect duplicate library names (add to libraries.byLibraryName ?) + Reporting.withCurrentPos(() -> { + final fail = Reporting.error; +// ammer-fragment-begin: register-v1 + // detect local path + var path = PositionTools.getInfos(info.herePos).file; + if (!Path.isAbsolute(path)) { + path = Path.join([Sys.getCwd(), path]); + } + path = Path.normalize(Path.directory(path)); + var binaryPath = Path.normalize(path + "/" + info.setupToBin); + + if (true) { //if (Context.defined('${info.name}_copy_binary')) { + var outputPath = Context.definedValue("ammer.outputPath"); + outputPath != null || throw fail("missing required define: ammer.outputPath"); + FileSystem.createDirectory(outputPath); + for (file in info.files) { + var dst = file.dst; + var dstFull = Path.normalize(Path.join([outputPath, dst])); + + var dstExists = FileSystem.exists(dstFull); + if (dstExists) { + !FileSystem.isDirectory(dstFull) || throw fail('$dst exists but is a directory'); + if (true) continue; + /* + // TODO: skip if configured (when running ammer proper?) + var digest = haxe.crypto.Sha256.make(File.getBytes(dstFull)).toHex(); + if (digest == file.digest) { + continue; + } else { + Sys.println('[ammer] $dst found with different contents (possibly an older version)'); + }*/ + } + + // at this point, either the required file does not exist + // or its digest does not match what we expect (currently disabled) + // -> look for a source + + var sourcesSkipped = []; + var sourcesAvailableLocally = []; + var sourcesAvailableOnline = []; + + for (source in file.sources) { + // skip sources which are not compatible with the current OS + var compatible = true; + if (source.os != null) { + var osInfo = ammer.internal.v1.OsInfo.info; + if (source.os != osInfo.os) compatible = false; + if (compatible && osInfo.architecture != null) { + if (source.architectures != null + && !source.architectures.contains(osInfo.architecture)) compatible = false; + } + if (compatible && osInfo.version != null) { + if (source.minVersion != null + && osInfo.versionCompare(osInfo.version, source.minVersion) < 0) compatible = false; + if (source.maxVersion != null + && osInfo.versionCompare(osInfo.version, source.maxVersion) > 0) compatible = false; + } + } + if (!compatible) { + sourcesSkipped.push(source); + continue; + } + + var srcFull = Path.normalize(Path.join([binaryPath, source.name])); + if (FileSystem.exists(srcFull)) { + sourcesAvailableLocally.push(source); + continue; + } + + if (source.downloadFrom != null) { + sourcesAvailableOnline.push(source); + continue; + } + + sourcesSkipped.push(source); + } + + // exactly one locally available, compatible source: copy it + if (!dstExists && sourcesAvailableLocally.length == 1) { + var source = sourcesAvailableLocally[0]; + var srcFull = Path.normalize(Path.join([binaryPath, source.name])); + Sys.println('[ammer] copying $srcFull -> $dstFull'); + File.copy(srcFull, dstFull); + continue; + } + + // at this point, we will prompt the user to choose an action, either + // because there are multiple locally available, compatible sources, + // or because a source will need to be downloaded + + Sys.println('[ammer] required file $dst not found for library ${info.name}'); + Sys.println(" options (press a key):"); + + var options = new Map(); + function option(char:Int, text:String, f:()->Void):Void { + Sys.println(' [${String.fromCharCode(char)}] $text'); + options[char] = f; + } + var yeses = "y0123456789".split(""); + function optionYes(text:String, f:()->Void):Void { + if (yeses.length == 0) { + Sys.println(' [ ] (too many options...) $text'); + } else { + var cc = yeses.shift().charCodeAt(0); + option(cc, text, f); + } + } + + function describe(source:ammer.internal.v1.LibInfo.LibInfoFileSource, online:Bool):String { + var infos = []; + if (online && source.downloadFrom != null) infos.push('URL: ${source.downloadFrom}'); + if (source.os != null) infos.push('OS: ${source.os}'); + if (source.minVersion != null) infos.push('OS version >= ${source.minVersion}'); + if (source.maxVersion != null) infos.push('OS version <= ${source.maxVersion}'); + if (source.architectures != null) infos.push('arch: ${source.architectures.join("/")}'); + if (infos.length == 0) return source.description; + return '${source.description} (${infos.join(", ")})'; + } + + var choiceDone = false; + var doDownload = null; + var doCopy = null; + if (dstExists) { + for (source in sourcesAvailableLocally) { + optionYes('override file using: ${describe(source, false)}', () -> { + doCopy = source; + }); + } + for (source in sourcesAvailableOnline) { + optionYes('download file now and override using: ${describe(source, true)}', () -> { + doDownload = source; + doCopy = source; + }); + } + option("s".code, "keep existing file", () -> {}); + } else { + for (source in sourcesAvailableLocally) { + optionYes('use: ${describe(source, false)}', () -> { + doCopy = source; + }); + } + for (source in sourcesAvailableOnline) { + optionYes('download file now: ${describe(source, true)}', () -> { + doDownload = source; + doCopy = source; + }); + } + if (sourcesAvailableLocally.length == 0 && sourcesAvailableOnline.length == 0) { + Sys.println(" This file is not available online: you may need to compile it locally."); + // TODO: add link to manual page (once it exists) + } + option("s".code, "ignore (program may not function correctly)", () -> {}); + } + if (sourcesSkipped.length > 0) { + option("i".code, 'show details of ${sourcesSkipped.length} skipped sources', () -> { + for (source in sourcesSkipped) { + Sys.println(' ${describe(source, true)}'); + } + choiceDone = false; + }); + } + option("q".code, "abort compilation", () -> throw fail("aborting")); + + while (!choiceDone) { + var choice = Sys.getChar(false); + if (!options.exists(choice)) continue; + choiceDone = true; + options[choice](); + } + + if (doDownload != null) { + Sys.println(" downloading file ..."); + var url = doDownload.downloadFrom; + var srcFull = Path.normalize(Path.join([binaryPath, doDownload.name])); + var followed = 0; + var downloaded = false; + while (!downloaded) { + var http = new haxe.Http(url); + var done = false; + var success = false; + try { + var status = "999"; + http.onBytes = (data) -> { + if (status.charAt(0) == "2") { + Sys.println(" file downloaded"); + /* + var digest = haxe.crypto.Sha256.make(data).toHex(); + if (digest != file.digest) { + Sys.println(" warning: file digest has changed (possibly a newer version)"); + } + */ + File.saveBytes(srcFull, data); + downloaded = true; + done = true; + success = true; + } else if (status.charAt(0) == "3") { + http.responseHeaders.exists("Location") || throw fail("redirect without Location header"); + Sys.println(" following redirect ..."); + url = http.responseHeaders["Location"]; + done = true; + success = true; + } + }; + http.onError = (msg) -> { + Sys.println(' download error: $msg'); + done = true; + success = false; + }; + http.onStatus = (s:Int) -> { + status = '$s'; + Sys.println(' status: $status'); + if (status.charAt(0) != "2" && status.charAt(0) != "3") { + done = true; + success = false; + } + } + http.request(false); + } catch (ex:Dynamic) { + Sys.println(' download error: $ex'); + done = true; + success = false; + } + // TODO: timeout? + while (!done) Sys.sleep(.25); + if (!success) fail("download error"); + followed++; + if (followed >= 5) fail("too many redirects"); + } + } + if (doCopy != null) { + var srcFull = Path.normalize(Path.join([binaryPath, doCopy.name])); + Sys.println('[ammer] copying $srcFull -> $dstFull'); + File.copy(srcFull, dstFull); + } + } + } + + function extend(target:Map, source:Map):Void { + for (k => v in source) { + if (target.exists(k)) throw fail("library replaces an existing type"); + target[k] = v; + } + } + extend(mergedInfo.arrays.byTypeId, info.arrays.byTypeId); + extend(mergedInfo.arrays.byElementTypeId, info.arrays.byElementTypeId); + extend(mergedInfo.boxes.byTypeId, info.boxes.byTypeId); + extend(mergedInfo.boxes.byElementTypeId, info.boxes.byElementTypeId); + extend(mergedInfo.callbacks.byTypeId, info.callbacks.byTypeId); + extend(mergedInfo.callbacks.byElementTypeId, info.callbacks.byElementTypeId); + extend(mergedInfo.enums, info.enums); + extend(mergedInfo.haxeRefs.byTypeId, info.haxeRefs.byTypeId); + extend(mergedInfo.haxeRefs.byElementTypeId, info.haxeRefs.byElementTypeId); + extend(mergedInfo.opaques, info.opaques); + extend(mergedInfo.structs, info.structs); + extend(mergedInfo.sublibraries, info.sublibraries); +// ammer-fragment-end: register-v1 + }); + } + + public static function initLibrary(lib:ClassType, name:String, options:LibContext.LibContextOptions):LibContext { + if (libraries.byLibraryName.exists(name)) + throw Reporting.error('duplicate definition of library "$name"'); + + var libId = Utils.typeId(lib); + var ctx = new LibContext(name, options); + ctx.isLibTypes = (libId == "ammer.internal.LibTypes.LibTypes"); + libraries.byLibraryName[name] = ctx; + libraries.byTypeId[libId] = ctx; + libraries.active.push(ctx); + + contextReady(lib, ctx); + + return ctx; + } + + public static function contextReady(lib:ClassType, ctx:LibContext):Void { + var libId = Utils.typeId(lib); + var curr = queuedSubtypes.first; + while (curr != null) { + var currId = Utils.typeId2(curr.lib); + if (Utils.typeId2(curr.lib) == libId) { + dequeueSubtype(curr); + curr.result = Reporting.withPosition(curr.pos, () -> curr.process(ctx)); + curr.done = true; + } + curr = curr.next; + } + } + + // TODO: support multiple libraries for a subtype + public static function initSubtype( + subId:String, + lib:Type, + process:(ctx:LibContext)->T, + ?processLibTypes:(ctx:LibContext)->T + ):T { + // enqueue deferred subtype processing + var queued:QueuedSubtype = { + subId: subId, + lib: lib, + pos: Reporting.currentPos(), + process: process, + result: null, + done: false, + prev: null, + next: null, + }; + enqueueSubtype(queued); + + // trigger typing of the library + var ctx = Types.resolveContext(lib); + ctx != null || { + trace("could not resolve context", subId, lib); + Context.fatalError("here", Context.currentPos()); + //throw 0; + }; + + if (queued.done) { + return queued.result; + } + + // if it was not processed, then either: + // - the library is currently being processed, or + // - the library has already finished processing + + if (!ctx.done) { + // currently being processed + dequeueSubtype(queued); + return process(ctx); + } else { + if (ctx.isLibTypes && processLibTypes != null) { + return processLibTypes(ctx); + } + + // already processed, report missing subtype link + // TODO: format types better + Reporting.error( + '$subId is declared as a subtype of library ${Utils.typeId2(lib)}, ' + + 'but the library was already fully processed. Please add a subtype ' + + 'link @:ammer.sub((_ : $subId)) to the library definition.'); + return null; + } + } +} + +#end diff --git a/src/ammer/internal/Bakery.hx b/src/ammer/internal/Bakery.hx new file mode 100644 index 0000000..995c55b --- /dev/null +++ b/src/ammer/internal/Bakery.hx @@ -0,0 +1,556 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Printer; +import haxe.macro.Type; +import haxe.macro.TypeTools; +import haxe.io.Path; +import sys.io.File; +import sys.FileSystem; +import ammer.core.utils.LineBuf; + +using Lambda; +using StringTools; + +// TODO: haxelib.json (extraParams for `--macro`) + +class Bakery { + public static final BAKE_PREFIX = "// ammer-bake: "; + static var ammerRootPath:String = { + var path = haxe.macro.PositionTools.getInfos((macro 0).pos).file; + if (!Path.isAbsolute(path)) { + path = Path.join([Sys.getCwd(), path]); + } + Path.normalize(Path.directory(Path.normalize(path)) + "/../"); + }; + + public static var isBaking = false; + public static var mainType:Type; + static var bakeOutput:String; + static var rootToBin:String; + static var fileSources:Array; + + static var downloadURL:Null; + static var description:Null; + static var os:Null; + static var architectures:Null>; + static var minVersion:Null; + static var maxVersion:Null; + + public static function init():Void { + if (isBaking) return; + if (!Config.getBool("ammer.bake", false)) return; + isBaking = true; + + var mainTypeStr = Config.getString("ammer.bake.mainType", null, true); + var mainTypePack = mainTypeStr.split("."); + mainType = Context.resolveType(TPath({ + // TODO: support subtypes? merge somehow with Utils.complexTypeExpr? + name: mainTypePack.pop(), + pack: mainTypePack, + }), Context.currentPos()); + + bakeOutput = Config.getPath("ammer.bake.output", null, true); + rootToBin = Config.getString("ammer.bake.rootToBin", null, true); + var sourceCtr = 0; + fileSources = [ while (true) { + var prefix = 'ammer.bake.fileSource.${sourceCtr++}'; + var anyData = false; + inline function c(x: Null):Null { + if (x != null) anyData = true; + return x; + } + var source:ammer.internal.v1.LibInfo.LibInfoFileSource = { + name: c(Config.getString('$prefix.name', null)), + downloadFrom: c(Config.getString('$prefix.downloadFrom', null)), + description: c(Config.getString('$prefix.description', null)), + os: c(Config.getString('$prefix.os', null)), + architectures: c(Config.getStringArray('$prefix.architectures', ",", null)), + minVersion: c(Config.getString('$prefix.minVersion', null)), + maxVersion: c(Config.getString('$prefix.maxVersion', null)), + }; + if (!anyData) break; + source; + } ]; + + Context.onAfterTyping(writeFiles); + } + + static function writeFiles(_):Void { + FileSystem.createDirectory(bakeOutput); + var printer = new Printer(" "); + var modules = BakeryOutput.withBufs(); + + var extraParams:String; + var targetId:String; + var targetCondition:String; + var targetDescription:String; + switch (Ammer.platform.kind) { + case Cpp: + extraParams = "cpp"; + targetId = "cpp"; + targetCondition = 'Context.definedValue("target.name") == "cpp"'; + targetDescription = "Haxe/C++"; + case Cs: + extraParams = "cs"; + targetId = "cs"; + targetCondition = 'Context.definedValue("target.name") == "cs"'; + targetDescription = "Haxe/C#"; + case Eval: + extraParams = "eval"; + targetId = "eval"; + targetCondition = 'Context.definedValue("target.name") == "eval"'; + targetDescription = "Haxe interpreter"; + case Hashlink: + // var platformConfig = (cast Ammer.platformConfig : ammer.core.plat.Hashlink.HashlinkConfig); + // TODO: HL/C? + extraParams = "hl"; + targetId = "hl"; + targetCondition = 'Context.definedValue("target.name") == "hl"'; + targetDescription = "Haxe/HashLink"; + case Java: + // var platformConfig = (cast Ammer.platformConfig : ammer.core.plat.Java.JavaConfig); + // TODO: JVM? + extraParams = "java"; + targetId = "java"; + targetCondition = 'Context.definedValue("target.name") == "java"'; + targetDescription = "Haxe/Java"; + case Lua: + extraParams = "lua"; + targetId = "lua"; + targetCondition = 'Context.definedValue("target.name") == "lua"'; + targetDescription = "Haxe/Lua"; + case Neko: + extraParams = "neko"; + targetId = "neko"; + targetCondition = 'Context.definedValue("target.name") == "neko"'; + targetDescription = "Haxe/Neko"; + case Nodejs: + extraParams = "js && nodejs"; + targetId = "nodejs"; + targetCondition = 'Context.definedValue("target.name") == "js" && Context.defined("nodejs")'; + targetDescription = "Haxe/Node.js"; + case Python: + extraParams = "python"; + targetId = "python"; + targetCondition = 'Context.definedValue("target.name") == "python"'; + targetDescription = "Haxe/Python"; + case None: Context.fatalError("cannot bake without selecting platform", Context.currentPos()); + } + + // TODO: use as fragment in AmmerBaked? + var osName = (switch (Sys.systemName()) { + case "Windows": "win"; + case "Linux": "linux"; + case "BSD": "bsd"; + case "Mac": "mac"; + case _: "unknown"; // TODO: throw? + }); + var extensionDll = (switch (Sys.systemName()) { + case "Windows": "dll"; + case "Mac": "dylib"; + case _: "so"; + }); + var prefixLib = (switch (Sys.systemName()) { + case "Windows": ""; + case _: "lib"; + }); + + for (t in Utils.modifiedTypes) { + switch (t.t.kind) { + case KAbstractImpl(_.get() => abs): + var typeId = Utils.typeId(abs); + var output = modules.outputSub(abs.pack, abs.module.split(".").pop(), abs.name); + output + .ail("#if !macro"); + var isEnum = false; + for (meta in t.t.meta.get()) { + if (meta.name == ":build" || meta.name == ":autoBuild") + continue; + if (meta.name.startsWith(":ammer.")) + continue; + if (meta.name == ":enum") { + isEnum = true; + continue; + } + output.ail(printer.printMetadata(meta)); + } + output + .ai('${isEnum ? "enum " : ""}abstract ${abs.name}(${printer.printComplexType(TypeTools.toComplexType(abs.type))})') + .map(abs.from, t -> t.field == null ? ' from ${printer.printComplexType(TypeTools.toComplexType(t.t))}' : "") + .map(abs.to, t -> t.field == null ? ' to ${printer.printComplexType(TypeTools.toComplexType(t.t))}' : "") + .al(' {') + .lmap(t.fields, field -> printer.printField(field) + ";") + .ail("}") + .ail("#end /*!macro*/"); + case KNormal: + var typeId = Utils.typeId(t.t); + var isLibrary = Ammer.libraries.byTypeId.exists(typeId); + var output = modules.outputSub(t.t.pack, t.t.module.split(".").pop(), t.t.name); + output + .ail("#if !macro"); + for (meta in t.t.meta.get()) { + if (meta.name == ":build" || meta.name == ":autoBuild") + continue; + if (meta.name.startsWith(":ammer.")) + continue; + output.ail(printer.printMetadata(meta)); + } + output + .ail('${t.t.isExtern ? "extern " : ""}class ${t.t.name} {') + .lmap(t.fields, field -> printer.printField(field) + ";") + .ail("}") + .ail("#end /*!macro*/"); + + if (isLibrary) { + var outputMacro = modules.outputSub(t.t.pack, t.t.module.split(".").pop() + ".macro", t.t.name); + var ctx = Ammer.libraries.byTypeId[typeId]; + var targetScript = ""; + if (Ammer.platform.kind == Cpp) { + var extc = ctx.libraryOptions.language.extension(); + var exth = ctx.libraryOptions.language.extensionHeader(); + targetScript = new LineBuf() + .ail('var outputPath = haxe.macro.Compiler.getOutput();') + .ail('sys.FileSystem.createDirectory(outputPath + "/ammer_build/ammer_${ctx.name}");') + .ail('var herePath = haxe.macro.PositionTools.getInfos(info.herePos).file;') + .ail('if (!haxe.io.Path.isAbsolute(herePath)) herePath = haxe.io.Path.join([Sys.getCwd(), herePath]);') + .ail('herePath = haxe.io.Path.normalize(haxe.io.Path.directory(herePath));') + .ail('sys.io.File.copy(herePath + "/lib.${ctx.name}.cpp_static.$extc", outputPath + "/ammer_build/ammer_${ctx.name}/lib.cpp_static.$extc");') + .ail('sys.io.File.copy(herePath + "/lib.${ctx.name}.cpp_static.$exth", outputPath + "/ammer_build/ammer_${ctx.name}/lib.cpp_static.$exth");') + .done(); + } + + if (typeId != "ammer.internal.LibTypes.LibTypes") { + function sortedKeys(map:Map):Array { + var keys = [ for (k in map.keys()) k ]; + keys.sort(Reflect.compare); + return keys; + } + var sortedArrayNames = sortedKeys(ctx.info.arrays.byElementTypeId); + var sortedBoxNames = sortedKeys(ctx.info.boxes.byElementTypeId); + var sortedCallbackNames = sortedKeys(ctx.info.callbacks.byElementTypeId); + var sortedEnumNames = sortedKeys(ctx.info.enums); + var sortedHaxeRefNames = sortedKeys(ctx.info.haxeRefs.byElementTypeId); + var sortedOpaqueNames = sortedKeys(ctx.info.opaques); + var sortedStructNames = sortedKeys(ctx.info.structs); + var sortedSublibraryNames = sortedKeys(ctx.info.sublibraries); + + // TODO: share this with ammer-core somehow? or else make outputPathRelative private again + //var outputPathRelative = ctx.library.outputPathRelative(); + //var sourcePath = 'prebuilt-$$osName-$targetId.bin'; + var destPath = (switch (Ammer.platform.kind) { + // C++ (static) does not put the glue code into a dynamic lib. + case Cpp: null; + + case Cs: 'ammer_${ctx.name}.dll'; + case Hashlink: 'ammer_${ctx.name}.hdll'; // TODO: HL/C uses the standard DLL naming + case Neko: 'ammer_${ctx.name}.ndll'; + case Nodejs: 'ammer_${ctx.name}.node'; + case Python: 'ammer_${ctx.name}.$${osName == "win" ? "pyd" : "so"}'; + case _: '$${prefixLib}ammer_${ctx.name}.$${extensionDll}'; + }); + //var outputPathRelative = Ammer.baseConfig.outputPath + "/" + destPath; + + function printString(s:String):String { + // not quote safe! + return s == null ? "null" : '"$s"'; + } + function printStringArray(s:Array):String { + return s == null ? "null" : '[${s.map(printString).join(", ")}]'; + } + function printExpr(e:Expr):String { + return e == null ? "null" : '(macro ${printer.printExpr(e)})'; + } + function printCt(ct:ComplexType):String { + return '(macro : ${printer.printComplexType(ct)})'; + } + /*function printType(t:Type):String { + return 'ComplexTypeTools.toType((macro : ${printer.printComplexType(TypeTools.toComplexType(t))}))'; + }*/ + + outputMacro.a( + File.getContent('$ammerRootPath/internal/v1/AmmerSetup.baked.hx') + .replace("/*libname*/", '${t.t.module.split(".").pop()}_${t.t.name}') + .replace("/*libinfo*/", new LineBuf() + .al("// ammer-bake-common") + .ail('/*ammer-bake-common-start*/ if ($targetCondition) {').i() + .ail(targetScript) + .ail('info.name = "${ctx.name}";') + .ail('info.arrays.byElementTypeId = [').i() + .lmap(sortedArrayNames, name -> { + var info = ctx.info.arrays.byElementTypeId[name]; + '"$name" => {\n' + + 'arrayCt: ${printCt(info.arrayCt)},\n' + + 'arrayRefCt: ${printCt(info.arrayRefCt)},\n' + + 'alloc: ${printExpr(info.alloc)},\n' + + 'fromHaxeCopy: ${printExpr(info.fromHaxeCopy)},\n' + + 'fromHaxeRef: ${printExpr(info.fromHaxeRef)},\n' + + '},'; + }) + .d().ail('];') + .ail('info.boxes.byElementTypeId = [').i() + .lmap(sortedBoxNames, name -> { + var info = ctx.info.boxes.byElementTypeId[name]; + '"$name" => {\n' + + 'boxCt: ${printCt(info.boxCt)},\n' + + 'alloc: ${printExpr(info.alloc)},\n' + + '},'; + }) + .d().ail('];') + .ail('info.callbacks.byElementTypeId = [').i() + .lmap(sortedCallbackNames, name -> { + var info = ctx.info.callbacks.byElementTypeId[name]; + '"$name" => {\n' + + 'isGlobal: ${info.isGlobal},\n' + + 'callbackCt: ${printCt(info.callbackCt)},\n' + + 'funCt: ${printCt(info.funCt)},\n' + + 'callbackName: ${printString(info.callbackName)},\n' + + '},'; + }) + .d().ail('];') + .ail('info.enums = [').i() + .lmap(sortedEnumNames, name -> { + var info = ctx.info.enums[name]; + '"$name" => {},\n'; + }) + .d().ail('];') + .ail('info.haxeRefs.byElementTypeId = [').i() + .lmap(sortedHaxeRefNames, name -> { + var info = ctx.info.haxeRefs.byElementTypeId[name]; + '"$name" => {' + + 'create: ${printExpr(info.create)},\n' + + '},\n'; + }) + .d().ail('];') + .ail('info.opaques = [').i() + .lmap(sortedOpaqueNames, name -> { + var info = ctx.info.opaques[name]; + '"$name" => {\n' + + 'opaqueName: "${info.opaqueName}",\n' + + '},'; + }) + .d().ail('];') + .ail('info.structs = [').i() + .lmap(sortedStructNames, name -> { + var info = ctx.info.structs[name]; + '"$name" => {\n' + + 'alloc: ${info.alloc},\n' + + 'gen: {\n' + + 'alloc: ${printString(info.gen.alloc)},\n' + + 'free: ${printString(info.gen.free)},\n' + + 'nullPtr: ${printString(info.gen.nullPtr)},\n' + + '},\n' + + 'structName: "${info.structName}",\n' + + '},'; + }) + .d().ail('];') + .ail('info.sublibraries = [').i() + .lmap(sortedSublibraryNames, name -> { + var info = ctx.info.sublibraries[name]; + '"$name" => {},'; + }) + .d().ail('];') + .ail('info.setupToBin = "${t.t.pack.map(_ -> "..").concat(rootToBin.split("/")).join("/")}";') + .ifi(destPath != null) + .ail("info.files.push({").i() + .ail('dst: \'$destPath\',') + .ail("sources: [").i() + .map(fileSources, source -> new LineBuf().ail("{").i() + .ail('name: ${printString(source.name)},') + //.ail('digest: ${printString(haxe.crypto.Sha256.make(File.getBytes('$bakeOutput/$rootToBin/${extensions(outputPathRelative)}')).toHex())},') + .ail('description: "pre-compiled library for $targetDescription",') + .ail('downloadFrom: ${printString(source.downloadFrom)},') + .ail('os: ${printString(source.os)},') + .ail('architectures: ${printStringArray(source.architectures)},') + .ail('minVersion: ${printString(source.minVersion)},') + .ail('maxVersion: ${printString(source.maxVersion)},') + .d().ail("},").done()) + .d().ail("],") + .d().ail("});") + .ifd() + .d().ail("/*ammer-bake-common-end*/ }") + .done()) + ); + } + outputMacro.al('class ${t.t.name} {}'); + } + case _: throw 0; + } + } + for (t in ammer.core.utils.TypeUtils.definedTypes) { + var extraMeta = []; + t.meta = [ for (meta in t.meta) { + if (meta.name == ":buildXml") switch (meta.params) { + case [{expr: EConst(CString(val))}] if (val.startsWith("")[0]; + var ctx = Ammer.libraries.byLibraryName[libname]; + ctx != null || throw 0; + var includePaths = ctx.originalOptions.includePaths.map(p -> macro $v{p.rel}); + var libraryPaths = ctx.originalOptions.libraryPaths.map(p -> macro $v{p.rel}); + var includePathsArr = macro $a{includePaths}; + var libraryPathsArr = macro $a{libraryPaths}; + extraMeta.push({ + name: ":build", + params: [macro ammer.internal.v1.RelativePathsHelper.build($includePathsArr, $libraryPathsArr)], + pos: meta.pos, + }); + continue; + case _: + } + meta; + } ]; + t.meta = t.meta.concat(extraMeta); + modules.outputSub(t.pack, t.name, t.name) + .ail(printer.printTypeDefinition(t, false)); + } + for (t in Utils.definedTypes) { + modules.outputSub(t.pack, t.name, t.name) + .ail(printer.printTypeDefinition(t, false)); + } + + modules.outputCombined(extraParams, bakeOutput); + + // TODO: paths (handle backslashes etc on Windows) + // This is a little "preprocessor" system to reduce code duplication. + // It isn't great! + FileSystem.createDirectory('$bakeOutput/ammer/internal/v1'); + for (req in [ + ["internal/v1/AmmerBaked.hx", "internal/v1/AmmerBaked.hx"], + ["internal/v1/LibInfo.hx", "internal/v1/LibInfo.hx"], + ["internal/v1/OsInfo.hx", "internal/v1/OsInfo.hx"], + ["internal/v1/RelativePathsHelper.hx", "internal/v1/RelativePathsHelper.hx"], + ["Lib.hx", "Lib.hx"], + ["Lib.macro.baked.hx", "Lib.macro.hx"], + ["internal/FilePtrOutput.hx", "internal/FilePtrOutput.hx"], + ]) { + var content = File.getContent('$ammerRootPath/${req[0]}') + .split("\n") + .map(l -> { + var lt = l.trim(); + if (!lt.startsWith("// ammer-include: ")) return l; + var params = lt.substr("// ammer-include: ".length).split(" "); + var refContent = File.getContent('$ammerRootPath/${params[0]}'); + var spl = refContent.split('// ammer-fragment-begin: ${params[1]}'); + spl.length == 2 || throw 'expected fragment begin ${params[1]} in ${params[0]}'; + spl = spl[1].split('// ammer-fragment-end: ${params[1]}'); + spl.length == 2 || throw 'expected fragment end ${params[1]} in ${params[0]}'; + spl[0]; + }) + .join("\n"); + File.saveContent('$bakeOutput/ammer/${req[1]}', content); + } + } + + public static function multiBake(platforms:Array, output:String):Void { + var modules = new BakeryOutput( + () -> { + platforms: ([] : Map), + common: ([] : Array), + }, + mod -> { + var platNames = [ for (k in mod.platforms.keys()) k ]; + platNames.sort(Reflect.compare); + var merged = new LineBuf(); + for (p in platNames) { + merged + .al('#if (${p})') + .al(mod.platforms[p] + .replace("// ammer-bake-common", mod.common.join("\n"))) + .al('#end /*(${p})*/'); + } + merged.done(); + } + ); + + function walkPath(base:String, ext:String):Void { + var path = '$base$ext'; + if (FileSystem.isDirectory(path)) { + FileSystem.readDirectory(path).iter(f -> { + if (f.startsWith(".")) return; + walkPath(base, '$ext/$f'); + }); + } else { + if (!path.endsWith(".hx")) return; + var content = File.getContent(path); + if (!content.startsWith(BAKE_PREFIX)) + throw Context.fatalError('unexpected file in bake path: $path', Context.currentPos()); + var lines = content.split("\n"); + var bakeParams = lines[0].substr(BAKE_PREFIX.length).split(" "); + + var bakePack = bakeParams[0].split("."); + var bakeModule = bakeParams[1]; + var bakeExtra = bakeParams.slice(2).join(" "); + if (path.endsWith(".macro.hx")) bakeExtra = "true"; // join macro files + + var content = []; + var bakeCommon = []; + var isCommon = false; + for (lnum => line in lines) { + if (line.contains("/*ammer-bake-common-start*/")) isCommon = true; + if (isCommon) bakeCommon.push(line); + else if (lnum >= 2) { + // remove bake prefix, package, and common lines (will be merged later) + content.push(line); + } + if (line.contains("/*ammer-bake-common-end*/")) isCommon = false; + } + + var outputSub = modules.outputSub(bakePack, bakeModule, "true"); + outputSub.platforms[bakeExtra] = content.join("\n"); + outputSub.common = outputSub.common.concat(bakeCommon); + } + } + platforms.iter(p -> walkPath(p, "")); + + modules.outputCombined("combined", output); + } +} + +class BakeryOutput { + public static function withBufs():BakeryOutput { + return new BakeryOutput(() -> new LineBuf(), buf -> buf.done()); + } + + var modules:Map> = []; + var create:()->T; + var finish:T->String; + + public function new(create:()->T, finish:T->String) { + this.create = create; + this.finish = finish; + } + + public function outputSub(pack:Array, module:String, sub:String):T { + var mid = pack.concat([module]).join("."); + if (!modules.exists(mid)) modules[mid] = new Map(); + if (!modules[mid].exists(sub)) modules[mid][sub] = create(); + return modules[mid][sub]; + } + + public function outputCombined(extraParams:String, outputPath:String):Void { + for (module => types in modules) { + var pack = module.split("."); + var module = pack.pop(); + if (module == "macro") { + module = '${pack.pop()}.$module'; + } + var subNames = [ for (k in types.keys()) k ]; + subNames.sort(Reflect.compare); + var moduleOutput = new LineBuf() + .ail('${Bakery.BAKE_PREFIX}${pack.join(".")} $module $extraParams') + .ail('package ${pack.join(".")};'); + for (subName in subNames) { + moduleOutput.al(finish(types[subName])); + } + + var out = outputPath + "/" + pack.join("/"); + FileSystem.createDirectory(out); + File.saveContent('${out}/${module}.hx', moduleOutput.done()); + } + } +} + +#end diff --git a/src/ammer/internal/Config.hx b/src/ammer/internal/Config.hx new file mode 100644 index 0000000..7fe79f3 --- /dev/null +++ b/src/ammer/internal/Config.hx @@ -0,0 +1,85 @@ +package ammer.internal; + +#if macro + +import haxe.io.Path; +import haxe.macro.Compiler; +import haxe.macro.Context; + +class Config { + static final BOOL_YES = ["yes", "y", "true", "1", "on"]; + static final BOOL_NO = ["no", "n", "false", "0", "off"]; + + public static function hasDefine(key:String):Bool { + return Context.defined(key); + } + + /** + Gets a compile-time define by `key`. If the specified key is not defined, + return the value `dv`, or throw an error if `doThrow` is `true`. + **/ + public static function getString(key:String, ?dv:String, ?doThrow:Bool = false):String { + if (Context.defined(key)) + return Context.definedValue(key); + if (doThrow) + Context.fatalError('missing required define: $key', Context.currentPos()); + return dv; + } + + public static function getStringArray(key:String, sep:String, ?dv:Array, ?doThrow:Bool = false):Array { + if (Context.defined(key)) + return Context.definedValue(key).split(sep); + if (doThrow) + Context.fatalError('missing required define: $key', Context.currentPos()); + return dv; + } + + /** + Gets a boolean from the compile-time define `key`. + **/ + public static function getBool(key:String, ?dv:Bool, ?doThrow:Bool = false):Bool { + if (Context.defined(key)) { + if (BOOL_YES.indexOf(Context.definedValue(key)) != -1) + return true; + if (BOOL_NO.indexOf(Context.definedValue(key)) != -1) + return false; + Context.fatalError('invalid define (should be yes or no): $key', Context.currentPos()); + } + if (doThrow) + Context.fatalError('missing required define: $key', Context.currentPos()); + return dv; + } + + public static function getInt(key:String, ?dv:Int, ?doThrow:Bool = false):Int { + if (Context.defined(key)) + return Std.parseInt(Context.definedValue(key)); + if (doThrow) + Context.fatalError('missing required define: $key', Context.currentPos()); + return dv; + } + + /** + Gets a path from the compile-time define `key`. If the path is relative, + resolve it relative to the current working directory. + **/ + public static function getPath(key:String, ?dv:String, ?doThrow:Bool = false):String { + var p = getString(key, dv, doThrow); + if (p != null && !Path.isAbsolute(p)) + p = Path.join([Sys.getCwd(), p]); + return p; + } + + public static function getEnum(key:String, map:Map, ?dv:T, ?doThrow:Bool = false):T { + var p = getString(key, null, doThrow); + if (p == null) + return dv; + if (!map.exists(p)) { + var keys = [for (k in map.keys()) k]; + keys.sort(Reflect.compare); + Context.fatalError('invalid define (should be one of ${keys.join(", ")})', Context.currentPos()); + } + return map[p]; + } +} + +#end diff --git a/src/ammer/internal/Entrypoint.hx b/src/ammer/internal/Entrypoint.hx new file mode 100644 index 0000000..d8d6f78 --- /dev/null +++ b/src/ammer/internal/Entrypoint.hx @@ -0,0 +1,922 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Compiler; +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.TypeTools; +import haxe.io.Path; +import ammer.core.utils.TypeUtils; + +using Lambda; + +/** + Various entrypoints into `ammer` processing from user-defined types extending + the marker types from `ammer.def.*`, as well as some standard library types. + + The methods in this class can be categorised as follows: + - `genericBuild...` methods for `ammer.ffi.*` types. + (`...Array`, `...Box`, `...HaxeRef`) + These are invoked via `@:genericBuild` on marker types in the `ammer.ffi.*` + package. The method creates a monomorphisation of the requested type (e.g. + an array of 32-bit integers). + - `genericBuild...` methods for `ammer.def.*` types. + (`...Library`, `...Opaque`, `...Struct`, `...Sublibrary`) + These exist to attach a marker type to be used as a superclass for the + user-declared type. No processing is performed here except for validating + the type parameters. + - `autoBuild...` methods for "processed" types. + (`...Library`, `...Opaque`, `...Struct`, `...Sublibrary`) + These are invoked via `@:autoBuild` on the type resulting from the previous + category. The actual processing of user-declared types is performed here. + - `build...` methods for `ammer.def.*` types. + (`...Enum`) + These are invoked with a direct `@:build` metadata on a user-declared type. +**/ +class Entrypoint { + public static function genericBuildArray():ComplexType { + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.ffi.Array + case TInst(_, [el]): + var el = TypeTools.followWithAbstracts(el); + var elId = Utils.typeId2(el); + if (Ammer.mergedInfo.arrays.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.arrays.byElementTypeId[elId].arrayCt; + } + Reporting.log('enter genericBuildArray $elId', "stage-ffi"); + + Ammer.initSubtype('ammer.ffi.Array<$elId>', el, ctx -> { + // was it initialised in the meanwhile? + if (Ammer.mergedInfo.arrays.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.arrays.byElementTypeId[elId].arrayCt; + } + Reporting.log('process genericBuildArray $elId', "stage-ffi"); + + var elRes = Types.resolveType(el, ctx); + + (elRes.wrap == null && elRes.unwrap == null) || throw Reporting.error("unsupported array element type"); + var elCt = elRes.haxeType; + var marshal = ctx.marshal.arrayPtr(elRes.marshal); + + var arrayTp:TypePath = { + name: 'Array_${elRes.marshal.mangled}', + pack: ["ammer", "gen"], + }; + var arrayCt = TPath(arrayTp); + + // force the box type to exist + var refElCt = elRes.marshal.arrayType != null ? elRes.marshal.arrayType : elCt; + var refEl = Context.resolveType(refElCt, Reporting.currentPos()); + var refElId = Utils.typeId2(refEl); + //Context.resolveType((macro : ammer.ffi.Box<$refElCt>), Reporting.currentPos()); + //var elBoxTp = Utils.expectTypePath(Ammer.mergedInfo.boxes.byElementTypeId[refElId].boxCt); + //var elBoxCt = TPath(elBoxTp); + + var defArray = macro class { + private var _ammer_native:Any; + private function new(native:Any) { + this._ammer_native = native; + } + public function get(index:Int):$elCt { + return $e{marshal.get(macro _ammer_native, macro index)}; + } + public function set(index:Int, val:$elCt):Void { + $e{marshal.set(macro _ammer_native, macro index, macro val)}; + } + //public function ref(index:Int):$elBoxCt { + // return @:privateAccess new $elBoxTp($e{marshal.ref(macro _ammer_native, macro index)}); + //} + public function free():Void { + $e{marshal.free(macro _ammer_native)}; + } + private static function nullPtr():$arrayCt { + return new $arrayTp($e{marshal.nullPtr}); + } + }; + defArray.name = arrayTp.name; + defArray.pack = arrayTp.pack; + Utils.defineType(defArray); + + // TODO: use abstract + var unref = marshal.fromHaxeRef != null + ? macro _ammer_core_native.unref() + : macro { + for (i in 0...vector.length) { + vector[i] = $e{marshal.get(macro _ammer_core_native, macro i)}; + } + }; + var defArrayRef = macro class { + private var _ammer_core_native:Dynamic; + private var vector:haxe.ds.Vector<$elCt>; + private function new( + _ammer_core_native:Dynamic, + vector:haxe.ds.Vector<$elCt> + ) { + this._ammer_core_native = _ammer_core_native; + this.vector = vector; + } + public var array(get, never):$arrayCt; + private inline function get_array():$arrayCt { + return @:privateAccess new $arrayTp(_ammer_core_native.ptr); + } + public function unref():Void { + if (_ammer_core_native != null) { + $unref; + _ammer_core_native = null; + } + } + }; + defArrayRef.name = 'ArrayRef_${elRes.marshal.mangled}'; + defArrayRef.pack = ["ammer", "gen"]; + var arrayRefCt = TPath({ + name: defArrayRef.name, + pack: defArrayRef.pack, + }); + Utils.defineType(defArrayRef); + + var array = { + arrayCt: arrayCt, + arrayRefCt: arrayRefCt, + alloc: marshal.alloc(macro _size), + fromHaxeCopy: marshal.fromHaxeCopy(macro _vec), + fromHaxeRef: marshal.fromHaxeRef != null ? marshal.fromHaxeRef(macro _vec) : null, + + elementType: el, + arrayMarshal: marshal, + }; + + Ammer.mergedInfo.arrays.byTypeId['ammer.gen.${defArray.name}.${defArray.name}'] = array; + Ammer.mergedInfo.arrays.byElementTypeId[elId] = array; + ctx.info.arrays.byTypeId['ammer.gen.${defArray.name}.${defArray.name}'] = array; + ctx.info.arrays.byElementTypeId[elId] = array; + + Reporting.log('exit genericBuildArray $elId', "stage-ffi"); + arrayCt; + }); + case _: throw 0; + }); + } + + public static function genericBuildBox():ComplexType { + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.ffi.Box + case TInst(_, [el]): + var elId = Utils.typeId2(el); + if (Ammer.mergedInfo.boxes.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.boxes.byElementTypeId[elId].boxCt; + } + Reporting.log('enter genericBuildBox $elId', "stage-ffi"); + + Ammer.initSubtype('ammer.ffi.Box<$elId>', el, ctx -> { + // was it initialised in the meanwhile? + if (Ammer.mergedInfo.boxes.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.boxes.byElementTypeId[elId].boxCt; + } + Reporting.log('process genericBuildBox $elId', "stage-ffi"); + + var elRes = Types.resolveType(el, ctx); + + var elCt = elRes.haxeType; + var marshal = ctx.marshal.boxPtr(elRes.marshal); + + var boxTp:TypePath = { + name: 'Box_${elRes.marshal.mangled}', + pack: ["ammer", "gen"], + }; + var boxCt = TPath(boxTp); + var defBox = macro class { + private var _ammer_native:Any; + private function new(native:Any) { + this._ammer_native = native; + } + public function get():$elCt { + return $e{Utils.exprMap(marshal.get(macro _ammer_native), elRes.wrap)}; + } + public function set(val:$elCt):Void { + $e{marshal.set(macro _ammer_native, Utils.exprMap(macro val, elRes.unwrap))}; + } + public function free():Void { + $e{marshal.free(macro _ammer_native)}; + } + private static function nullPtr():$boxCt { + return new $boxTp($e{marshal.nullPtr}); + } + }; + defBox.name = boxTp.name; + defBox.pack = boxTp.pack; + Utils.defineType(defBox); + + var box = { + elementType: el, + boxCt: boxCt, + alloc: marshal.alloc, + boxMarshal: marshal, + }; + Ammer.mergedInfo.boxes.byTypeId['ammer.gen.${defBox.name}.${defBox.name}'] = box; + Ammer.mergedInfo.boxes.byElementTypeId[elId] = box; + ctx.info.boxes.byTypeId['ammer.gen.${defBox.name}.${defBox.name}'] = box; + ctx.info.boxes.byElementTypeId[elId] = box; + + Reporting.log('exit genericBuildBox $elId', "stage-ffi"); + boxCt; + }); + case _: throw 0; + }); + } + + public static function buildBytes():Array { + var pos = Context.currentPos(); + return Reporting.withPosition(pos, () -> { + var implType = Context.getLocalClass().get(); + + Reporting.log("enter buildBytes", "stage-ffi"); + + var lib = Context.resolveType((macro : ammer.internal.LibTypes), pos); + var fields:Array = Ammer.initSubtype(Utils.typeId(implType), lib, ctx -> { + Reporting.log("process buildBytes", "stage-ffi"); + var marshal = ctx.marshal.bytes(); + var toHaxeRef = marshal.toHaxeRef != null + ? macro return $e{marshal.toHaxeRef(macro _ammer_native, macro size)} + : macro throw "platform does not support non-copy references to Bytes"; + var fromHaxeRef = marshal.fromHaxeRef != null + ? macro return @:privateAccess new ammer.ffi.BytesRef($e{marshal.fromHaxeRef(macro bytes)}, bytes) + : macro return @:privateAccess new ammer.ffi.BytesRef($e{marshal.fromHaxeCopy(macro bytes)}, bytes); + var fromHaxeRefForce = marshal.fromHaxeRef != null + ? macro return @:privateAccess new ammer.ffi.BytesRef($e{marshal.fromHaxeRef(macro bytes)}, bytes) + : macro throw "platform does not support non-copy references from Bytes"; + var fields = (macro class { + private var _ammer_native:Any; + private function new(native:Any) { + this._ammer_native = native; + } + + public function get8(index:Int):Int return $e{marshal.get8(macro _ammer_native, macro index)}; + public function get16(index:Int):Int return $e{marshal.get16(macro _ammer_native, macro index)}; + public function get32(index:Int):Int return $e{marshal.get32(macro _ammer_native, macro index)}; + public function get64(index:Int):haxe.Int64 return $e{marshal.get64(macro _ammer_native, macro index)}; + + public function set8(index:Int, val:Int):Void $e{marshal.set8(macro _ammer_native, macro index, macro val)}; + public function set16(index:Int, val:Int):Void $e{marshal.set16(macro _ammer_native, macro index, macro val)}; + public function set32(index:Int, val:Int):Void $e{marshal.set32(macro _ammer_native, macro index, macro val)}; + public function set64(index:Int, val:haxe.Int64):Void $e{marshal.set64(macro _ammer_native, macro index, macro val)}; + + public function get16be(index:Int):Int return $e{marshal.get16be(macro _ammer_native, macro index)}; + public function get32be(index:Int):Int return $e{marshal.get32be(macro _ammer_native, macro index)}; + public function get64be(index:Int):haxe.Int64 return $e{marshal.get64be(macro _ammer_native, macro index)}; + public function set16be(index:Int, val:Int):Void $e{marshal.set16be(macro _ammer_native, macro index, macro val)}; + public function set32be(index:Int, val:Int):Void $e{marshal.set32be(macro _ammer_native, macro index, macro val)}; + public function set64be(index:Int, val:haxe.Int64):Void $e{marshal.set64be(macro _ammer_native, macro index, macro val)}; + + public function get16le(index:Int):Int return $e{marshal.get16le(macro _ammer_native, macro index)}; + public function get32le(index:Int):Int return $e{marshal.get32le(macro _ammer_native, macro index)}; + public function get64le(index:Int):haxe.Int64 return $e{marshal.get64le(macro _ammer_native, macro index)}; + public function set16le(index:Int, val:Int):Void $e{marshal.set16le(macro _ammer_native, macro index, macro val)}; + public function set32le(index:Int, val:Int):Void $e{marshal.set32le(macro _ammer_native, macro index, macro val)}; + public function set64le(index:Int, val:haxe.Int64):Void $e{marshal.set64le(macro _ammer_native, macro index, macro val)}; + + public static function alloc(size:Int):ammer.ffi.Bytes return new ammer.ffi.Bytes($e{marshal.alloc(macro size)}); + public static function zalloc(size:Int):ammer.ffi.Bytes return new ammer.ffi.Bytes($e{marshal.zalloc(macro size)}); + public static function nullPtr():ammer.ffi.Bytes return new ammer.ffi.Bytes($e{marshal.nullPtr}); + public function free():Void { + $e{marshal.free(macro _ammer_native)}; + _ammer_native = null; + } + public function copy(size:Int):Bytes return new ammer.ffi.Bytes($e{marshal.copy(macro _ammer_native, macro size)}); + public static function blit(source:Bytes, sourcepos:Int, dest:Bytes, destpos:Int, size:Int):Void { + $e{marshal.blit(macro source._ammer_native, macro sourcepos, macro dest._ammer_native, macro destpos, macro size)}; + } + public function offset(pos:Int):Bytes return new ammer.ffi.Bytes($e{marshal.offset(macro _ammer_native, macro pos)}); + + public function toHaxeCopy(size:Int):haxe.io.Bytes return $e{marshal.toHaxeCopy(macro _ammer_native, macro size)}; + public static function fromHaxeCopy(bytes:haxe.io.Bytes):Bytes return new ammer.ffi.Bytes($e{marshal.fromHaxeCopy(macro bytes)}); + public function toHaxeRef(size:Int):haxe.io.Bytes $toHaxeRef; + public static function fromHaxeRef(bytes:haxe.io.Bytes):ammer.ffi.BytesRef $fromHaxeRef; + public static function fromHaxeRefForce(bytes:haxe.io.Bytes):ammer.ffi.BytesRef $fromHaxeRefForce; + }).fields; + + // TODO: use abstract + var ptr = marshal.fromHaxeRef != null + ? macro _ammer_core_native.ptr + : macro _ammer_core_native; + var unref = marshal.fromHaxeRef != null + ? macro _ammer_core_native.unref() + : macro { + for (i in 0...hbytes.length) { + hbytes.set(i, $e{marshal.get8(ptr, macro i)}); + } + $e{marshal.free(ptr)}; + }; + var defBytesRef = macro class { + private var _ammer_core_native:Dynamic; + private var hbytes:haxe.io.Bytes; + private function new( + _ammer_core_native:Dynamic, + hbytes:haxe.io.Bytes + ) { + this._ammer_core_native = _ammer_core_native; + this.hbytes = hbytes; + } + public var bytes(get, never):ammer.ffi.Bytes; + private inline function get_bytes():ammer.ffi.Bytes { + return @:privateAccess new ammer.ffi.Bytes($ptr); + } + public function unref():Void { + if (_ammer_core_native != null) { + $unref; + _ammer_core_native = null; + } + } + }; + defBytesRef.name = "BytesRef"; + defBytesRef.pack = ["ammer", "ffi"]; + var bytesRefCt = TPath({ + name: defBytesRef.name, + pack: defBytesRef.pack, + }); + Utils.defineType(defBytesRef); + + fields; + }); + + Reporting.log("exit buildBytes", "stage-ffi"); + Utils.modifyType(implType, fields); + }); + } + + public static function genericBuildCallback():ComplexType { + var pos = Context.currentPos(); + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.ffi.Callback + case TInst(_, [cbType, fnType, callTarget, callArgs, lib]): + switch (fnType) { + case TFun(_, _): + case _: throw Reporting.error("ammer.ffi.Callback second type parameter should be a function type"); + } + + var isGlobal = false; + var callTarget:Expr = (switch (callTarget) { + case TInst(_.get() => {kind: KExpr({expr: EConst(CString("global"))})}, []): + isGlobal = true; + null; + case TInst(_.get() => {kind: KExpr({expr: EArrayDecl([expr])})}, []): + expr; + case _: + throw Reporting.error("ammer.ffi.Callback third type parameter should be an array with a single expression or \"global\""); + }); + var callArgs = Utils.exprArrayOfType(callArgs); + callArgs != null + || throw Reporting.error("ammer.ffi.Callback fourth type parameter should be an array of expressions"); + + var printer = new haxe.macro.Printer(); + var elId = StringTools.hex(haxe.crypto.Crc32.make(haxe.io.Bytes.ofString(ammer.core.utils.Mangle.parts([ + Utils.typeId2(cbType), + Utils.typeId2(fnType), + printer.printExpr(callTarget), + printer.printExpr(macro $a{callArgs}), + ]))), 8); + if (Ammer.mergedInfo.callbacks.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.callbacks.byElementTypeId[elId].callbackCt; + } + Reporting.log('enter genericBuildCallback $elId', "stage"); + + var callbackTp:TypePath = { + name: 'Callback_$elId', + pack: ["ammer", "gen"], + }; + var callbackCt = TPath(callbackTp); + var funCt = TypeTools.toComplexType(fnType); + var callback:ammer.internal.v1.LibInfo.LibInfoCallback = { + isGlobal: isGlobal, + callbackCt: callbackCt, + funCt: funCt, + callbackName: null, + }; + var typeId = Utils.typeIdTp(callbackTp); + + Ammer.initSubtype('ammer.ffi.Callback<$elId>', lib, ctx -> { + // was it initialised in the meanwhile? + if (Ammer.mergedInfo.callbacks.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.callbacks.byElementTypeId[elId].callbackCt; + } + Reporting.log('process genericBuildCallback $elId', "stage"); + callback.ctx = ctx; + Ammer.mergedInfo.callbacks.byTypeId[typeId] = callback; + Ammer.mergedInfo.callbacks.byElementTypeId[elId] = callback; + ctx.info.callbacks.byTypeId[typeId] = callback; + ctx.info.callbacks.byElementTypeId[elId] = callback; + + var cbArgsRes; + var cbRetRes; + switch (cbType) { + case TFun(args, ret): + cbArgsRes = args.map(arg -> Types.resolveType(arg.t, ctx)); + cbRetRes = Types.resolveType(ret, ctx); + case _: throw Reporting.error("ammer.ffi.Callback first type parameter should be a function type"); + } + + var invoke = []; + for (idx => arg in cbArgsRes) { + if (arg.wrap != null) { + var ident = 'arg$idx'; + invoke.push(macro var $ident = $e{Utils.exprMap(macro $i{ident}, arg.wrap)}); + } + } + invoke.push(isGlobal + ? (macro return @:privateAccess $p{Utils.accessTp(callbackTp)}.stored.value($a{callArgs})) + : (macro return $e{callTarget}.value($a{callArgs}))); + callback.callbackName = ctx.library.addStaticCallback( + cbRetRes.marshal, + cbArgsRes.map(arg -> arg.marshal), + macro $b{invoke} + ); + + var defCallback = isGlobal + ? (macro class { + static var stored:ammer.ffi.Haxe<$funCt> = null; + public static function store(val:$funCt):Void { + if (stored != null) stored.decref(); + stored = ammer.Lib.createHaxeRef((_ : $funCt), val); + stored.incref(); + } + }) + : (macro class {}); + defCallback.name = callbackTp.name; + defCallback.pack = callbackTp.pack; + Utils.defineType(defCallback); + + Reporting.log('exit genericBuildCallback $elId', "stage"); + callbackCt; + }); + case _: throw 0; + }); + } + + public static function genericBuildHaxeRef():ComplexType { + var pos = Context.currentPos(); + return Reporting.withPosition(pos, () -> switch (Context.getLocalType()) { + // ammer.ffi.HaxeRef + case TInst(_, [el]): + var elId = Utils.typeId2(el); + if (Ammer.mergedInfo.haxeRefs.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.haxeRefs.byElementTypeId[elId].marshal.refCt; + } + Reporting.log('enter genericBuildHaxeRef $elId', "stage-ffi"); + + Ammer.initSubtype('ammer.ffi.Haxe<$elId>', el, ctx -> { + // was it initialised in the meanwhile? + if (Ammer.mergedInfo.haxeRefs.byElementTypeId.exists(elId)) { + return Ammer.mergedInfo.haxeRefs.byElementTypeId[elId].marshal.refCt; + } + Reporting.log('process genericBuildHaxeRef $elId', "stage-ffi"); + + var elCt = TypeTools.toComplexType(el); + var marshal = ctx.marshal.haxePtr(elCt); + var create = marshal.create(macro _hxval); + var haxeRef = { + ctx: ctx, + create: create, + elementType: el, + marshal: marshal, + }; + + var typeId = Utils.typeIdTp(ammer.core.utils.TypeUtils.complexTypeToPath(haxeRef.marshal.refCt)); + Ammer.mergedInfo.haxeRefs.byTypeId[typeId] = haxeRef; + Ammer.mergedInfo.haxeRefs.byElementTypeId[elId] = haxeRef; + ctx.info.haxeRefs.byTypeId[typeId] = haxeRef; + ctx.info.haxeRefs.byElementTypeId[elId] = haxeRef; + + Reporting.log('exit genericBuildHaxeRef $elId', "stage-ffi"); + haxeRef.marshal.refCt; + }, ctxLibTypes -> { + var elCt = TypeTools.toComplexType(el); + Ammer.mergedInfo.haxeRefs.byElementTypeId.exists(".Any.Any") || throw 0; + //var anyRefCt = Ammer.haxes.byElementTypeId[".Any.Any"].marshal.refCt; + + // TODO: emit warning? + Reporting.log('exit genericBuildHaxeRef $elId (as HaxeAnyRef)', "stage-ffi"); + (macro : ammer.internal.LibTypes.HaxeAnyRef<$elCt>); + }); + case _: throw 0; + }); + } + + public static function genericBuildLibrary():ComplexType { + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.def.Library + case TInst(_, [tp1]): + var e1 = Utils.exprOfType(tp1); + (e1 != null && Utils.stringOfParam(tp1) != null) + || throw Reporting.error("ammer.def.Library: type parameter should be a string literal"); + TPath({ + pack: ["ammer", "internal"], + name: "Entrypoint", + sub: "LibraryProcessed", + params: [TPExpr(e1)], + }); + case _: throw 0; + }); + } + + public static function genericBuildOpaque():ComplexType { + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.def.Opaque + case TInst(_, [tp1, tp2]): + var e1 = Utils.exprOfType(tp1); + (e1 != null && Utils.stringOfParam(tp1) != null) + || throw Reporting.error("ammer.def.Opaque: first type parameter should be a string literal"); + Utils.classOfParam(tp2) != null + || throw Reporting.error("ammer.def.Opaque: second type parameter should be the parent library"); + TPath({ + pack: ["ammer", "internal"], + name: "Entrypoint", + sub: "OpaqueProcessed", + params: [TPExpr(e1), TPType(TypeTools.toComplexType(tp2))], + }); + case _: throw 0; + }); + } + + public static function genericBuildStruct():ComplexType { + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.def.Struct + case TInst(_, params = [tp1, tp2]): + var e1 = Utils.exprOfType(tp1); + (e1 != null && Utils.stringOfParam(tp1) != null) + || throw Reporting.error("ammer.def.Opaque: first type parameter should be a string literal"); + Utils.classOfParam(tp2) != null + || throw Reporting.error("ammer.def.Opaque: second type parameter should be the parent library"); + TPath({ + pack: ["ammer", "internal"], + name: "Entrypoint", + sub: "StructProcessed", + params: [TPExpr(e1), TPType(TypeTools.toComplexType(tp2))], + }); + case _: throw 0; + }); + } + + public static function genericBuildSublibrary():ComplexType { + return Reporting.withCurrentPos(() -> switch (Context.getLocalType()) { + // ammer.def.Sublibrary + case TInst(_, [tp1]): + Utils.classOfParam(tp1) != null + || throw Reporting.error("ammer.def.Sublibrary: type parameter should be the parent library"); + TPath({ + pack: ["ammer", "internal"], + name: "Entrypoint", + sub: "SublibraryProcessed", + params: [TPType(TypeTools.toComplexType(tp1))], + }); + case _: throw 0; + }); + } + + public static function buildEnum(name:String, el:Expr, lib:Expr):Array { + return Reporting.withCurrentPos(() -> { + var implType = Context.getLocalClass().get(); + var absType = (switch (implType.kind) { + case KAbstractImpl(_.get() => abs): abs; + case _: throw Reporting.error("ammer.def.Enum.build should be applied onto an enum abstract"); + }); + var implId = Utils.typeId(absType); + Reporting.log('enter buildEnum $implId "$name"', "stage"); + + var haxeEl = absType.type; + + var elCt = Utils.complexTypeExpr(el); + elCt != null || throw Reporting.error("ammer.def.Enum.build second argument should be an ammer FFI type"); + var el = Context.resolveType(elCt, Reporting.currentPos()); + + var libCt = Utils.complexTypeExpr(lib); + libCt != null || throw Reporting.error("ammer.def.Enum.build third argument should be the parent library"); + var lib = Context.resolveType(libCt, Reporting.currentPos()); + + var options:ammer.internal.v1.LibInfo.LibInfoEnum = {}; + Ammer.mergedInfo.enums[implId] = options; + + var implFields = Context.getBuildFields(); + var fields:Array = Ammer.initSubtype(implId, lib, ctx -> { + Reporting.log('process buildEnum $implId "$name"', "stage"); + + var rawElRes = Types.resolveType(el, ctx); + var elRes = ctx.marshal.enumInt(name, rawElRes.marshal); + switch (elRes.mangled) { + case "u1" | "i8" | "i16" | "i32" | "u8" | "u16" | "u32": + // TODO: other types: (though floats and strings don't make much sense for enums) + // "i64" | "u64" + // "f32" | "f64" + // "s" + case _: throw Reporting.error("ammer.def.Enum.build first argument can only be a primitve type"); + } + options.marshal = elRes; + + var extract = new ammer.core.utils.LineBuf() + .ail('#include ') + .ail('int main() {') + .i(); + var extractedFields = []; + var dbgCtr = 0; + for (field in implFields) switch (field.kind) { + case FVar(_, null) if (!field.access.contains(AStatic)): + var native = field.name; + for (meta in Meta.extract(field.meta, Meta.ENUM_FIELD)) switch (meta) { + case PMNative(name): native = name; + case _: throw 0; + } + extract.ail('printf("%d\\n", $native);'); + extractedFields.push(field); + case _: throw Reporting.error("unexpected field"); + } + extract.d().ail("}"); + + var data = ctx.prebuildImmediate(extract.done()).split("\n"); + [ for (field in implFields) { + var index = extractedFields.indexOf(field); + if (index == -1) { + field; + } else { + var val = Std.parseInt(data[index]); + Utils.updateField(field, FVar(null, macro $v{val})); + } + } ]; + // TODO: re-process through Fields? + }); + + Reporting.log('exit buildEnum $implId "$name" (${fields.length} fields)', "stage"); + Utils.modifyType(implType, fields); + }); + } + + public static function autoBuildLibrary():Array { + return Reporting.withCurrentPos(() -> { + var implType = Context.getLocalClass().get(); + var implId = Utils.typeId(implType); + var libname = Utils.stringOfParam(implType.superClass.params[0]); + libname != null || throw 0; + + Reporting.log('enter autoBuildLibrary $implId "$libname"', "stage"); + + var options:LibContext.LibContextOptions = { + name: libname, + headers: [], + defines: [], + definesCodeOnly: [], + includePaths: [], + libraryPaths: [], + frameworks: [], + language: C, + linkNames: [], + }; + var optionsLinkNameDefault = true; + var fieldOptions:ammer.internal.v1.LibInfo.LibInfoLibrary = {}; + var subtypes = []; + + var pathToFile = Path.normalize(Path.directory(haxe.macro.PositionTools.getInfos(implType.pos).file)); + if (!Path.isAbsolute(pathToFile)) pathToFile = Path.join([Sys.getCwd(), pathToFile]); + function relPath(p:String):{rel:String, abs:String} { + return { + rel: Path.normalize('${implType.pack.join("/")}/$p'), + abs: Path.join([pathToFile, p]), + }; + } + + for (meta in Meta.extract(implType.meta.get(), Meta.LIBRARY_CLASS)) switch (meta) { + case PMLib_Define(v): options.defines.push(v); + case PMLib_Define_CodeOnly(v): options.definesCodeOnly.push(v); + case PMLib_Framework(v): options.frameworks.push(v); + case PMLib_Frameworks(v): options.frameworks = options.frameworks.concat(v); + case PMLib_IncludePath(v): options.includePaths.push(relPath(v)); + case PMLib_IncludePaths(v): options.includePaths = options.includePaths.concat(v.map(relPath)); + case PMLib_Language(v): options.language = v; + case PMLib_LibraryPath(v): options.libraryPaths.push(relPath(v)); + case PMLib_LibraryPaths(v): options.libraryPaths = options.libraryPaths.concat(v.map(relPath)); + case PMLib_LinkName(v): optionsLinkNameDefault = false; options.linkNames.push(v); + case PMLib_LinkNames(v): optionsLinkNameDefault = false; options.linkNames = options.linkNames.concat(v); + case PMLib_Headers_Include(v): options.headers.push(IncludeLocal(v)); + case PMLib_Headers_Import(v): options.headers.push(ImportLocal(v)); + case PMLib_Headers_IncludeLocal(v): options.headers.push(IncludeLocal(v)); + case PMLib_Headers_ImportLocal(v): options.headers.push(ImportLocal(v)); + case PMLib_Headers_IncludeGlobal(v): options.headers.push(IncludeGlobal(v)); + case PMLib_Headers_ImportGlobal(v): options.headers.push(ImportGlobal(v)); + case PMNativePrefix(v): fieldOptions.nativePrefix = v; + case PMSub(v): subtypes.push(v); + case _: throw 0; + } + if (optionsLinkNameDefault) options.linkNames.push(libname); + + var isLibTypes = (implId == "ammer.internal.LibTypes.LibTypes"); + var fields:Array; + var implFields = Context.getBuildFields(); + if (isLibTypes && Bakery.isBaking) { + // When baking, `LibTypes` is treated as a sublibrary of the main + // library. As a result, its methods are compiled into the main library + // and it is not necessary to distribute an additional dynamic library + // with the program. When multiple baked libraries are used together, + // one `LibTypes` version is selected according to the order in which + // the baked libraries are introduced in the compiler invocation. + Reporting.log('processing autoBuildLibrary $implId as a sublibrary for the main type', "stage"); + + var options:ammer.internal.v1.LibInfo.LibInfoSublibrary = {}; + Ammer.mergedInfo.sublibraries[implId] = options; + + fields = Ammer.initSubtype(implId, Bakery.mainType, ctx -> { + Reporting.log('process autoBuildLibrary $implId "$libname"', "stage"); + options.ctx = ctx; + ctx.info.sublibraries[implId] = options; + + // TODO: this is a bit hacky; it exists so that `Types.resolveContext` + // finds the main library instead of `LibTypes`. + Ammer.libraries.byLibraryName["libtypes"] = ctx; + Ammer.libraries.byTypeId[implId] = ctx; + + Ammer.contextReady(implType, ctx); + subtypes.iter(Utils.triggerTyping); + Fields.process( + implFields, + ctx, + FCSublibrary(options) + ); + }); + } else { + var ctx = Ammer.initLibrary(implType, libname, options); + subtypes.iter(Utils.triggerTyping); + fields = Fields.process( + implFields, + ctx, + FCLibrary(fieldOptions) + ); + ctx.finalise(); + } + + Reporting.log('exit autoBuildLibrary $implId "$libname" (${fields.length} fields)', "stage"); + Utils.modifyType(implType, fields); + }); + } + + public static function autoBuildOpaque():Array { + return Reporting.withCurrentPos(() -> { + var implRef = Context.getLocalClass(); + var implType = implRef.get(); + var implId = Utils.typeId(implType); + var opaqueName = Utils.stringOfParam(implType.superClass.params[0]); + opaqueName != null || throw 0; + + Reporting.log('enter autoBuildOpaque $implId "$opaqueName"', "stage"); + + implType.params.length == 0 || throw Reporting.error("ammer opaque types cannot have type parameters"); + + var lib = Utils.classOfParam(implType.superClass.params[1]); + var options:ammer.internal.v1.LibInfo.LibInfoOpaque = { + implType: TInst(implRef, []), + opaqueName: opaqueName, + }; + Ammer.mergedInfo.opaques[implId] = options; + + for (meta in Meta.extract(implType.meta.get(), Meta.OPAQUE_CLASS)) switch (meta) { + case PMNativePrefix(v): options.nativePrefix = v; + case _: throw 0; + } + + var implFields = Context.getBuildFields(); + var fields:Array = Ammer.initSubtype(implId, implType.superClass.params[1], ctx -> { + Reporting.log('process autoBuildOpaque $implId "$opaqueName"', "stage"); + options.ctx = ctx; + ctx.info.opaques[implId] = options; + Fields.process( + implFields, + ctx, + FCOpaque(options) + ); + }); + + Reporting.log('exit autoBuildOpaque $implId "$opaqueName" (${fields.length} fields)', "stage"); + Utils.modifyType(implType, fields); + }); + } + + public static function autoBuildStruct():Array { + return Reporting.withCurrentPos(() -> { + var implRef = Context.getLocalClass(); + var implType = implRef.get(); + var implId = Utils.typeId(implType); + var structName = Utils.stringOfParam(implType.superClass.params[0]); + structName != null || throw 0; + + Reporting.log('enter autoBuildStruct $implId "$structName"', "stage"); + + implType.params.length == 0 || throw Reporting.error("ammer struct types cannot have type parameters"); + + var lib = Utils.classOfParam(implType.superClass.params[1]); + var alloc = false; + var options:ammer.internal.v1.LibInfo.LibInfoStruct = { + alloc: false, + gen: {}, + implType: TInst(implRef, []), + structName: structName, + }; + Ammer.mergedInfo.structs[implId] = options; + + for (meta in Meta.extract(implType.meta.get(), Meta.STRUCT_CLASS)) switch (meta) { + case PMAlloc: + options.alloc = true; + options.gen = { + alloc: "alloc", + free: "free", + nullPtr: "nullPtr", + }; + case PMGen_Alloc(v): + options.alloc = true; + options.gen.alloc = v; + case PMGen_Free(v): + options.alloc = true; + options.gen.free = v; + case PMGen_NullPtr(v): + options.alloc = true; + options.gen.nullPtr = v; + case PMNativePrefix(v): options.nativePrefix = v; + case PMSub(v): Utils.triggerTyping(v); // TODO: copy approach in Sublibrary + case _: throw 0; + } + + var implFields = Context.getBuildFields(); + var fields:Array = Ammer.initSubtype(implId, implType.superClass.params[1], ctx -> { + Reporting.log('process autoBuildStruct $implId "$structName"', "stage"); + + // On C++, we add an include of the glue code generated by `ammer-core`, + // such that the types declared therein (or in the headers transitively + // included by the glue code) are visible in the C++ definitions. + if (Context.definedValue("target.name") == "cpp") { + var exth = ctx.libraryOptions.language.extensionHeader(); + var headerCode = '#include "${implType.pack.map(_ -> "../").join("")}../ammer_build/ammer_${ctx.name}/lib.cpp_static.$exth"'; + implType.meta.add(":headerCode", [macro $v{headerCode}], Reporting.currentPos()); + } + + options.ctx = ctx; + ctx.info.structs[implId] = options; + Fields.process( + implFields, + ctx, + FCStruct(options) + ); + }); + + Reporting.log('exit autoBuildStruct $implId "$structName" (${fields.length} fields)', "stage"); + Utils.modifyType(implType, fields); + }); + } + + public static function autoBuildSublibrary():Array { + return Reporting.withCurrentPos(() -> { + var implRef = Context.getLocalClass(); + var implType = implRef.get(); + var implId = Utils.typeId(implType); + Reporting.log('enter autoBuildSublibrary $implId', "stage"); + + var lib = Utils.classOfParam(implType.superClass.params[0]); + var options:ammer.internal.v1.LibInfo.LibInfoSublibrary = {}; + var subtypes = []; + Ammer.mergedInfo.sublibraries[implId] = options; + + for (meta in Meta.extract(implType.meta.get(), Meta.SUBLIBRARY_CLASS)) switch (meta) { + case PMNativePrefix(v): options.nativePrefix = v; + case PMSub(v): subtypes.push(v); + case _: throw 0; + } + + var implFields = Context.getBuildFields(); + var fields:Array = Ammer.initSubtype(implId, implType.superClass.params[0], ctx -> { + Reporting.log('process autoBuildSublibrary $implId', "stage"); + options.ctx = ctx; + ctx.info.sublibraries[implId] = options; + Ammer.contextReady(implType, ctx); + subtypes.iter(Utils.triggerTyping); + Fields.process( + implFields, + ctx, + FCSublibrary(options) + ); + }); + + Reporting.log('exit autoBuildSublibrary $implId (${fields.length} fields)', "stage"); + Utils.modifyType(implType, fields); + }); + } +} + +#else + +@:autoBuild(ammer.internal.Entrypoint.autoBuildLibrary()) +class LibraryProcessed<@:const Name> {} + +@:autoBuild(ammer.internal.Entrypoint.autoBuildOpaque()) +class OpaqueProcessed<@:const Name, Lib> {} + +@:autoBuild(ammer.internal.Entrypoint.autoBuildStruct()) +class StructProcessed<@:const Name, Lib> {} + +@:autoBuild(ammer.internal.Entrypoint.autoBuildSublibrary()) +class SublibraryProcessed {} + +#end diff --git a/src/ammer/internal/Fields.hx b/src/ammer/internal/Fields.hx new file mode 100644 index 0000000..da93c42 --- /dev/null +++ b/src/ammer/internal/Fields.hx @@ -0,0 +1,561 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.TypeTools; + +using Lambda; +using StringTools; + +enum FieldContext { + FCLibrary(options:ammer.internal.v1.LibInfo.LibInfoLibrary); + FCOpaque(options:ammer.internal.v1.LibInfo.LibInfoOpaque); + FCStruct(options:ammer.internal.v1.LibInfo.LibInfoStruct); + FCSublibrary(options:ammer.internal.v1.LibInfo.LibInfoSublibrary); +} + +typedef CategorisedMethod = { + nativeName:String, + metas:Array, + field:Field, + fun:Function, +}; +typedef CategorisedVar = { + nativeName:String, + metas:Array, + field:Field, + ct:ComplexType, + isFinal:Bool, +}; + +class Fields { + static function categorise( + fields:Array, + fieldCtx:FieldContext + ):{ + staticMethods:Array, + staticVars:Array, + instanceMethods:Array, + instanceVars:Array, + passMethods:Array, + } { + var nativePrefix:String = null; + switch (fieldCtx) { + case FCLibrary(options): + nativePrefix = options.nativePrefix; + case FCOpaque(options): + nativePrefix = options.nativePrefix; + case FCStruct(options): + nativePrefix = options.nativePrefix; + case FCSublibrary(options): + nativePrefix = options.nativePrefix; + } + + var staticMethods = []; + var staticVars = []; + var instanceMethods = []; + var instanceVars = []; + var passMethods = []; + for (field in fields) Reporting.withPosition(field.pos, () -> { + var nativeName = nativePrefix != null + ? nativePrefix + field.name + : field.name; + + // validate field access modifiers + var isInstance = !field.access.contains(AStatic); + if (isInstance && !fieldCtx.match(FCStruct(_) | FCOpaque(_))) + return Reporting.error('non-static field ${field.name} only allowed in struct types and opaque types'); + + var isFinal = field.access.contains(AFinal); + + for (access in field.access) switch (access) { + case AOverride: Reporting.error('"override" not allowed in ammer definitions'); + case ADynamic: Reporting.error('"dynamic" not allowed in ammer definitions'); + case AInline: Reporting.error('"inline" not allowed in ammer definitions'); + case AMacro: Reporting.error('"macro" not allowed in ammer definitions'); + case AExtern: Reporting.error('"extern" not allowed in ammer definitions'); + case AAbstract: Reporting.error('"abstract" not allowed in ammer definitions'); + case AOverload: Reporting.error('"overload" not allowed in ammer definitions'); + case _: + } + + // parse metadata + var allowHaxe = false; + var defaultNativeName = true; + var metas = Meta.extract(field.meta, Meta.COMMON_FIELD, false); + for (meta in metas) switch (meta) { + case PMHaxe: allowHaxe = true; + case PMNative(name): defaultNativeName = false; nativeName = name; + case _: throw 0; + } + if (allowHaxe) { + defaultNativeName || throw Reporting.error("@:ammer.native has no effect on @:ammer.haxe fields"); + } + + // categorise + switch (field.kind) { + case FFun(f): + if (allowHaxe) { + f.expr != null || throw Reporting.error("function body required for @:ammer.haxe functions"); + passMethods.push({ + nativeName: null, + metas: metas, + field: field, + fun: f, + }); + } else { + f.expr == null || throw Reporting.error("function body not allowed (unless @:ammer.haxe is added)"); + f.ret != null || return Reporting.error("type annotation required for return type"); + !isFinal || return Reporting.error('"final" not allowed on methods'); + for (arg in f.args) arg.type != null || return Reporting.error("type annotation required for arguments"); + (isInstance ? instanceMethods : staticMethods).push({ + nativeName: nativeName, + metas: metas, + field: field, + fun: f, + }); + } + case FVar(ct, def): + !allowHaxe || return Reporting.error("@:ammer.haxe not yet supported on variable fields"); + ct != null || return Reporting.error("type annotation required"); + def == null || return Reporting.error("default values are not allowed"); + (isInstance ? instanceVars : staticVars).push({ + nativeName: nativeName, + metas: metas, + field: field, + ct: ct, + isFinal: isFinal, + }); + case _: return Reporting.error("invalid field kind"); + } + }); + return { + staticMethods: staticMethods, + staticVars: staticVars, + instanceMethods: instanceMethods, + instanceVars: instanceVars, + passMethods: passMethods, + }; + } + + public static function process( + fields:Array, + ctx:LibContext, + fieldCtx:FieldContext + ):Array { + var categorised = categorise(fields, fieldCtx); + var processed:Array = []; + + // create type representation + var libraryOptions:ammer.internal.v1.LibInfo.LibInfoLibrary; + var opaqueOptions:ammer.internal.v1.LibInfo.LibInfoOpaque; + var structOptions:ammer.internal.v1.LibInfo.LibInfoStruct; + var sublibraryOptions:ammer.internal.v1.LibInfo.LibInfoSublibrary; + switch (fieldCtx) { + case FCLibrary(options): + libraryOptions = options; + case FCOpaque(options): + // TODO: reduce duplication between opaque and struct cases + opaqueOptions = options; + options.marshal = ctx.marshal.opaque(options.opaqueName); + processed.push({ + name: "_ammer_native", + meta: [], + pos: Reporting.currentPos(), + kind: FVar(options.marshal.type.haxeType, null), + doc: null, + access: [APrivate], + }); + processed.push({ + name: "new", + meta: [], + pos: Reporting.currentPos(), + kind: FFun({ + ret: null, + expr: macro _ammer_native = native, + args: [{ name: "native", type: options.marshal.type.haxeType }], + }), + doc: null, + access: [APrivate], + }); + var implCt = TypeTools.toComplexType(options.implType); + var implTp = (switch (implCt) { + case TPath(tp): tp; + case _: throw 0; + }); + processed.push({ + name: "_ammer_lib_nullPtr", + meta: [], + pos: Reporting.currentPos(), + kind: FFun({ + ret: implCt, + expr: macro return new $implTp($e{options.marshal.nullPtr}), + args: [], + }), + doc: null, + access: [APrivate, AStatic], + }); + case FCStruct(options): + structOptions = options; + + // TODO: this is not great, is there better API design? + var structEmpty = ctx.marshal.structPtr(options.structName, [], false); + options.marshalOpaque = structEmpty.type; + options.marshalDeref = structEmpty.typeDeref; + + // TODO: store the fieldRefs? + var fieldRefs = categorised.instanceVars.map(f -> { + var fieldType = Types.resolveComplexType(f.ct, ctx); + var fref = ctx.marshal.fieldRef(f.nativeName, fieldType.marshal); + fref; + }); + options.marshal = ctx.marshal.structPtr( + options.structName, + fieldRefs, + options.gen.alloc != null + || options.gen.free != null + || options.gen.nullPtr != null + ); + processed.push({ + name: "_ammer_native", + meta: [], + pos: Reporting.currentPos(), + kind: FVar(options.marshal.type.haxeType, null), + doc: null, + access: [APrivate], + }); + processed.push({ + name: "new", + meta: [], + pos: Reporting.currentPos(), + kind: FFun({ + ret: null, + expr: macro _ammer_native = native, + args: [{ name: "native", type: options.marshal.type.haxeType }], + }), + doc: null, + access: [APrivate], + }); + var implCt = TypeTools.toComplexType(options.implType); + var implTp = (switch (implCt) { + case TPath(tp): tp; + case _: throw 0; + }); + + if (options.gen.alloc != null) { + processed.push({ + name: options.gen.alloc, + meta: [], + pos: Reporting.currentPos(), + kind: FFun({ + ret: implCt, + expr: macro return new $implTp($e{options.marshal.alloc}), + args: [], + }), + doc: null, + access: [APublic, AStatic], + }); + } + if (options.gen.free != null) { + processed.push({ + name: options.gen.free, + meta: [], + pos: Reporting.currentPos(), + kind: FFun({ + ret: (macro : Void), + expr: macro $e{options.marshal.free(macro _ammer_native)}, + args: [], + }), + doc: null, + access: [APublic], + }); + } + if (options.gen.nullPtr != null) { + processed.push({ + name: options.gen.nullPtr, + meta: [], + pos: Reporting.currentPos(), + kind: FFun({ + ret: implCt, + expr: macro return new $implTp($e{options.marshal.nullPtr}), + args: [], + }), + doc: null, + access: [APublic, AStatic], + }); + } + case FCSublibrary(options): + sublibraryOptions = options; + } + + // process fields + function processMethod(method:CategorisedMethod, isInstance:Bool):Void { + var argCount = method.fun.args.length; + + // process method metadata + var retCCast = null; + var cPrereturn = ""; + var cReturn = "%CALL%"; + var derivedRet = null; + var derivedRetType = null; + for (meta in Meta.extract(method.field.meta, Meta.COMMON_METHOD)) switch (meta) { + case PMNative(name): // already processed in `categorised`, ignore + case PMC_Cast(to): retCCast = to; + case PMC_MacroCall: // no-op + case PMC_Prereturn(expr): cPrereturn = expr; + case PMC_Return(expr): + // ret.mangled != "v" || throw Reporting.error(":ammer.c.return cannot be used on a method with `Void` return type"); + cReturn = expr; + case PMRet_Derive(expr, ct): + derivedRet = expr; + derivedRetType = ct; + case _: throw 0; + } + + // process argument metadata + var skipArgs = [ for (idx in 0...argCount) false ]; + var derivedHaxe = [ for (idx in 0...argCount) null ]; + var replaceHaxe = [ for (idx in 0...argCount) null ]; // TODO: messy! + var replaceArgType = [ for (idx in 0...argCount) null ]; + var cCast = [ for (idx in 0...argCount) null ]; // TODO: messy! + for (idx in 0...argCount) { + method.fun.args[idx].meta != null || continue; + for (meta in Meta.extract(method.fun.args[idx].meta, Meta.METHOD_ARG)) switch (meta) { + case PMC_Cast(to): cCast[idx] = to; + case PMSkip: skipArgs[idx] = true; + case PMDerive(e): derivedHaxe[idx] = e; + // TODO: c.derive + case _: throw 0; + } + } + var nonSkipCtr = 0; + var derivedC = [ for (idx in 0...argCount) skipArgs[idx] + ? null + : ((cCast[idx] != null ? '(${cCast[idx]})' : "") + '_arg${nonSkipCtr++}') ]; + var preExprs = []; + + // TODO: sanity checks, e.g. disallow skip and derive on the same argument + // TODO: ammer.argN... variant of metadata? + // TODO: ammer.ret... metadata + + // process special types + // TODO: retAlloc is not a very clean solution + var retAlloc = null; + function localResolve(res:Type, idx:Int):Types.ResolvedType { + if (Types.TYPES.this_.match(res)) { + isInstance || throw Reporting.error("ammer.ffi.This can only be used in instance methods of opaque or struct types"); + (structOptions != null || opaqueOptions != null) || throw Reporting.error("ammer.ffi.This can only be used in instance methods of opaque or struct types"); + idx != -1 || throw Reporting.error("ammer.ffi.This cannot be used as the return type"); + derivedHaxe[idx] = macro this; + res = structOptions != null ? structOptions.implType : opaqueOptions.implType; + } else if (Types.TYPES.derefThis.match(res)) { + isInstance || throw Reporting.error("ammer.ffi.This can only be used in instance methods of opaque or struct types"); + (structOptions != null || opaqueOptions != null) || throw Reporting.error("ammer.ffi.This can only be used in instance methods of opaque or struct types"); + idx != -1 || throw Reporting.error("ammer.ffi.This cannot be used as the return type"); + derivedHaxe[idx] = macro this; + var implCt = TypeTools.toComplexType(structOptions != null ? structOptions.implType : opaqueOptions.implType); + res = haxe.macro.ComplexTypeTools.toType((macro : ammer.ffi.Deref<$implCt>)); + } else { + switch (res) { + case TInst(Utils.typeId(_.get()) => id, []) if (Ammer.mergedInfo.callbacks.byTypeId.exists(id)): + var callback = Ammer.mergedInfo.callbacks.byTypeId[id]; + derivedC[idx] = callback.callbackName; + if (callback.isGlobal) { + var ident = 'arg$idx'; + preExprs.push(macro $p{Utils.accessTp(Utils.expectTypePath(callback.callbackCt))}.store($i{ident})); + replaceHaxe[idx] = macro 0; + replaceArgType[idx] = callback.funCt; + } else { + derivedHaxe[idx] = macro 0; + } + res = Types.TYPES.i32.type; + case TInst(Utils.typeId(_.get()) => "ammer.ffi.Alloc.Alloc", params): + idx == -1 || throw Reporting.error("ammer.ffi.Alloc cannot be used as an argument type"); + params.length == 1 || throw Reporting.error("ammer.ffi.Alloc should have one type parameter"); + var type = Utils.classOfParam(params[0]).get(); + var id = Utils.typeId(type); + Ammer.mergedInfo.structs[id].marshal != null || throw Reporting.error("type parameter should be a struct"); + Ammer.mergedInfo.structs[id].alloc || throw Reporting.error("struct is not allocatable"); + var ct = TypeTools.toComplexType(params[0]); + var tp = (switch (ct) { + case TPath(tp): tp; + case _: throw 0; + }); + retAlloc = Ammer.mergedInfo.structs[id].structName; + res = params[0]; + case TInst(Utils.typeId(_.get()) => "ammer.ffi.Unsupported.Unsupported", [val]): + // TODO: allow ammer.ffi.Unsupported<""> for ignored return values + idx != -1 || throw Reporting.error("ammer.ffi.Unsupported cannot be used as the return type"); + var expr = Utils.stringOfParam(val); + expr != null || throw Reporting.error("ammer.def.Unsupported type parameter should be a string"); + derivedHaxe[idx] = (macro 0); + derivedC[idx] = expr; + res = Types.TYPES.i32.type; + case TType(_): return localResolve(TypeTools.follow(res, true), idx); + case _: + } + } + return Types.resolveType(res, ctx); + } + function localResolveCt(ct:ComplexType, idx:Int):Types.ResolvedType { + var res = Reporting.resolveType(ct, Reporting.currentPos()); + return localResolve(res, idx); + } + + // resolve types + var ret = localResolveCt(method.fun.ret, -1); + var args = method.fun.args.mapi((idx, arg) -> skipArgs[idx] ? null : localResolveCt(arg.type, idx)); + + // create native representation + var nativeCall = '${method.nativeName}(${[ for (idx in 0...argCount) if (!skipArgs[idx]) derivedC[idx] ].join(", ")})'; + var native = ctx.library.addFunction( + ret.marshal, + [ for (idx in 0...argCount) if (!skipArgs[idx]) args[idx].marshal ], + cPrereturn + "\n" + (retAlloc != null + // TODO: configure malloc, memcpy + ? '_return = ($retAlloc*)malloc(sizeof($retAlloc)); +$retAlloc retval = ${cReturn.replace("%CALL%", nativeCall)}; +memcpy(_return, &retval, sizeof($retAlloc));' + : '${ret.marshal.mangled != "v" ? "_return = " : ""}${retCCast != null ? '($retCCast)' : ""}${cReturn.replace("%CALL%", nativeCall)};'), + { + comment: 'original field name: ${method.field.name}', + } + ); + + // create Haxe call + var call:Expr = { + expr: ECall( + native, + [ for (idx in 0...argCount) if (!skipArgs[idx]) { + var expr = derivedHaxe[idx] != null + ? derivedHaxe[idx] + : (replaceHaxe[idx] != null + ? replaceHaxe[idx] + : { expr: EConst(CIdent('arg$idx')), pos: method.field.pos }); + Utils.exprMap(expr, args[idx].unwrap); + } ] + ), + pos: method.field.pos, + }; + var finalExprs = derivedRet != null + ? [macro (var ret = $e{Utils.exprMap(call, ret.wrap)}), macro return $derivedRet] + : [macro return $e{Utils.exprMap(call, ret.wrap)}]; + processed.push(Utils.updateField(method.field, FFun({ + ret: derivedRetType != null ? derivedRetType : ret.haxeType, + expr: macro $b{preExprs.concat(finalExprs)}, + args: [ for (idx in 0...argCount) { + derivedHaxe[idx] == null || continue; + ({name: 'arg$idx', type: skipArgs[idx] + ? method.fun.args[idx].type + : (replaceArgType[idx] != null + ? replaceArgType[idx] + : args[idx].haxeType)}:FunctionArg); + } ], + }))); + } + + for (method in categorised.staticMethods) + Reporting.withPosition(method.field.pos, () -> processMethod(method, false)); + for (method in categorised.instanceMethods) + Reporting.withPosition(method.field.pos, () -> processMethod(method, true)); + for (method in categorised.passMethods) + processed.push(method.field); + + // TODO: reduce duplication? + for (v in categorised.staticVars) Reporting.withPosition(v.field.pos, () -> { + var fieldType = Types.resolveComplexType(v.ct, ctx); + var marshal = ctx.marshal.fieldRef(v.nativeName, fieldType.marshal); + var getter = ctx.library.addFunction( + marshal.type, + [], + '_return = ${v.nativeName};' + ); + if (v.isFinal) { + processed.push({ + name: 'get_${v.field.name}', + pos: v.field.pos, + kind: FFun({ + ret: fieldType.haxeType, + expr: macro return $e{Utils.exprMap(macro $getter(), fieldType.wrap)}, + args: [], + }), + access: [APrivate, AStatic], + }); + v.field.access.remove(AFinal); // Haxe#8859 + processed.push(Utils.updateField(v.field, FProp("get", "never", fieldType.haxeType, null))); + } else { + processed.push({ + name: 'get_${v.field.name}', + pos: v.field.pos, + kind: FFun({ + ret: fieldType.haxeType, + expr: macro return $e{Utils.exprMap(macro $getter(), fieldType.wrap)}, + args: [], + }), + access: [APrivate, AStatic], + }); + var setter = ctx.library.addFunction( + ctx.marshal.void(), + [marshal.type], + '${v.nativeName} = _arg0;' + ); + processed.push({ + name: 'set_${v.field.name}', + pos: v.field.pos, + kind: FFun({ + ret: fieldType.haxeType, + expr: macro { + $setter($e{Utils.exprMap(macro val, fieldType.unwrap)}); + return val; + }, + args: [{ + name: "val", + type: fieldType.haxeType, + }], + }), + access: [APrivate, AStatic], + }); + processed.push(Utils.updateField(v.field, FProp("get", "set", fieldType.haxeType, null))); + } + }); + + for (v in categorised.instanceVars) Reporting.withPosition(v.field.pos, () -> { + var fieldType = Types.resolveComplexType(v.ct, ctx); + var marshal = ctx.marshal.fieldRef(v.nativeName, fieldType.marshal); + processed.push({ + name: 'get_${v.field.name}', + pos: v.field.pos, + kind: FFun({ + ret: fieldType.haxeType, + expr: macro return $e{Utils.exprMap(structOptions.marshal.fieldGet[v.nativeName](macro _ammer_native), fieldType.wrap)}, + args: [], + }), + access: [APrivate], + }); + processed.push({ + name: 'set_${v.field.name}', + pos: v.field.pos, + kind: FFun({ + ret: fieldType.haxeType, + expr: macro { + $e{structOptions.marshal.fieldSet[v.nativeName](macro _ammer_native, Utils.exprMap(macro val, fieldType.unwrap))}; + return val; + }, + args: [{ + name: "val", + type: fieldType.haxeType, + }], + }), + access: [APrivate], + }); + processed.push(Utils.updateField(v.field, FProp("get", "set", fieldType.haxeType, null))); + }); + + return processed; + } +} + +#end diff --git a/src/ammer/internal/FilePtrOutput.hx b/src/ammer/internal/FilePtrOutput.hx new file mode 100644 index 0000000..96af5ce --- /dev/null +++ b/src/ammer/internal/FilePtrOutput.hx @@ -0,0 +1,23 @@ +// ammer-bake: ammer.internal FilePtrOutput !macro +package ammer.internal; + +import ammer.ffi.FilePtr; + +class FilePtrOutput extends haxe.io.Output { + var file:FilePtr; + function new(file:FilePtr) { + this.file = file; + } + + override public function writeByte(c:Int):Void file.fputc(c); + override public function writeBytes(s:haxe.io.Bytes, pos:Int, len:Int):Int { + if (pos < 0 || len < 0 || pos + len > s.length) + throw haxe.io.Error.OutsideBounds; + var bytesRef = ammer.ffi.Bytes.fromHaxeRef(s.sub(pos, len)); + var ret = file.fwrite(bytesRef.bytes.offset(pos), 1, len); + bytesRef.unref(); + return ret; + } + override public function flush():Void file.fflush(); + override public function close():Void file.fclose(); // TODO: only close output? +} diff --git a/src/ammer/internal/LibContext.hx b/src/ammer/internal/LibContext.hx new file mode 100644 index 0000000..6d90e41 --- /dev/null +++ b/src/ammer/internal/LibContext.hx @@ -0,0 +1,185 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; + +typedef LibContextOptions = { + name:String, + headers:Array, + defines:Array, + definesCodeOnly:Array, + includePaths:Array<{rel:String, abs:String}>, + libraryPaths:Array<{rel:String, abs:String}>, + frameworks:Array, + language:ammer.core.LibraryLanguage, + linkNames:Array, +}; + +class LibContext { + public var name:String; + public var originalOptions:LibContextOptions; + public var libraryOptions:ammer.core.LibraryConfig; + public var library:ammer.core.Library; + public var marshal:ammer.core.Marshal; + public var headers:Array; + public var prebuild:Array<{ + code:String, + process:String->Void, + }> = []; + public var done:Bool = false; + + public var info = new ammer.internal.v1.LibInfo(); + public var infoV1:Null; + + public var isLibTypes:Bool = false; + + public function new(name:String, options:LibContextOptions) { + this.name = name; + originalOptions = options; + + var libName = 'ammer_${options.name}'; + libraryOptions = (switch (Context.definedValue("target.name")) { + case "cpp": ({name: libName} : ammer.core.plat.Cpp.CppLibraryConfig); + case "cs": ({name: libName} : ammer.core.plat.Cs.CsLibraryConfig); + case "hl": ({name: libName} : ammer.core.plat.Hashlink.HashlinkLibraryConfig); + case "java": ({ + name: libName, + jvm: Context.defined("jvm"), + } : ammer.core.plat.Java.JavaLibraryConfig); + case "lua": ({name: libName} : ammer.core.plat.Lua.LuaLibraryConfig); + case "neko": ({name: libName} : ammer.core.plat.Neko.NekoLibraryConfig); + case "js": ({name: libName} : ammer.core.plat.Nodejs.NodejsLibraryConfig); + case "python": ({name: libName} : ammer.core.plat.Python.PythonLibraryConfig); + + case _: ({name: libName} : ammer.core.plat.None.NoneLibraryConfig); + }); + + libraryOptions.language = options.language; + libraryOptions.defines = options.defines; + libraryOptions.definesCodeOnly = options.definesCodeOnly; + libraryOptions.includePaths = options.includePaths.map(p -> p.abs); + libraryOptions.libraryPaths = options.libraryPaths.map(p -> p.abs); + libraryOptions.frameworks = options.frameworks; + libraryOptions.linkNames = options.linkNames; + libraryOptions.pos = Reporting.currentPos(); + + // process configuration defines + // TODO: -D for defines + var prefix = 'ammer.lib.${options.name}'; + if (Config.hasDefine('$prefix.frameworks')) + libraryOptions.frameworks = Config.getStringArray('$prefix.frameworks', ","); + if (Config.hasDefine('$prefix.linkNames')) + libraryOptions.linkNames = Config.getStringArray('$prefix.linkNames', ","); + if (Config.hasDefine('$prefix.includePaths')) + libraryOptions.includePaths = Config.getStringArray('$prefix.includePaths', ","); + if (Config.hasDefine('$prefix.libraryPaths')) + libraryOptions.libraryPaths = Config.getStringArray('$prefix.libraryPaths', ","); + if (Config.hasDefine('$prefix.language')) + libraryOptions.language = Config.getEnum('$prefix.language', [ + "c" => ammer.core.LibraryLanguage.C, + "cpp" => Cpp, + "objc" => ObjectiveC, + "objcpp" => ObjectiveCpp, + ], C); + + // process includes + if (Config.hasDefine('$prefix.headers') + || Config.hasDefine('$prefix.headers.includeLocal') + || Config.hasDefine('$prefix.headers.includeGlobal') + || Config.hasDefine('$prefix.headers.importLocal') + || Config.hasDefine('$prefix.headers.importGlobal')) { + headers = []; + if (Config.hasDefine('$prefix.headers')) + headers = headers.concat(Config.getStringArray('$prefix.headers', ",").map(ammer.core.SourceInclude.IncludeLocal)); + if (Config.hasDefine('$prefix.headers.includeLocal')) + headers = headers.concat(Config.getStringArray('$prefix.headers.includeLocal', ",").map(ammer.core.SourceInclude.IncludeLocal)); + if (Config.hasDefine('$prefix.headers.includeGlobal')) + headers = headers.concat(Config.getStringArray('$prefix.headers.includeGlobal', ",").map(ammer.core.SourceInclude.IncludeGlobal)); + if (Config.hasDefine('$prefix.headers.importLocal')) + headers = headers.concat(Config.getStringArray('$prefix.headers.importLocal', ",").map(ammer.core.SourceInclude.ImportLocal)); + if (Config.hasDefine('$prefix.headers.importGlobal')) + headers = headers.concat(Config.getStringArray('$prefix.headers.importGlobal', ",").map(ammer.core.SourceInclude.ImportGlobal)); + } else { + headers = options.headers; + } + + library = Ammer.platform.createLibrary(libraryOptions); + for (header in headers) library.addInclude(header); + marshal = library.marshal(); + } + + public function prebuildImmediate(code:String):String { + // TODO: cache + var ret:String = null; + var program = new ammer.core.utils.LineBuf() + .lmap(headers, header -> header.toCode()) + .a(code) + .done(); + Ammer.builder.build(new ammer.core.build.BuildProgram([ + BOAlways(File('${Ammer.baseConfig.buildPath}/ammer_${name}'), EnsureDirectory), + BOAlways( + File('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild.c'), // TODO: append cache key or delete? + WriteContent(program) + ), + BODependent( + File('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild.o'), // TODO: should be .obj on MSVC + File('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild.c'), + CompileObject(C, { + defines: libraryOptions.defines, + includePaths: libraryOptions.includePaths, + }) + ), + BODependent( + File('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild'), + File('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild.o'), + LinkExecutable(C, { + defines: libraryOptions.defines, + libraryPaths: libraryOptions.libraryPaths, // unnecessary? + libraries: [], + linkName: null, + }) + ), + BOAlways( + File(""), + Command('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild', [], (code, process) -> { + code == 0 || throw 0; + ret = process.stdout.readAll().toString(); + }) + ), + ])); + return ret; + } + + public function finalise():Void { + Ammer.libraries.active.remove(this); + + if (prebuild.length > 0) { + // var program = new ammer.core.utils.LineBuf(); + // program + // .ail("int main() {}"); + // Ammer.builder.build(new ammer.core.BuildProgram([ + // BOAlways(File('${Ammer.baseConfig.buildPath}/ammer_${name}'), EnsureDirectory), + // BOAlways( + // File('${Ammer.baseConfig.buildPath}/ammer_${name}/prebuild.c'), + // WriteContent(program.done()) + // ), + // ])); + } + + // the latest version is always generated, older ones can be generated on + // demand by baked libraries using defines + //if (Context.defined("ammer_baked_v1_needed")) { infoVX = ... } + infoV1 = info; + done = true; + Ammer.platform.addLibrary(library); + } + + public function toString():String { + return 'LibContext($name)'; + } +} + +#end diff --git a/src/ammer/internal/LibTypes.hx b/src/ammer/internal/LibTypes.hx new file mode 100644 index 0000000..d445662 --- /dev/null +++ b/src/ammer/internal/LibTypes.hx @@ -0,0 +1,67 @@ +package ammer.internal; + +#if !macro + +// TODO: version LibTypes as well? + +@:ammer.lib.linkNames([]) +@:ammer.sub((_ : ammer.ffi.Bytes)) +@:ammer.sub((_ : ammer.ffi.FilePtr)) +@:ammer.sub((_ : ammer.ffi.FilePtr.FilePos)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Box)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Array)) +//@:ammer.sub((_ : ammer.ffi.Array)) +@:ammer.sub((_ : ammer.ffi.Haxe)) +class LibTypes extends ammer.def.Library<"libtypes"> {} + +abstract HaxeAnyRef({ + var value(get, never):Any; + function incref():Void; + function decref():Void; +}/*ammer.ffi.Haxe*/)/* to ammer.ffi.Haxe*/ { + public inline function new(r:ammer.ffi.Haxe) { + this = r; + } + + public var value(get, never):T; + inline function get_value():T { + return (cast this.value : T); + } + //inline function set_value(value:T):T { + // return (cast (this.value = value) : T); + //} + + public inline function incref():Void this.incref(); + public inline function decref():Void this.decref(); + + public inline function toNative():Any return this; +} + +#else +class LibTypes {} +class LibTypes_LibTypes_AmmerSetup { + public static function init():Void {} +} +#end diff --git a/src/ammer/internal/Meta.hx b/src/ammer/internal/Meta.hx new file mode 100644 index 0000000..115be53 --- /dev/null +++ b/src/ammer/internal/Meta.hx @@ -0,0 +1,276 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Expr; + +using Lambda; +using StringTools; + +// TODO: parse meta positions as well for more precise errors? +typedef MetaParser = (args:Array)->Null; + +enum ParsedMeta { + // c.* + PMC_Cast(_:String); + PMC_MacroCall; + PMC_Prereturn(_:String); + PMC_Return(_:String); + // gen.* + PMGen_Alloc(_:String); + PMGen_Free(_:String); + PMGen_NullPtr(_:String); + // lib.* + PMLib_Define(_:String); + PMLib_Define_CodeOnly(_:String); + // TODO: PMLib_Defines(_:Array); + PMLib_Framework(_:String); + PMLib_Frameworks(_:Array); + PMLib_IncludePath(_:String); + PMLib_IncludePaths(_:Array); + PMLib_Language(_:ammer.core.LibraryLanguage); + PMLib_LibraryPath(_:String); + PMLib_LibraryPaths(_:Array); + PMLib_LinkName(_:String); + PMLib_LinkNames(_:Array); + PMLib_Headers_Include(_:String); + PMLib_Headers_Import(_:String); + PMLib_Headers_IncludeLocal(_:String); + PMLib_Headers_ImportLocal(_:String); + PMLib_Headers_IncludeGlobal(_:String); + PMLib_Headers_ImportGlobal(_:String); + // ret.* + PMRet_Derive(e:Expr, ct:ComplexType); + // other + PMAlloc; // TODO: rename to something better? + PMDerive(_:Expr); + PMHaxe; + PMNative(_:String); + PMNativePrefix(_:String); + PMSkip; + PMSub(_:ComplexType); +} + +class Meta { + static function parseComplexType(e:Expr, arg:Int):ComplexType { + var ct = Utils.complexTypeExpr(e); + if (ct == null) + throw 'expected reference to type using (_ : path.to.Type) syntax (argument ${arg + 1})'; + return ct; + } + + static function parseExpr(e:Expr, arg:Int):Expr { + return e; + } + + static function parseString(e:Expr, arg:Int):String { + return (switch (e.expr) { + case EConst(CString(v)): v; + case _: throw 'expected string constant (argument ${arg + 1})'; + }); + } + + static function parseStringArray(e:Expr, arg:Int):Array { + return (switch (e.expr) { + case EArrayDecl(vs): [ for (v in vs) switch (v.expr) { + case EConst(CString(v)): v; + case _: throw 'expected string constant (argument ${arg + 1})'; + } ]; + case _: throw 'expected array of string constants (argument ${arg + 1})'; + }); + } + + static function parseEnum(map:Map):(e:Expr, arg:Int)->T { + return (e:Expr, arg:Int) -> { + switch (e.expr) { + case EConst(CIdent(id)): + if (!map.exists(id)) { + var keys = [for (k in map.keys()) k]; + keys.sort(Reflect.compare); + throw 'invalid value, should be one of ${keys.join(", ")} (argument ${arg + 1})'; + } + map[id]; + case _: throw 'expected identifier (argument ${arg + 1})'; + } + }; + } + + static function parser0(v:ParsedMeta):MetaParser { + return (args) -> { + if (args.length != 0) + throw 'expected no arguments (${args.length} provided)'; + v; + }; + } + + static function parser1(p1:(Expr, Int)->T1, f:(T1)->ParsedMeta):MetaParser { + return (args) -> { + if (args.length != 1) + throw 'expected 1 argument (${args.length} provided)'; + f(p1(args[0], 0)); + }; + } + + static function parser2(p1:(Expr, Int)->T1, p2:(Expr, Int)->T2, f:(T1, T2)->ParsedMeta):MetaParser { + return (args) -> { + if (args.length != 2) + throw 'expected 2 arguments (${args.length} provided)'; + f(p1(args[0], 0), p2(args[1], 1)); + }; + } + + /** + Metadata allowed for the class defining a library. + **/ + public static final LIBRARY_CLASS = [ + "lib.define" => parser1(parseString, PMLib_Define), + "lib.define.codeOnly" => parser1(parseString, PMLib_Define_CodeOnly), + "lib.framework" => parser1(parseString, PMLib_Framework), + "lib.frameworks" => parser1(parseStringArray, PMLib_Frameworks), + "lib.includePath" => parser1(parseString, PMLib_IncludePath), + "lib.includePaths" => parser1(parseStringArray, PMLib_IncludePaths), + "lib.libraryPath" => parser1(parseString, PMLib_LibraryPath), + "lib.libraryPaths" => parser1(parseStringArray, PMLib_LibraryPaths), + "lib.language" => parser1(parseEnum([ + "C" => ammer.core.LibraryLanguage.C, + "Cpp" => Cpp, + "ObjC" => ObjectiveC, + "ObjCpp" => ObjectiveCpp, + ]), PMLib_Language), + "lib.linkName" => parser1(parseString, PMLib_LinkName), + "lib.linkNames" => parser1(parseStringArray, PMLib_LinkNames), + "lib.headers.include" => parser1(parseString, PMLib_Headers_Include), + "lib.headers.import" => parser1(parseString, PMLib_Headers_Import), + "lib.headers.includeLocal" => parser1(parseString, PMLib_Headers_IncludeLocal), + "lib.headers.importLocal" => parser1(parseString, PMLib_Headers_ImportLocal), + "lib.headers.includeGlobal" => parser1(parseString, PMLib_Headers_IncludeGlobal), + "lib.headers.importGlobal" => parser1(parseString, PMLib_Headers_ImportGlobal), + "nativePrefix" => parser1(parseString, PMNativePrefix), + "sub" => parser1(parseComplexType, PMSub), + ]; + + /** + Metadata allowed for the class defining a sublibrary. + **/ + public static final SUBLIBRARY_CLASS = [ + "nativePrefix" => parser1(parseString, PMNativePrefix), + "sub" => parser1(parseComplexType, PMSub), // TODO: disallow? + ]; + + public static final COMMON_METHOD = [ + "haxe" => parser0(PMHaxe), + "native" => parser1(parseString, PMNative), + "macroCall" => parser0(PMC_MacroCall), + "c.cast" => parser1(parseString, PMC_Cast), + "c.macroCall" => parser0(PMC_MacroCall), + "c.prereturn" => parser1(parseString, PMC_Prereturn), + "c.return" => parser1(parseString, PMC_Return), + // "cpp.constructor", + // "cpp.member", + "ret.derive" => parser2(parseExpr, parseComplexType, PMRet_Derive), + ]; + + /** + Metadata allowed for a method of a library. + **/ + public static final LIBRARY_METHOD = COMMON_METHOD; + + /** + Metadata allowed for a method of a struct. + **/ + public static final STRUCT_METHOD = COMMON_METHOD; + + /** + Metadata allowed for a variable of a struct. + **/ + public static final STRUCT_VAR = [ + "haxe" => parser0(PMHaxe), + "native" => parser1(parseString, PMNative), + // TODO: get/set/ref + ]; + + public static final COMMON_FIELD = [ + "haxe" => parser0(PMHaxe), + "native" => parser1(parseString, PMNative), + ]; + + public static final ENUM_FIELD = [ + "native" => parser1(parseString, PMNative), + ]; + + // /** + // Metadata allowed for a variable of a library. + // **/ + // public static final LIBRARY_VARIABLE = [ + // "native", + // ]; + + /** + Metadata allowed for the class defining a struct type. + **/ + public static final STRUCT_CLASS = [ + "alloc" => parser0(PMAlloc), + "nativePrefix" => parser1(parseString, PMNativePrefix), + "sub" => parser1(parseComplexType, PMSub), + // "struct", // TODO: deprecation warning? + "gen.alloc" => parser1(parseString, PMGen_Alloc), + "gen.free" => parser1(parseString, PMGen_Free), + "gen.nullPtr" => parser1(parseString, PMGen_NullPtr), + ]; + + /** + Metadata allowed for the class defining an opaque type. + **/ + public static final OPAQUE_CLASS = [ + "nativePrefix" => parser1(parseString, PMNativePrefix), + //"sub" => parser1(parseComplexType, PMSub), + ]; + + public static final METHOD_ARG = [ + "c.cast" => parser1(parseString, PMC_Cast), + "skip" => parser0(PMSkip), + "derive" => parser1(parseExpr, PMDerive), + ]; + + /** + Iterate through the given `metas`. Any entries that do not start with + `:ammer` will be ignored. + + If `strict` is `true`, all `:ammer.*` metadata must be present in the + `parsers` map to be accepted; an error is thrown if not. + + If `strict` is `false`, `:ammer.*` metadata which are not present in the + `parsers` map are ignored. + **/ + public static function extract( + metas:Metadata, + parsers:Map, + strict:Bool = true + ):Array { + var ret = []; + for (meta in metas) { + if (!meta.name.startsWith(":ammer.")) + continue; + var id = meta.name.substr(":ammer.".length); + Reporting.withPosition(meta.pos, () -> { + var parser = parsers[id]; + if (parser == null) { + if (strict) { + var ids = [ for (k => _ in parsers) k ]; + ids.sort(Reflect.compare); + Reporting.error('unsupported or incorrectly specified ammer metadata ${meta.name} (should be one of ${ids.join(", ")})'); + } + return; + } + try { + ret.push(parser(meta.params)); + } catch (error:String) { + Reporting.error('cannot parse ammer metadata ${meta.name}: $error'); + } + }); + } + return ret; + } +} + +#end diff --git a/src/ammer/internal/Reporting.hx b/src/ammer/internal/Reporting.hx new file mode 100644 index 0000000..a974f9f --- /dev/null +++ b/src/ammer/internal/Reporting.hx @@ -0,0 +1,110 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; +using haxe.macro.PositionTools; + +class Reporting { + public static var positionStack:Array = []; + static var debugStreams:Map; + static var imports:Map, + usings:Array, + module:String, + }> = []; + + static function shouldEmit(stream:String):Bool { + if (debugStreams == null) { + debugStreams = [ + "stage" => false, + "stage-ffi" => false, + ]; + switch (Config.getString("ammer.debug")) { + case null: + case "all": for (k in debugStreams.keys()) debugStreams[k] = true; + case s: for (k in s.split(",")) debugStreams[k] = true; + } + } + return debugStreams[stream]; + } + + public static function pushPosition(pos:Position):Void { + positionStack.push(pos); + } + public static function popPosition():Void { + positionStack.length > 0 || throw 0; + positionStack.pop(); + } + public static function withPosition(pos:Position, f:()->T):T { + pushPosition(pos); + var ret = f(); + // TODO: throw does not reset the stack, implement catchable errors + popPosition(); + return ret; + } + public static function withCurrentPos(f:()->T, recordImports:Bool = true):T { + if (recordImports) { + var filename = Context.currentPos().getInfos().file; + if (!imports.exists(filename)) { + imports[filename] = { + imports: [ for (imp in Context.getLocalImports()) { + var path = imp.path.map(p -> p.name).join("."); + switch (imp.mode) { + case INormal: path; + case IAsName(alias): '$path as $alias'; + case IAll: "*"; + }; + } ], + usings: [ for (use in Context.getLocalUsing()) { + var cls = use.get(); + cls.pack.concat([cls.module.split(".").pop(), cls.name]).join("."); + } ], + module: Context.getLocalModule(), + }; + } + } + return withPosition(Context.currentPos(), f); + } + + public static function currentPos():Position { + positionStack.length > 0 || throw 0; + return positionStack[positionStack.length - 1]; + } + + public static function log(msg:String, stream:String, ?pos:haxe.PosInfos):Void { + if (shouldEmit(stream)) + Sys.println('[ammer:$stream] $msg (${pos.fileName}:${pos.lineNumber})'); + } + + public static function warning(msg:String):Void { + Context.warning(msg, currentPos()); + } + public static function error(msg:String):Void { + Context.error(msg, currentPos()); + } + + public static function resolveType(ct:ComplexType, pos:Position):Type { + var curPos = Context.currentPos(); + var curFilename = curPos.getInfos().file; + var filename = pos.getInfos().file; + if (curFilename != filename) { + var importsForFile = imports[filename]; + importsForFile != null || throw 0; + return Context.withImports( + importsForFile.imports.concat([importsForFile.module]), + importsForFile.usings, + () -> Context.resolveType(ct, pos) + ); + } else { + return Context.resolveType(ct, pos); + } + } + + // TODO: catchable, emitted error that does not immediately abort? + // throw Reporting.error("...") ? +} + +#end diff --git a/src/ammer/internal/Types.hx b/src/ammer/internal/Types.hx new file mode 100644 index 0000000..4f7592a --- /dev/null +++ b/src/ammer/internal/Types.hx @@ -0,0 +1,365 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; +import haxe.macro.TypeTools; + +using Lambda; + +typedef ResolvedType = { + marshal:ammer.core.TypeMarshal, + haxeType:ComplexType, + wrap:NullExpr>, + unwrap:NullExpr>, +}; + +typedef CommonType = { + type:Type, + id:String, + match:(Type)->Bool, +}; + +class Types { + static function prepareType(type:Type):CommonType { + var ret = { + type: type, + id: null, + match: null, + }; + switch (type) { + case TInst(_.get() => a, []): + ret.id = '${a.pack.join(".")}.${a.module.split(".").pop()}.${a.name}'; + ret.match = (ctype:Type) -> switch (ctype) { + case TInst(_.get() => b, []): a.name == b.name && a.module == b.module; + case _: false; + }; + case TInst(_.get() => a, [TInst(_.get() => a2, [])]): + ret.id = '${a.pack.join(".")}.${a.module.split(".").pop()}.${a.name}'; + ret.match = (ctype:Type) -> switch (ctype) { + case TInst(_.get() => b, [TInst(_.get() => b2, [])]): + a.name == b.name && a.module == b.module + && a2.name == b2.name && a2.module == b2.module; + case _: false; + }; + case TAbstract(_.get() => a, []): + ret.id = '${a.pack.join(".")}.${a.module.split(".").pop()}.${a.name}'; + ret.match = (ctype:Type) -> switch (ctype) { + case TAbstract(_.get() => b, []): a.name == b.name && a.module == b.module; + case _: false; + }; + case _: throw 'how to match $type ?'; + } + return ret; + } + + public static var TYPES = { + var herePos = (macro null).pos; + function c(ct:ComplexType, enable:Bool = true):CommonType { + if (!enable) return null; + // do not follow type: on some targets, some primitive types are typedefs + return prepareType(Context.resolveType(ct, herePos)); + } + var hasSingle = (switch (Context.definedValue("target.name")) { + case "cpp" | "cs" | "hl" | "java": true; + case _: false; + }); + { + void: c((macro : ammer.ffi.Void)), hxVoid: c((macro : Void)), + bool: c((macro : ammer.ffi.Bool)), hxBool: c((macro : Bool)), + u8: c((macro : ammer.ffi.UInt8)), + u16: c((macro : ammer.ffi.UInt16)), + u32: c((macro : ammer.ffi.UInt32)), hxU32: c((macro : UInt)), + u64: c((macro : ammer.ffi.UInt64)), + i8: c((macro : ammer.ffi.Int8)), + i16: c((macro : ammer.ffi.Int16)), + i32: c((macro : ammer.ffi.Int32)), hxI32: c((macro : Int)), + i64: c((macro : ammer.ffi.Int64)), hxI64: c((macro : haxe.Int64)), + f32: c((macro : ammer.ffi.Float32)), hxF32: c((macro : Single), hasSingle), + f64: c((macro : ammer.ffi.Float64)), hxF64: c((macro : Float)), + string: c((macro : ammer.ffi.String)), hxString: c((macro : String)), + bytes: /*c((macro : ammer.ffi.Bytes)),*/ (null : CommonType), + hxBytes: c((macro : haxe.io.Bytes)), //? + this_: c((macro : ammer.ffi.This)), + derefThis: c((macro : ammer.ffi.Deref)), + }; + }; + static function initTypes():Void { + if (TYPES == null || TYPES.bytes != null) return; + var herePos = (macro null).pos; + function c(ct:ComplexType):CommonType { + return prepareType(Context.resolveType(ct, herePos)); + } + TYPES.bytes = c((macro : ammer.ffi.Bytes)); + } + + public static function resolveComplexType( + ct:ComplexType, + lib:LibContext + ):ResolvedType { + return resolveType(Reporting.resolveType(ct, Reporting.currentPos()), lib); + } + + static function resolveContexts(type:Type):Array { + //var followedType = TypeTools.follow(type); + var ret:Array = []; + function c(target:CommonType):Bool { + if (target == null) return false; + return target.match(type); + } + initTypes(); + // ammer.ffi.* types + c(TYPES.void) + || c(TYPES.bool) + || c(TYPES.u8) + || c(TYPES.u16) + || c(TYPES.u32) + || c(TYPES.u64) + || c(TYPES.i8) + || c(TYPES.i16) + || c(TYPES.i32) + || c(TYPES.i64) + || c(TYPES.f32) + || c(TYPES.f64) + || c(TYPES.string) + || c(TYPES.bytes) + // Haxe shortcuts + || c(TYPES.hxVoid) + || c(TYPES.hxBool) + || c(TYPES.hxU32) + || c(TYPES.hxI32) + || c(TYPES.hxI64) + || c(TYPES.hxF32) + || c(TYPES.hxF64) + || c(TYPES.hxString) + //|| c(TYPES.hxBytes) + || { + ret = (switch (type) { + case TInst(Utils.typeId(_.get()) => id, []): + if (Ammer.mergedInfo.structs.exists(id)) { + Ammer.mergedInfo.structs[id].ctx != null || throw "context for struct not initialised yet"; + [Ammer.mergedInfo.structs[id].ctx]; + } else if (Ammer.mergedInfo.opaques.exists(id)) [Ammer.mergedInfo.opaques[id].ctx]; + else if (Ammer.mergedInfo.sublibraries.exists(id)) [Ammer.mergedInfo.sublibraries[id].ctx]; + else if (Ammer.mergedInfo.arrays.byTypeId.exists(id)) throw 0; // Ammer.arrays.byTypeId[id].ctx; + else if (Ammer.mergedInfo.boxes.byTypeId.exists(id)) throw 0; // Ammer.boxes.byTypeId[id].ctx; + else if (Ammer.mergedInfo.callbacks.byTypeId.exists(id)) [Ammer.mergedInfo.callbacks.byTypeId[id].ctx]; + // TODO: enums ? + else if (Ammer.mergedInfo.haxeRefs.byTypeId.exists(id)) [Ammer.mergedInfo.haxeRefs.byTypeId[id].ctx]; + else if (Ammer.libraries.byTypeId.exists(id)) [Ammer.libraries.byTypeId[id]]; + else []; + case TInst(Utils.typeId(_.get()) => "ammer.ffi.Deref.Deref", [type]): + resolveContexts(type); + case TAbstract(_, []): + var next = TypeTools.followWithAbstracts(type, true); + if (Utils.typeId2(type) != Utils.typeId2(next)) resolveContexts(next); + else []; + case TFun(args, ret): + var ret = []; + var retMap:Map = []; + for (arg in args) { + for (ctx in resolveContexts(arg.t)) { + if (retMap.exists(ctx.name)) continue; + retMap[ctx.name] = true; + ret.push(ctx); + } + } + ret; + case TType(_): return resolveContexts(TypeTools.follow(type, true)); + case _: trace(type); throw 0; + }); + true; + }; + return ret; + } + + public static function resolveContext(type:Type):LibContext { + var ret = resolveContexts(type); + if (ret.length > 1) { + trace("contexts", ret); + throw "multiple contexts ..."; + } else if (ret.length == 0) { + return Ammer.libraries.byTypeId["ammer.internal.LibTypes.LibTypes"]; + } + return ret[0]; + } + + public static function resolveType( + type:Type, + lib:LibContext + ):ResolvedType { + var marshal = lib.marshal; + var ret:ResolvedType = null; + function c( + target:CommonType, ffi:()->ammer.core.TypeMarshal, + ?haxeType:ComplexType, ?wrap:Expr->Expr, ?unwrap:Expr->Expr + ):Bool { + if (target == null) return false; + if (target.match(type)) { + var retMarshal = ffi(); + ret = { + marshal: retMarshal, + haxeType: haxeType != null ? haxeType : retMarshal.haxeType, + wrap: wrap, + unwrap: unwrap, + }; + return true; + } + return false; + } + initTypes(); + // check abstracts first: may unify with primitive types otherwise + (switch (type) { + case TAbstract(_.get() => abs, []): + // make sure build macros for the abstract get triggered + if (abs.impl != null) { + abs.impl.get(); + } + + var id = Utils.typeId(abs); + if (Ammer.mergedInfo.enums.exists(id)) { + ret = { + marshal: Ammer.mergedInfo.enums[id].marshal, + haxeType: Ammer.mergedInfo.enums[id].marshal.haxeType, + wrap: e -> e, + unwrap: e -> e, + }; + true; + } else { + false; + } + case _: false; + }) + // ammer.ffi.* types + || c(TYPES.void, marshal.void ) + || c(TYPES.bool, marshal.bool ) + || c(TYPES.u8, marshal.uint8 ) + || c(TYPES.u16, marshal.uint16 ) + || c(TYPES.u32, marshal.uint32 ) + || c(TYPES.u64, marshal.uint64 ) + || c(TYPES.i8, marshal.int8 ) + || c(TYPES.i16, marshal.int16 ) + || c(TYPES.i32, marshal.int32 ) + || c(TYPES.i64, marshal.int64 ) + || c(TYPES.f32, marshal.float32) + || c(TYPES.f64, marshal.float64) + || c(TYPES.string, marshal.string ) + || c(TYPES.bytes, () -> marshal.bytes().type, + (macro : ammer.ffi.Bytes), + e -> macro @:privateAccess new ammer.ffi.Bytes($e), + e -> macro @:privateAccess $e._ammer_native) + || c(TYPES.this_, () -> throw Reporting.error("ammer.ffi.This type not allowed here")) + // Haxe shortcuts + || c(TYPES.hxVoid, marshal.void ) + || c(TYPES.hxBool, marshal.bool ) + || c(TYPES.hxU32, marshal.uint32 ) + || c(TYPES.hxI32, marshal.int32 ) + || c(TYPES.hxI64, marshal.int64 ) + || c(TYPES.hxF32, marshal.float32) + || c(TYPES.hxF64, marshal.float64) + || c(TYPES.hxString, marshal.string ) + //|| c(TYPES.hxBytes, () -> marshal.bytes().type) + || { + // TODO: better handling of typarams, better errors... + // TODO: cache ResolvedTypes directly in the relevant info structs? + switch (type) { + case TInst(Utils.typeId(_.get()) => id, []) if (Ammer.mergedInfo.structs.exists(id)): + Ammer.mergedInfo.structs[id].marshalOpaque != null || throw 0; + var ct = TypeTools.toComplexType(type); + var tp = Utils.expectTypePath(ct); + ret = { + marshal: Ammer.mergedInfo.structs[id].marshalOpaque, + haxeType: ct, + wrap: e -> macro @:privateAccess new $tp($e), + unwrap: e -> macro @:privateAccess $e._ammer_native, + }; + true; + case TInst(Utils.typeId(_.get()) => id, []) if (Ammer.mergedInfo.opaques.exists(id)): + Ammer.mergedInfo.opaques[id].marshal != null || throw 0; + var ct = TypeTools.toComplexType(type); + var tp = Utils.expectTypePath(ct); + ret = { + marshal: Ammer.mergedInfo.opaques[id].marshal.type, + haxeType: ct, + wrap: e -> macro @:privateAccess new $tp($e), + unwrap: e -> macro @:privateAccess $e._ammer_native, + }; + true; + case TInst(Utils.typeId(_.get()) => id, []) if (Ammer.mergedInfo.arrays.byTypeId.exists(id)): + var array = Ammer.mergedInfo.arrays.byTypeId[id]; + var tp = Utils.expectTypePath(array.arrayCt); + ret = { + marshal: array.arrayMarshal.type, + haxeType: TypeTools.toComplexType(type), + wrap: e -> macro @:privateAccess new $tp($e), + unwrap: e -> macro @:privateAccess $e._ammer_native, + }; + true; + case TInst(Utils.typeId(_.get()) => id, []) if (Ammer.mergedInfo.boxes.byTypeId.exists(id)): + var box = Ammer.mergedInfo.boxes.byTypeId[id]; + var marshal = box.boxMarshal; + var tp = Utils.expectTypePath(box.boxCt); + ret = { + marshal: marshal.type, + haxeType: TypeTools.toComplexType(type), + wrap: e -> macro @:privateAccess new $tp($e), + unwrap: e -> macro @:privateAccess $e._ammer_native, + }; + true; + case TInst(Utils.typeId(_.get()) => id, []) if (Ammer.mergedInfo.haxeRefs.byTypeId.exists(id)): + var haxeRef = Ammer.mergedInfo.haxeRefs.byTypeId[id]; + ret = { + marshal: haxeRef.marshal.type, + haxeType: haxeRef.marshal.refCt, + wrap: e -> macro $e{haxeRef.marshal.restore(e)}, + unwrap: e -> macro $e{e}.handle, + }; + true; + case TAbstract(Utils.typeId(_.get()) => "ammer.internal.LibTypes.HaxeAnyRef", [el]): + // var elId = Utils.typeId2(el); + var ct = TypeTools.toComplexType(type); + var elCt = TypeTools.toComplexType(el); + var haxeRef = Ammer.mergedInfo.haxeRefs.byElementTypeId[".Any.Any"]; + haxeRef != null || throw 0; + var refCt = haxeRef.marshal.refCt; + ret = { + marshal: haxeRef.marshal.type, + haxeType: ct, // haxeRef.marshal.refCt, + wrap: e -> macro new ammer.internal.LibTypes.HaxeAnyRef<$elCt>($e{haxeRef.marshal.restore(e)}), + unwrap: e -> macro ($e{e}.toNative() : $refCt).handle, + }; + true; + case TInst(Utils.typeId(_.get()) => "ammer.ffi.Deref.Deref", [type = TInst(Utils.typeId(_.get()) => id, [])]) if (Ammer.mergedInfo.structs.exists(id)): + Ammer.mergedInfo.structs[id].marshalOpaque != null || throw 0; + var ct = TypeTools.toComplexType(type); + var tp = Utils.expectTypePath(ct); + ret = { + marshal: Ammer.mergedInfo.structs[id].marshalDeref, + haxeType: ct, + wrap: e -> macro @:privateAccess new $tp($e), + unwrap: e -> macro @:privateAccess $e._ammer_native, + }; + true; + case TAbstract(abs, _): + var next = TypeTools.followWithAbstracts(type, true); + if (Utils.typeId2(type) != Utils.typeId2(next)) return resolveType(next, lib); + false; + case TType(_): return resolveType(TypeTools.follow(type, true), lib); + case TInst(Utils.typeId(_.get()) => id, []): + trace("type id was", id); + false; + case _: false; + } + }; + if (ret == null) { + // TODO: error + trace("type:", type); + throw 0; + } + return ret; + } +} + +#end diff --git a/src/ammer/internal/Utils.hx b/src/ammer/internal/Utils.hx new file mode 100644 index 0000000..a4eb9e8 --- /dev/null +++ b/src/ammer/internal/Utils.hx @@ -0,0 +1,191 @@ +package ammer.internal; + +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; +import ammer.internal.Reporting.currentPos; +import ammer.internal.Reporting.resolveType; + +using Lambda; + +class Utils { + // These methods are inserted into `Lib.macro.hx` when baking a library. + // This avoids code duplication/synchronisation issues. Importantly, the code + // is just string-pasted, so it is important that the `import`s that are + // in `Lib.macro.baked.hx` are sufficient for the code to work. + +// ammer-fragment-begin: lib-baked + public static function access(t:{pack:Array, module:String, name:String}, ?field:String):Array { + return t.pack.concat([t.module.split(".").pop(), t.name]).concat(field != null ? [field] : []); + } + + public static function accessTp(t:TypePath):Array { + return t.pack.concat([t.name]).concat(t.sub != null ? [t.sub] : []); + } + + public static function complexTypeExpr(e:Expr):Null { + function helper(e:Expr, fallback:Bool):Null { + return (switch (e.expr) { + case EParenthesis(e): complexTypeExpr(e); + case ECheckType(_, ct): ct; + case _ if (!fallback): + // TODO: kind of a hack + var str = new haxe.macro.Printer().printExpr(e); + helper(Context.parse('(_ : $str)', e.pos), true); + case _: null; + }); + } + return helper(e, false); + } + + public static function isNull(e:Expr):Bool { + return e == null || e.expr.match(EConst(CIdent("null"))); + } + + // FFI type resolution allows some convenience shortcuts (e.g. Haxe `Int` is + // the same as `ammer.ffi.Int32`). To avoid conflicts with multiple type IDs + // resolving to the same thing, this function normalises the Haxe shortcuts + // back to the `ammer.ffi.*` equivalents. + public static function normaliseTypeId(id:String):String { + return (switch (id) { + case ".StdTypes.Void": "ammer.ffi.Void.Void"; + case ".StdTypes.Bool": "ammer.ffi.Bool.Bool"; + case ".UInt.UInt": "ammer.ffi.UInt32.UInt32"; + case ".StdTypes.Int": "ammer.ffi.Int32.Int32"; + case "haxe.Int64.Int64": "ammer.ffi.Int64.Int64"; + case ".StdTypes.Single": "ammer.ffi.Float32.Float32"; + case ".StdTypes.Float": "ammer.ffi.Float64.Float64"; + case ".String.String": "ammer.ffi.String.String"; + + // platform specific + case "cs.StdTypes.UInt8": "ammer.ffi.UInt8.UInt8"; + case "hl.UI16.UI16": "ammer.ffi.UInt16.UInt16"; + case "java.StdTypes.Int8": "ammer.ffi.Int8.Int8"; + case "java.StdTypes.Char16": "ammer.ffi.UInt16.UInt16"; + case "java.StdTypes.Int16": "ammer.ffi.Int16.Int16"; + + case _: id; + }); + } + + public static function triggerTyping(ct:ComplexType):Null { + var type = resolveType(ct, currentPos()); + return (switch (type) { + case TInst(ref, _): ref.get(); + case _: null; + }); + } + + public static function typeId(t:{pack:Array, module:String, name:String}):String { + return normaliseTypeId('${t.pack.join(".")}.${t.module.split(".").pop()}.${t.name}'); + } + + public static function typeId2(t:Type):String { + return normaliseTypeId(switch (t) { + case TInst(_.get() => t, _): '${t.pack.join(".")}.${t.module.split(".").pop()}.${t.name}'; + case TAbstract(_.get() => t, _): '${t.pack.join(".")}.${t.module.split(".").pop()}.${t.name}'; + case TFun(args, ret): '(${args.map(arg -> typeId2(arg.t)).join(",")})->${typeId2(ret)}'; + case TType(_.get() => t, []): typeId2(t.type); + case TDynamic(_): ".Any.Any"; // ? + case _: trace(t); throw 0; + }); + } + + public static function typeIdCt(ct:ComplexType):String { + return normaliseTypeId(switch (ct) { + case TPath(tp): '${tp.pack.join(".")}.${tp.name}.${tp.sub == null ? tp.name : tp.sub}'; + case _: throw 0; + }); + } + + public static function expectTypePath(ct:ComplexType):TypePath { + return (switch (ct) { + case TPath(tp): tp; + case _: throw 0; + }); + } +// ammer-fragment-end: lib-baked + + public static function typeIdTp(tp:TypePath):String { + return normaliseTypeId('${tp.pack.join(".")}.${tp.name}.${tp.sub == null ? tp.name : tp.sub}'); + } + + public static function exprOfType(ty:Type):Null { + return (switch (ty) { + case TInst(_.get() => {kind: KExpr(expr)}, []): expr; + case _: null; + }); + } + + public static function exprArrayOfType(ty:Type):Null> { + return (switch (ty) { + case TInst(_.get() => {kind: KExpr({expr: EArrayDecl(exprs)})}, []): exprs; + case _: null; + }); + } + + public static function stringOfParam(ty:Type):Null { + return (switch (ty) { + case TInst(_.get() => {kind: KExpr({expr: EConst(CString(val))})}, []): val; + case _: null; + }); + } + + public static function stringArrayOfParam(ty:Type):Null> { + return (switch (ty) { + case TInst(_.get() => {kind: KExpr({expr: EArrayDecl(vals)})}, []): + [ for (val in vals) switch (val.expr) { + case EConst(CString(val)): val; + case _: return null; + } ]; + case _: null; + }); + } + + public static function classOfParam(tp:Type):Null> { + return (switch (tp) { + case TInst(lib, []): lib; + case _: null; + }); + } + + public static function funOfParam(tp:Type):Null<{args:Array<{t:Type, opt:Bool, name:String}>, ret:Type}> { + return (switch (tp) { + case TFun(args, ret): {args: args, ret: ret}; + case _: null; + }); + } + + public static function updateField(field:Field, kind:FieldType):Field { + return { + pos: field.pos, + name: field.name, + meta: [], + kind: kind, + doc: field.doc, + access: field.access, + }; + } + + public static function exprMap(e:Expr, op:NullExpr>):Expr { + if (op == null) + return e; + return op(e); + } + + public static var definedTypes:Array = []; + public static function defineType(tdef:TypeDefinition):Void { + definedTypes.push(tdef); + Context.defineType(tdef); + } + + public static var modifiedTypes:Array<{t:ClassType, fields:Array}> = []; + public static function modifyType(t:ClassType, fields:Array):Array { + modifiedTypes.push({t: t, fields: fields}); + return fields; + } +} + +#end diff --git a/src/ammer/internal/v1/AmmerBaked.hx b/src/ammer/internal/v1/AmmerBaked.hx new file mode 100644 index 0000000..0ff05da --- /dev/null +++ b/src/ammer/internal/v1/AmmerBaked.hx @@ -0,0 +1,41 @@ +// ammer-bake: ammer.internal.v1 AmmerBaked true +package ammer.internal.v1; + +#if macro + +import haxe.macro.Context; +import haxe.macro.PositionTools; +import haxe.io.Path; +import sys.FileSystem; +import sys.io.File; + +using StringTools; + +#if ammer +typedef AmmerBaked = Ammer; +#else +class AmmerBaked { + // copied from ammer.core.BuildProgram + // TODO: configurable MSVC and system + static var useMSVC = Sys.systemName() == "Windows"; + static var extensionDll = (switch (Sys.systemName()) { + case "Windows": "dll"; + case "Mac": "dylib"; + case _: "so"; + }); + static function extensions(path:String):String { + return path + .replace("%OBJ%", useMSVC ? "obj" : "o") + .replace("%LIB%", useMSVC ? "" : "lib") + .replace("%DLL%", extensionDll); + } + + public static var mergedInfo = new ammer.internal.v1.LibInfo(); + public static function registerBakedLibraryV1(info:ammer.internal.v1.LibInfo):Void { + var fail = Context.fatalError.bind(_, Context.currentPos()); + // ammer-include: internal/Ammer.hx register-v1 + } +} +#end + +#end diff --git a/src/ammer/internal/v1/AmmerSetup.baked.hx b/src/ammer/internal/v1/AmmerSetup.baked.hx new file mode 100644 index 0000000..2f994aa --- /dev/null +++ b/src/ammer/internal/v1/AmmerSetup.baked.hx @@ -0,0 +1,28 @@ +#if macro +import haxe.macro.Context; +import haxe.macro.ComplexTypeTools; +class /*libname*/_AmmerSetup { + public static function init():Void { + var osName = (switch (Sys.systemName()) { + case "Windows": "win"; + case "Linux": "linux"; + case "BSD": "bsd"; + case "Mac": "mac"; + case _: "unknown"; + }); + var extensionDll = (switch (Sys.systemName()) { + case "Windows": "dll"; + case "Mac": "dylib"; + case _: "so"; + }); + var prefixLib = (switch (Sys.systemName()) { + case "Windows": ""; + case _: "lib"; + }); + var info = new ammer.internal.v1.LibInfo(); + info.herePos = (macro 0).pos; + /*libinfo*/ + ammer.internal.v1.AmmerBaked.registerBakedLibraryV1(info); + } +} +#end diff --git a/src/ammer/internal/v1/LibInfo.hx b/src/ammer/internal/v1/LibInfo.hx new file mode 100644 index 0000000..8bbd989 --- /dev/null +++ b/src/ammer/internal/v1/LibInfo.hx @@ -0,0 +1,179 @@ +// ammer-bake: ammer.internal.v1 LibInfo true +package ammer.internal.v1; + +#if macro + +import haxe.macro.Expr; +import haxe.macro.Type; + +typedef LibInfoArray = { + arrayCt:ComplexType, + arrayRefCt:ComplexType, + alloc:Expr, // == arrayMarshal.alloc(macro _size) + fromHaxeCopy:Expr, // == arrayMarshal.fromHaxeCopy(macro _vec) + fromHaxeRef:Null, // == arrayMarshal.fromHaxeRef(macro _vec) + + #if ammer + ?elementType:Type, + ?arrayMarshal:ammer.core.MarshalArray, + #end +}; + +typedef LibInfoBox = { + boxCt:ComplexType, + alloc:Expr, // == boxMarshal.alloc + + #if ammer + ?elementType:Type, + ?boxMarshal:ammer.core.MarshalBox, + #end +}; + +typedef LibInfoCallback = { + isGlobal:Bool, + callbackCt:ComplexType, + funCt:ComplexType, + callbackName:String, + + #if ammer + ?ctx:LibContext, + #end +}; + +typedef LibInfoEnum = { + #if ammer + ?marshal:ammer.core.TypeMarshal, + #end +}; + +typedef LibInfoHaxeRef = { + create:Expr, // == marshal.create(macro _hxval) + + #if ammer + ?ctx:LibContext, + ?elementType:Type, + ?marshal:ammer.core.MarshalHaxe, + #end +}; + +typedef LibInfoLibrary = { + #if ammer + ?nativePrefix:String, + #end +}; + +typedef LibInfoOpaque = { + opaqueName:String, + + #if ammer + ?ctx:LibContext, + ?implType:Type, + ?marshal:ammer.core.MarshalOpaque, + ?nativePrefix:String, + #end +}; + +typedef LibInfoStruct = { + alloc:Bool, + ?gen:{ + ?alloc:String, + ?free:String, + ?nullPtr:String, + }, + structName:String, + + #if ammer + ?ctx:LibContext, + ?implType:Type, + // used for recursive field refs + ?marshalOpaque:ammer.core.TypeMarshal, + ?marshalDeref:ammer.core.TypeMarshal, + ?marshal:ammer.core.MarshalStruct, + ?nativePrefix:String, + #end +}; + +typedef LibInfoSublibrary = { + #if ammer + ?ctx:LibContext, + ?nativePrefix:String, + #end +}; + +typedef LibInfoFileSource = { + // local filename + ?name:String, + + ?description:String, + + // hash of file + //?digest:String, + + // pre-baked release download info + // URL for automatic download + ?downloadFrom:String, + + // operating system + ?os:String, + + // supported architectures (an array to support fat binaries) + ?architectures:Array, + + // minimum OS version supported by file + ?minVersion:String, + + // maximum OS version supported by file + ?maxVersion:String, +}; + +typedef LibInfoFile = { + // destination filename (may contain %DLL% etc) + dst:String, + sources:Array, +}; + +class LibInfo { + public var name:String; + public var herePos:Position; + public var setupToBin:String; + + // String key is typeId of the implType + + public var arrays:{ + byTypeId:Map, + byElementTypeId:Map, + } = { + byTypeId: [], + byElementTypeId: [], + }; + public var boxes:{ + byTypeId:Map, + byElementTypeId:Map, + } = { + byTypeId: [], + byElementTypeId: [], + }; + public var callbacks:{ + byTypeId:Map, + byElementTypeId:Map, + } = { + byTypeId: [], + byElementTypeId: [], + }; + public var enums:Map = []; + public var haxeRefs:{ + byTypeId:Map, + byElementTypeId:Map, + } = { + byTypeId: [], + byElementTypeId: [], + }; + public var opaques:Map = []; + public var structs:Map = []; + public var sublibraries:Map = []; + public var files:Array = []; + + public function new() {} +} + +#end diff --git a/src/ammer/internal/v1/OsInfo.hx b/src/ammer/internal/v1/OsInfo.hx new file mode 100644 index 0000000..ed42eb5 --- /dev/null +++ b/src/ammer/internal/v1/OsInfo.hx @@ -0,0 +1,76 @@ +// ammer-bake: ammer.internal.v1 OsInfo true +package ammer.internal.v1; + +#if macro + +/** + Provides more complete info about the operating system. Possible values for + the data fields are as follows: + + - `os`: "windows", "mac", "linux" + - `version` (for "windows"): (TODO) + - `version` (for "mac"): "11.2.3", "10.9.5", etc + - `version` (for "linux"): (TODO) + - `architecture`: "x86_64", etc + + Keys are `null` when unknown. + + Additionally, `versionCompare` provides a function with the same signature + as `Reflect.compare` to compare two version strings for the given OS. +**/ +class OsInfo { + public static var info(get, never):OsInfo; + static var infoL:OsInfo; + static function get_info():OsInfo { + if (infoL != null) + return infoL; + return infoL = new OsInfo(); + } + + public var os(default, null):Null; + public var version(default, null):Null; + public var architecture(default, null):Null; + public var versionCompare(default, null):(a:String, b:String)->Int; + + function new() { + var osRaw = Sys.systemName(); + versionCompare = (a:String, b:String) -> Reflect.compare(a, b); + function run(cmd:String, args:Array):String { + var proc = new sys.io.Process(cmd, args); + var code = proc.exitCode(); + var stdout = proc.stdout.readAll().toString(); + proc.close(); + return stdout; + } + // simplified semver: only accepts X.Y.Z format + function semverCompare(a:String, b:String):Int { + var as = a.split(".").map(Std.parseInt); + var bs = b.split(".").map(Std.parseInt); + (as.length == 3 + && as[0] != null && as[0] >= 0 + && as[1] != null && as[1] >= 0 + && as[2] != null && as[2] >= 0) || throw 'invalid semver $a'; + (bs.length == 3 + && bs[0] != null && bs[0] >= 0 + && bs[1] != null && bs[1] >= 0 + && bs[2] != null && bs[2] >= 0) || throw 'invalid semver $b'; + for (i in 0...3) { + if (as[0] != bs[0]) + return as[0] < bs[0] ? -1 : 1; + } + return 0; + } + switch (osRaw) { + case "Mac": + os = "mac"; + version = run("sw_vers", ["-productVersion"]); + architecture = run("uname", ["-m"]); + versionCompare = semverCompare; + case _: + os = osRaw.toLowerCase(); + // TODO + } + } +} + +#end diff --git a/src/ammer/internal/v1/RelativePathsHelper.hx b/src/ammer/internal/v1/RelativePathsHelper.hx new file mode 100644 index 0000000..02c41f0 --- /dev/null +++ b/src/ammer/internal/v1/RelativePathsHelper.hx @@ -0,0 +1,35 @@ +// ammer-bake: ammer.internal.v1 RelativePathsHelper true +package ammer.internal.v1; + +#if macro + +import haxe.macro.Compiler; +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.PositionTools; +import haxe.io.Path; + +using StringTools; + +class RelativePathsHelper { + public static function build(includePaths:Array, libraryPaths:Array):Array { + if (includePaths.length == 0 && libraryPaths.length == 0) return null; + + var cls = Context.getLocalClass().get(); + + var outputPath = Compiler.getOutput(); + var rootPath = PositionTools.getInfos(cls.pos).file; + if (!Path.isAbsolute(rootPath)) rootPath = Path.join([Sys.getCwd(), rootPath]); + rootPath = Path.normalize(Path.directory(rootPath) + "/" + cls.pack.map(_ -> "../").join("")); + var xml = '' + + includePaths.map(path -> '').join("") + + '' + + libraryPaths.map(path -> '').join("") + + ''; + + cls.meta.add(":buildXml", [macro $v{xml}], cls.pos); + return null; + } +} + +#end diff --git a/src/ammer/patch/PatchCpp.hx b/src/ammer/patch/PatchCpp.hx deleted file mode 100644 index 373687e..0000000 --- a/src/ammer/patch/PatchCpp.hx +++ /dev/null @@ -1,141 +0,0 @@ -package ammer.patch; - -import haxe.macro.Expr; - -class PatchCpp { - public static function patch(ctx:AmmerContext):Void { - var pos = ctx.implType.pos; - ctx.externIsExtern = false; - var headerCode = '#include "../ammer/ammer_${ctx.libraryConfig.name}.cpp.${ctx.libraryConfig.abi == Cpp ? "cpp" : "c"}"'; - ctx.externMeta.push({ - name: ":headerCode", - params: [{expr: EConst(CString(headerCode)), pos: ctx.implType.pos}], - pos: pos - }); - var cppFileCode = '#define AMMER_CODE_${ctx.index} -#include "../ammer/ammer_${ctx.libraryConfig.name}.cpp.${ctx.libraryConfig.abi == Cpp ? "cpp" : "c"}" -#undef AMMER_CODE_${ctx.index}'; - ctx.externMeta.push({ - name: ":cppFileCode", - params: [{expr: EConst(CString(cppFileCode)), pos: ctx.implType.pos}], - pos: pos - }); - var lb = new LineBuf(); - lb.ai('\n'); - lb.indent(() -> { - for (path in ctx.libraryConfig.includePath) - lb.ai('\n'); - }); - lb.ai('\n'); - lb.ai('\n'); - lb.indent(() -> { - for (path in ctx.libraryConfig.libraryPath) - lb.ai('\n'); - for (name in ctx.libraryConfig.linkName) { - lb.ai('\n'); - lb.ai('\n'); - } - }); - lb.ai('\n'); - ctx.externMeta.push({ - name: ":buildXml", - params: [{expr: EConst(CString(lb.dump())), pos: pos}], - pos: pos - }); - for (t in FFITools.CONSTANT_TYPES) { - if (!ctx.ffiConstants.exists(t.ffi)) - continue; - var hxType = t.haxe; - if (t.ffi == String) - hxType = (macro : cpp.ConstPointer); - ctx.externFields.push({ - access: [AStatic], - name: 'ammer_g_${t.name}', - kind: FFun({ - args: [], - expr: { - var vars = [ for (constant in ctx.ffiConstants[t.ffi]) { - macro untyped __cpp__($v{'${constant.native}'}); - } ]; - macro return $a{vars}; - }, - ret: (macro : Array<$hxType>) - }), - pos: pos - }); - } - } - - public static function patchType(ctx:AmmerTypeContext):Void { - var headerCode = '#include "../ammer/ammer_${ctx.libraryCtx.libraryConfig.name}.cpp.${ctx.libraryCtx.libraryConfig.abi == Cpp ? "cpp" : "c"}"'; - ctx.implType.meta.add( - ":headerCode", - [{expr: EConst(CString(headerCode)), pos: ctx.implType.pos}], - ctx.implType.pos - ); - } -} - -class PatchCppMethod extends ammer.patch.PatchMethod { - override public function visitArgument(i:Int, ffi:FFIType):Void { - switch (ffi) { - case NoSize(t): - return visitArgument(i, t); - case SizeOfReturn: - ctx.callArgs[i] = macro cpp.Pointer.addressOf(($e{Utils.id("_retSize")} : cpp.Reference)); - ctx.wrapExpr = macro { - var _retSize:cpp.SizeT = 0; - ${ctx.wrapExpr}; - }; - case Bytes | WithSize(_, Bytes): - externArgs.push({ - name: '_arg$i', - type: (macro:cpp.Pointer) - }); - return; - case ClosureData(_): - ctx.callArgs[i] = macro 0; - case OutPointer(LibType(_, _)): - ctx.callArgs[i] = macro untyped __cpp__("&{0}->ammerNative.ptr", $e{ctx.callArgs[i]}); - case Unsupported(_): - ctx.callArgs[i] = macro 0; - case _: - } - super.visitArgument(i, ffi); - } - - override public function finish():Void { - ctx.top.externFields.push({ - access: [APublic, AStatic, AExtern], - name: ctx.ffi.uniqueName, - kind: FFun({ - args: externArgs, - expr: null, - ret: mapType(ctx.ffi.ret) - }), - meta: [ - { - name: ":native", - params: [{expr: EConst(CString('::${ammer.stub.StubCpp.mapMethodName(ctx.ffi.uniqueName)}')), pos: ctx.ffi.field.pos}], - pos: ctx.ffi.field.pos - } - ], - pos: ctx.ffi.field.pos - }); - } - - public static function mapType(t:FFIType):ComplexType { - return (switch (t) { - case Bytes: (macro:cpp.ConstPointer); - case String: (macro:cpp.ConstPointer); - case ArrayDynamic(idx, _) | WithSize(_, ArrayDynamic(idx, _)) | ArrayFixed(idx, _, _): Ammer.typeMap['ammer.externs.AmmerArray_$idx.AmmerArray_$idx'].nativeType; - case SizeOfReturn: (macro:cpp.Pointer); - case SizeOf(_): (macro:cpp.SizeT); - case LibType(t, _) | Nested(LibType(t, _)) | Alloc(LibType(t, _)) | LibIntEnum(t, _): t.nativeType; - case Derived(_, t) | WithSize(_, t) | NoSize(t) | SameSizeAs(t, _): mapType(t); - case Closure(idx, args, ret, mode): - TFunction(args.filter(a -> !a.match(ClosureDataUse)).map(mapType), mapType(ret)); - case _: t.toComplexType(); - }); - } -} diff --git a/src/ammer/patch/PatchCross.hx b/src/ammer/patch/PatchCross.hx deleted file mode 100644 index d0d3ca8..0000000 --- a/src/ammer/patch/PatchCross.hx +++ /dev/null @@ -1,9 +0,0 @@ -package ammer.patch; - -import haxe.macro.Expr; - -class PatchCross { - public static function patch(ctx:AmmerContext):Void {} -} - -class PatchCrossMethod extends ammer.patch.PatchMethod {} diff --git a/src/ammer/patch/PatchEval.hx b/src/ammer/patch/PatchEval.hx deleted file mode 100644 index c5c9f6c..0000000 --- a/src/ammer/patch/PatchEval.hx +++ /dev/null @@ -1,13 +0,0 @@ -package ammer.patch; - -import haxe.macro.Expr; - -class PatchEval { - public static function patch(ctx:AmmerContext):Void {} -} - -class PatchEvalMethod extends ammer.patch.PatchMethod { - public function new(ctx:AmmerMethodPatchContext) { - super(ctx); - } -} diff --git a/src/ammer/patch/PatchHl.hx b/src/ammer/patch/PatchHl.hx deleted file mode 100644 index d6637e7..0000000 --- a/src/ammer/patch/PatchHl.hx +++ /dev/null @@ -1,90 +0,0 @@ -package ammer.patch; - -import haxe.macro.Expr; - -class PatchHl { - public static function patch(ctx:AmmerContext):Void { - var pos = ctx.implType.pos; - for (t in FFITools.CONSTANT_TYPES) { - if (!ctx.ffiConstants.exists(t.ffi)) - continue; - var hxType = t.haxe; - if (t.ffi == String) - hxType = (macro : hl.Bytes); - ctx.externFields.push({ - access: [AStatic], - name: 'ammer_g_${t.name}', - kind: FFun({ - args: [], - expr: null, - ret: (macro : hl.NativeArray<$hxType>) - }), - meta: [ - { - name: ":hlNative", - params: [ - {expr: EConst(CString('ammer_${ctx.libraryConfig.name}')), pos: pos}, - {expr: EConst(CString('g_${t.name}_${ctx.index}')), pos: pos} - ], - pos: pos - } - ], - pos: pos - }); - } - } -} - -class PatchHlMethod extends ammer.patch.PatchMethod { - override public function visitArgument(i:Int, ffi:FFIType):Void { - switch (ffi) { - case SizeOfReturn: - ctx.wrapExpr = macro { - var _retSize = 0; - ${ctx.wrapExpr}; - }; - case ClosureData(_): - ctx.callArgs[i] = macro 0; - case Unsupported(_): - ctx.callArgs[i] = macro 0; - case _: - } - super.visitArgument(i, ffi); - } - - override public function finish():Void { - ctx.top.externFields.push({ - access: [APublic, AStatic], - name: ctx.ffi.uniqueName, - kind: FFun({ - args: externArgs, - expr: null, - ret: mapType(ctx.ffi.ret) - }), - meta: [ - { - name: ":hlNative", - params: [ - {expr: EConst(CString('ammer_${ctx.top.libraryConfig.name}')), pos: ctx.ffi.field.pos}, - {expr: EConst(CString(ammer.stub.StubHl.mapMethodName(ctx.ffi.uniqueName))), pos: ctx.ffi.field.pos} - ], - pos: ctx.ffi.field.pos - } - ], - pos: ctx.ffi.field.pos - }); - } - - public static function mapType(t:FFIType):ComplexType { - return (switch (t) { - case Bytes | String: (macro:hl.Bytes); - case SizeOfReturn: (macro:hl.Ref); - case ArrayDynamic(idx, _) | ArrayFixed(idx, _, _): Ammer.typeMap['ammer.externs.AmmerArray_$idx.AmmerArray_$idx'].nativeType; - case LibType(t, _) | Nested(LibType(t, _)) | Alloc(LibType(t, _)) | LibIntEnum(t, _): t.nativeType; - case Derived(_, t) | WithSize(_, t) | NoSize(t) | SameSizeAs(t, _): mapType(t); - case Closure(idx, args, ret, mode): - TFunction(args.filter(a -> !a.match(ClosureDataUse)).map(mapType), mapType(ret)); - case _: t.toComplexType(); - }); - } -} diff --git a/src/ammer/patch/PatchLua.hx b/src/ammer/patch/PatchLua.hx deleted file mode 100644 index 4f40efa..0000000 --- a/src/ammer/patch/PatchLua.hx +++ /dev/null @@ -1,75 +0,0 @@ -package ammer.patch; - -import haxe.macro.Expr; - -class PatchLua { - public static function patch(ctx:AmmerContext):Void { - var pos = ctx.implType.pos; - ctx.externIsExtern = false; - var lib = ammer.build.BuildTools.extensions('ammer_${ctx.libraryConfig.name}.%DLL%'); - var load = 'assert(package.loadlib("$lib", "g_init_${ctx.index}"))()'; - ctx.externFields.push({ - access: [AStatic], - kind: FVar((macro:lua.Table), macro untyped __lua__($v{load})), - name: "ammerNative", - pos: pos - }); - for (t in FFITools.CONSTANT_TYPES) { - if (!ctx.ffiConstants.exists(t.ffi)) - continue; - var hxType = t.haxe; - ctx.externFields.push({ - access: [AStatic], - name: 'ammer_g_${t.name}', - kind: FFun({ - args: [], - expr: macro return $p{["ammerNative", 'g_${t.name}_${ctx.index}']}(), - ret: (macro : lua.Table) - }), - pos: pos - }); - } - } -} - -class PatchLuaMethod extends ammer.patch.PatchMethod { - override public function visitArgument(i:Int, ffi:FFIType):Void { - switch (ffi) { - case SizeOfReturn: - ctx.wrapExpr = macro { - var _retSize = 0; - ${ctx.wrapExpr}; - }; - case Unsupported(_): - ctx.callArgs[i] = macro 0; - case _: - } - super.visitArgument(i, ffi); - } - - override public function finish():Void { - var callArgs:Array = [ for (i in 0...externArgs.length) Utils.arg(i) ]; - ctx.top.externFields.push({ - access: [APublic, AStatic, AInline], - name: ctx.ffi.uniqueName, - kind: FFun({ - args: externArgs, - expr: macro return untyped ammerNative[$v{ammer.stub.StubLua.mapMethodName(ctx.ffi.uniqueName)}]($a{callArgs}), - ret: mapType(ctx.ffi.ret) - }), - pos: ctx.ffi.field.pos - }); - } - - public static function mapType(t:FFIType):ComplexType { - return (switch (t) { - case Bytes: (macro:String); - case ArrayFixed(idx, _, _): Ammer.typeMap['ammer.externs.AmmerArray_$idx.AmmerArray_$idx'].nativeType; - case LibType(t, _) | Nested(LibType(t, _)) | Alloc(LibType(t, _)) | LibIntEnum(t, _): t.nativeType; - case Derived(_, t) | WithSize(_, t) | NoSize(t) | SameSizeAs(t, _): mapType(t); - case Closure(idx, args, ret, mode): - TFunction(args.filter(a -> !a.match(ClosureDataUse)).map(mapType), mapType(ret)); - case _: t.toComplexType(); - }); - } -} diff --git a/src/ammer/patch/PatchMethod.hx b/src/ammer/patch/PatchMethod.hx deleted file mode 100644 index 9a4e573..0000000 --- a/src/ammer/patch/PatchMethod.hx +++ /dev/null @@ -1,175 +0,0 @@ -package ammer.patch; - -import haxe.macro.Expr; -import haxe.macro.ExprTools; - -using StringTools; -using ammer.FFITools; - -// TODO: commonPatchArgument almost the same as commonUnpatchReturn -// same for UnpatchArgument and PatchReturn -class PatchMethod { - public static function commonPatchArgument(e:Expr, t:FFIType):Expr { - return (switch (t) { - case NoSize(t): - commonPatchArgument(e, t); - case Bytes: - macro($e : ammer.conv.Bytes).toNative1(); - case String: - macro($e : ammer.conv.CString).toNative(); - case ArrayDynamic(_, _) | WithSize(_, ArrayDynamic(_, _)) | ArrayFixed(_, _, _): - macro @:privateAccess $e.toNative().ammerNative; - case LibType(_, _) | Nested(LibType(_, _)) | Alloc(LibType(_, _)): - macro @:privateAccess $e.ammerNative; - case LibIntEnum(_, _): - macro @:privateAccess $e.ammerNative; - case Closure(_, args, ret, _): - commonPatchClosure(e, args, ret); - case WithSize(_, t): - commonPatchArgument(e, t); - case _: - e; - }); - } - - public static function commonUnpatchArgument(e:Expr, t:FFIType):Expr { - return (switch (t) { - case WithSize(size, Bytes): - macro ammer.conv.Bytes.fromNative(cast $e, $size); - case String: - macro ammer.conv.CString.fromNative($e); - case WithSize(size, ArrayDynamic(t, _)): - var implTypePath = Ammer.ctx.arrayTypes[t].implTypePath; - macro @:privateAccess new ammer.conv.ArrayWrapper(new $implTypePath($e), $size); - case ArrayFixed(t, _, size): - var implTypePath = Ammer.ctx.arrayTypes[t].implTypePath; - macro @:privateAccess new ammer.conv.ArrayWrapper(new $implTypePath($e), $v{size}); - case LibType(t, _) | Nested(LibType(t, _)) | Alloc(LibType(t, _)): - var implTypePath = t.implTypePath; - macro @:privateAccess new $implTypePath($e); - case LibIntEnum(t, _): - macro @:privateAccess $p{Utils.access(t.implType, "ammerFromNative")}($e); - case _: - e; - }); - } - - public static function commonPatchReturn(e:Expr, t:FFIType):Expr { - return (switch (t) { - case WithSize(size, Bytes): - macro ammer.conv.Bytes.fromNative(cast $e, $size); - case String: - macro ammer.conv.CString.fromNative($e); - case WithSize(size, ArrayDynamic(t, _)): - var implTypePath = Ammer.ctx.arrayTypes[t].implTypePath; - macro @:privateAccess new ammer.conv.ArrayWrapper(new $implTypePath($e), $size); - case ArrayFixed(t, _, size): - var implTypePath = Ammer.ctx.arrayTypes[t].implTypePath; - macro @:privateAccess new ammer.conv.ArrayWrapper(new $implTypePath($e), $v{size}); - case LibType(t, _) | Nested(LibType(t, _)) | Alloc(LibType(t, _)): - var implTypePath = t.implTypePath; - macro @:privateAccess new $implTypePath($e); - case LibIntEnum(t, _): - macro @:privateAccess $p{Utils.access(t.implType, "ammerFromNative")}($e); - case SameSizeAs(t, arg): - commonPatchReturn(e, WithSize(macro $e{Utils.arg(arg)}.length, t)); - case _: - e; - }); - } - - public static function commonUnpatchReturn(e:Expr, t:FFIType):Expr { - return (switch (t) { - case NoSize(t): - commonUnpatchReturn(e, t); - case Bytes: - macro($e : ammer.conv.Bytes).toNative1(); - case String: - macro($e : ammer.conv.CString).toNative(); - case ArrayDynamic(_, _) | WithSize(_, ArrayDynamic(_, _)) | ArrayFixed(_, _, _): - macro @:privateAccess $e.toNative().ammerNative; - case LibType(_, _) | Nested(LibType(_, _)) | Alloc(LibType(_, _)): - macro @:privateAccess $e.ammerNative; - case LibIntEnum(_, _): - macro @:privateAccess $e.ammerNative; - case Closure(_, args, ret, _): - throw "too deep"; - case WithSize(_, t): - commonUnpatchReturn(e, t); - case _: - e; - }); - } - - public static function commonPatchClosure(original:Expr, args:Array, ret:FFIType):Expr { - var norm = args.map(FFITools.normalise).filter(a -> !a.match(Derived(_, _) | SizeOfReturn | ClosureDataUse | ClosureData(_))); - var normRet = FFITools.normalise(ret); - var callArgs = [ for (i in 0...norm.length) Utils.arg(i) ]; - var wrapExpr = macro _closure($a{callArgs}); - // unpatch return - wrapExpr = commonUnpatchReturn(wrapExpr, ret); - wrapExpr = macro return $wrapExpr; - // unpatch args - for (i in 0...norm.length) { - callArgs[i] = commonUnpatchArgument(callArgs[i], norm[i]); - } - return { - expr: EFunction(FAnonymous, { - args: [ for (i in 0...norm.length) { - name: '_carg$i', - type: (switch (Ammer.config.platform) { - case Cpp: ammer.patch.PatchCpp.PatchCppMethod.mapType; - case Hl: ammer.patch.PatchHl.PatchHlMethod.mapType; - case Lua: ammer.patch.PatchLua.PatchLuaMethod.mapType; - case _: mapType; - })(norm[i]) - } ], - expr: ExprTools.map(wrapExpr, function walk(e:Expr):Expr { - return (switch (e.expr) { - case EConst(CIdent(n)) if (n.startsWith("_arg")): - {expr: EConst(CIdent('_carg${n.substr(4)}')), pos: e.pos}; - case EConst(CIdent("_closure")): original; - case _: ExprTools.map(e, walk); - }); - }), - ret: (switch (Ammer.config.platform) { - case Cpp: ammer.patch.PatchCpp.PatchCppMethod.mapType; - case Hl: ammer.patch.PatchHl.PatchHlMethod.mapType; - case Lua: ammer.patch.PatchLua.PatchLuaMethod.mapType; - case _: mapType; - })(normRet) - }), - pos: original.pos - }; - } - - final ctx:AmmerMethodPatchContext; - final externArgs:Array = []; - - public function new(ctx:AmmerMethodPatchContext) { - this.ctx = ctx; - } - - public function visitArgument(i:Int, ffi:FFIType):Void { - externArgs.push({ - name: '_arg$i', - type: (switch (Ammer.config.platform) { - case Cpp: ammer.patch.PatchCpp.PatchCppMethod.mapType; - case Hl: ammer.patch.PatchHl.PatchHlMethod.mapType; - case Lua: ammer.patch.PatchLua.PatchLuaMethod.mapType; - case _: mapType; - })(ffi) - }); - } - - public function finish():Void {} - - public static function mapType(t:FFIType):ComplexType { - return (switch (t) { - case Derived(_, t) | NoSize(t) | SameSizeAs(t, _): mapType(t); - case Closure(idx, args, ret, mode): - TFunction(args.filter(a -> !a.match(ClosureDataUse)).map(mapType), mapType(ret)); - case _: t.toComplexType(); - }); - } -} diff --git a/src/ammer/stub/StubBaseC.hx b/src/ammer/stub/StubBaseC.hx deleted file mode 100644 index 996fea3..0000000 --- a/src/ammer/stub/StubBaseC.hx +++ /dev/null @@ -1,48 +0,0 @@ -package ammer.stub; - -import ammer.FFIType; - -class StubBaseC { - public static function mapTypeC(t:FFIType, name:String):String { - return (switch (t) { - case Void: "void"; - case Bool: "bool"; - case Integer(Signed8): "int8_t"; - case Integer(Signed16): "int16_t"; - case Integer(Signed32): "int32_t"; - case Integer(Signed64): "int64_t"; - case Integer(Unsigned8): "uint8_t"; - case Integer(Unsigned16): "uint16_t"; - case Integer(Unsigned32): "uint32_t"; - case Integer(Unsigned64): "uint64_t"; - case Float(Float32): "float"; - case Float(Float64): "double"; - case Bytes: "unsigned char *"; - case String: "const char *"; - case ArrayDynamic(_, t): '${mapTypeC(t, "")} *'; - case ArrayFixed(_, t, _): '${mapTypeC(t, "")} *'; - case This: throw "!"; - case LibType(t, _) | Nested(LibType(t, _)) | Alloc(LibType(t, _)): - t.kind.match(Pointer(false)) ? t.nativeName : '${t.nativeName} *'; - case LibIntEnum(t, _): '${t.nativeName}'; - case LibSub(_): throw "!"; - case OutPointer(LibType(t, _)): - t.kind.match(Pointer(false)) ? '${t.nativeName} *' : '${t.nativeName} **'; - case OutPointer(_): throw "!"; - case Nested(_) | Alloc(_): throw "!"; - case Derived(_, t): return mapTypeC(t, name); - case WithSize(_, t): return mapTypeC(t, name); - case Closure(_, args, ret, _): - return '${mapTypeC(ret, "")} (* $name)(${args.map(mapTypeC.bind(_, "")).join(", ")})'; - case ClosureDataUse: "void *"; - case ClosureData(_): "void *"; - case NoSize(t): return mapTypeC(t, name); - case SizeOfReturn: "size_t *"; - case SizeOf(_): "int"; - case SizeOfField(_): "int"; - case SameSizeAs(t, _): return mapTypeC(t, name); - case Unsupported(cName): cName; - case NativeHl(_, _, _): throw "!"; - }) + (name != "" ? ' $name' : ""); - } -} diff --git a/src/ammer/stub/StubCpp.hx b/src/ammer/stub/StubCpp.hx deleted file mode 100644 index b892cc2..0000000 --- a/src/ammer/stub/StubCpp.hx +++ /dev/null @@ -1,166 +0,0 @@ -package ammer.stub; - -import ammer.Config.AmmerLibraryConfig; - -using ammer.FFITools; -using StringTools; - -class StubCpp { - static var library:AmmerLibraryConfig; - static var lb:LineBuf; - - static function generateHeader():Void { - lb.ai("#include \n"); - for (header in library.headers) - lb.ai('#include <${header}>\n'); - } - - static function mapTypeC(t:FFIType, name:String, closure:Bool = false):String { - return (switch (t) { - case Closure(_, _, _): '::Dynamic $name'; - case ClosureDataUse: 'void * $name'; - case ClosureData(_): 'int $name'; - case LibIntEnum(_, _): 'int $name'; - case Nested(LibType(t, _)) if (closure): '${t.nativeName} $name'; - case _: StubBaseC.mapTypeC(t, name); - }); - } - - static function unmapTypeC(t:FFIType, name:String):String { - return (switch (t) { - case LibIntEnum(t, _): '${t.nativeName} $name'; - case _: mapTypeC(t, name); - }); - } - - public static function mapMethodName(name:String):String { - return 'w_$name'; - } - - static function generateClosureWrappers(ctx:AmmerContext):Void { - for (i in 0...ctx.closureTypes.length) { - lb.ai('#ifdef AMMER_CODE_${ctx.index}\n'); - var method = ctx.closureTypes[i]; - lb.ai('static ${mapTypeC(method.ret, "")} wc_${i}_${ctx.index}('); - lb.a([ for (i in 0...method.args.length) mapTypeC(method.args[i], 'arg_$i', true) ].filter(a -> a != null).join(", ")); - lb.a(") {\n"); - lb.indent(() -> { - lb.ai('::Dynamic cl = ::Dynamic((hx::Object *)(${method.dataAccess.join("->")}));\n'); - lb.ai("::hx::NativeAttach attach_gc;\n"); - if (method.ret == Void) - lb.ai(""); - else - lb.ai("return "); - lb.a('('); - lb.a(mapTypeC(method.ret, "")); - lb.a(')'); - if (method.ret.match(LibIntEnum(_, _))) { - lb.a("(int)"); - } - lb.a('(cl('); - lb.a([ for (i in 0...method.args.length) switch (method.args[i]) { - case String: '::cpp::Pointer(arg_$i)'; - case LibType(t, _): '::cpp::Pointer<${t.nativeName}>(arg_$i)'; - case Nested(LibType(t, _)): '::cpp::Pointer<${t.nativeName}>(&arg_$i)'; - case ClosureDataUse: continue; - case _: 'arg_$i'; - } ].join(", ")); - lb.a('));\n'); - }); - lb.ai("}\n"); - lb.ai("#endif\n"); - } - } - - static function generateArrayWrappers(ctx:AmmerContext):Void { - for (i in 0...ctx.arrayTypes.length) { - lb.ai('typedef ${mapTypeC(ctx.arrayTypes[i].ffi, "")} wt_array_${i}_${ctx.index};\n'); - } - } - - static function generateMethod(method:FFIMethod, ctx:AmmerContext):Void { - lb.ai('${mapTypeC(method.ret, "")} ${mapMethodName(method.uniqueName)}('); - if (method.args.length == 0) - lb.a("void"); - else - lb.a([ for (i in 0...method.args.length) mapTypeC(method.args[i], 'arg_$i') ].join(", ")); - lb.a(")\n"); - lb.ai('#ifdef AMMER_CODE_${ctx.index}\n'); - lb.ai("{\n"); - lb.indent(() -> { - if (method.cPrereturn != null) - lb.ai('${method.cPrereturn}\n'); - switch (method.ret) { - case Alloc(LibType(t, _)): - lb.ai('${t.nativeName} *ret_alloc = (${t.nativeName} *)malloc(sizeof(${t.nativeName}));\n'); - case _: - } - var callArgs = [ for (i in 0...method.args.length) { - switch (method.args[i]) { - case Closure(idx, _, _, _): - var cl = ctx.closureTypes[idx]; - '(${unmapTypeC(cl.ret, "")} (*)(${cl.args.map(a -> unmapTypeC(a, "")).join(", ")}))(&wc_${idx}_${ctx.index})'; - case ClosureData(f): 'arg_$f.mPtr'; - case LibIntEnum(t, _): '(${t.nativeName})arg_$i'; - case Nested(LibType(_, _)): '(*arg_$i)'; - case Unsupported(cName): '($cName)0'; - case _: 'arg_$i'; - } - } ]; - if (method.isCppMemberCall) - callArgs.pop(); - var call = '${method.native}(' + callArgs.join(", ") + ')'; - if (method.isCppConstructor) - call = 'new $call'; - if (method.isCppMemberCall) - call = 'arg_${callArgs.length}->$call'; - if (method.ret == Void) - lb.ai(""); - else if (method.ret.match(Alloc(LibType(_, _)))) - lb.ai("*ret_alloc = "); - else { - lb.ai("return ("); - lb.a(mapTypeC(method.ret, "")); - lb.ai(")"); - } - if (method.cReturn != null) { - lb.a(method.cReturn - .replace("%RET_ELEM_TYPE%", switch (mapTypeC(method.ret, "")) { - case t if (t.endsWith(" *")): t.substr(0, t.length - 2); - case _: "?"; - }) - .replace("%RET_TYPE%", mapTypeC(method.ret, "")) - .replace("%CALL%", call)); - lb.a(";\n"); - } - else - lb.a('$call;\n'); - if (method.ret.match(Alloc(LibType(_, _)))) - lb.ai("return ret_alloc;\n"); - }); - lb.ai("}\n"); - lb.ai("#else\n"); - lb.ai(";\n"); - lb.ai("#endif\n"); - } - - public static function generate(config:Config, library:AmmerLibraryConfig):Void { - StubCpp.library = library; - lb = new LineBuf(); - generateHeader(); - var generated:Map = []; - for (ctx in library.contexts) { - generateClosureWrappers(ctx); - generateArrayWrappers(ctx); - lb.ai('extern "C" {\n'); - for (method in ctx.ffiMethods) { - if (generated.exists(method.uniqueName)) - continue; // TODO: make sure the field has the same signature - generated[method.uniqueName] = true; - generateMethod(method, ctx); - } - lb.ai("}\n"); - } - Utils.update('${config.output}/ammer/ammer_${library.name}.cpp.${library.abi == Cpp ? "cpp" : "c"}', lb.dump()); - } -} diff --git a/src/ammer/stub/StubEval.hx b/src/ammer/stub/StubEval.hx deleted file mode 100644 index cb3d6a7..0000000 --- a/src/ammer/stub/StubEval.hx +++ /dev/null @@ -1,7 +0,0 @@ -package ammer.stub; - -import ammer.Config.AmmerLibraryConfig; - -class StubEval { - public static function generate(config:Config, library:AmmerLibraryConfig):Void {} -} diff --git a/src/ammer/stub/StubHl.hx b/src/ammer/stub/StubHl.hx deleted file mode 100644 index ebc2a6a..0000000 --- a/src/ammer/stub/StubHl.hx +++ /dev/null @@ -1,220 +0,0 @@ -package ammer.stub; - -import ammer.Config.AmmerLibraryConfig; - -using ammer.FFITools; -using StringTools; - -class StubHl { - static var CONSTANT_TYPES_HL:Map = [ - Integer(Signed32) => {hlt: "i32", c: "int"}, - String => {hlt: "bytes", c: "char *"}, - Bool => {hlt: "bool", c: "bool"}, - Float(Float32) => {hlt: "f64", c: "double"}, - ]; - - static var library:AmmerLibraryConfig; - static var lb:LineBuf; - - static function generateHeader():Void { - lb.ai('#define HL_NAME(n) ammer_${library.name}_ ## n\n'); - lb.ai("#include \n"); - lb.ai("#include \n"); - for (header in library.headers) - lb.ai('#include <${header}>\n'); - } - - static function mapTypeHlFFI(t:FFIType):String { - return (switch (t) { - case Void: "_VOID"; - case Bool: "_BOOL"; - case Integer(Signed8 | Unsigned8): "_I8"; - case Integer(Signed16 | Unsigned16): "_I16"; - case Integer(Signed32 | Unsigned32): "_I32"; - case Integer(Signed64 | Unsigned64): "_I64"; - case Float(Float32): "_F32"; - case Float(Float64): "_F64"; - case Bytes: "_BYTES"; - case String: "_BYTES"; - case ArrayDynamic(idx, _) | ArrayFixed(idx, _, _): '_ABSTRACT(${Ammer.typeMap['ammer.externs.AmmerArray_$idx.AmmerArray_$idx'].nativeName})'; - case Derived(_, t): mapTypeHlFFI(t); - case WithSize(_, t): mapTypeHlFFI(t); - case Closure(_, args, ret, _): '_FUN(${mapTypeHlFFI(ret)}, ${args.map(mapTypeHlFFI).filter(a -> a != null).join(" ")})'; - case ClosureDataUse: null; - case ClosureData(_): "_I32"; // dummy - case LibType(t, _): '_ABSTRACT(${t.nativeName})'; - case LibIntEnum(_, _): "_I32"; - case OutPointer(LibType(t, _)): '_OBJ(_ABSTRACT(${t.nativeName}))'; - case Nested(LibType(t, _)): '_ABSTRACT(${t.nativeName})'; - case Alloc(LibType(t, _)): '_ABSTRACT(${t.nativeName})'; - case NoSize(t): mapTypeHlFFI(t); - case SizeOfReturn: "_REF(_I32)"; - case SizeOf(_): "_I32"; - case SizeOfField(_): "_I32"; - case SameSizeAs(t, _): mapTypeHlFFI(t); - case NativeHl(_, ffiName, _): ffiName; - case Unsupported(_): "_I32"; // dummy - case _: throw "!"; - }); - } - - static function mapTypeC(t:FFIType, name:String, closure:Bool = false):String { - return (switch (t) { - case Closure(_, _, _, _): 'vclosure *$name'; - case ClosureDataUse: 'void *$name'; - case ClosureData(_): 'int $name'; - case OutPointer(LibType(_, _)): 'vdynamic *$name'; - case Nested(LibType(t, _)) if (closure): '${t.nativeName} $name'; - case NativeHl(_, _, cName): '$cName $name'; - case Unsupported(_): 'int $name'; - case _: StubBaseC.mapTypeC(t, name); - }); - } - - static function generateClosureWrappers(ctx:AmmerContext):Void { - for (i in 0...ctx.closureTypes.length) { - var method = ctx.closureTypes[i]; - lb.ai('static ${mapTypeC(method.ret, "")} wc_${i}_${ctx.index}('); - lb.a([ for (i in 0...method.args.length) mapTypeC(method.args[i], 'arg_$i', true) ].filter(a -> a != null).join(", ")); - lb.a(") {\n"); - lb.indent(() -> { - lb.ai('vclosure *cl = (vclosure *)(${method.dataAccess.join("->")});\n'); - inline function print(withValue:Bool):Void { - if (method.ret == Void) - lb.ai(""); - else - lb.ai("return "); - lb.a('(('); - lb.a(mapTypeC(method.ret, "")); - lb.a(' (*)('); - lb.a((withValue ? ["vdynamic *"] : []).concat([ for (i in 0...method.args.length) switch (method.args[i]) { - case ClosureDataUse: continue; - case _: mapTypeC(method.args[i], ""); - } ]).filter(a -> a != null).join(", ")); - lb.a('))(cl->fun))('); - lb.a((withValue ? ["cl->value"] : []).concat([ for (i in 0...method.args.length) switch (method.args[i]) { - case ClosureDataUse: continue; - case Nested(LibType(t, _)): '&arg_$i'; - case _: 'arg_$i'; - } ]).join(", ")); - lb.a(');\n'); - } - lb.ai("if (cl->hasValue)\n"); - lb.indent(() -> print(true)); - lb.ai("else\n"); - lb.indent(() -> print(false)); - }); - lb.ai("}\n"); - } - } - - static function generateArrayWrappers(ctx:AmmerContext):Void { - for (i in 0...ctx.arrayTypes.length) { - lb.ai('typedef ${mapTypeC(ctx.arrayTypes[i].ffi, "")} wt_array_${i}_${ctx.index};\n'); - } - } - - public static function mapMethodName(name:String):String { - return 'w_$name'; - } - - static function generateMethod(method:FFIMethod, ctx:AmmerContext):Void { - lb.ai('HL_PRIM ${mapTypeC(method.ret, "")} HL_NAME(${mapMethodName(method.uniqueName)})('); - if (method.args.length == 0) - lb.a("void"); - else - lb.a([ for (i in 0...method.args.length) mapTypeC(method.args[i], 'arg_$i') ].filter(a -> a != null).join(", ")); - lb.a(") {\n"); - lb.indent(() -> { - if (method.cPrereturn != null) - lb.ai('${method.cPrereturn}\n'); - switch (method.ret) { - case Alloc(LibType(t, _)): - lb.ai('${t.nativeName} *ret_alloc = (${t.nativeName} *)malloc(sizeof(${t.nativeName}));\n'); - case _: - } - var callArgs = [ for (i in 0...method.args.length) { - switch (method.args[i]) { - case Closure(idx, _, _, _): '&wc_${idx}_${ctx.index}'; - case ClosureData(f): '(void *)arg_$f'; - case OutPointer(LibType(t, _)): '(${t.nativeName} **)(&(((void **)arg_$i)[1]))'; - case Nested(LibType(_, _)): '(*arg_$i)'; - case Unsupported(cName): '($cName)0'; - case _: 'arg_$i'; - } - } ]; - if (method.isCppMemberCall) - callArgs.pop(); - var call = '${method.native}(' + callArgs.join(", ") + ')'; - if (method.isCppConstructor) - call = 'new $call'; - if (method.isCppMemberCall) - call = 'arg_${callArgs.length}->$call'; - if (method.ret == Void) - lb.ai(""); - else if (method.ret.match(Alloc(LibType(_, _)))) - lb.ai("*ret_alloc = "); - else - lb.ai("return "); - if (method.cReturn != null) { - // TODO: RET_ELEM_TYPE should be implemented better - // TODO: remove duplication of this in StubCpp and StubLua - lb.a(method.cReturn - .replace("%RET_ELEM_TYPE%", switch (mapTypeC(method.ret, "")) { - case t if (t.endsWith(" *")): t.substr(0, t.length - 2); - case _: "?"; - }) - .replace("%RET_TYPE%", mapTypeC(method.ret, "")) - .replace("%CALL%", call)); - lb.a(";\n"); - } - else - lb.a('$call;\n'); - if (method.ret.match(Alloc(LibType(_, _)))) - lb.ai("return ret_alloc;\n"); - }); - lb.ai("}\n"); - lb.ai('DEFINE_PRIM(${mapTypeHlFFI(method.ret)}, ${mapMethodName(method.uniqueName)}, '); - if (method.args.length == 0) - lb.a("_NO_ARG"); - else - lb.a([ for (arg in method.args) mapTypeHlFFI(arg) ].filter(a -> a != null).join(" ")); - lb.a(");\n"); - } - - static function generateConstants(ctx:AmmerContext):Void { - for (t in FFITools.CONSTANT_TYPES) { - if (!ctx.ffiConstants.exists(t.ffi)) - continue; - lb.ai('HL_PRIM varray *HL_NAME(g_${t.name}_${ctx.index})(void) {\n'); - lb.indent(() -> { - lb.ai('varray *ret = hl_alloc_array(&hlt_${CONSTANT_TYPES_HL[t.ffi].hlt}, ${ctx.ffiConstants[t.ffi].length});\n'); - for (constant in ctx.ffiConstants[t.ffi]) { - lb.ai('hl_aptr(ret, ${CONSTANT_TYPES_HL[t.ffi].c})[${constant.index}] = ${constant.native};\n'); - } - lb.ai('return ret;\n'); - }); - lb.ai("}\n"); - lb.ai('DEFINE_PRIM(_ARR, g_${t.name}_${ctx.index}, _NO_ARG);\n'); - } - } - - public static function generate(config:Config, library:AmmerLibraryConfig):Void { - StubHl.library = library; - lb = new LineBuf(); - generateHeader(); - var generated:Map = []; - for (ctx in library.contexts) { - generateClosureWrappers(ctx); - generateArrayWrappers(ctx); - for (method in ctx.ffiMethods) { - if (generated.exists(method.uniqueName)) - continue; // TODO: make sure the field has the same signature - generated[method.uniqueName] = true; - generateMethod(method, ctx); - } - generateConstants(ctx); - } - Utils.update('${config.hl.build}/ammer_${library.name}.hl.${library.abi.fileExtension()}', lb.dump()); - } -} diff --git a/src/ammer/stub/StubLua.hx b/src/ammer/stub/StubLua.hx deleted file mode 100644 index 7b0fae9..0000000 --- a/src/ammer/stub/StubLua.hx +++ /dev/null @@ -1,216 +0,0 @@ -package ammer.stub; - -import ammer.Config.AmmerLibraryConfig; - -using ammer.FFITools; -using StringTools; - -class StubLua { - static var CONSTANT_TYPES_LUA:Map = [ - Integer(Signed32) => "integer", - String => "string", - Bool => "boolean", - Float(Float32) => "number", - ]; - - static var library:AmmerLibraryConfig; - static var lb:LineBuf; - - static function generateHeader():Void { - lb.ai("#ifdef __cplusplus\n"); - lb.ai("extern \"C\" {\n"); - lb.ai("#endif\n"); - lb.ai("#include \n"); - lb.ai("#include \n"); - lb.ai("#include \n"); - lb.ai("#include \n"); - lb.ai("#ifdef __cplusplus\n"); - lb.ai("}\n"); - lb.ai("#endif\n"); - for (header in library.headers) - lb.ai('#include <${header}>\n'); - } - - static function mapTypeC(t:FFIType, name:String):String { - return (switch (t) { - case SizeOfReturn: "size_t" + (name != "" ? ' $name' : ""); - case _: StubBaseC.mapTypeC(t, name); - }); - } - - public static function mapMethodName(name:String):String { - return 'w_$name'; - } - - static function box(t:FFIType, expr:String, size:Null):String { - return (switch (t) { - case Bool: 'lua_pushboolean(L, $expr)'; - case Integer(_): 'lua_pushinteger(L, $expr)'; - case Float(_): 'lua_pushnumber(L, $expr)'; - case String | Bytes if (size != null): 'lua_pushlstring(L, $expr, $size)'; - case ArrayFixed(_, _, _): 'lua_pushlightuserdata(L, $expr)'; - case WithSize(_, String | Bytes): 'lua_pushlstring(L, $expr, $size)'; - case String | Bytes: 'lua_pushstring(L, $expr)'; - case SameSizeAs(t, _): box(t, expr, size); - case LibType(_, _): 'lua_pushlightuserdata(L, $expr)'; - case LibIntEnum(_, _): 'lua_pushinteger(L, $expr)'; - case _: throw "!"; - }); - } - - static function unbox(t:FFIType, i:Int):String { - return (switch (t) { - case Void: null; - case Bool: 'lua_toboolean(L, $i)'; - case Integer(_): 'lua_tointeger(L, $i)'; - case Float(_): 'lua_tonumber(L, $i)'; - case String: - lb.ai('size_t arg_${i - 1}_size = 0;\n'); - 'lua_tolstring(L, $i, &arg_${i - 1}_size)'; - case Bytes: - lb.ai('size_t arg_${i - 1}_size = 0;\n'); - '(unsigned char *)lua_tolstring(L, $i, &arg_${i - 1}_size)'; - case ArrayFixed(_, _, _): 'lua_touserdata(L, $i)'; - case NoSize(t): unbox(t, i); - case SizeOf(_): 'lua_tointeger(L, $i)'; - case SizeOfReturn: "0"; - case LibType(t, _): '(${t.nativeName} *)lua_touserdata(L, $i)'; - case Nested(LibType(t, _)): '(${t.nativeName})lua_touserdata(L, $i)'; - case LibIntEnum(_, _): 'lua_tointeger(L, $i)'; - case WithSize(_, String | Bytes): - lb.ai('size_t arg_${i - 1}_size = 0;\n'); - 'lua_tolstring(L, $i, &arg_${i - 1}_size)'; - case Unsupported(_): null; - case _: throw "!"; - }); - } - - static function generateArrayWrappers(ctx:AmmerContext):Void { - for (i in 0...ctx.arrayTypes.length) { - lb.ai('typedef ${mapTypeC(ctx.arrayTypes[i].ffi, "")} wt_array_${i}_${ctx.index};\n'); - } - } - - static function generateMethod(method:FFIMethod):Void { - lb.ai('static int ${mapMethodName(method.uniqueName)}(lua_State *L) {\n'); - lb.indent(() -> { - var sizeOfReturn = null; - for (i in 0...method.args.length) { - if (method.args[i] == SizeOfReturn) - sizeOfReturn = 'arg_$i'; - var unboxed = unbox(method.args[i], i + 1); - if (unboxed == null) - continue; - lb.ai('${mapTypeC(method.args[i], 'arg_$i')} = $unboxed;\n'); - } - switch (method.ret) { - case SameSizeAs(_, i): sizeOfReturn = 'arg_${i}_size'; - case _: - } - if (method.cPrereturn != null) - lb.ai('${method.cPrereturn}\n'); - var callArgs = [ for (i in 0...method.args.length) switch (method.args[i]) { - case SizeOfReturn: '&arg_$i'; - case Unsupported(cName): '($cName)0'; - case _: 'arg_$i'; - } ]; - if (method.isCppMemberCall) - callArgs.pop(); - var call = '${method.native}(' + callArgs.join(", ") + ')'; - if (method.isCppConstructor) - call = 'new $call'; - if (method.isCppMemberCall) - call = 'arg_${callArgs.length}->$call'; - if (method.ret == Void) - lb.ai(""); - else - lb.ai('${mapTypeC(method.ret, 'ret')} = '); - if (method.cReturn != null) { - lb.a(method.cReturn - .replace("%RET_ELEM_TYPE%", switch (mapTypeC(method.ret, "")) { - case t if (t.endsWith(" *")): t.substr(0, t.length - 2); - case _: "?"; - }) - .replace("%RET_TYPE%", mapTypeC(method.ret, "")) - .replace("%CALL%", call)); - lb.a(";\n"); - } - else - lb.a('$call;\n'); - if (method.ret == Void) - lb.ai("return 0;\n"); - else { - lb.ai(box(method.ret, "ret", sizeOfReturn)); - lb.a(";\n"); - lb.ai("return 1;\n"); - } - }); - lb.ai("}\n"); - } - - static function generateConstants(ctx:AmmerContext):Array { - return [ for (t in FFITools.CONSTANT_TYPES) { - if (!ctx.ffiConstants.exists(t.ffi)) - continue; - var method = 'g_${t.name}_${ctx.index}'; - lb.ai('static int $method(lua_State *L) {\n'); - lb.indent(() -> { - lb.ai("lua_newtable(L);\n"); - for (constant in ctx.ffiConstants[t.ffi]) { - lb.ai('lua_pushinteger(L, ${constant.index});\n'); - lb.ai('lua_push${CONSTANT_TYPES_LUA[t.ffi]}(L, ${constant.native});\n'); - lb.ai("lua_settable(L, -3);\n"); - } - lb.ai('return 1;\n'); - }); - lb.ai("}\n"); - method; - } ]; - } - - static function generateInit(ctx:AmmerContext, varMethods:Array):Void { - lb.ai("#ifdef __cplusplus\n"); - lb.ai("extern \"C\" {\n"); - lb.ai("#endif\n"); - lb.ai('int g_init_${ctx.index}(lua_State *L) {\n'); - lb.indent(() -> { - lb.ai("luaL_Reg wrap[] = {\n"); - lb.indent(() -> { - for (method in ctx.ffiMethods) { - lb.ai('{"${mapMethodName(method.uniqueName)}", ${mapMethodName(method.uniqueName)}},\n'); - } - for (method in varMethods) { - lb.ai('{"$method", $method},\n'); - } - lb.ai("{NULL, NULL}\n"); - }); - lb.ai("};\n"); - lb.ai("lua_newtable(L);\n"); - lb.ai("luaL_setfuncs(L, wrap, 0);\n"); - lb.ai("return 1;\n"); - }); - lb.ai("}\n"); - lb.ai("#ifdef __cplusplus\n"); - lb.ai("}\n"); - lb.ai("#endif\n"); - } - - public static function generate(config:Config, library:AmmerLibraryConfig):Void { - StubLua.library = library; - lb = new LineBuf(); - generateHeader(); - var generated:Map = []; - for (ctx in library.contexts) { - generateArrayWrappers(ctx); - for (method in ctx.ffiMethods) { - if (generated.exists(method.uniqueName)) - continue; // TODO: make sure the field has the same signature - generated[method.uniqueName] = true; - generateMethod(method); - } - var varMethods = generateConstants(ctx); - generateInit(ctx, varMethods); - } - Utils.update('${config.lua.build}/ammer_${library.name}.lua.${library.abi == Cpp ? "cpp" : "c"}', lb.dump()); - } -} diff --git a/test/native-gen/NativeGen.hx b/test/native-gen/NativeGen.hx new file mode 100644 index 0000000..8c3624f --- /dev/null +++ b/test/native-gen/NativeGen.hx @@ -0,0 +1,127 @@ +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr; +import sys.io.File; +import sys.FileSystem; + +using StringTools; + +class NativeGen { + public static function deleteFields():Array { + return []; + } + public static function generate():Void { + var pos = Context.currentPos(); + function params(isConst:Array):Array { + return [ for (i in 0...isConst.length) { + name: 'T$i', + meta: isConst[i] ? [{ + pos: pos, + name: ":const", + }] : [], + } ]; + } + Context.onTypeNotFound(rname -> { + if (rname == "Single") return { + name: rname, + pack: [], + kind: TDAlias((macro : Float)), + fields: [], + pos: pos, + }; + var pack = rname.split("."); + var name = pack.pop(); + if (pack.length > 0 && pack[0] == "ammer") { + if (rname == "ammer.Syntax") { + return { + name: name, + pack: pack, + kind: TDClass(null, [], true, false, false), + fields: [], + pos: pos, + }; + } + if (pack[1] == "def") { + return { + name: name, + pack: pack, + params: (switch (rname) { + case "ammer.def.Library": params([true]); + case "ammer.def.Struct": params([true, false]); + case "ammer.def.Sublibrary": params([false]); + case _: []; + }), + kind: TDClass(null, [], false, false, false), + meta: [{ + name: ":autoBuild", + params: [macro NativeGen.deleteFields()], + pos: pos, + }], + fields: [], + pos: pos, + }; + } + return { + name: name, + pack: pack, + params: [], + kind: TDClass(null, [], false, false, false), + fields: [], + pos: pos, + }; + } + return null; + }); + for (test in FileSystem.readDirectory("../src/test")) { + if (test.startsWith(".") || !test.endsWith(".hx")) continue; + Context.resolveType(TPath({ + name: test.substr(0, test.length - 3), + pack: ["test"], + }), pos); + } + Context.onAfterTyping(types -> { + var outputs:Map> = []; + for (common in FileSystem.readDirectory("common-header")) { + outputs[common] = ["(HEADER)" => File.getContent('common-header/$common')]; + } + for (t in types) switch (t) { + case TClassDecl(_.get() => cls = { pack: ["test"], name: _.startsWith("Test") => true }): + for (code in cls.meta.extract(":ammertest.code")) switch (code.params) { + case [{expr: EConst(CString(output))}, {expr: EMeta(_, {expr: EConst(CString(code))})}]: + code.startsWith("") || throw 0; + code = code.substr("".length); + code.endsWith("") || throw 0; + code = code.substr(0, code.length - "".length); + if (!outputs.exists(output)) outputs[output] = new Map(); + var typeId = cls.pack.concat([cls.name]).join("."); + outputs[output][typeId] = '// $typeId\n$code'; + case _: throw 0; + } + case _: + } + for (common in FileSystem.readDirectory("common-footer")) { + outputs[common]["(FOOTER)"] = File.getContent('common-footer/$common'); + } + var sortedOutputs = [ for (k in outputs.keys()) k ]; + sortedOutputs.sort(Reflect.compare); + for (output in sortedOutputs) { + var codes = outputs[output]; + var merged = new StringBuf(); + var sortedKeys = [ for (k in codes.keys()) k ]; + sortedKeys.sort((a, b) -> + a == "(HEADER)" ? -1 : + a == "(FOOTER)" ? 1 : + b == "(HEADER)" ? 1 : + b == "(FOOTER)" ? -1 : Reflect.compare(a, b)); + for (k in sortedKeys) { + merged.add(codes[k]); + } + Sys.println('$output ... ${sortedKeys.length}'); + File.saveContent('../native-src/$output', merged.toString()); + } + }); + } +} + +#end diff --git a/test/native-gen/ammer/def/Enum.hx b/test/native-gen/ammer/def/Enum.hx new file mode 100644 index 0000000..2076a85 --- /dev/null +++ b/test/native-gen/ammer/def/Enum.hx @@ -0,0 +1,13 @@ +package ammer.def; + +#if macro + +import haxe.macro.Expr; + +class Enum { + public static function build(name, ffi, lib):Array { + return null; + } +} + +#end diff --git a/test/native-gen/common-footer/native.h b/test/native-gen/common-footer/native.h new file mode 100644 index 0000000..d4aa34d --- /dev/null +++ b/test/native-gen/common-footer/native.h @@ -0,0 +1,3 @@ +#ifdef __cplusplus +} +#endif diff --git a/test/native-gen/common-header/native.c b/test/native-gen/common-header/native.c new file mode 100644 index 0000000..d147ba5 --- /dev/null +++ b/test/native-gen/common-header/native.c @@ -0,0 +1,7 @@ +#include +#include +#include +#include + +#include "native.h" +#include "utf8.h" diff --git a/test/native-gen/common-header/native.h b/test/native-gen/common-header/native.h new file mode 100644 index 0000000..5f4b362 --- /dev/null +++ b/test/native-gen/common-header/native.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #define LIB_EXPORT __declspec(dllexport) +#else + #define LIB_EXPORT +#endif + +#include +#include +#include diff --git a/test/native-gen/common-header/templates.cpp b/test/native-gen/common-header/templates.cpp new file mode 100644 index 0000000..bccf742 --- /dev/null +++ b/test/native-gen/common-header/templates.cpp @@ -0,0 +1 @@ +#include "templates.hpp" diff --git a/test/native-gen/common-header/templates.hpp b/test/native-gen/common-header/templates.hpp new file mode 100644 index 0000000..c52ad91 --- /dev/null +++ b/test/native-gen/common-header/templates.hpp @@ -0,0 +1,10 @@ +#pragma once + +#ifdef _WIN32 + #define LIB_EXPORT __declspec(dllexport) +#else + #define LIB_EXPORT +#endif + +#include +#include diff --git a/test/native-gen/make.hxml b/test/native-gen/make.hxml new file mode 100644 index 0000000..c5f9cf2 --- /dev/null +++ b/test/native-gen/make.hxml @@ -0,0 +1,4 @@ +-cp ../src +-cp . +--macro NativeGen.generate() +--no-output diff --git a/test/native-gen/test/Test.hx b/test/native-gen/test/Test.hx new file mode 100644 index 0000000..1912961 --- /dev/null +++ b/test/native-gen/test/Test.hx @@ -0,0 +1,16 @@ +package test; + +import haxe.io.Bytes; + +// some methods from Test class in Haxe unit test sources +@:autoBuild(NativeGen.deleteFields()) +class Test { + public function new() {} + function eq(v:T, v2:T, ?pos:haxe.PosInfos) {} + function feq(v:Float, v2:Float, ?pos:haxe.PosInfos) {} + function aeq(expected:Array, actual:Array, ?pos:haxe.PosInfos) {} + function beq(a:Bytes, b:Bytes, ?pos:haxe.PosInfos) {} + function t(v:Bool, ?pos:haxe.PosInfos) {} + function f(v:Bool, ?pos:haxe.PosInfos) {} + function noAssert(?pos:haxe.PosInfos) {} +} diff --git a/tests/native/Makefile.linux b/test/native-src/Makefile.linux similarity index 100% rename from tests/native/Makefile.linux rename to test/native-src/Makefile.linux diff --git a/tests/native/Makefile.osx b/test/native-src/Makefile.osx similarity index 100% rename from tests/native/Makefile.osx rename to test/native-src/Makefile.osx diff --git a/tests/native/Makefile.win b/test/native-src/Makefile.win similarity index 100% rename from tests/native/Makefile.win rename to test/native-src/Makefile.win diff --git a/test/native-src/utf8.c b/test/native-src/utf8.c new file mode 100644 index 0000000..8f3d856 --- /dev/null +++ b/test/native-src/utf8.c @@ -0,0 +1,26 @@ +#include "utf8.h" + +LIB_EXPORT int utf8_decode(unsigned char **ptr) { + int cc1 = *((*ptr)++); if (cc1 < 0x80) return cc1; + int cc2 = *((*ptr)++) & 0x7F; if (cc1 < 0xE0) return ((cc1 & 0x3F) << 6) | cc2; + int cc3 = *((*ptr)++) & 0x7F; if (cc1 < 0xF0) return ((cc1 & 0x1F) << 12) | (cc2 << 6) | cc3; + int cc4 = *((*ptr)++) & 0x7F; return ((cc1 & 0x0F) << 18) | (cc2 << 12) | (cc3 << 6) | cc4; +} + +LIB_EXPORT void utf8_encode(unsigned char **ptr, int cc) { + if (cc <= 0x7F) { + *((*ptr)++) = cc; + } else if (cc <= 0x7FF) { + *((*ptr)++) = 0xC0 | (cc >> 6); + *((*ptr)++) = 0x80 | (cc & 0x3F); + } else if (cc <= 0xFFFF) { + *((*ptr)++) = 0xE0 | (cc >> 12); + *((*ptr)++) = 0x80 | ((cc >> 6) & 0x3F); + *((*ptr)++) = 0x80 | (cc & 0x3F); + } else { + *((*ptr)++) = 0xF0 | (cc >> 18); + *((*ptr)++) = 0x80 | ((cc >> 12) & 0x3F); + *((*ptr)++) = 0x80 | ((cc >> 6) & 0x3F); + *((*ptr)++) = 0x80 | (cc & 0x3F); + } +} diff --git a/tests/native/utf8.h b/test/native-src/utf8.h similarity index 73% rename from tests/native/utf8.h rename to test/native-src/utf8.h index 2f6c310..fc885e9 100644 --- a/tests/native/utf8.h +++ b/test/native-src/utf8.h @@ -1,9 +1,9 @@ #pragma once #ifdef _WIN32 - #define LIB_EXPORT __declspec(dllexport) + #define LIB_EXPORT __declspec(dllexport) #else - #define LIB_EXPORT + #define LIB_EXPORT #endif #include diff --git a/tests/Main.hx b/test/src/Main.hx similarity index 100% rename from tests/Main.hx rename to test/src/Main.hx diff --git a/test/src/def/Native.hx b/test/src/def/Native.hx new file mode 100644 index 0000000..1d0ae66 --- /dev/null +++ b/test/src/def/Native.hx @@ -0,0 +1,15 @@ +package def; + +@:ammer.sub((_ : test.TestArrays.TestArraysNative)) +@:ammer.sub((_ : test.TestBytes.TestBytesNative)) +@:ammer.sub((_ : test.TestCInjection.TestCInjectionNative)) +@:ammer.sub((_ : test.TestCallback.TestCallbackNative)) +@:ammer.sub((_ : test.TestConstants.TestConstantsNative)) +@:ammer.sub((_ : test.TestDatatypes.TestDatatypesNative)) +@:ammer.sub((_ : test.TestEnums.TestEnumsNative)) +@:ammer.sub((_ : test.TestHaxe.TestHaxeNative)) +@:ammer.sub((_ : test.TestHaxeRef.TestHaxeRefNative)) +@:ammer.sub((_ : test.TestMaths.TestMathsNative)) +@:ammer.sub((_ : test.TestSignature.TestSignatureNative)) +@:ammer.sub((_ : test.TestStrings.TestStringsNative)) +class Native extends ammer.def.Library<"native"> {} diff --git a/test/src/def/Templates.hx b/test/src/def/Templates.hx new file mode 100644 index 0000000..19da0c9 --- /dev/null +++ b/test/src/def/Templates.hx @@ -0,0 +1,7 @@ +package def; + +import ammer.ffi.*; + +@:ammer.lib.language(Cpp) +@:ammer.sub((_ : test.TestCpp.TestCppNative)) +class Templates extends ammer.def.Library<"templates"> {} diff --git a/test/src/test/Test.hx b/test/src/test/Test.hx new file mode 100644 index 0000000..850c9ce --- /dev/null +++ b/test/src/test/Test.hx @@ -0,0 +1,38 @@ +package test; + +import utest.Assert; +import utest.Async; +import haxe.io.Bytes; + +// some methods from Test class in Haxe unit test sources +class Test implements utest.ITest { + public function new() {} + + function eq(v:T, v2:T, ?pos:haxe.PosInfos) { + Assert.equals(v, v2, pos); + } + + function feq(v:Float, v2:Float, ?pos:haxe.PosInfos) { + Assert.floatEquals(v, v2, pos); + } + + function aeq(expected:Array, actual:Array, ?pos:haxe.PosInfos) { + Assert.same(expected, actual, pos); + } + + function beq(a:Bytes, b:Bytes, ?pos:haxe.PosInfos) { + Assert.isTrue(a.compare(b) == 0, pos); + } + + function t(v:Bool, ?pos:haxe.PosInfos) { + Assert.isTrue(v, pos); + } + + function f(v:Bool, ?pos:haxe.PosInfos) { + Assert.isFalse(v, pos); + } + + function noAssert(?pos:haxe.PosInfos) { + t(true, pos); + } +} diff --git a/test/src/test/TestArrays.hx b/test/src/test/TestArrays.hx new file mode 100644 index 0000000..15007e6 --- /dev/null +++ b/test/src/test/TestArrays.hx @@ -0,0 +1,70 @@ +package test; + +@:ammertest.code("native.h", + LIB_EXPORT int take_array_fixed(int a[3]); + LIB_EXPORT int take_array(int *a, size_t b); + LIB_EXPORT void take_array_modify(int *a); +) +@:ammertest.code("native.c", + LIB_EXPORT int take_array_fixed(int a[3]) { + if (a[0] != 1 || a[1] != 2 || a[2] != 4) + return -1; + return a[0] + a[1] + a[2]; + } + LIB_EXPORT int take_array(int *a, size_t b) { + if (b != 3 || a[0] != 1 || a[1] != 2 || a[2] != 4) + return -1; + return a[0] + a[1] + a[2]; + } + LIB_EXPORT void take_array_modify(int *a) { + a[1] = 42; + } +) +class TestArraysNative extends ammer.def.Sublibrary { + public static function take_array_fixed(a:ammer.ffi.Array):Int; + public static function take_array( + @:ammer.skip _:haxe.ds.Vector, + @:ammer.derive(ammer.Lib.vecToArrayCopy(arg0)) _:ammer.ffi.Array, + @:ammer.derive(arg0.length) _:ammer.ffi.Size + ):Int; + public static function take_array_modify(a:ammer.ffi.Array):Void; +} + +class TestArrays extends Test implements ammer.Syntax { + function testArrays() { + var arr = ammer.Lib.allocArray(Int, 3); + arr.set(0, 1); + arr.set(1, 2); + arr.set(2, 4); + eq(TestArraysNative.take_array_fixed(arr), 7); + + var vec:haxe.ds.Vector = haxe.ds.Vector.fromArrayCopy([1, 2, 4]); + var arr = ammer.Lib.vecToArrayCopy(vec); + eq(TestArraysNative.take_array_fixed(arr), 7); + + eq(TestArraysNative.take_array(vec), 7); + + #if !(lua || neko || js || python) + eq(@ret TestArraysNative.take_array_fixed(@ref vec), 7); + + var arrRef = ammer.Lib.vecToArrayRefForce(vec); + eq(TestArraysNative.take_array_fixed(arrRef.array), 7); + TestArraysNative.take_array_modify(arrRef.array); + arrRef.unref(); + eq(vec[1], 42); + #end + + vec[1] = 2; + + TestArraysNative.take_array_modify(@copyfree vec); + eq(vec[1], 2); + + TestArraysNative.take_array_modify(@copy vec); + eq(vec[1], 2); + + #if !(lua || neko || js || python) + TestArraysNative.take_array_modify(@ref vec); + eq(vec[1], 42); + #end + } +} diff --git a/test/src/test/TestBytes.hx b/test/src/test/TestBytes.hx new file mode 100644 index 0000000..de76793 --- /dev/null +++ b/test/src/test/TestBytes.hx @@ -0,0 +1,65 @@ +package test; + +import haxe.io.Bytes as HB; +import ammer.ffi.Bytes as AB; + +@:ammertest.code("native.h", + LIB_EXPORT unsigned char *ident_bytes(unsigned char *a, size_t b); + LIB_EXPORT unsigned char *give_bytes(size_t len); +) +@:ammertest.code("native.c", + LIB_EXPORT unsigned char *ident_bytes(unsigned char *a, size_t b) { + return memcpy(malloc(b), a, b); + } + LIB_EXPORT unsigned char *give_bytes(size_t len) { + unsigned char *ret = malloc(len); + for (size_t i = 0; i < len; i++) { + ret[i] = i + 1; + } + return ret; + } +) +class TestBytesNative extends ammer.def.Sublibrary { + public static function ident_bytes(_:AB, _:Int):AB; + + @:ammer.native("ident_bytes") public static function ident_bytes_1( + @:ammer.skip _:HB, + @:ammer.derive(ammer.ffi.Bytes.fromHaxeCopy(arg0)) _:AB, + @:ammer.derive(arg0.length) _:Int + ):AB; + + @:ammer.ret.derive(ret.toHaxeCopy(arg1), (_ : HB)) + @:ammer.native("ident_bytes") public static function ident_bytes_2( + _:AB, + _:Int + ):AB; + + @:ammer.ret.derive(ret.toHaxeCopy(arg0.length), (_ : HB)) + @:ammer.native("ident_bytes") public static function ident_bytes_3( + @:ammer.skip _:HB, + @:ammer.derive(AB.fromHaxeCopy(arg0)) _:AB, + @:ammer.derive(arg0.length) _:Int + ):AB; + + @:ammer.ret.derive(ret.toHaxeCopy(arg0), (_ : HB)) + public static function give_bytes(len:Int):AB; +} + +class TestBytes extends Test { + function testIdent() { + beq(TestBytesNative.ident_bytes(AB.fromHaxeCopy(HB.ofHex("")), 0).toHaxeCopy(0), HB.ofHex("")); + beq(TestBytesNative.ident_bytes(AB.fromHaxeCopy(HB.ofHex("00")), 1).toHaxeCopy(1), HB.ofHex("00")); + + var b = HB.ofHex("0001FEFF"); + var c = HB.ofHex("AA01FEFF"); + beq(TestBytesNative.ident_bytes_1(b).toHaxeCopy(4), b); + beq(TestBytesNative.ident_bytes_2(AB.fromHaxeCopy(b), 4), b); + beq(TestBytesNative.ident_bytes_3(b), b); + } + + function testReturns() { + beq(TestBytesNative.give_bytes(0), HB.ofHex("")); + beq(TestBytesNative.give_bytes(1), HB.ofHex("01")); + beq(TestBytesNative.give_bytes(10), HB.ofHex("0102030405060708090A")); + } +} diff --git a/test/src/test/TestCInjection.hx b/test/src/test/TestCInjection.hx new file mode 100644 index 0000000..3d7cf99 --- /dev/null +++ b/test/src/test/TestCInjection.hx @@ -0,0 +1,34 @@ +package test; + +@:ammertest.code("native.h", + LIB_EXPORT void save_num(int); + LIB_EXPORT int get_saved_num(void); + LIB_EXPORT int *pointer_saved_num(void); +) +@:ammertest.code("native.c", + static int saved_num = 0; + LIB_EXPORT void save_num(int num) { + saved_num = num; + } + LIB_EXPORT int get_saved_num(void) { + return saved_num; + } + LIB_EXPORT int *pointer_saved_num(void) { + return &saved_num; + } +) +class TestCInjectionNative extends ammer.def.Sublibrary { + @:ammer.c.prereturn("save_num(5);") + public static function get_saved_num():Int; + + @:ammer.c.prereturn("save_num(11);") + @:ammer.c.return("*(%CALL%)") + public static function pointer_saved_num():Int; +} + +class TestCInjection extends Test { + function testInjection() { + eq(TestCInjectionNative.get_saved_num(), 5); + eq(TestCInjectionNative.pointer_saved_num(), 11); + } +} diff --git a/test/src/test/TestCallback.hx b/test/src/test/TestCallback.hx new file mode 100644 index 0000000..e6505f1 --- /dev/null +++ b/test/src/test/TestCallback.hx @@ -0,0 +1,232 @@ +package test; + +import ammer.ffi.Callback; +import ammer.ffi.Haxe; +import ammer.ffi.Int32; + +@:build(ammer.def.Enum.build("enum enum_constants_cb", ammer.ffi.Int32, TestCallbackNative)) +enum abstract NativeEnum2(Int) from Int to Int { + @:ammer.native("e_const_cb0") var EConst0; + @:ammer.native("e_const_cb1") var EConst1; + @:ammer.native("e_const_cb10") var EConst10; +} + +@:ammer.alloc +// TODO: this should be added automatically on cpp-static +@:headerCode('#include "/DevProjects/Repos/ammer/test/native-src/native.h"') +class NativeCallbackData extends ammer.def.Struct<"callback_data_t", TestCallbackNative> { + public var user_data:Haxe<(NativeCallbackData)->Int>; + public var foo:Int; +} + +@:ammer.sub((_ : test.TestCallback.NativeCallbackData)) +@:ammertest.code("native.h", + enum enum_constants_cb { + e_const_cb0 = 0, + e_const_cb1 = 1, + e_const_cb10 = 10 + }; + + typedef struct { + void *user_data; + int foo; + } callback_data_t; + LIB_EXPORT void save_func(int (* func)(int, int, void*), void *user_data); + LIB_EXPORT int call_func(void); + LIB_EXPORT int call_func_2(void *user_data, int (* func)(void *, const char *)); + LIB_EXPORT int call_func_3(void *user_data, int (* func)(callback_data_t *)); + LIB_EXPORT bool call_func_4(void *user_data, enum enum_constants_cb (* func)(void *, enum enum_constants_cb)); + + LIB_EXPORT void save_static_func(const char* (* func)(int, const char*)); + LIB_EXPORT const char* call_static_func(int, const char*); +) +@:ammertest.code("native.c", + static int (* cached_func)(int, int, void *); + static void *cached_user_data; + LIB_EXPORT void save_func(int (* func)(int, int, void *), void *user_data) { + cached_func = func; + cached_user_data = user_data; + } + LIB_EXPORT int call_func(void) { + return cached_func(1, 2, cached_user_data); + } + LIB_EXPORT int call_func_2(void *user_data, int (* func)(void *, const char *)) { + return func(user_data, "foobar") * 2; + } + static callback_data_t call_func_3_data; + LIB_EXPORT int call_func_3(void *user_data, int (* func)(callback_data_t *)) { + call_func_3_data.user_data = user_data; + call_func_3_data.foo = 59; + return func(&call_func_3_data); + } + LIB_EXPORT bool call_func_4(void *user_data, enum enum_constants_cb (* func)(void *, enum enum_constants_cb)) { + return func(user_data, e_const_cb1) == e_const_cb10; + } + + static const char* (* cached_static_func)(int, const char*); + LIB_EXPORT void save_static_func(const char* (* func)(int, const char*)) { + cached_static_func = func; + } + LIB_EXPORT const char* call_static_func(int a, const char* b) { + return cached_static_func(a, b); + } +) +class TestCallbackNative extends ammer.def.Sublibrary { + public static function save_func( + _:ammer.ffi.Callback< + (Int32, Int32, Haxe<(Int, Int)->Int>)->Int32, + (Int, Int)->Int, + [arg2], + [arg0, arg1], + TestCallbackNative + >, + _:Haxe<(Int, Int)->Int> + ):Void; + public static function call_func():Int; + public static function call_func_2( + _:Haxe<(String)->Int>, + _:Callback< + (Haxe<(String)->Int>, String)->Int32, + (String)->Int32, + [arg0], + [arg1], + TestCallbackNative + > + ):Int32; + public static function call_func_3( + _:Haxe<(NativeCallbackData)->Int>, + _:Callback< + (NativeCallbackData)->Int32, + (NativeCallbackData)->Int32, + [arg0.user_data], + [arg0], + TestCallbackNative + > + ):Int32; + public static function call_func_4( + _:Haxe<(NativeEnum2)->NativeEnum2>, + _:Callback< + (Haxe<(NativeEnum2)->NativeEnum2>, NativeEnum2)->NativeEnum2, + (NativeEnum2)->NativeEnum2, + [arg0], + [arg1], + TestCallbackNative + > + ):Bool; + + public static function save_static_func( + _:Callback< + (Int32, ammer.ffi.String) -> ammer.ffi.String, + (Int, String) -> String, + "global", + [arg0, arg1], + TestCallbackNative + > + ):Void; + public static function call_static_func(a:Int, b:String):String; +} + +class TestCallback extends Test implements ammer.Syntax { + var wasCalled = false; + var counterSet = false; + var callA = -1; + var callB = -1; + + function callback(a:Int, b:Int):Int { + wasCalled = true; + callA = a; + callB = b; + return a + b; + } + + function createClosure():((Int, Int)->Int) { + var counter = 0; + return ((a, b) -> { + counter++; + if (counter >= 3) + counterSet = true; + return a + b; + }); + } + + function testCallback() { + wasCalled = false; + var clRef = ammer.Lib.createHaxeRef((_ : (Int, Int)->Int), callback); + clRef.incref(); + TestCallbackNative.save_func(clRef); + eq(wasCalled, false); + eq(TestCallbackNative.call_func(), 3); + clRef.decref(); + eq(wasCalled, true); + eq(callA, 1); + eq(callB, 2); + + var clRef = ammer.Lib.createHaxeRef((_ : (Int, Int)->Int), (a:Int, b:Int) -> { + wasCalled = true; + callA = a; + callB = b; + a + b; + }); + wasCalled = false; + clRef.incref(); + TestCallbackNative.save_func(clRef); + eq(wasCalled, false); + eq(TestCallbackNative.call_func(), 3); + clRef.decref(); + eq(wasCalled, true); + eq(callA, 1); + eq(callB, 2); + + wasCalled = false; + var clRef = ammer.Lib.createHaxeRef((_ : (String)->Int), (x:String) -> { + wasCalled = true; + eq(x, "foobar"); + 2; + }); + clRef.incref(); + eq(TestCallbackNative.call_func_2(clRef), 4); + clRef.decref(); + eq(wasCalled, true); + + counterSet = false; + var clRef2 = ammer.Lib.createHaxeRef((_ : (Int, Int)->Int), createClosure()); + clRef2.incref(); + TestCallbackNative.save_func(clRef2); + eq(TestCallbackNative.call_func(), 3); + eq(TestCallbackNative.call_func(), 3); + eq(TestCallbackNative.call_func(), 3); + clRef2.decref(); + eq(counterSet, true); + + wasCalled = false; + var clRef = ammer.Lib.createHaxeRef((_ : NativeCallbackData -> Int), (data:NativeCallbackData) -> { + eq(data.foo, 59); + wasCalled = true; + 77; + }); + clRef.incref(); + eq(TestCallbackNative.call_func_3(clRef), 77); + clRef.decref(); + eq(wasCalled, true); + + wasCalled = false; + var clRef = ammer.Lib.createHaxeRef((_ : NativeEnum2 -> NativeEnum2), (data:NativeEnum2) -> { + eq(data, NativeEnum2.EConst1); + wasCalled = true; + NativeEnum2.EConst10; + }); + clRef.incref(); + TestCallbackNative.call_func_4(clRef); + clRef.decref(); + eq(wasCalled, true); + } + + static function staticCallback(a:Int, b:String):String { + return '$a$b'; + } + + function testStaticCallback():Void { + TestCallbackNative.save_static_func(staticCallback); + eq(TestCallbackNative.call_static_func(2, "foo"), "2foo"); + } +} diff --git a/test/src/test/TestConstants.hx b/test/src/test/TestConstants.hx new file mode 100644 index 0000000..42f2d3f --- /dev/null +++ b/test/src/test/TestConstants.hx @@ -0,0 +1,38 @@ +package test; + +@:ammertest.code("native.h", + #define DEFINE_INT 42 + #define DEFINE_INT_EXPR (8 * 9) + #define DEFINE_STRING "foo" + #define DEFINE_STRING_EXPR ("foo" "bar" "foo") + #define DEFINE_BOOL 1 + #define DEFINE_BOOL_EXPR ((1 == 1) ? 1 : 0) + #define DEFINE_FLOAT 5.3 + #define DEFINE_FLOAT_EXPR (5.3 * 2) +) +class TestConstantsNative extends ammer.def.Sublibrary { + @:ammer.native("DEFINE_INT") public static final define_int:Int; + @:ammer.native("DEFINE_INT_EXPR") public static final define_int_expr:Int; + @:ammer.native("DEFINE_STRING") public static final define_string:String; + @:ammer.native("DEFINE_STRING_EXPR") public static final define_string_expr:String; + @:ammer.native("DEFINE_BOOL") public static final define_bool:Bool; + @:ammer.native("DEFINE_BOOL_EXPR") public static final define_bool_expr:Bool; + @:ammer.native("DEFINE_FLOAT") public static final define_float:Float; + @:ammer.native("DEFINE_FLOAT_EXPR") public static final define_float_expr:Float; + + // TODO: test globals (public static var) + // TODO: add read-only/write-only meta +} + +class TestConstants extends Test { + function testDefines() { + eq(TestConstantsNative.define_int, 42); + eq(TestConstantsNative.define_int_expr, 72); + eq(TestConstantsNative.define_string, "foo"); + eq(TestConstantsNative.define_string_expr, "foobarfoo"); + eq(TestConstantsNative.define_bool, true); + eq(TestConstantsNative.define_bool_expr, true); + feq(TestConstantsNative.define_float, 5.3); + feq(TestConstantsNative.define_float_expr, 10.6); + } +} diff --git a/test/src/test/TestCpp.hx b/test/src/test/TestCpp.hx new file mode 100644 index 0000000..8733533 --- /dev/null +++ b/test/src/test/TestCpp.hx @@ -0,0 +1,71 @@ +package test; + +@:ammer.sub((_ : XTemplatesStruct)) +@:ammertest.code("templates.hpp", + template + LIB_EXPORT int_t templated_add_ints(int_t a, int_t b); + + LIB_EXPORT void cpp_nop(void); + + struct TemplatesStruct { + uint32_t member_int; + public: + TemplatesStruct() : member_int(5) {} + uint32_t add(uint32_t x); + }; +) +@:ammertest.code("templates.cpp", + template + LIB_EXPORT int_t templated_add_ints(int_t a, int_t b) { + return a + b; + } + + template LIB_EXPORT int templated_add_ints(int a, int b); + template LIB_EXPORT uint64_t templated_add_ints(uint64_t a, uint64_t b); + + LIB_EXPORT void cpp_nop(void) {} + + uint32_t TemplatesStruct::add(uint32_t x) { + return this->member_int + x; + } +) +class TestCppNative extends ammer.def.Sublibrary { + @:ammer.native("templated_add_ints") public static function templated_add_ints32(a:Int, b:Int):Int; + public static function cpp_nop():Void; +} + +@:ammer.alloc +class XTemplatesStruct extends ammer.def.Struct<"TemplatesStruct", def.Templates> { + public var member_int:UInt32; + + // @:ammer.native("TemplatesStruct") + // @:ammer.cpp.constructor + // public static function new_():XTemplatesStruct; + + // @:ammer.cpp.member + // public function add(x:UInt32):UInt32; +} + +class TestCpp extends Test { + function testTemplates() { + eq(TestCppNative.templated_add_ints32(0, 0), 0); + eq(TestCppNative.templated_add_ints32(1, 2), 3); + eq(TestCppNative.templated_add_ints32(-1, 1), 0); + eq(TestCppNative.templated_add_ints32(0xFFFFFFFF, 1), 0); + eq(TestCppNative.templated_add_ints32(0x7F000000, 0xFFFFFF), 0x7FFFFFFF); + eq(TestCppNative.templated_add_ints32(-0x7FFFFFFF, 0x7FFFFFFF), 0); + } + + function testCppLinkage() { + TestCppNative.cpp_nop(); + eq(1, 1); + } + /* + function testStructMembers() { + var obj = XTemplatesStruct.new_(); + eq(obj.member_int, 5); + obj.member_int = 7; + eq(obj.member_int, 7); + eq(obj.add(13), 20); + }*/ +} diff --git a/test/src/test/TestDatatypes.hx b/test/src/test/TestDatatypes.hx new file mode 100644 index 0000000..63079f6 --- /dev/null +++ b/test/src/test/TestDatatypes.hx @@ -0,0 +1,234 @@ +package test; + +import ammer.ffi.This; + +// TODO: "opaque" is now a misnomer +@:ammer.nativePrefix("opaque_") +@:ammer.alloc +class NativeOpaque extends ammer.def.Struct<"opaque_type_t", def.Native> { + @:ammer.native("member_int") public var member_int:Int; + @:ammer.native("member_float") public var member_float:Float; + @:ammer.native("member_string") public var member_string:String; + /* + @:ammer.native("member_int_array_fixed") public var member_int_array_fixed:ammer.ffi.ArrayFixed; + #if (hl || cpp) + @:ammer.native("member_int_array") public var member_int_array:ammer.ffi.ArrayDynamic; + @:ammer.native("member_int_array_size") public var member_int_array_size:ammer.ffi.SizeOf<"member_int_array">; + @:ammer.native("member_string_array") public var member_string_array:ammer.ffi.ArrayDynamic; + @:ammer.native("member_string_array_size") public var member_string_array_size:ammer.ffi.SizeOf<"member_string_array">; + #end +*/ + public function get_int(_:This):Int; + public function get_float(_:This):Float; + public function get_string(_:This):String; + public function get_int_alt(_:Int, _:This, _:Int):Int; + //public function get_bytes(_:This, _:SizeOfReturn):Bytes; + + public function get_int_nested(_:ammer.ffi.Deref):Int; +} +/* +class NativeOpaque2 extends ammer.def.PointerNoStar<"opaque_type_ptr", def.Native> { + @:ammer.native("opaque_get_int") public function get_int(_:This):Int; +} +*/ + +@:ammer.sub((_ : test.TestDatatypes.NativeOpaque)) +// @:ammer.sub((_ : test.TestDatatypes.NativeOpaque2)) +@:ammertest.code("native.h", + typedef struct { + int member_int; + double member_float; + const char *member_string; + + int member_int_array_fixed[8]; + int *member_int_array; + int member_int_array_size; + const char **member_string_array; + int member_string_array_size; + } opaque_type_t; + typedef opaque_type_t *opaque_type_ptr; + + LIB_EXPORT opaque_type_ptr create_opaque(void); + LIB_EXPORT int opaque_get_int(opaque_type_ptr a); + LIB_EXPORT int opaque_get_int_nested(opaque_type_t a); + LIB_EXPORT double opaque_get_float(opaque_type_ptr a); + LIB_EXPORT const char *opaque_get_string(opaque_type_ptr a); + LIB_EXPORT int opaque_get_int_alt(int a, opaque_type_ptr b, int c); + LIB_EXPORT unsigned char *opaque_get_bytes(opaque_type_ptr a, size_t *b); + LIB_EXPORT void opaque_indirect(opaque_type_ptr *out); + LIB_EXPORT opaque_type_t create_opaque_noalloc(void); + LIB_EXPORT bool opaque_take_nested(opaque_type_t a); +) +@:ammertest.code("native.c", + LIB_EXPORT opaque_type_ptr create_opaque(void) { + opaque_type_ptr ret = malloc(sizeof(opaque_type_t)); + ret->member_int = 1; + ret->member_float = 2.0f; + ret->member_string = "3"; + for (int i = 0; i < 8; i++) { + ret->member_int_array_fixed[i] = 0xB0057ED + i; + } + ret->member_int_array = (int *)calloc(17, sizeof(int)); + ret->member_int_array_size = 17; + for (int i = 0; i < 17; i++) { + ret->member_int_array[i] = 0xB00573D + i; + } + ret->member_string_array = (const char **)calloc(3, sizeof(char *)); + ret->member_string_array_size = 3; + ret->member_string_array[0] = "arrfoo"; + ret->member_string_array[1] = "arrbar"; + ret->member_string_array[2] = "arrbaz"; + return ret; + } + LIB_EXPORT int opaque_get_int(opaque_type_ptr a) { + return a->member_int; + } + LIB_EXPORT int opaque_get_int_nested(opaque_type_t a) { + return a.member_int; + } + LIB_EXPORT double opaque_get_float(opaque_type_ptr a) { + return a->member_float; + } + LIB_EXPORT const char *opaque_get_string(opaque_type_ptr a) { + return a->member_string; + } + LIB_EXPORT int opaque_get_int_alt(int a, opaque_type_ptr b, int c) { + return a + b->member_int + c; + } + LIB_EXPORT unsigned char *opaque_get_bytes(opaque_type_ptr a, size_t *b) { + size_t len = strlen(a->member_string); + unsigned char *ret = malloc(len); + memcpy(ret, a->member_string, len); + *b = len; + return ret; + } + LIB_EXPORT void opaque_indirect(opaque_type_ptr *out) { + opaque_type_ptr ret = malloc(sizeof(opaque_type_t)); + ret->member_int = 10; + ret->member_float = 4.0f; + ret->member_string = "indirect"; + *out = ret; + } + LIB_EXPORT opaque_type_t create_opaque_noalloc(void) { + return (opaque_type_t){ + .member_int = 61, + .member_float = 5.2f, + .member_string = "noalloc", + .member_int_array_fixed = {9, 10, 11, 12, 13, 14, 15, 16}, + .member_int_array = NULL, + .member_int_array_size = 0, + .member_string_array = NULL, + .member_string_array_size = 0, + }; + } + LIB_EXPORT bool opaque_take_nested(opaque_type_t a) { + float diff = a.member_float - 5.4f; + return a.member_int == 62 + && (diff > -.0001f && diff < .0001f) + && strcmp(a.member_string, "noalloc") == 0; + //&& a.member_int_array_fixed[7] == 47 + //&& a.member_int_array == NULL + //&& a.member_string_array == NULL; + } +) +class TestDatatypesNative extends ammer.def.Sublibrary { + public static function create_opaque():NativeOpaque; + //@:ammer.native("create_opaque") public static function create_opaque2():NativeOpaque2; + + public static function opaque_indirect(_:ammer.ffi.Box):Void; + public static function create_opaque_noalloc():ammer.ffi.Alloc; + public static function opaque_take_nested(a:ammer.ffi.Deref):Bool; +} + +class TestDatatypes extends Test { + function testOpaque() { + var opaque = TestDatatypesNative.create_opaque(); + + eq(opaque.get_int(), 1); + feq(opaque.get_float(), 2.0); + eq(opaque.get_string(), "3"); + eq(opaque.get_int_alt(3, 4), 8); + /* + var opaque = TestDatatypesNative.create_opaque2(); + eq(opaque.get_int(), 1);*/ + } + + function testVariables() { + var opaque = TestDatatypesNative.create_opaque(); + opaque.member_int = 3; + eq(opaque.get_int(), 3); + opaque.member_int = 5; + eq(opaque.member_int, 5); + opaque.member_float = 3.12; + feq(opaque.get_float(), 3.12); + opaque.member_float = 5.12; + feq(opaque.member_float, 5.12); + // passing strings directly might be a bit dangerous + opaque.member_string = "foo"; + eq(opaque.get_string(), "foo"); + opaque.member_string = "bar"; + eq(opaque.member_string, "bar"); + //beq(opaque.get_bytes(), haxe.io.Bytes.ofHex("626172")); + } + + function testAlloc() { + var opaque = NativeOpaque.alloc(); + opaque.member_int = 7; + eq(opaque.get_int(), 7); + opaque.member_int = 49; + eq(opaque.member_int, 49); + opaque.free(); + } + + function testOutPointer() { + var opaqueBox = ammer.Lib.allocBox(NativeOpaque); + TestDatatypesNative.opaque_indirect(opaqueBox); + var opaque = opaqueBox.get(); + eq(opaque.member_int, 10); + feq(opaque.member_float, 4.0); + eq(opaque.member_string, "indirect"); + opaque.free(); + } + + function testNested() { + var opaque = TestDatatypesNative.create_opaque_noalloc(); + eq(opaque.get_int(), 61); + feq(opaque.get_float(), 5.2); + eq(opaque.get_string(), "noalloc"); + //for (i in 0...8) { + // eq(opaque.member_int_array_fixed[i], 9 + i); + //} + opaque.member_int = 62; + eq(opaque.get_int_nested(), 62); + opaque.member_float = 5.4; + //opaque.member_int_array_fixed[7] = 47; + eq(TestDatatypesNative.opaque_take_nested(opaque), true); + opaque.free(); + } + /* + function testArray() { + #if (hl || cpp) + var opaque = TestDatatypesNative.create_opaque(); + var arr = opaque.member_int_array_fixed; + eq(arr.length, 8); + for (i in 0...8) { + eq(arr[i], 0xB0057ED + i); + arr[i] = 0xDE7500B + i; + } + for (i in 0...8) { + eq(arr[i], 0xDE7500B + i); + } + var arr = opaque.member_int_array; + eq(arr.length, 17); + for (i in 0...17) { + eq(arr[i], 0xB00573D + i); + } + var arr = opaque.member_string_array; + eq(arr[0], "arrfoo"); + eq(arr[1], "arrbar"); + eq(arr[2], "arrbaz"); + #else + noAssert(); + #end + }*/ +} diff --git a/test/src/test/TestEnums.hx b/test/src/test/TestEnums.hx new file mode 100644 index 0000000..2cb53a5 --- /dev/null +++ b/test/src/test/TestEnums.hx @@ -0,0 +1,46 @@ +package test; + +@:build(ammer.def.Enum.build("enum enum_constants", ammer.ffi.Int32, def.Native)) +enum abstract NativeEnum(Int) from Int to Int { + @:ammer.native("e_const0") var EConst0; + @:ammer.native("e_const1") var EConst1; + @:ammer.native("e_const10") var EConst10; +} + +@:ammertest.code("native.h", + enum enum_constants { + e_const0 = 0, + e_const1 = 1, + e_const10 = 10 + }; + + //enum enum_flags { + // e_foo = 1, + // e_bar = 2, + // e_baz = 4 + //}; + + LIB_EXPORT bool take_enum(enum enum_constants a, enum enum_constants b, enum enum_constants c); + LIB_EXPORT enum enum_constants give_enum(void); +) +@:ammertest.code("native.c", + LIB_EXPORT bool take_enum(enum enum_constants a, enum enum_constants b, enum enum_constants c) { + return (a == e_const10) + && (b == e_const1) + && (c == e_const0); + } + LIB_EXPORT enum enum_constants give_enum(void) { + return e_const10; + } +) +class TestEnumsNative extends ammer.def.Sublibrary { + public static function take_enum(a:NativeEnum, b:NativeEnum, c:NativeEnum):Bool; + public static function give_enum():NativeEnum; +} + +class TestEnums extends Test { + function testEnums() { + eq(TestEnumsNative.take_enum(NativeEnum.EConst10, NativeEnum.EConst1, NativeEnum.EConst0), true); + eq(TestEnumsNative.give_enum(), NativeEnum.EConst10); + } +} diff --git a/test/src/test/TestHaxe.hx b/test/src/test/TestHaxe.hx new file mode 100644 index 0000000..5e08553 --- /dev/null +++ b/test/src/test/TestHaxe.hx @@ -0,0 +1,22 @@ +package test; + +@:ammertest.code("native.h", + LIB_EXPORT int func_under_haxe(int a, int b); +) +@:ammertest.code("native.c", + LIB_EXPORT int func_under_haxe(int a, int b) { + return a + b; + } +) +class TestHaxeNative extends ammer.def.Sublibrary { + private static function func_under_haxe(a:Int, b:Int):Int; + @:ammer.haxe public static function func(a:Int, b:Int):Int { + return 42 + func_under_haxe(a, b); + } +} + +class TestHaxe extends Test { + function testHaxe() { + eq(TestHaxeNative.func(1, 2), 45); + } +} diff --git a/test/src/test/TestHaxeRef.hx b/test/src/test/TestHaxeRef.hx new file mode 100644 index 0000000..bf4e8dd --- /dev/null +++ b/test/src/test/TestHaxeRef.hx @@ -0,0 +1,39 @@ +package test; + +import ammer.ffi.Haxe; + +@:structInit +class HaxeType { + public var val:Array; +} + +@:ammertest.code("native.h", + LIB_EXPORT void save_haxe(void* a); + LIB_EXPORT void *load_haxe(void); +) +@:ammertest.code("native.c", + static void *saved_haxe = 0; + LIB_EXPORT void save_haxe(void* a) { + saved_haxe = a; + } + LIB_EXPORT void *load_haxe(void) { + return saved_haxe; + } +) +@:ammer.sub((_ : ammer.ffi.Haxe)) +class TestHaxeRefNative extends ammer.def.Sublibrary { + public static function save_haxe(_:Haxe):Void; + public static function load_haxe():Haxe; +} + +class TestHaxeRef extends Test { + function testHaxe() { + function nested() { + TestHaxeRefNative.save_haxe(ammer.Lib.createHaxeRef(HaxeType, ({ + val: [1, 2, 3], + } : HaxeType))); + } + nested(); + aeq(TestHaxeRefNative.load_haxe().value.val, [1, 2, 3]); + } +} diff --git a/test/src/test/TestMaths.hx b/test/src/test/TestMaths.hx new file mode 100644 index 0000000..6f253ec --- /dev/null +++ b/test/src/test/TestMaths.hx @@ -0,0 +1,157 @@ +package test; + +import ammer.ffi.Int8; +import ammer.ffi.Int16; +import ammer.ffi.Int32; +import ammer.ffi.Int64; +import ammer.ffi.UInt8; +import ammer.ffi.UInt16; +import ammer.ffi.UInt32; +import ammer.ffi.UInt64; + +@:ammertest.code("native.h", + LIB_EXPORT int add_ints(int a, int b); + LIB_EXPORT unsigned int add_uints(unsigned int a, unsigned int b); + LIB_EXPORT float add_singles(float a, float b); + LIB_EXPORT double add_floats(double a, double b); + LIB_EXPORT bool logic_and(bool a, bool b); + LIB_EXPORT bool logic_or(bool a, bool b); + LIB_EXPORT int logic_ternary(bool a, int b, int c); + + LIB_EXPORT int8_t add_i8(int8_t a, int8_t b); + LIB_EXPORT int16_t add_i16(int16_t a, int16_t b); + LIB_EXPORT int32_t add_i32(int32_t a, int32_t b); + LIB_EXPORT int64_t add_i64(int64_t a, int64_t b); + LIB_EXPORT uint8_t add_u8(uint8_t a, uint8_t b); + LIB_EXPORT uint16_t add_u16(uint16_t a, uint16_t b); + LIB_EXPORT uint32_t add_u32(uint32_t a, uint32_t b); + LIB_EXPORT uint64_t add_u64(uint64_t a, uint64_t b); +) +@:ammertest.code("native.c", + LIB_EXPORT int add_ints(int a, int b) { + return a + b; + } + LIB_EXPORT unsigned int add_uints(unsigned int a, unsigned int b) { + return a + b; + } + LIB_EXPORT float add_singles(float a, float b) { + return a + b; + } + LIB_EXPORT double add_floats(double a, double b) { + return a + b; + } + LIB_EXPORT bool logic_and(bool a, bool b) { + return a && b; + } + LIB_EXPORT bool logic_or(bool a, bool b) { + return a || b; + } + LIB_EXPORT int logic_ternary(bool a, int b, int c) { + return a ? b : c; + } + + LIB_EXPORT int8_t add_i8(int8_t a, int8_t b) { + return a + b; + } + LIB_EXPORT int16_t add_i16(int16_t a, int16_t b) { + return a + b; + } + LIB_EXPORT int32_t add_i32(int32_t a, int32_t b) { + return a + b; + } + LIB_EXPORT int64_t add_i64(int64_t a, int64_t b) { + return a + b; + } + LIB_EXPORT uint8_t add_u8(uint8_t a, uint8_t b) { + return a + b; + } + LIB_EXPORT uint16_t add_u16(uint16_t a, uint16_t b) { + return a + b; + } + LIB_EXPORT uint32_t add_u32(uint32_t a, uint32_t b) { + return a + b; + } + LIB_EXPORT uint64_t add_u64(uint64_t a, uint64_t b) { + return a + b; + } +) +class TestMathsNative extends ammer.def.Sublibrary { + public static function add_ints(_:Int, _:Int):Int; + public static function add_uints(_:UInt, _:UInt):UInt; + #if !(lua || neko || js || python) + public static function add_singles(_:Single, _:Single):Single; + #end + public static function add_floats(_:Float, _:Float):Float; + public static function logic_and(_:Bool, _:Bool):Bool; + public static function logic_or(_:Bool, _:Bool):Bool; + public static function logic_ternary(_:Bool, _:Int, _:Int):Int; + + public static function add_i8(_:Int8, _:Int8):Int8; + public static function add_i16(_:Int16, _:Int16):Int16; + public static function add_i32(_:Int32, _:Int32):Int32; + public static function add_i64(_:Int64, _:Int64):Int64; + public static function add_u8(_:UInt8, _:UInt8):UInt8; + public static function add_u16(_:UInt16, _:UInt16):UInt16; + public static function add_u32(_:UInt32, _:UInt32):UInt32; + public static function add_u64(_:UInt64, _:UInt64):UInt64; +} + +class TestMaths extends Test { + function testInts() { + eq(TestMathsNative.add_ints(0, 0), 0); + eq(TestMathsNative.add_ints(1, 2), 3); + eq(TestMathsNative.add_ints(-1, 1), 0); + eq(TestMathsNative.add_ints(0xFFFFFFFF, 1), 0); + eq(TestMathsNative.add_ints(0x7F000000, 0xFFFFFF), 0x7FFFFFFF); + eq(TestMathsNative.add_ints(-0x7FFFFFFF, 0x7FFFFFFF), 0); + } + + function testUInts() { + eq(TestMathsNative.add_uints(0, 0), 0); + eq(TestMathsNative.add_uints(1, 2), 3); + eq(TestMathsNative.add_uints(0x7F000000, 0xFFFFFF), 0x7FFFFFFF); + // RHS is 0xFFFFFFFE but Haxe compiles the literal to -2 + eq(TestMathsNative.add_uints(0x7FFFFFFF, 0x7FFFFFFF), ((0x7FFFFFFF + 0x7FFFFFFF): UInt)); + } + + #if !(lua || neko || js || python) + function testSingles() { + eq(TestMathsNative.add_singles(0., 0.), (0.:Single)); + feq(TestMathsNative.add_singles(1., 2.), (3.:Single)); + feq(TestMathsNative.add_singles(-1., 1.), (0.:Single)); + feq(TestMathsNative.add_singles(-1e10, 1e10), (0.:Single)); + feq(TestMathsNative.add_singles(-1e10, 1e9), (-9e9:Single)); + } + #end + + function testFloats() { + eq(TestMathsNative.add_floats(0., 0.), 0.); + feq(TestMathsNative.add_floats(1., 2.), 3.); + feq(TestMathsNative.add_floats(-1., 1.), 0.); + feq(TestMathsNative.add_floats(-1e10, 1e10), 0.); + feq(TestMathsNative.add_floats(-1e10, 1e9), -9e9); + } + + function testBools() { + eq(TestMathsNative.logic_and(false, false), false); + eq(TestMathsNative.logic_and(true, false), false); + eq(TestMathsNative.logic_and(false, true), false); + eq(TestMathsNative.logic_and(true, true), true); + eq(TestMathsNative.logic_or(false, false), false); + eq(TestMathsNative.logic_or(true, false), true); + eq(TestMathsNative.logic_or(false, true), true); + eq(TestMathsNative.logic_or(true, true), true); + eq(TestMathsNative.logic_ternary(true, 3, 5), 3); + eq(TestMathsNative.logic_ternary(false, 3, 5), 5); + } + + function testBitWidths() { + eq(TestMathsNative.add_u8(142, 193), 79); + eq(TestMathsNative.add_u16(25679, 49565), 9708); + eq(TestMathsNative.add_u32(0xBF86404F, 0xBF863D5D), 0x7F0C7DAC); + var a = haxe.Int64.make(0xBBFBCDC4, 0x2397F34F); + var b = haxe.Int64.make(0x5ADF2061, 0x3E99B3E1); + var c = haxe.Int64.make(0x16DAEE25, 0x6231A730); + t(TestMathsNative.add_u64(a, b) == c); // see #10760 + } +} diff --git a/test/src/test/TestSignature.hx b/test/src/test/TestSignature.hx new file mode 100644 index 0000000..678dc17 --- /dev/null +++ b/test/src/test/TestSignature.hx @@ -0,0 +1,135 @@ +package test; + +import ammer.ffi.Unsupported; + +@:ammer.sub((_ : test.TestSignature.TestSignatureNative2)) +@:ammer.sub((_ : test.TestSignature.TestSignatureNative3)) +@:ammertest.code("native.h", + LIB_EXPORT int take_0(void); + LIB_EXPORT int take_1(int a1); + LIB_EXPORT int take_2(int a1, int a2); + LIB_EXPORT int take_3(int a1, int a2, int a3); + LIB_EXPORT int take_4(int a1, int a2, int a3, int a4); + LIB_EXPORT int take_5(int a1, int a2, int a3, int a4, int a5); + LIB_EXPORT int take_6(int a1, int a2, int a3, int a4, int a5, int a6); + LIB_EXPORT int take_7(int a1, int a2, int a3, int a4, int a5, int a6, int a7); + LIB_EXPORT int take_8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8); + LIB_EXPORT int take_9(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9); + LIB_EXPORT int take_10(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10); + LIB_EXPORT void nop(void); + LIB_EXPORT bool take_unsupported(void *a, double b); +) +@:ammertest.code("native.c", + LIB_EXPORT int take_0(void) { + return 0; + } + LIB_EXPORT int take_1(int a1) { + return 1; + } + LIB_EXPORT int take_2(int a1, int a2) { + return 2; + } + LIB_EXPORT int take_3(int a1, int a2, int a3) { + return 3; + } + LIB_EXPORT int take_4(int a1, int a2, int a3, int a4) { + return 4; + } + LIB_EXPORT int take_5(int a1, int a2, int a3, int a4, int a5) { + return 5; + } + LIB_EXPORT int take_6(int a1, int a2, int a3, int a4, int a5, int a6) { + return 6; + } + LIB_EXPORT int take_7(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { + return 7; + } + LIB_EXPORT int take_8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { + return 8; + } + LIB_EXPORT int take_9(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { + return 9; + } + LIB_EXPORT int take_10(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) { + return 10; + } + LIB_EXPORT void nop(void) {} + LIB_EXPORT bool take_unsupported(void *a, double b) { + return a == 0 && abs(b) < .0001; + } +) +class TestSignatureNative extends ammer.def.Sublibrary { + public static function take_0():Int; + public static function take_1(_:Int):Int; + public static function take_2(_:Int, _:Int):Int; + public static function take_3(_:Int, _:Int, _:Int):Int; + public static function take_4(_:Int, _:Int, _:Int, _:Int):Int; + public static function take_5(_:Int, _:Int, _:Int, _:Int, _:Int):Int; + public static function take_6(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; + public static function take_7(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; + public static function take_8(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; + public static function take_9(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; + public static function take_10(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; + public static function nop():Void; + public static function take_unsupported(_:Unsupported<"(void *)0">, _:Unsupported<"(double)0">):Bool; +} + +@:ammertest.code("native.h", + LIB_EXPORT int take_0alt(void); +) +@:ammertest.code("native.c", + LIB_EXPORT int take_0alt(void) { + return 0; + } +) +class TestSignatureNative2 extends ammer.def.Sublibrary { + public static function take_0():Int; + public static function take_0alt():Int; +} + +@:ammer.nativePrefix("prefixed_") +@:ammertest.code("native.h", + LIB_EXPORT void prefixed_nop2(void); +) +@:ammertest.code("native.c", + LIB_EXPORT void prefixed_nop2(void) {} +) +class TestSignatureNative3 extends ammer.def.Sublibrary { + public static function nop2():Void; + @:ammer.native("take_0") public static function take_0():Int; +} + +class TestSignature extends Test { + function testArgCount() { + eq(TestSignatureNative.take_0(), 0); + eq(TestSignatureNative.take_1(1), 1); + eq(TestSignatureNative.take_2(1, 2), 2); + eq(TestSignatureNative.take_3(1, 2, 3), 3); + eq(TestSignatureNative.take_4(1, 2, 3, 4), 4); + eq(TestSignatureNative.take_5(1, 2, 3, 4, 5), 5); + eq(TestSignatureNative.take_6(1, 2, 3, 4, 5, 6), 6); + eq(TestSignatureNative.take_7(1, 2, 3, 4, 5, 6, 7), 7); + eq(TestSignatureNative.take_8(1, 2, 3, 4, 5, 6, 7, 8), 8); + eq(TestSignatureNative.take_9(1, 2, 3, 4, 5, 6, 7, 8, 9), 9); + eq(TestSignatureNative.take_10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 10); + } + + function testVoid() { + TestSignatureNative.nop(); + noAssert(); + } + + function testMultipleClasses() { + eq(TestSignatureNative2.take_0(), 0); + eq(TestSignatureNative2.take_0alt(), 0); + } + + function testPrefix() { + TestSignatureNative3.nop2(); + eq(TestSignatureNative3.take_0(), 0); + } + + function testUnsupported() { + t(TestSignatureNative.take_unsupported()); + } +} diff --git a/test/src/test/TestStrings.hx b/test/src/test/TestStrings.hx new file mode 100644 index 0000000..0f4934f --- /dev/null +++ b/test/src/test/TestStrings.hx @@ -0,0 +1,53 @@ +package test; + +@:ammertest.code("native.h", + LIB_EXPORT const char *ident_string(const char *a); + LIB_EXPORT const char *rev_string(const char *a); + LIB_EXPORT bool check_string(const char *a, int id); +) +@:ammertest.code("native.c", + LIB_EXPORT const char *ident_string(const char *a) { + return strdup(a); + } + LIB_EXPORT const char *rev_string(const char *a) { + int len = strlen(a); + char *ret = malloc(len + 1); + int *cc = malloc(len * sizeof(int)); + int pos = 0; + while (*a != 0) cc[pos++] = utf8_decode((unsigned char **)&a); + char *retcur = ret; + while (pos > 0) utf8_encode((unsigned char **)&retcur, cc[--pos]); + *retcur = '\0'; + return ret; + } + LIB_EXPORT bool check_string(const char *a, int id) { + static const char *strings[] = { + "foo", + "\x42\xE0\xB2\xA0\xEA\xAF\x8D\xF0\x9F\x90\x84", + }; + return strcmp(a, strings[id]) == 0; + } +) +class TestStringsNative extends ammer.def.Sublibrary { + public static function ident_string(_:String):String; + public static function rev_string(_:String):String; + public static function check_string(_:String, _:Int):Bool; +} + +class TestStrings extends Test { + function testAscii() { + eq(TestStringsNative.ident_string(""), ""); + eq(TestStringsNative.ident_string("aaaa"), "aaaa"); + eq(TestStringsNative.rev_string(""), ""); + eq(TestStringsNative.rev_string("abc"), "cba"); + eq(TestStringsNative.check_string("foo", 0), true); + } + + #if !java + function testUnicode() { + eq(TestStringsNative.ident_string("\u0042\u0CA0\uABCD\u{1F404}"), "\u0042\u0CA0\uABCD\u{1F404}"); + eq(TestStringsNative.rev_string("\u0042\u0CA0\uABCD\u{1F404}"), "\u{1F404}\uABCD\u0CA0\u0042"); + eq(TestStringsNative.check_string("\u0042\u0CA0\uABCD\u{1F404}", 1), true); + } + #end +} diff --git a/tests/Native.hx b/tests/Native.hx deleted file mode 100644 index 7d26689..0000000 --- a/tests/Native.hx +++ /dev/null @@ -1,151 +0,0 @@ -import ammer.*; -import ammer.ffi.*; -import haxe.io.Bytes; - -class Native extends Library<"native"> { - @:ammer.native("DEFINE_INT") public static var define_int:Int; - @:ammer.native("DEFINE_INT_EXPR") public static var define_int_expr:Int; - @:ammer.native("DEFINE_STRING") public static var define_string:String; - @:ammer.native("DEFINE_STRING_EXPR") public static var define_string_expr:String; - @:ammer.native("DEFINE_BOOL") public static var define_bool:Bool; - @:ammer.native("DEFINE_BOOL_EXPR") public static var define_bool_expr:Bool; - @:ammer.native("DEFINE_FLOAT") public static var define_float:Float; - @:ammer.native("DEFINE_FLOAT_EXPR") public static var define_float_expr:Float; - - public static function take_0():Int; - public static function take_1(_:Int):Int; - public static function take_2(_:Int, _:Int):Int; - public static function take_3(_:Int, _:Int, _:Int):Int; - public static function take_4(_:Int, _:Int, _:Int, _:Int):Int; - public static function take_5(_:Int, _:Int, _:Int, _:Int, _:Int):Int; - public static function take_6(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; - public static function take_7(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; - public static function take_8(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; - public static function take_9(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; - public static function take_10(_:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int, _:Int):Int; - public static function nop():Void; - - public static function add_ints(_:Int, _:Int):Int; - public static function add_uints(_:UInt, _:UInt):UInt; - #if !lua - public static function add_singles(_:Single, _:Single):Single; - #end - public static function add_floats(_:Float, _:Float):Float; - public static function logic_and(_:Bool, _:Bool):Bool; - public static function logic_or(_:Bool, _:Bool):Bool; - public static function logic_ternary(_:Bool, _:Int, _:Int):Int; - - public static function id_string(_:String):String; - public static function rev_string(_:String):String; - - public static function id_bytes(a:Bytes, b:SizeOf<"a">):SameSizeAs; - public static function id_bytes_1(a:Bytes, _:NoSize, b:SizeOf<"a">):SameSizeAs; - public static function id_bytes_2(_:NoSize, a:Bytes, b:SizeOf<"a">):SameSizeAs; - public static function id_bytes_3(a:Bytes, b:SizeOf<"a">, _:NoSize):SameSizeAs; - public static function id_bytes_4(_:NoSize, b:SizeOf<"a">, a:Bytes):SameSizeAs; - public static function id_bytes_5(b:SizeOf<"a">, a:Bytes, _:NoSize):SameSizeAs; - public static function id_bytes_6(b:SizeOf<"a">, _:NoSize, a:Bytes):SameSizeAs; - public static function give_bytes(_:Int, _:SizeOfReturn):Bytes; - - @:ammer.c.prereturn("save_num(5);") - public static function get_saved_num():Int; - - @:ammer.c.prereturn("save_num(11);") - @:ammer.c.return("*(%CALL%)") - public static function pointer_saved_num():Int; - - #if (hl || cpp) - public static function save_func(f:Closure<(Int, Int, ClosureDataUse)->Int, "once">, _:ClosureData<"f">):Void; - public static function call_func():Int; - public static function call_func_2(_:ClosureData<"f">, f:Closure<(ClosureDataUse, String)->Int, "once">):Int; - public static function call_func_3(_:ClosureData<"f">, f:Closure<(NativeCallbackData)->Int, "once">):Int; - public static function call_func_4(_:ClosureData<"f">, f:Closure<(ClosureDataUse, NativeEnum)->NativeEnum, "once">):Bool; - #end - - public static function create_opaque():NativeOpaque; - @:ammer.native("create_opaque") public static function create_opaque2():NativeOpaque2; - - #if (hl || cpp) - public static function opaque_indirect(_:OutPointer):Void; - public static function create_opaque_noalloc():ammer.ffi.Alloc; - public static function opaque_take_nested(a:ammer.ffi.Nested):Bool; - - public static function take_array_fixed(a:ammer.ffi.ArrayFixed):Int; - public static function take_array(a:ammer.ffi.ArrayDynamic, b:ammer.ffi.SizeOf<"a">):Int; - public static function take_array_modify(a:NoSize>):Void; - #end - - public static function take_enum(a:NativeEnum, b:NativeEnum, c:NativeEnum):Bool; - public static function give_enum():NativeEnum; - - public static function take_unsupported(a:Unsupported<"void *">, b:Unsupported<"double">):Bool; - - public static function add_i8(a:Int8, b:Int8):Int8; - public static function add_i16(a:Int16, b:Int16):Int16; - public static function add_i32(a:Int32, b:Int32):Int32; - #if !hl - public static function add_i64(a:Int64, b:Int64):Int64; - #end - public static function add_u8(a:UInt8, b:UInt8):UInt8; - public static function add_u16(a:UInt16, b:UInt16):UInt16; - public static function add_u32(a:UInt32, b:UInt32):UInt32; - #if !hl - public static function add_u64(a:UInt64, b:UInt64):UInt64; - #end -} - -class Native2 extends Library<"native"> { - public static function take_0():Int; - public static function take_0alt():Int; -} - -@:ammer.nativePrefix("prefixed_") -class NativePrefixed extends Library<"native"> { - public static function nop2():Void; - @:ammer.native("take_0") public static function take_0():Int; -} - -// TODO: "opaque" is now a misnomer -@:ammer.nativePrefix("opaque_") -@:ammer.struct -class NativeOpaque extends Pointer<"opaque_type_t", Native> { - @:ammer.native("member_int") public var member_int:Int; - @:ammer.native("member_float") public var member_float:Float; - @:ammer.native("member_string") public var member_string:String; - - @:ammer.native("member_int_array_fixed") public var member_int_array_fixed:ammer.ffi.ArrayFixed; - #if (hl || cpp) - @:ammer.native("member_int_array") public var member_int_array:ammer.ffi.ArrayDynamic; - @:ammer.native("member_int_array_size") public var member_int_array_size:ammer.ffi.SizeOf<"member_int_array">; - @:ammer.native("member_string_array") public var member_string_array:ammer.ffi.ArrayDynamic; - @:ammer.native("member_string_array_size") public var member_string_array_size:ammer.ffi.SizeOf<"member_string_array">; - #end - - public function get_int(_:This):Int; - public function get_float(_:This):Float; - public function get_string(_:This):String; - public function get_int_alt(_:Int, _:This, _:Int):Int; - public function get_bytes(_:This, _:SizeOfReturn):Bytes; - - #if (hl || cpp) - public function get_int_nested(_:Nested):Int; - #end -} - -class NativeOpaque2 extends PointerNoStar<"opaque_type_ptr", Native> { - @:ammer.native("opaque_get_int") public function get_int(_:This):Int; -} - -#if (hl || cpp) -@:ammer.struct -class NativeCallbackData extends Pointer<"callback_data_t", Native> { - public var user_data:ClosureDataUse; - public var foo:Int; -} -#end - -class NativeEnum extends IntEnum<"enum enum_constants", Native> { - @:ammer.native("e_const0") public static var EConst0:NativeEnum; - @:ammer.native("e_const1") public static var EConst1:NativeEnum; - @:ammer.native("e_const10") public static var EConst10:NativeEnum; -} diff --git a/tests/Templates.hx b/tests/Templates.hx deleted file mode 100644 index e08a3a9..0000000 --- a/tests/Templates.hx +++ /dev/null @@ -1,22 +0,0 @@ -import ammer.Library; -import ammer.ffi.*; -import haxe.io.Bytes; - -@:ammer.sub((_ : XTemplatesStruct)) -class Templates extends Library<"templates"> { - @:ammer.native("templated_add_ints") public static function templated_add_ints32(a:Int, b:Int):Int; - - public static function cpp_nop():Void; -} - -@:ammer.struct -class XTemplatesStruct extends ammer.Pointer<"TemplatesStruct", Templates> { - public var member_int:UInt32; - - @:ammer.native("TemplatesStruct") - @:ammer.cpp.constructor - public static function new_():XTemplatesStruct; - - @:ammer.cpp.member - public function add(x:UInt32):UInt32; -} diff --git a/tests/build-cpp.hxml b/tests/build-cpp.hxml deleted file mode 100644 index 8108fbd..0000000 --- a/tests/build-cpp.hxml +++ /dev/null @@ -1,11 +0,0 @@ ---library utest ---library ammer ---main Main --D ammer.lib.native.include=native --D ammer.lib.native.library=native --D ammer.lib.native.headers=tmp.native.h --D ammer.lib.templates.abi=cpp --D ammer.lib.templates.include=native --D ammer.lib.templates.library=native --D ammer.lib.templates.headers=templates.hpp ---cpp bin/cpp diff --git a/tests/build-eval.hxml b/tests/build-eval.hxml deleted file mode 100644 index 2a5195e..0000000 --- a/tests/build-eval.hxml +++ /dev/null @@ -1,12 +0,0 @@ ---library utest ---library ammer ---main Main --D ammer.lib.native.include=native --D ammer.lib.native.library=native --D ammer.lib.templates.abi=cpp --D ammer.lib.templates.include=native --D ammer.lib.templates.library=native --D ammer.lib.templates.headers=templates.hpp --D ammer.eval.build=bin/eval --D ammer.eval.output=. ---interp diff --git a/tests/build-hl.hxml b/tests/build-hl.hxml deleted file mode 100644 index c0d3749..0000000 --- a/tests/build-hl.hxml +++ /dev/null @@ -1,10 +0,0 @@ ---library utest ---library ammer ---main Main --D ammer.lib.native.include=native --D ammer.lib.native.library=native --D ammer.lib.templates.abi=cpp --D ammer.lib.templates.include=native --D ammer.lib.templates.library=native --D ammer.lib.templates.headers=templates.hpp ---hl bin/hl/test.hl diff --git a/tests/build-lua.hxml b/tests/build-lua.hxml deleted file mode 100644 index 4d46a2f..0000000 --- a/tests/build-lua.hxml +++ /dev/null @@ -1,11 +0,0 @@ ---library utest ---library ammer ---main Main --D ammer.lib.native.include=native --D ammer.lib.native.library=native --D ammer.lib.templates.abi=cpp --D ammer.lib.templates.include=native --D ammer.lib.templates.library=native --D ammer.lib.templates.headers=templates.hpp --D lua_vanilla ---lua bin/lua/test.lua diff --git a/tests/native/native.c b/tests/native/native.c deleted file mode 100644 index 5680288..0000000 --- a/tests/native/native.c +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include -#include - -#include "native.h" -#include "utf8.h" - -LIB_EXPORT int take_0(void) { - return 0; -} -LIB_EXPORT int take_0alt(void) { - return 0; -} -LIB_EXPORT int take_1(int a1) { - return 1; -} -LIB_EXPORT int take_2(int a1, int a2) { - return 2; -} -LIB_EXPORT int take_3(int a1, int a2, int a3) { - return 3; -} -LIB_EXPORT int take_4(int a1, int a2, int a3, int a4) { - return 4; -} -LIB_EXPORT int take_5(int a1, int a2, int a3, int a4, int a5) { - return 5; -} -LIB_EXPORT int take_6(int a1, int a2, int a3, int a4, int a5, int a6) { - return 6; -} -LIB_EXPORT int take_7(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { - return 7; -} -LIB_EXPORT int take_8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { - return 8; -} -LIB_EXPORT int take_9(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { - return 9; -} -LIB_EXPORT int take_10(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) { - return 10; -} -LIB_EXPORT void nop(void) {} -LIB_EXPORT void prefixed_nop2(void) {} - -LIB_EXPORT int add_ints(int a, int b) { - return a + b; -} -LIB_EXPORT unsigned int add_uints(unsigned int a, unsigned int b) { - return a + b; -} -LIB_EXPORT float add_singles(float a, float b) { - return a + b; -} -LIB_EXPORT double add_floats(double a, double b) { - return a + b; -} -LIB_EXPORT bool logic_and(bool a, bool b) { - return a && b; -} -LIB_EXPORT bool logic_or(bool a, bool b) { - return a || b; -} -LIB_EXPORT int logic_ternary(bool a, int b, int c) { - return a ? b : c; -} - -LIB_EXPORT const char *id_string(const char *a) { - return strdup(a); -} -LIB_EXPORT const char *rev_string(const char *a) { - int len = strlen(a); - char *ret = malloc(len + 1); - int *cc = malloc(len * sizeof(int)); - int pos = 0; - while (*a != 0) cc[pos++] = utf8_decode((unsigned char **)&a); - char *retcur = ret; - while (pos > 0) utf8_encode((unsigned char **)&retcur, cc[--pos]); - *retcur = '\0'; - return ret; -} - -LIB_EXPORT unsigned char *id_bytes(unsigned char *a, size_t b) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *id_bytes_1(unsigned char *a, unsigned char *c, size_t b) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *id_bytes_2(unsigned char *c, unsigned char *a, size_t b) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *id_bytes_3(unsigned char *a, size_t b, unsigned char *c) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *id_bytes_4(unsigned char *c, size_t b, unsigned char *a) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *id_bytes_5(size_t b, unsigned char *a, unsigned char *c) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *id_bytes_6(size_t b, unsigned char *c, unsigned char *a) { - return memcpy(malloc(b), a, b); -} -LIB_EXPORT unsigned char *give_bytes(int n, size_t *ret_size) { - unsigned char *ret = malloc(n); - for (int i = 0; i < n; i++) ret[i] = i + 1; - *ret_size = n; - return ret; -} - -static int saved_num = 0; -LIB_EXPORT void save_num(int num) { - saved_num = num; -} -LIB_EXPORT int get_saved_num(void) { - return saved_num; -} -LIB_EXPORT int *pointer_saved_num(void) { - return &saved_num; -} - -static int (* cached_func)(int, int, void *); -static void *cached_user_data; -LIB_EXPORT void save_func(int (* func)(int, int, void *), void *user_data) { - cached_func = func; - cached_user_data = user_data; -} -LIB_EXPORT int call_func(void) { - return cached_func(1, 2, cached_user_data); -} -LIB_EXPORT int call_func_2(void *user_data, int (* func)(void *, const char *)) { - return func(user_data, "foobar") * 2; -} -LIB_EXPORT int call_func_3(void *user_data, int (* func)(callback_data_t *)) { - callback_data_t data = { - .user_data = user_data, - .foo = 59 - }; - return func(&data); -} -LIB_EXPORT bool call_func_4(void *user_data, enum enum_constants (* func)(void *, enum enum_constants)) { - return func(user_data, e_const1) == e_const10; -} - -LIB_EXPORT opaque_type_ptr create_opaque(void) { - opaque_type_ptr ret = malloc(sizeof(opaque_type_t)); - ret->member_int = 1; - ret->member_float = 2.0f; - ret->member_string = "3"; - for (int i = 0; i < 8; i++) { - ret->member_int_array_fixed[i] = 0xB0057ED + i; - } - ret->member_int_array = (int *)calloc(17, sizeof(int)); - ret->member_int_array_size = 17; - for (int i = 0; i < 17; i++) { - ret->member_int_array[i] = 0xB00573D + i; - } - ret->member_string_array = (const char **)calloc(3, sizeof(char *)); - ret->member_string_array_size = 3; - ret->member_string_array[0] = "arrfoo"; - ret->member_string_array[1] = "arrbar"; - ret->member_string_array[2] = "arrbaz"; - return ret; -} -LIB_EXPORT int opaque_get_int(opaque_type_ptr a) { - return a->member_int; -} -LIB_EXPORT int opaque_get_int_nested(opaque_type_t a) { - return a.member_int; -} -LIB_EXPORT double opaque_get_float(opaque_type_ptr a) { - return a->member_float; -} -LIB_EXPORT const char *opaque_get_string(opaque_type_ptr a) { - return a->member_string; -} -LIB_EXPORT int opaque_get_int_alt(int a, opaque_type_ptr b, int c) { - return a + b->member_int + c; -} -LIB_EXPORT unsigned char *opaque_get_bytes(opaque_type_ptr a, size_t *b) { - size_t len = strlen(a->member_string); - unsigned char *ret = malloc(len); - memcpy(ret, a->member_string, len); - *b = len; - return ret; -} -LIB_EXPORT void opaque_indirect(opaque_type_ptr *out) { - opaque_type_ptr ret = malloc(sizeof(opaque_type_t)); - ret->member_int = 10; - ret->member_float = 4.0f; - ret->member_string = "indirect"; - *out = ret; -} -LIB_EXPORT opaque_type_t create_opaque_noalloc(void) { - return (opaque_type_t){ - .member_int = 61, - .member_float = 5.2f, - .member_string = "noalloc", - .member_int_array_fixed = {9, 10, 11, 12, 13, 14, 15, 16}, - .member_int_array = NULL, - .member_int_array_size = 0, - .member_string_array = NULL, - .member_string_array_size = 0, - }; -} -LIB_EXPORT bool opaque_take_nested(opaque_type_t a) { - float diff = a.member_float - 5.4f; - return a.member_int == 62 - && (diff > -.0001f && diff < .0001f) - && strcmp(a.member_string, "noalloc") == 0 - && a.member_int_array_fixed[7] == 47 - && a.member_int_array == NULL - && a.member_string_array == NULL; -} - -LIB_EXPORT bool take_enum(enum enum_constants a, enum enum_constants b, enum enum_constants c) { - return (a == e_const10) - && (b == e_const1) - && (c == e_const0); -} -LIB_EXPORT enum enum_constants give_enum(void) { - return e_const10; -} - -LIB_EXPORT int take_array_fixed(int a[3]) { - if (a[0] != 1 || a[1] != 2 || a[2] != 4) - return -1; - return a[0] + a[1] + a[2]; -} -LIB_EXPORT int take_array(int *a, size_t b) { - if (b != 3 || a[0] != 1 || a[1] != 2 || a[2] != 4) - return -1; - return a[0] + a[1] + a[2]; -} -LIB_EXPORT void take_array_modify(int *a) { - a[1] = 42; -} - -LIB_EXPORT bool take_unsupported(void *a, double b) { - return a == 0 && abs(b) < .0001; -} - -LIB_EXPORT int8_t add_i8(int8_t a, int8_t b) { - return a + b; -} -LIB_EXPORT int16_t add_i16(int16_t a, int16_t b) { - return a + b; -} -LIB_EXPORT int32_t add_i32(int32_t a, int32_t b) { - return a + b; -} -LIB_EXPORT int64_t add_i64(int64_t a, int64_t b) { - return a + b; -} -LIB_EXPORT uint8_t add_u8(uint8_t a, uint8_t b) { - return a + b; -} -LIB_EXPORT uint16_t add_u16(uint16_t a, uint16_t b) { - return a + b; -} -LIB_EXPORT uint32_t add_u32(uint32_t a, uint32_t b) { - return a + b; -} -LIB_EXPORT uint64_t add_u64(uint64_t a, uint64_t b) { - return a + b; -} diff --git a/tests/native/native.h b/tests/native/native.h deleted file mode 100644 index e370fcc..0000000 --- a/tests/native/native.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef _WIN32 - #define LIB_EXPORT __declspec(dllexport) -#else - #define LIB_EXPORT -#endif - -#include -#include -#include - -LIB_EXPORT int take_0(void); -LIB_EXPORT int take_0alt(void); -LIB_EXPORT int take_1(int a1); -LIB_EXPORT int take_2(int a1, int a2); -LIB_EXPORT int take_3(int a1, int a2, int a3); -LIB_EXPORT int take_4(int a1, int a2, int a3, int a4); -LIB_EXPORT int take_5(int a1, int a2, int a3, int a4, int a5); -LIB_EXPORT int take_6(int a1, int a2, int a3, int a4, int a5, int a6); -LIB_EXPORT int take_7(int a1, int a2, int a3, int a4, int a5, int a6, int a7); -LIB_EXPORT int take_8(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8); -LIB_EXPORT int take_9(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9); -LIB_EXPORT int take_10(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10); -LIB_EXPORT void nop(void); -LIB_EXPORT void prefixed_nop2(void); - -LIB_EXPORT int add_ints(int a, int b); -LIB_EXPORT unsigned int add_uints(unsigned int a, unsigned int b); -LIB_EXPORT float add_singles(float a, float b); -LIB_EXPORT double add_floats(double a, double b); -LIB_EXPORT bool logic_and(bool a, bool b); -LIB_EXPORT bool logic_or(bool a, bool b); -LIB_EXPORT int logic_ternary(bool a, int b, int c); - -LIB_EXPORT const char *id_string(const char *a); -LIB_EXPORT const char *rev_string(const char *a); - -LIB_EXPORT unsigned char *id_bytes(unsigned char *a, size_t b); -LIB_EXPORT unsigned char *id_bytes_1(unsigned char *a, unsigned char *c, size_t b); -LIB_EXPORT unsigned char *id_bytes_2(unsigned char *c, unsigned char *a, size_t b); -LIB_EXPORT unsigned char *id_bytes_3(unsigned char *a, size_t b, unsigned char *c); -LIB_EXPORT unsigned char *id_bytes_4(unsigned char *c, size_t b, unsigned char *a); -LIB_EXPORT unsigned char *id_bytes_5(size_t b, unsigned char *a, unsigned char *c); -LIB_EXPORT unsigned char *id_bytes_6(size_t b, unsigned char *c, unsigned char *a); -LIB_EXPORT unsigned char *give_bytes(int n, size_t *ret); - -LIB_EXPORT void save_num(int); -LIB_EXPORT int get_saved_num(void); -LIB_EXPORT int *pointer_saved_num(void); - -typedef struct { - void *user_data; - int foo; -} callback_data_t; - -enum enum_constants { - e_const0 = 0, - e_const1 = 1, - e_const10 = 10 -}; - -enum enum_flags { - e_foo = 1, - e_bar = 2, - e_baz = 4 -}; - -LIB_EXPORT void save_func(int (* func)(int, int, void*), void *user_data); -LIB_EXPORT int call_func(void); -LIB_EXPORT int call_func_2(void *user_data, int (* func)(void *, const char *)); -LIB_EXPORT int call_func_3(void *user_data, int (* func)(callback_data_t *)); -LIB_EXPORT bool call_func_4(void *user_data, enum enum_constants (* func)(void *, enum enum_constants)); - -typedef struct { - int member_int; - double member_float; - const char *member_string; - - int member_int_array_fixed[8]; - int *member_int_array; - int member_int_array_size; - const char **member_string_array; - int member_string_array_size; -} opaque_type_t; -typedef opaque_type_t *opaque_type_ptr; - -LIB_EXPORT opaque_type_ptr create_opaque(void); -LIB_EXPORT int opaque_get_int(opaque_type_ptr a); -LIB_EXPORT int opaque_get_int_nested(opaque_type_t a); -LIB_EXPORT double opaque_get_float(opaque_type_ptr a); -LIB_EXPORT const char *opaque_get_string(opaque_type_ptr a); -LIB_EXPORT int opaque_get_int_alt(int a, opaque_type_ptr b, int c); -LIB_EXPORT unsigned char *opaque_get_bytes(opaque_type_ptr a, size_t *b); -LIB_EXPORT void opaque_indirect(opaque_type_ptr *out); -LIB_EXPORT opaque_type_t create_opaque_noalloc(void); -LIB_EXPORT bool opaque_take_nested(opaque_type_t a); - -#define DEFINE_INT 42 -#define DEFINE_INT_EXPR (8 * 9) -#define DEFINE_STRING "foo" -#define DEFINE_STRING_EXPR ("foo" "bar" "foo") -#define DEFINE_BOOL 1 -#define DEFINE_BOOL_EXPR ((1 == 1) ? 1 : 0) -#define DEFINE_FLOAT 5.3 -#define DEFINE_FLOAT_EXPR (5.3 * 2) - -LIB_EXPORT bool take_enum(enum enum_constants a, enum enum_constants b, enum enum_constants c); -LIB_EXPORT enum enum_constants give_enum(void); - -LIB_EXPORT int take_array_fixed(int a[3]); -LIB_EXPORT int take_array(int *a, size_t b); -LIB_EXPORT void take_array_modify(int *a); - -LIB_EXPORT bool take_unsupported(void *a, double b); - -LIB_EXPORT int8_t add_i8(int8_t a, int8_t b); -LIB_EXPORT int16_t add_i16(int16_t a, int16_t b); -LIB_EXPORT int32_t add_i32(int32_t a, int32_t b); -LIB_EXPORT int64_t add_i64(int64_t a, int64_t b); -LIB_EXPORT uint8_t add_u8(uint8_t a, uint8_t b); -LIB_EXPORT uint16_t add_u16(uint16_t a, uint16_t b); -LIB_EXPORT uint32_t add_u32(uint32_t a, uint32_t b); -LIB_EXPORT uint64_t add_u64(uint64_t a, uint64_t b); - -#ifdef __cplusplus -} -#endif diff --git a/tests/native/templates.cpp b/tests/native/templates.cpp deleted file mode 100644 index 68a4b33..0000000 --- a/tests/native/templates.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "templates.hpp" - -template -LIB_EXPORT int_t templated_add_ints(int_t a, int_t b) { - return a + b; -} - -template LIB_EXPORT int templated_add_ints(int a, int b); -template LIB_EXPORT uint64_t templated_add_ints(uint64_t a, uint64_t b); - -LIB_EXPORT void cpp_nop(void) {} - -uint32_t TemplatesStruct::add(uint32_t x) { - return this->member_int + x; -} diff --git a/tests/native/templates.hpp b/tests/native/templates.hpp deleted file mode 100644 index 53747bf..0000000 --- a/tests/native/templates.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#ifdef _WIN32 - #define LIB_EXPORT __declspec(dllexport) -#else - #define LIB_EXPORT -#endif - -#include -#include - -template -LIB_EXPORT int_t templated_add_ints(int_t a, int_t b); - -LIB_EXPORT void cpp_nop(void); - -struct TemplatesStruct { - uint32_t member_int; -public: - TemplatesStruct() : member_int(5) {} - uint32_t add(uint32_t x); -}; diff --git a/tests/native/utf8.c b/tests/native/utf8.c deleted file mode 100644 index 829f16e..0000000 --- a/tests/native/utf8.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "utf8.h" - -LIB_EXPORT int utf8_decode(unsigned char **ptr) { - int cc1 = *((*ptr)++); if (cc1 < 0x80) return cc1; - int cc2 = *((*ptr)++) & 0x7F; if (cc1 < 0xE0) return ((cc1 & 0x3F) << 6) | cc2; - int cc3 = *((*ptr)++) & 0x7F; if (cc1 < 0xF0) return ((cc1 & 0x1F) << 12) | (cc2 << 6) | cc3; - int cc4 = *((*ptr)++) & 0x7F; return ((cc1 & 0x0F) << 18) | (cc2 << 12) | (cc3 << 6) | cc4; -} - -LIB_EXPORT void utf8_encode(unsigned char **ptr, int cc) { - if (cc <= 0x7F) { - *((*ptr)++) = cc; - } else if (cc <= 0x7FF) { - *((*ptr)++) = 0xC0 | (cc >> 6); - *((*ptr)++) = 0x80 | (cc & 0x3F); - } else if (cc <= 0xFFFF) { - *((*ptr)++) = 0xE0 | (cc >> 12); - *((*ptr)++) = 0x80 | ((cc >> 6) & 0x3F); - *((*ptr)++) = 0x80 | (cc & 0x3F); - } else { - *((*ptr)++) = 0xF0 | (cc >> 18); - *((*ptr)++) = 0x80 | ((cc >> 12) & 0x3F); - *((*ptr)++) = 0x80 | ((cc >> 6) & 0x3F); - *((*ptr)++) = 0x80 | (cc & 0x3F); - } -} diff --git a/tests/test-linux.sh b/tests/test-linux.sh deleted file mode 100755 index 9ef9780..0000000 --- a/tests/test-linux.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -echo "building native library ..." -(cd native; make -f Makefile.linux) - -echo "testing hl ..." -haxe build-hl.hxml \ -&& (cd bin/hl; LD_LIBRARY_PATH=../../native hl test.hl) - -echo "testing cpp ..." -haxe build-cpp.hxml \ -&& (cd bin/cpp; LD_LIBRARY_PATH=../../native ./Main) - -echo "testing eval ..." -LD_LIBRARY_PATH=native haxe -D "ammer.eval.hxDir=$TEST_HXDIR" build-eval.hxml diff --git a/tests/test-osx.sh b/tests/test-osx.sh deleted file mode 100755 index a31a315..0000000 --- a/tests/test-osx.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -echo "building native library ..." -(cd native; make -f Makefile.osx) - -echo "testing hl ..." -haxe build-hl.hxml \ -&& (cd bin/hl; DYLD_LIBRARY_PATH=../../native hl test.hl) - -echo "testing cpp ..." -haxe build-cpp.hxml \ -&& (cd bin/cpp; DYLD_LIBRARY_PATH=../../native ./Main) - -echo "testing eval ..." -DYLD_LIBRARY_PATH=native haxe -D "ammer.eval.haxeDir=$TEST_HXDIR" build-eval.hxml diff --git a/tests/test-windows.bat b/tests/test-windows.bat deleted file mode 100755 index 4f3852a..0000000 --- a/tests/test-windows.bat +++ /dev/null @@ -1,41 +0,0 @@ -@echo off - - -echo "building native library ..." -cd native -nmake /f Makefile.win -cd .. - - -echo "testing hl ..." -haxe -D ammer.hl.hlInclude=%HLPATH%/include -D ammer.hl.hlLibrary=%HLPATH% build-hl.hxml -IF %ERRORLEVEL% NEQ 0 ( echo "build of hl tests failed" && goto :cpp) -pushd bin\hl -copy ..\..\native\native.dll . -hl test.hl -popd -IF %ERRORLEVEL% NEQ 0 ( echo "hl tests failed" && goto :cpp) - - -:cpp - -echo "testing cpp ..." -rd /q /s bin\cpp - -haxe build-cpp.hxml -IF %ERRORLEVEL% NEQ 0 ( echo "build of cpp tests failed" && goto :eval) - -cd bin\cpp -copy ..\..\native\native.dll . -Main.exe -cd ../.. -IF %ERRORLEVEL% NEQ 0 ( echo "cpp tests failed" && goto :eval) - - -:eval - - -echo "testing eval ..." -copy native\native.dll . -haxe -D ammer.eval.haxeDir=%HAXEPATH% build-eval.hxml -IF %ERRORLEVEL% NEQ 0 ( echo "eval tests failed") diff --git a/tests/test/Test.hx b/tests/test/Test.hx deleted file mode 100644 index 1ca8c7d..0000000 --- a/tests/test/Test.hx +++ /dev/null @@ -1,81 +0,0 @@ -package test; - -import utest.Assert; -import utest.Async; -import haxe.io.Bytes; - -// copy of Test from Haxe unit test sources -// + beq, noExc -class Test implements utest.ITest { - public function new() {} - - function eq(v:T, v2:T, ?pos:haxe.PosInfos) { - Assert.equals(v, v2, pos); - } - - function feq(v:Float, v2:Float, ?pos:haxe.PosInfos) { - Assert.floatEquals(v, v2, pos); - } - - function aeq(expected:Array, actual:Array, ?pos:haxe.PosInfos) { - Assert.same(expected, actual, pos); - } - - function beq(a:Bytes, b:Bytes, ?pos:haxe.PosInfos) { - Assert.isTrue(a.compare(b) == 0, pos); - } - - function t(v, ?pos:haxe.PosInfos) { - Assert.isTrue(v, pos); - } - - function f(v, ?pos:haxe.PosInfos) { - Assert.isFalse(v, pos); - } - - function assert(?message:String, ?pos:haxe.PosInfos) { - Assert.fail(message, pos); - } - - function exc(f:Void->Void, ?pos:haxe.PosInfos) { - Assert.raises(f, pos); - } - - function noExc(f:Void->Void, ?pos:haxe.PosInfos) { - Assert.isTrue(try { - f(); - true; - } catch (e:Dynamic) false, pos); - } - - function unspec(f:Void->Void, ?pos) { - try { - f(); - } catch (e:Dynamic) {} - noAssert(); - } - - function allow(v:T, values:Array, ?pos) { - Assert.contains(v, values, pos); - } - - function noAssert(?pos:haxe.PosInfos) { - t(true, pos); - } - - function hf(c:Class, n:String, ?pos:haxe.PosInfos) { - t(Lambda.has(Type.getInstanceFields(c), n)); - } - - function nhf(c:Class, n:String, ?pos:haxe.PosInfos) { - f(Lambda.has(Type.getInstanceFields(c), n)); - } - - function hsf(c:Class, n:String, ?pos:haxe.PosInfos) { - t(Lambda.has(Type.getClassFields(c), n)); - } - - function nhsf(c:Class, n:String, ?pos:haxe.PosInfos) { - f(Lambda.has(Type.getClassFields(c), n)); - } -} diff --git a/tests/test/TestArrays.hx b/tests/test/TestArrays.hx deleted file mode 100644 index 278107f..0000000 --- a/tests/test/TestArrays.hx +++ /dev/null @@ -1,20 +0,0 @@ -package test; - -using ammer.conv.ArrayTools; - -class TestArrays extends Test { - function testArrays() { - #if (hl || cpp) - var arr = haxe.ds.Vector.fromArrayCopy([1, 2, 4]); - eq(Native.take_array_fixed(arr.asCopy()), 7); - eq(Native.take_array(arr.asCopy()), 7); - eq(Native.take_array_fixed(arr.asShared()), 7); - eq(Native.take_array(arr.asShared()), 7); - - Native.take_array_modify(arr.asShared()); - eq(arr[1], 42); - #else - noAssert(); - #end - } -} diff --git a/tests/test/TestBytes.hx b/tests/test/TestBytes.hx deleted file mode 100644 index f2ef1bf..0000000 --- a/tests/test/TestBytes.hx +++ /dev/null @@ -1,30 +0,0 @@ -package test; - -import haxe.io.Bytes; - -class TestBytes extends Test { - function testId() { - beq(Native.id_bytes(Bytes.ofHex("")), Bytes.ofHex("")); - beq(Native.id_bytes(Bytes.ofHex("00")), Bytes.ofHex("00")); - - var b = Bytes.ofHex("0001FEFF"); - var c = Bytes.ofHex("AA01FEFF"); - - beq(Native.id_bytes(b), b); - beq(Native.id_bytes_1(b, c), b); - beq(Native.id_bytes_2(c, b), b); - beq(Native.id_bytes_3(b, c), b); - beq(Native.id_bytes_4(c, b), b); - beq(Native.id_bytes_5(b, c), b); - beq(Native.id_bytes_6(c, b), b); - - // should not have been modified - beq(b, Bytes.ofHex("0001FEFF")); - } - - function testReturns() { - beq(Native.give_bytes(0), Bytes.ofHex("")); - beq(Native.give_bytes(1), Bytes.ofHex("01")); - beq(Native.give_bytes(10), Bytes.ofHex("0102030405060708090A")); - } -} diff --git a/tests/test/TestCInjection.hx b/tests/test/TestCInjection.hx deleted file mode 100644 index f41b8a0..0000000 --- a/tests/test/TestCInjection.hx +++ /dev/null @@ -1,12 +0,0 @@ -package test; - -class TestCInjection extends Test { - function testInjection() { - #if (hl || cpp || lua) - eq(Native.get_saved_num(), 5); - eq(Native.pointer_saved_num(), 11); - #else - noAssert(); - #end - } -} diff --git a/tests/test/TestCallback.hx b/tests/test/TestCallback.hx deleted file mode 100644 index 360225f..0000000 --- a/tests/test/TestCallback.hx +++ /dev/null @@ -1,70 +0,0 @@ -package test; - -class TestCallback extends Test { - var wasCalled = false; - var counterSet = false; - var callA = -1; - var callB = -1; - - function callback(a:Int, b:Int):Int { - wasCalled = true; - callA = a; - callB = b; - return a + b; - } - - function testCallback() { - #if (hl || cpp) - wasCalled = false; - Native.save_func(callback); - eq(wasCalled, false); - eq(Native.call_func(), 3); - eq(wasCalled, true); - eq(callA, 1); - eq(callB, 2); - - wasCalled = false; - eq(Native.call_func_2(x -> { - wasCalled = true; - eq(x, "foobar"); - 2; - }), 4); - eq(wasCalled, true); - - counterSet = false; - Native.save_func(createClosure()); - eq(Native.call_func(), 3); - eq(Native.call_func(), 3); - eq(Native.call_func(), 3); - eq(counterSet, true); - - wasCalled = false; - eq(Native.call_func_3(data -> { - eq(data.foo, 59); - wasCalled = true; - 77; - }), 77); - eq(wasCalled, true); - - wasCalled = false; - eq(Native.call_func_4(data -> { - eq(data, Native.NativeEnum.EConst1); - wasCalled = true; - Native.NativeEnum.EConst10; - }), true); - eq(wasCalled, true); - #else - noAssert(); - #end - } - - function createClosure():((Int, Int)->Int) { - var counter = 0; - return ((a, b) -> { - counter++; - if (counter >= 3) - counterSet = true; - return a + b; - }); - } -} diff --git a/tests/test/TestCpp.hx b/tests/test/TestCpp.hx deleted file mode 100644 index 4d6d526..0000000 --- a/tests/test/TestCpp.hx +++ /dev/null @@ -1,27 +0,0 @@ -package test; - -import Templates.XTemplatesStruct; - -class TestCpp extends Test { - function testTemplates() { - eq(Templates.templated_add_ints32(0, 0), 0); - eq(Templates.templated_add_ints32(1, 2), 3); - eq(Templates.templated_add_ints32(-1, 1), 0); - eq(Templates.templated_add_ints32(0xFFFFFFFF, 1), 0); - eq(Templates.templated_add_ints32(0x7F000000, 0xFFFFFF), 0x7FFFFFFF); - eq(Templates.templated_add_ints32(-0x7FFFFFFF, 0x7FFFFFFF), 0); - } - - function testCppLinkage() { - Templates.cpp_nop(); - eq(1, 1); - } - - function testStructMembers() { - var obj = XTemplatesStruct.new_(); - eq(obj.member_int, 5); - obj.member_int = 7; - eq(obj.member_int, 7); - eq(obj.add(13), 20); - } -} diff --git a/tests/test/TestEnums.hx b/tests/test/TestEnums.hx deleted file mode 100644 index 1449c80..0000000 --- a/tests/test/TestEnums.hx +++ /dev/null @@ -1,8 +0,0 @@ -package test; - -class TestEnums extends Test { - function testEnums() { - eq(Native.take_enum(Native.NativeEnum.EConst10, Native.NativeEnum.EConst1, Native.NativeEnum.EConst0), true); - eq(Native.give_enum(), Native.NativeEnum.EConst10); - } -} diff --git a/tests/test/TestMaths.hx b/tests/test/TestMaths.hx deleted file mode 100644 index ed26d83..0000000 --- a/tests/test/TestMaths.hx +++ /dev/null @@ -1,62 +0,0 @@ -package test; - -class TestMaths extends Test { - function testInts() { - eq(Native.add_ints(0, 0), 0); - eq(Native.add_ints(1, 2), 3); - eq(Native.add_ints(-1, 1), 0); - eq(Native.add_ints(0xFFFFFFFF, 1), 0); - eq(Native.add_ints(0x7F000000, 0xFFFFFF), 0x7FFFFFFF); - eq(Native.add_ints(-0x7FFFFFFF, 0x7FFFFFFF), 0); - } - - function testUInts() { - eq(Native.add_uints(0, 0), 0); - eq(Native.add_uints(1, 2), 3); - eq(Native.add_uints(0x7F000000, 0xFFFFFF), 0x7FFFFFFF); - eq(Native.add_uints(0x7FFFFFFF, 0x7FFFFFFF), 0xFFFFFFFE); - } - - #if !lua - function testSingles() { - eq(Native.add_singles(0., 0.), (0.:Single)); - feq(Native.add_singles(1., 2.), (3.:Single)); - feq(Native.add_singles(-1., 1.), (0.:Single)); - feq(Native.add_singles(-1e10, 1e10), (0.:Single)); - feq(Native.add_singles(-1e10, 1e9), (-9e9:Single)); - } - #end - - function testFloats() { - eq(Native.add_floats(0., 0.), 0.); - feq(Native.add_floats(1., 2.), 3.); - feq(Native.add_floats(-1., 1.), 0.); - feq(Native.add_floats(-1e10, 1e10), 0.); - feq(Native.add_floats(-1e10, 1e9), -9e9); - } - - function testBools() { - eq(Native.logic_and(false, false), false); - eq(Native.logic_and(true, false), false); - eq(Native.logic_and(false, true), false); - eq(Native.logic_and(true, true), true); - eq(Native.logic_or(false, false), false); - eq(Native.logic_or(true, false), true); - eq(Native.logic_or(false, true), true); - eq(Native.logic_or(true, true), true); - eq(Native.logic_ternary(true, 3, 5), 3); - eq(Native.logic_ternary(false, 3, 5), 5); - } - - function testBitWidths() { - eq(Native.add_u8(142, 193), 79); - eq(Native.add_u16(25679, 49565), 9708); - eq(Native.add_u32(0xBF86404F, 0xBF863D5D), 0x7F0C7DAC); - #if cpp - var a = haxe.Int64.make(0xBBFBCDC4, 0x2397F34F); - var b = haxe.Int64.make(0x5ADF2061, 0x3E99B3E1); - var c = haxe.Int64.make(0x16DAEE25, 0x6231A730); - eq(Native.add_u64(a, b), c); - #end - } -} diff --git a/tests/test/TestOpaque.hx b/tests/test/TestOpaque.hx deleted file mode 100644 index 82a94ba..0000000 --- a/tests/test/TestOpaque.hx +++ /dev/null @@ -1,102 +0,0 @@ -package test; - -import Native.NativeOpaque; -import Native.NativeOpaque2; - -class TestOpaque extends Test { - function testOpaque() { - var opaque:NativeOpaque = Native.create_opaque(); - eq(opaque.get_int(), 1); - feq(opaque.get_float(), 2.0); - eq(opaque.get_string(), "3"); - eq(opaque.get_int_alt(3, 4), 8); - - var opaque:NativeOpaque2 = Native.create_opaque2(); - eq(opaque.get_int(), 1); - } - - function testVariables() { - var opaque:NativeOpaque = Native.create_opaque(); - opaque.member_int = 3; - eq(opaque.get_int(), 3); - opaque.member_int = 5; - eq(opaque.member_int, 5); - opaque.member_float = 3.12; - feq(opaque.get_float(), 3.12); - opaque.member_float = 5.12; - feq(opaque.member_float, 5.12); - // passing strings directly might be a bit dangerous - opaque.member_string = "foo"; - eq(opaque.get_string(), "foo"); - opaque.member_string = "bar"; - eq(opaque.member_string, "bar"); - beq(opaque.get_bytes(), haxe.io.Bytes.ofHex("626172")); - } - - function testAlloc() { - var opaque:NativeOpaque = NativeOpaque.alloc(); - opaque.member_int = 7; - eq(opaque.get_int(), 7); - opaque.member_int = 49; - eq(opaque.member_int, 49); - opaque.free(); - } - - function testOutPointer() { - #if (hl || cpp) - var opaque = NativeOpaque.nullPointer(); - Native.opaque_indirect(opaque); - eq(opaque.member_int, 10); - feq(opaque.member_float, 4.0); - eq(opaque.member_string, "indirect"); - opaque.free(); - #else - noAssert(); - #end - } - - function testNested() { - #if (hl || cpp) - var opaque:NativeOpaque = Native.create_opaque_noalloc(); - eq(opaque.get_int(), 61); - feq(opaque.get_float(), 5.2); - eq(opaque.get_string(), "noalloc"); - for (i in 0...8) { - eq(opaque.member_int_array_fixed[i], 9 + i); - } - opaque.member_int = 62; - opaque.member_float = 5.4; - opaque.member_int_array_fixed[7] = 47; - eq(Native.opaque_take_nested(opaque), true); - opaque.free(); - #else - noAssert(); - #end - } - - function testArray() { - #if (hl || cpp) - var opaque = Native.create_opaque(); - var arr = opaque.member_int_array_fixed; - eq(arr.length, 8); - for (i in 0...8) { - eq(arr[i], 0xB0057ED + i); - arr[i] = 0xDE7500B + i; - } - for (i in 0...8) { - eq(arr[i], 0xDE7500B + i); - } - var arr = opaque.member_int_array; - eq(arr.length, 17); - for (i in 0...17) { - eq(arr[i], 0xB00573D + i); - } - var arr = opaque.member_string_array; - eq(arr[0], "arrfoo"); - eq(arr[1], "arrbar"); - eq(arr[2], "arrbaz"); - #else - noAssert(); - #end - } -} diff --git a/tests/test/TestSignature.hx b/tests/test/TestSignature.hx deleted file mode 100644 index 2bdf1f4..0000000 --- a/tests/test/TestSignature.hx +++ /dev/null @@ -1,36 +0,0 @@ -package test; - -class TestSignature extends Test { - function testArgCount() { - eq(Native.take_0(), 0); - eq(Native.take_1(1), 1); - eq(Native.take_2(1, 2), 2); - eq(Native.take_3(1, 2, 3), 3); - eq(Native.take_4(1, 2, 3, 4), 4); - eq(Native.take_5(1, 2, 3, 4, 5), 5); - eq(Native.take_6(1, 2, 3, 4, 5, 6), 6); - eq(Native.take_7(1, 2, 3, 4, 5, 6, 7), 7); - eq(Native.take_8(1, 2, 3, 4, 5, 6, 7, 8), 8); - eq(Native.take_9(1, 2, 3, 4, 5, 6, 7, 8, 9), 9); - eq(Native.take_10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 10); - } - - function testVoid() { - Native.nop(); - noAssert(); - } - - function testMultipleClasses() { - eq(Native.Native2.take_0(), 0); - eq(Native.Native2.take_0alt(), 0); - } - - function testPrefix() { - Native.NativePrefixed.nop2(); - eq(Native.NativePrefixed.take_0(), 0); - } - - function testUnsupported() { - t(Native.take_unsupported()); - } -} diff --git a/tests/test/TestStrings.hx b/tests/test/TestStrings.hx deleted file mode 100644 index 06cb2c2..0000000 --- a/tests/test/TestStrings.hx +++ /dev/null @@ -1,15 +0,0 @@ -package test; - -class TestStrings extends Test { - function testId() { - eq(Native.id_string(""), ""); - eq(Native.id_string("aaaa"), "aaaa"); - eq(Native.rev_string(""), ""); - eq(Native.rev_string("abc"), "cba"); - } - - function testUnicode() { - eq(Native.id_string("\u0042\u0CA0\uABCD\u{1F404}"), "\u0042\u0CA0\uABCD\u{1F404}"); - eq(Native.rev_string("\u0042\u0CA0\uABCD\u{1F404}"), "\u{1F404}\uABCD\u0CA0\u0042"); - } -} diff --git a/tests/test/TestVariables.hx b/tests/test/TestVariables.hx deleted file mode 100644 index b22a802..0000000 --- a/tests/test/TestVariables.hx +++ /dev/null @@ -1,18 +0,0 @@ -package test; - -class TestVariables extends Test { - function testDefines() { - #if (hl || cpp || lua) - eq(Native.define_int, 42); - eq(Native.define_int_expr, 72); - eq(Native.define_string, "foo"); - eq(Native.define_string_expr, "foobarfoo"); - eq(Native.define_bool, true); - eq(Native.define_bool_expr, true); - feq(Native.define_float, 5.3); - feq(Native.define_float_expr, 10.6); - #else - noAssert(); - #end - } -}