From e6dee6bd79f6ffba6426f096c876215b56aad182 Mon Sep 17 00:00:00 2001 From: sandyx86 Date: Sun, 18 Aug 2024 10:14:03 -0500 Subject: [PATCH] remove cmake --- .cirrus.yml | 63 -- .github/scripts/android_test_driver.sh | 63 -- .github/scripts/android_test_main.sh | 63 -- .github/workflows/main.yml | 373 ------- .gitignore | 5 - ANNOUNCE | 55 - ANNOUNCE.1.0 | 33 - ANNOUNCE.1.1 | 33 - ANNOUNCE.1.2 | 33 - ANNOUNCE.1.3 | 41 - ANNOUNCE.1.4 | 79 -- ANNOUNCE.1.5 | 55 - ANNOUNCE.1.6 | 64 -- ANNOUNCE.1.6.1 | 41 - ANNOUNCE.1.7 | 68 -- ANNOUNCE.1.8 | 37 - ANNOUNCE.1.8.1 | 37 - ANNOUNCE.1.9 | 77 -- ANNOUNCE.2.0 | 88 -- ANNOUNCE.2.1 | 79 -- Block.h | 1 - Block_private.h | 1 - CMake/CMakeLists.txt | 17 - CMake/typeinfo_test.cc | 62 -- CMakeLists.txt | 454 --------- Config.cmake.in | 3 - NSBlocks.m | 79 -- Protocol2.m | 45 - README.windows | 11 - Test/ARCTest_arc.m | 135 --- Test/AllocatePair.m | 51 - Test/AssociatedObject.m | 61 -- Test/AssociatedObject2.m | 37 - Test/BlockImpTest.m | 66 -- Test/BlockTest_arc.m | 17 - Test/BoxedForeignException.m | 105 -- Test/CMakeLists.txt | 188 ---- Test/CXXException.cc | 21 - Test/CXXException.m | 70 -- Test/Category.m | 26 - Test/ConstantString.m | 21 - Test/DirectMethods.m | 45 - Test/ExceptionTest.m | 101 -- Test/FastARC.m | 104 -- Test/FastARCPool.m | 41 - Test/FastPathAlloc.m | 129 --- Test/FastRefCount.m | 167 ---- Test/ForeignException.m | 65 -- Test/Forward.m | 71 -- Test/ForwardDeclareProtocol.m | 16 - Test/ForwardDeclareProtocolAccess.m | 28 - Test/GNUmakefile | 8 - Test/IVarOverlap.m | 28 - Test/IVarSuperclassOverlap.m | 99 -- Test/ManyManySelectors.m | 55 - Test/MethodArguments.m | 91 -- Test/NestedExceptions.m | 39 - Test/NilException.m | 49 - Test/ObjCXXEHInterop.m | 24 - Test/ObjCXXEHInterop.mm | 41 - Test/ObjCXXEHInteropTwice.mm | 22 - Test/ObjCXXEHInterop_arc.m | 22 - Test/ObjCXXEHInterop_arc.mm | 18 - Test/PropertyAttributeTest.m | 77 -- Test/PropertyIntrospectionTest.m | 41 - Test/PropertyIntrospectionTest2_arc.m | 740 -------------- Test/ProtocolCreation.m | 115 --- Test/ProtocolExtendedProperties.m | 19 - Test/ResurrectInDealloc_arc.m | 39 - Test/RuntimeTest.m | 343 ------- Test/RuntimeTest.xcodeproj/project.pbxproj | 203 ---- Test/SuperMethodMissing.m | 46 - Test/Test.h | 51 - Test/Test.m | 71 -- Test/UnexpectedException.m | 56 -- Test/WeakBlock_arc.m | 15 - Test/WeakImportClass.m | 12 - Test/WeakRefLoad.m | 22 - Test/WeakReferences_arc.m | 45 - Test/alias.m | 16 - Test/alignTest.m | 87 -- Test/category_properties.m | 41 - Test/exchange.m | 23 - Test/hash_table_delete.c | 64 -- Test/hash_test.c | 97 -- Test/ivar_arc.m | 77 -- Test/ivar_atomic.m | 24 - Test/minRep1.mm | 15 - Test/msgInterpose.m | 85 -- Test/objc_msgSend.m | 265 ----- Test/objc_msgSend_WoA64.mm | 148 --- Test/setSuperclass.m | 328 ------ Test/zeroSizedIVar.m | 68 -- abi_version.c | 130 --- alias.h | 27 - alias_table.c | 128 --- arc.mm | 1057 -------------------- asmconstants.h | 29 - associate.m | 469 --------- block_to_imp.c | 338 ------- block_trampolines.S | 232 ----- blocks_runtime.h | 125 --- blocks_runtime.m | 324 ------ blocks_runtime_np.m | 57 -- buffer.h | 62 -- caps.c | 42 - category.h | 62 -- category_loader.c | 100 -- class.h | 468 --------- class_table.c | 602 ----------- cmake_uninstall.cmake.in | 22 - common.S | 18 - constant_string.h | 7 - dtable.c | 861 ---------------- dtable.h | 141 --- dwarf_eh.h | 327 ------ eh_personality.c | 772 -------------- eh_trampoline.cc | 9 - eh_win32_msvc.cc | 287 ------ encoding2.c | 639 ------------ fast_paths.m | 62 -- gc_none.c | 121 --- gc_ops.h | 60 -- hash_table.h | 577 ----------- hooks.c | 3 - ivar.c | 190 ---- ivar.h | 204 ---- legacy.c | 461 --------- legacy.h | 15 - legacy_malloc.c | 43 - libobjc.pc.in | 12 - loader.c | 400 -------- loader.h | 67 -- lock.h | 114 --- method.h | 103 -- module.h | 102 -- mutation.m | 13 - nsobject.h | 10 - objc_msgSend.S | 20 - objc_msgSend.aarch64.S | 250 ----- objc_msgSend.arm.S | 146 --- objc_msgSend.mips.S | 207 ---- objc_msgSend.riscv64.S | 141 --- objc_msgSend.x86-32.S | 132 --- objc_msgSend.x86-64.S | 315 ------ objcxx_eh.cc | 311 ------ objcxx_eh.h | 66 -- objcxx_eh_mingw.cc | 134 --- objcxx_eh_private.h | 241 ----- pool.h | 57 -- pool.hh | 45 - prepare_android_env.sh | 32 - properties.h | 239 ----- properties.m | 613 ------------ protocol.c | 712 ------------- protocol.h | 243 ----- runtime.c | 849 ---------------- safewindows.h | 20 - sarray2.c | 254 ----- sarray2.h | 150 --- selector.h | 72 -- selector_table.cc | 741 -------------- sendmsg2.c | 493 --------- spinlock.h | 81 -- statics_loader.c | 72 -- string_hash.h | 34 - type_encoding_cases.h | 36 - unwind-arm.h | 217 ---- unwind-itanium.h | 188 ---- unwind.h | 5 - visibility.h | 24 - 171 files changed, 24386 deletions(-) delete mode 100644 .cirrus.yml delete mode 100644 .github/scripts/android_test_driver.sh delete mode 100755 .github/scripts/android_test_main.sh delete mode 100644 .github/workflows/main.yml delete mode 100644 .gitignore delete mode 100644 ANNOUNCE delete mode 100644 ANNOUNCE.1.0 delete mode 100644 ANNOUNCE.1.1 delete mode 100644 ANNOUNCE.1.2 delete mode 100644 ANNOUNCE.1.3 delete mode 100644 ANNOUNCE.1.4 delete mode 100644 ANNOUNCE.1.5 delete mode 100644 ANNOUNCE.1.6 delete mode 100644 ANNOUNCE.1.6.1 delete mode 100644 ANNOUNCE.1.7 delete mode 100644 ANNOUNCE.1.8 delete mode 100644 ANNOUNCE.1.8.1 delete mode 100644 ANNOUNCE.1.9 delete mode 100644 ANNOUNCE.2.0 delete mode 100644 ANNOUNCE.2.1 delete mode 100644 Block.h delete mode 100644 Block_private.h delete mode 100644 CMake/CMakeLists.txt delete mode 100644 CMake/typeinfo_test.cc delete mode 100644 CMakeLists.txt delete mode 100644 Config.cmake.in delete mode 100644 NSBlocks.m delete mode 100644 Protocol2.m delete mode 100644 README.windows delete mode 100644 Test/ARCTest_arc.m delete mode 100644 Test/AllocatePair.m delete mode 100644 Test/AssociatedObject.m delete mode 100644 Test/AssociatedObject2.m delete mode 100644 Test/BlockImpTest.m delete mode 100644 Test/BlockTest_arc.m delete mode 100644 Test/BoxedForeignException.m delete mode 100644 Test/CMakeLists.txt delete mode 100644 Test/CXXException.cc delete mode 100644 Test/CXXException.m delete mode 100644 Test/Category.m delete mode 100644 Test/ConstantString.m delete mode 100644 Test/DirectMethods.m delete mode 100644 Test/ExceptionTest.m delete mode 100644 Test/FastARC.m delete mode 100644 Test/FastARCPool.m delete mode 100644 Test/FastPathAlloc.m delete mode 100644 Test/FastRefCount.m delete mode 100644 Test/ForeignException.m delete mode 100644 Test/Forward.m delete mode 100644 Test/ForwardDeclareProtocol.m delete mode 100644 Test/ForwardDeclareProtocolAccess.m delete mode 100644 Test/GNUmakefile delete mode 100644 Test/IVarOverlap.m delete mode 100644 Test/IVarSuperclassOverlap.m delete mode 100644 Test/ManyManySelectors.m delete mode 100644 Test/MethodArguments.m delete mode 100644 Test/NestedExceptions.m delete mode 100644 Test/NilException.m delete mode 100644 Test/ObjCXXEHInterop.m delete mode 100644 Test/ObjCXXEHInterop.mm delete mode 100644 Test/ObjCXXEHInteropTwice.mm delete mode 100644 Test/ObjCXXEHInterop_arc.m delete mode 100644 Test/ObjCXXEHInterop_arc.mm delete mode 100644 Test/PropertyAttributeTest.m delete mode 100644 Test/PropertyIntrospectionTest.m delete mode 100644 Test/PropertyIntrospectionTest2_arc.m delete mode 100644 Test/ProtocolCreation.m delete mode 100644 Test/ProtocolExtendedProperties.m delete mode 100644 Test/ResurrectInDealloc_arc.m delete mode 100644 Test/RuntimeTest.m delete mode 100644 Test/RuntimeTest.xcodeproj/project.pbxproj delete mode 100644 Test/SuperMethodMissing.m delete mode 100644 Test/Test.h delete mode 100644 Test/Test.m delete mode 100644 Test/UnexpectedException.m delete mode 100644 Test/WeakBlock_arc.m delete mode 100644 Test/WeakImportClass.m delete mode 100644 Test/WeakRefLoad.m delete mode 100644 Test/WeakReferences_arc.m delete mode 100644 Test/alias.m delete mode 100644 Test/alignTest.m delete mode 100644 Test/category_properties.m delete mode 100644 Test/exchange.m delete mode 100644 Test/hash_table_delete.c delete mode 100644 Test/hash_test.c delete mode 100644 Test/ivar_arc.m delete mode 100644 Test/ivar_atomic.m delete mode 100644 Test/minRep1.mm delete mode 100644 Test/msgInterpose.m delete mode 100644 Test/objc_msgSend.m delete mode 100644 Test/objc_msgSend_WoA64.mm delete mode 100644 Test/setSuperclass.m delete mode 100644 Test/zeroSizedIVar.m delete mode 100644 abi_version.c delete mode 100644 alias.h delete mode 100644 alias_table.c delete mode 100644 arc.mm delete mode 100644 asmconstants.h delete mode 100644 associate.m delete mode 100644 block_to_imp.c delete mode 100644 block_trampolines.S delete mode 100644 blocks_runtime.h delete mode 100644 blocks_runtime.m delete mode 100644 blocks_runtime_np.m delete mode 100644 buffer.h delete mode 100644 caps.c delete mode 100644 category.h delete mode 100644 category_loader.c delete mode 100644 class.h delete mode 100644 class_table.c delete mode 100644 cmake_uninstall.cmake.in delete mode 100644 common.S delete mode 100644 constant_string.h delete mode 100644 dtable.c delete mode 100644 dtable.h delete mode 100644 dwarf_eh.h delete mode 100644 eh_personality.c delete mode 100644 eh_trampoline.cc delete mode 100644 eh_win32_msvc.cc delete mode 100644 encoding2.c delete mode 100644 fast_paths.m delete mode 100644 gc_none.c delete mode 100644 gc_ops.h delete mode 100644 hash_table.h delete mode 100644 hooks.c delete mode 100644 ivar.c delete mode 100644 ivar.h delete mode 100644 legacy.c delete mode 100644 legacy.h delete mode 100644 legacy_malloc.c delete mode 100644 libobjc.pc.in delete mode 100644 loader.c delete mode 100644 loader.h delete mode 100644 lock.h delete mode 100644 method.h delete mode 100644 module.h delete mode 100644 mutation.m delete mode 100644 nsobject.h delete mode 100644 objc_msgSend.S delete mode 100644 objc_msgSend.aarch64.S delete mode 100644 objc_msgSend.arm.S delete mode 100644 objc_msgSend.mips.S delete mode 100644 objc_msgSend.riscv64.S delete mode 100644 objc_msgSend.x86-32.S delete mode 100644 objc_msgSend.x86-64.S delete mode 100644 objcxx_eh.cc delete mode 100644 objcxx_eh.h delete mode 100644 objcxx_eh_mingw.cc delete mode 100644 objcxx_eh_private.h delete mode 100644 pool.h delete mode 100644 pool.hh delete mode 100644 prepare_android_env.sh delete mode 100644 properties.h delete mode 100644 properties.m delete mode 100644 protocol.c delete mode 100644 protocol.h delete mode 100644 runtime.c delete mode 100644 safewindows.h delete mode 100644 sarray2.c delete mode 100644 sarray2.h delete mode 100644 selector.h delete mode 100644 selector_table.cc delete mode 100644 sendmsg2.c delete mode 100644 spinlock.h delete mode 100644 statics_loader.c delete mode 100644 string_hash.h delete mode 100644 type_encoding_cases.h delete mode 100644 unwind-arm.h delete mode 100644 unwind-itanium.h delete mode 100644 unwind.h delete mode 100644 visibility.h diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 4a43645..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,63 +0,0 @@ -libcxxrt_freebsd_task: - matrix: - - freebsd_instance: - image_family: freebsd-13-3 - - freebsd_instance: - image_family: freebsd-15-0-snap - - freebsd_instance: - image_family: freebsd-14-0 - - install_script: pkg install -y cmake ninja git - - clone_script: | - if [ -z "$CIRRUS_PR" ]; then - git clone --recursive --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR - git reset --hard $CIRRUS_CHANGE_IN_REPO - else - git clone --recursive https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR - git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR - git reset --hard $CIRRUS_CHANGE_IN_REPO - fi - git submodule sync - git submodule update - - build_script: | - mkdir Build - cd Build - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ - ninja - - test_script: cd Build && ctest -j4 - -libcxxrt_master_task: - freebsd_instance: - image_family: freebsd-14-0 - install_script: pkg install -y cmake ninja git - - clone_script: | - if [ -z "$CIRRUS_PR" ]; then - git clone --recursive --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR - git reset --hard $CIRRUS_CHANGE_IN_REPO - else - git clone --recursive https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR - git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR - git reset --hard $CIRRUS_CHANGE_IN_REPO - fi - git submodule sync - git submodule update - - install_libcxxrt_script: | - git clone https://github.com/libcxxrt/libcxxrt.git - mkdir -p libcxxrt/Build - cd libcxxrt/Build - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ - ninja - cp lib/libcxxrt.so /usr/local/lib - - build_script: | - mkdir Build - cd Build - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ - ninja - - test_script: cd Build && ctest -j4 diff --git a/.github/scripts/android_test_driver.sh b/.github/scripts/android_test_driver.sh deleted file mode 100644 index f64462f..0000000 --- a/.github/scripts/android_test_driver.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -# This script is run on the emulator to run the tests - -# Get list of all binaries in pwd for later iteration (only include binaries) -BINARIES=$(find . -maxdepth 1 -type f ! -name "*.so" ! -name "android_test_driver.sh") - -TOTAL=0 -PASS=0 -SKIP=0 -FAIL=0 - -# Run each binary, measure time and return value (PASS or FAIL). Print stdout and stderr only after a failure -for BINARY in $BINARIES; do - TOTAL=$((TOTAL + 1)) - - START_TIME=$(date +%s) - # Busybox date does not support %N, so we can't get milliseconds this way - #START_TIME_MS=$((START_TIME * 1000 + $(date +%N) / 1000000)) - - OUTPUT=$("$BINARY" 2>&1) - EXIT_CODE=$? - - END_TIME=$(date +%s) - #END_TIME_MS=$((END_TIME * 1000 + $(date +%N) / 1000000)) - #ELAPSED_TIME=$((END_TIME_MS - START_TIME_MS)) - ELAPSED_TIME=$((END_TIME - START_TIME)) - - BINARY_NAME=$(basename "$BINARY") - - if [ $EXIT_CODE -eq 0 ]; then - PASS=$((PASS + 1)) - echo "PASSED ($EXIT_CODE): $BINARY_NAME (${ELAPSED_TIME}s)" - elif [ $EXIT_CODE -eq 77 ]; then - SKIP=$((SKIP + 1)) - echo "SKIPPED: $BINARY_NAME" - else - FAIL=$((FAIL + 1)) - echo "FAILED ($EXIT_CODE): $BINARY_NAME (${ELAPSED_TIME}s)" - if [ -z "$OUTPUT" ]; then - echo "No output written to stdout." - else - echo "Output:" - echo "$OUTPUT" - fi - fi -done - -if [ $TOTAL -eq 0 ]; then - echo "No tests found. Exiting." - exit 1 -fi - -PERCENTAGE=$(((PASS + SKIP) * 100 / TOTAL)) -echo "$PERCENTAGE% Passed. Total: $TOTAL, Passed: $PASS, Skipped: $SKIP, Failed: $FAIL" -echo "Finished running tests. Exiting." - -# Exit with corresponding return value -if [ $FAIL -eq 0 ]; then - exit 0 -else - exit 1 -fi diff --git a/.github/scripts/android_test_main.sh b/.github/scripts/android_test_main.sh deleted file mode 100755 index 29abcbe..0000000 --- a/.github/scripts/android_test_main.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -main () { - # first argument is the build directory - local BUILD_DIR=$1 - # second argument is the android ndk sysroot - local ANDROID_NDK_SYSROOT=$2 - # third argument is the target triple - # e.g. arm-linux-androideabi, aarch64-linux-android, x86_64-linux-android - local TARGET_TRIPLE=$3 - - if [ ! -d "$BUILD_DIR" ] - then - echo "Build directory argument not found" - exit 1 - fi - if [ ! -d "$ANDROID_NDK_SYSROOT" ] - then - echo "Android NDK sysroot argument not found" - exit 1 - fi - if [ -z "$TARGET_TRIPLE" ] - then - echo "Target triple argument not found" - exit 1 - fi - - # We need to run the emulator with root permissions - # This is needed to run the tests - adb root - - local TEMP_DIR=$(mktemp -d) - - # Copy libobjc.so and test binaries to temporary directory - cp $BUILD_DIR/libobjc.so* $TEMP_DIR - cp $BUILD_DIR/Test/* $TEMP_DIR - - for file in $TEMP_DIR/*; do - # Check if file is a binary - if ! file $file | grep -q "ELF" - then - rm $file - continue - fi - - # Set runtime path to ORIGIN - patchelf --set-rpath '$ORIGIN' $file - done - - # Copy libc++_shared.so (required by libobjc2) - cp $ANDROID_NDK_SYSROOT/usr/lib/$TARGET_TRIPLE/libc++_shared.so $TEMP_DIR - - adb shell rm -rf /data/local/tmp/libobjc2_tests - adb push $TEMP_DIR /data/local/tmp/libobjc2_tests - - # Copy android_test_driver.sh to device - adb push $BUILD_DIR/../.github/scripts/android_test_driver.sh /data/local/tmp/libobjc2_tests - - # Run the tests - adb shell "cd /data/local/tmp/libobjc2_tests && sh android_test_driver.sh" -} - -main "$@" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 1400c15..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,373 +0,0 @@ -name: Libobjc2 CI - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [ master ] - pull_request: - branches: [ master ] - # Automatically run the workflow once a month - schedule: - - cron: '0 4 1 * *' - # Allow running the workflow manually - workflow_dispatch: - -jobs: - ubuntu: - strategy: - matrix: - # Build each combination of OS and release/debug variants - os: [ "ubuntu-22.04", "ubuntu-20.04" ] - build-type: [ Release, Debug ] - blocks-runtime: [ "EMBEDDED", "swift-5.10-RELEASE" ] - cxxlib: [ "libc++", "libstdc++" ] - llvm-version: [10, 11, 12, 13, 14, 15] - # Don't bother testing the LLVM versions that aren't in the default image for the different platforms - exclude: - - os: "ubuntu-22.04" - llvm-version: 10 - - os: "ubuntu-22.04" - llvm-version: 11 - - os: "ubuntu-22.04" - llvm-version: 12 - - os: "ubuntu-20.04" - llvm-version: 11 - - os: "ubuntu-20.04" - llvm-version: 13 - - os: "ubuntu-20.04" - llvm-version: 14 - - os: "ubuntu-20.04" - llvm-version: 15 - # Don't abort runners if a single one fails - fail-fast: false - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} ${{ matrix.build-type }} LLVM-${{ matrix.llvm-version }} ${{ matrix.cxxlib }} BlocksRuntime-${{ matrix.blocks-runtime }} - steps: - - uses: actions/checkout@v3 - - name: Install dependencies - run: | - sudo apt install ninja-build - if [ "${{ matrix.cxxlib }}" = "libc++" ]; then - sudo apt remove -y 'libc++*' - apt search libunwind - sudo apt install libc++-${{matrix.llvm-version}}-dev libc++abi-${{matrix.llvm-version}}-dev - sudo apt install libunwind-${{matrix.llvm-version}}-dev || true - fi - if [ "${{ matrix.blocks-runtime }}" != "EMBEDDED" ]; then - git clone --depth 1 --branch "${{ matrix.blocks-runtime }}" https://github.com/apple/swift-corelibs-libdispatch.git ${{github.workspace}}/swift-corelibs-libdispatch - cmake -B ${{github.workspace}}/swift-corelibs-libdispatch/build -G Ninja -DINSTALL_PRIVATE_HEADERS=ON -DCMAKE_C_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm-version}} -S ${{github.workspace}}/swift-corelibs-libdispatch - pushd ${{github.workspace}}/swift-corelibs-libdispatch/build - ninja - sudo ninja install - popd - fi - - name: Configure CMake - run: | - export LDFLAGS=-L/usr/lib/llvm-${{ matrix.llvm-version }}/lib/ - if [ "${{ matrix.blocks-runtime }}" != "EMBEDDED" ]; then - export EMBEDDED_BLOCKS_RUNTIME=OFF - else - export EMBEDDED_BLOCKS_RUNTIME=ON - fi - ls -lahR /usr/lib/llvm-${{ matrix.llvm-version }}/lib/ - cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build-type}} -G Ninja -DTESTS=ON -DEMBEDDED_BLOCKS_RUNTIME=$EMBEDDED_BLOCKS_RUNTIME -DCMAKE_C_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_OBJC_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_ASM_COMPILER=clang-${{matrix.llvm-version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm-version}} -DCMAKE_OBJCXX_COMPILER=clang++-${{matrix.llvm-version}} -DCMAKE_CXX_FLAGS="-stdlib=${{matrix.cxxlib}}" - # Build with a nice ninja status line - - name: Build - working-directory: ${{github.workspace}}/build - run: | - NINJA_STATUS="%p [%f:%s/%t] %o/s, %es" ninja - - name: Test - working-directory: ${{github.workspace}}/build - run: | - ctest --output-on-failure -j 4 - - qemu-crossbuild: - strategy: - matrix: - build-type: [ Release, Debug ] - llvm-version: [13, 14, 15] - arch: - - name: armhf - system-processor: arm - triple: arm-linux-gnueabihf - rtld: ld-linux-armhf.so.3 - # The C++ exception tests are failing, disable them until they are debugged. - unsupported: -E CXX - - name: arm64 - system-processor: aarch64 - triple: aarch64-linux-gnu - rtld: ld-linux-aarch64.so.1 - - name: riscv64 - system-processor: riscv64 - triple: riscv64-linux-gnu - rtld: ld-linux-riscv64-lp64d.so.1 - - name: ppc64el - system-processor: powerpc64le - triple: powerpc64le-linux-gnu - rtld: ld64.so.2 - # lld versions prior to 15 do not support R_RISCV_ALIGN relocations - exclude: - - llvm-version: 13 - arch: - name: riscv64 - - llvm-version: 14 - arch: - name: riscv64 - # Don't abort runners if a single one fails - fail-fast: false - runs-on: ubuntu-latest - name: Cross-build for ${{ matrix.arch.triple }} LLVM-${{ matrix.llvm-version}} ${{ matrix.build-type }} - steps: - - uses: actions/checkout@v3 - - name: Install cross-compile toolchain and QEMU - run: | - sudo apt update - sudo apt install libstdc++-9-dev-${{ matrix.arch.name }}-cross qemu-user ninja-build - - name: Configure CMake - run: | - export LDFLAGS="-L/usr/lib/llvm-${{ matrix.llvm-version }}/lib/ -fuse-ld=lld-${{ matrix.llvm-version}} -Wl,--dynamic-linker=/usr/${{ matrix.arch.triple }}/lib/${{ matrix.arch.rtld }},-rpath,/usr/${{ matrix.arch.triple }}/lib" - cmake -B ${{github.workspace}}/build \ - -DCMAKE_SYSTEM_NAME=Linux \ - -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch.system-processor }} \ - -DCMAKE_C_COMPILER=clang-${{ matrix.llvm-version }} \ - -DCMAKE_CXX_COMPILER=clang++-${{ matrix.llvm-version }} \ - -DCMAKE_ASM_COMPILER=clang-${{ matrix.llvm-version }} \ - -DCMAKE_C_COMPILER_TARGET=${{ matrix.arch.triple }} \ - -DCMAKE_CXX_COMPILER_TARGET=${{ matrix.arch.triple }} \ - -DCMAKE_OBJC_COMPILER_TARGET=${{ matrix.arch.triple }} \ - -DCMAKE_OBJCXX_COMPILER_TARGET=${{ matrix.arch.triple }} \ - -DCMAKE_ASM_COMPILER_TARGET=${{ matrix.arch.triple }} \ - -DCMAKE_BUILD_TYPE=${{matrix.build-type}} \ - -DTESTS=ON \ - -G Ninja \ - # Build with a nice ninja status line - - name: Build - working-directory: ${{github.workspace}}/build - run: | - NINJA_STATUS="%p [%f:%s/%t] %o/s, %es" ninja -v - - name: Test - working-directory: ${{github.workspace}}/build - run: | - ctest --output-on-failure -j 4 ${{ matrix.arch.unsupported }} - - windows: - strategy: - matrix: - # Build each combination of OS and release/debug variants - os: [ windows-2022, windows-2019 ] - build-type: [ Release, Debug ] - arch: [ x64_x86, x64 ] - include: - - arch: x64_x86 - flags: -m32 - - arch: x64 - flags: -m64 - - os: windows-2022 - vspath: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build - - os: windows-2019 - vspath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build - # Don't abort runners if a single one fails - fail-fast: false - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} ${{ matrix.build-type }} ${{ matrix.arch }} - steps: - - name: look at VS install - shell: cmd - run: | - dir "${{ matrix.vspath }}" - - uses: actions/checkout@v3 - with: - submodules: true - - name: Install dependencies - run: | - choco.exe install ninja - - name: Configure CMake - shell: cmd - run: | - call "${{ matrix.vspath }}\vcvarsall.bat" ${{ matrix.arch }} - set CFLAGS=${{ matrix.flags }} - set CXXFLAGS=${{ matrix.flags }} - set OBJCFLAGS=${{ matrix.flags }} - set OBJCXXFLAGS=${{ matrix.flags }} - set ASMFLAGS=${{ matrix.flags }} - mkdir build - cd build - cmake .. -G Ninja -DTESTS=ON -DCMAKE_C_COMPILER="c:/Program Files/LLVM/bin/clang.exe" -DCMAKE_CXX_COMPILER="c:/Program Files/LLVM/bin/clang.exe" -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} - - name: Build - shell: cmd - working-directory: build - run: | - call "${{ matrix.vspath }}\vcvarsall.bat" ${{ matrix.arch }} - ninja - - name: Test - shell: cmd - working-directory: build - run: | - ctest -j 4 --output-on-failure -T test - - mingw: - strategy: - matrix: - # Build each combination of OS and release/debug variants - os: [ windows-2019 ] - msystem: [ ucrt64, mingw64, clang64 ] - build-type: [ Release, Debug ] - include: - - msystem: ucrt64 - package-prefix: ucrt-x86_64 - cmake-flags: LDFLAGS="-fuse-ld=lld -lstdc++ -lgcc_s" - - msystem: mingw64 - package-prefix: x86_64 - cmake-flags: LDFLAGS="-fuse-ld=lld -lstdc++ -lgcc_s" - - msystem: clang64 - package-prefix: clang-x86_64 - cmake-flags: LDFLAGS="-lc++" - # Don't abort runners if a single one fails - fail-fast: false - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} ${{ matrix.msystem }} ${{ matrix.build-type}} - defaults: - run: - shell: msys2 {0} - steps: - - uses: actions/checkout@v3 - - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.msystem }} - update: true - install: git mingw-w64-${{ matrix.package-prefix }}-clang mingw-w64-${{ matrix.package-prefix }}-lld mingw-w64-${{ matrix.package-prefix }}-cmake - - name: Configure CMake - run: | - mkdir build - cd build - ${{ matrix.cmake-flags }} cmake .. -DTESTS=ON -DCMAKE_C_COMPILER="clang" -DCMAKE_CXX_COMPILER="clang++" -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} - - name: Build - working-directory: build - run: | - cmake --build . - - name: Test - working-directory: build - run: | - ctest -j 4 --output-on-failure -T test ${{ matrix.ctest-flags }} - - name: Install - working-directory: build - run: | - cmake --install . --prefix=../dist - - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.msystem }}-${{ matrix.build-type }} - path: dist/ - - android: - strategy: - matrix: - # Build each combination of OS and release/debug variants - os: [ ubuntu-20.04 ] - build-type: [ Release, Debug ] - arch: - - name: x86_64 - triple: x86_64-linux-android - emu-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - - name: arm64-v8a - triple: aarch64-linux-android - # Google broke ARM64 emulation on x86_64 hosts. A workaround is to overwrite the qemu machine type. - emu-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel off -qemu -machine virt - api-level: [ 27, 33 ] - # Please note that: - # - arm64-v8a emulation on a x86_64 host currently is only possible up to API level 27 Oreo - # - armeabi-v7a is only supported up to API level 24 - exclude: - - api-level: 33 - arch: - name: arm64-v8a - # Don't abort runners if a single one fails - fail-fast: false - runs-on: ${{ matrix.os }} - name: Android ${{ matrix.build-type }} ${{ matrix.arch.name }} API-${{ matrix.api-level }} - steps: - - uses: actions/checkout@v4 - - name: Install Dependencies - run: | - sudo apt-get update -y - sudo apt-get install patchelf ninja-build -y - - name: Enable KVM - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - name: AVD cache - uses: actions/cache@v4 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }}-${{ matrix.arch.name }} - - name: Create AVD and Snapshot for Caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: hmelder/android-emulator-runner@v2.33.2 - with: - api-level: ${{ matrix.api-level }} - arch: ${{ matrix.arch.name }} - force-avd-creation: false - emulator-options: ${{ matrix.arch.emu-options }} - disable-animations: true - script: echo "Generated AVD snapshot for caching." - # We are using the default NDK from the GitHub Actions runner. - - name: Configure CMake - run: | - export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64 - export CCPREFIX=$TOOLCHAIN/bin/${{ matrix.arch.triple }}${{ matrix.api-level }} - export CC="$CCPREFIX-clang" - export CXX="$CCPREFIX-clang++" - export OBJC="$CCPREFIX-clang" - export OBJCXX="$CCPREFIX-clang++" - export AS="$CCPREFIX-clang" - export LD="$TOOLCHAIN/bin/ld.lld" - export AR="$TOOLCHAIN/bin/llvm-ar" - export RANLIB="$TOOLCHAIN/bin/llvm-ranlib" - export STRIP="$TOOLCHAIN/bin/llvm-strip" - export NM="$TOOLCHAIN/bin/llvm-nm" - export OBJDUMP="$TOOLCHAIN/bin/llvm-objdump" - export LDFLAGS="-fuse-ld=lld" - export LIBS="-lc++_shared" - - cmake -B ${{github.workspace}}/build \ - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ - -DANDROID_ABI=${{ matrix.arch.name }} \ - -DANDROID_NDK=$ANDROID_NDK_HOME \ - -DANDROID_STL=c++_shared \ - -DCMAKE_FIND_USE_CMAKE_PATH=false \ - -DCMAKE_C_COMPILER=$CC \ - -DCMAKE_CXX_COMPILER=$CXX \ - -DCMAKE_ASM_COMPILER=$AS \ - -DCMAKE_BUILD_TYPE=${{matrix.build-type}} \ - -DTESTS=ON \ - -DANDROID_PLATFORM=android-${{ matrix.api-level }} \ - -G Ninja - - name: Build - working-directory: ${{github.workspace}}/build - run: | - NINJA_STATUS="%p [%f:%s/%t] %o/s, %es" ninja -v - - name: Test - uses: hmelder/android-emulator-runner@v2.33.2 - with: - api-level: ${{ matrix.api-level }} - arch: ${{ matrix.arch.name }} - force-avd-creation: false - emulator-options: ${{ matrix.arch.emu-options }} - disable-animations: true - target: default - script: | - ${{github.workspace}}/.github/scripts/android_test_main.sh ${{github.workspace}}/build ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot ${{ matrix.arch.triple }} - - - # Fake check that can be used as a branch-protection rule. - all-checks: - needs: [ubuntu, windows, qemu-crossbuild] - runs-on: ubuntu-latest - steps: - - name: Dummy step - run: true - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a69c57e..0000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*~ -.*.sw? -[b|B]uild -Debug -Release diff --git a/ANNOUNCE b/ANNOUNCE deleted file mode 100644 index 3f5aba2..0000000 --- a/ANNOUNCE +++ /dev/null @@ -1,55 +0,0 @@ -GNUstep Objective-C Runtime 2.2 -=============================== - -This is the second update to the second major release of the GNUstep Objective-C -runtime (a.k.a. libobjc2). This runtime was designed to support the features -of modern dialects of Objective-C for use with GNUstep and other Objective-C -programs. - -Highlights of this release include: - -- Initial support for RISC-V 64-bit (rv64) including an architecture-specific - objc_msgSend, and block trampoline implementation. Please note that - double-precision floating-point support (rv64d) is required for the - objc_msgSend implementation. -- Initial support for Windows on ARM64 with fast-path objc_msgSend. -- Numerous improvements to the Objective-C++ exception interoperation code. - The runtime now dynamically detects whether the libcxxrt, libsupc++, or - libc++abi variant of the Itanium C++ Exception ABI is being used. This is - the first version to support exception interoperability with libc++abi. -- Because we no longer need to identify the specific C++ runtime, we can link - to it indirectly via the C++ standard library, which enables more C++ to be - used in the Objective-C runtime. -- The minimum CMake version has been bumped to 3.16, which supports - Objective-C. This support is now used, simplifying the build. -- Support for GC mode is gone. Apple dropped support for this a long time ago. -- `objc_setUncaughtExceptionHandler` is added, which avoids consuming code - needing to access a library-owned global. -- The selector-table code has been rewritten in C++, improving performance of - adding selectors. This is unlikely to have a measurable impact on - performance outside of contrived test cases, but the new code is more - maintainable. -- Several bug fixes in the ARC code, especially in corner cases surrounding - weak references. -- Support for fast-path allocation / initialisation functions. Root classes - that opt into this should implement `+_TrivialAllocInit` (this can be an - empty method, it is not called). Clang 18 or later will emit calls to the - fast-path functions for `+alloc`, `+allocWithZone:` and `+alloc` + `-init` - calls. This should improve code density as well as performance. - -You may obtain the code for this release from git and use the 2.2 branch: - -https://github.com/gnustep/libobjc2.git - -Alternatively, a tarball is available from: - -https://github.com/gnustep/libobjc2/archive/v2.2.zip -https://github.com/gnustep/libobjc2/archive/v2.2.tar.gz - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements a superset of Apple's Objective-C Runtime APIs. - -If you come across any problems, please file them in the issue tracker: - -https://github.com/gnustep/libobjc2/issues diff --git a/ANNOUNCE.1.0 b/ANNOUNCE.1.0 deleted file mode 100644 index 730984d..0000000 --- a/ANNOUNCE.1.0 +++ /dev/null @@ -1,33 +0,0 @@ -GNUstep Objective-C Runtime 1.0 -=============================== - -This is the first official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.0 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.0.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully compatible with the -FSF's GCC Objective-C ABI and also implements a new ABI that is supported by -Clang and is required for some of the newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is entirely new (MIT licensed) code and -may still contain bugs. If you come across any problems, please report them to -the GNUstep Developer mailing list . A 1.1 release, -fixing any bugs that are encountered in wider deployment, is planned to -coincide with the next GNUstep release. diff --git a/ANNOUNCE.1.1 b/ANNOUNCE.1.1 deleted file mode 100644 index 68d0a2f..0000000 --- a/ANNOUNCE.1.1 +++ /dev/null @@ -1,33 +0,0 @@ -GNUstep Objective-C Runtime 1.1 -=============================== - -This is the second official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. This release contains -minor bug fixes and provides compatibility with synthesised declared properties -from GCC 4.6 and recent versions of clang. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.1 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.1.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully compatible with the -FSF's GCC Objective-C ABI and also implements a new ABI that is supported by -Clang and is required for some of the newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is entirely new (MIT licensed) code and -may still contain bugs. If you come across any problems, please report them to -the GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.2 b/ANNOUNCE.1.2 deleted file mode 100644 index aa54c99..0000000 --- a/ANNOUNCE.1.2 +++ /dev/null @@ -1,33 +0,0 @@ -GNUstep Objective-C Runtime 1.2 -=============================== - -This is the 1.2 release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. This release contains -several bug fixes, and is tested with the current GNUstep trunk, so will be -compatible with the upcoming GNUstep release. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.2 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.2.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully compatible with the -FSF's GCC Objective-C ABI and also implements a new ABI that is supported by -Clang and is required for some of the newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is entirely new (MIT licensed) code and -may still contain bugs. If you come across any problems, please report them to -the GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.3 b/ANNOUNCE.1.3 deleted file mode 100644 index 59d96f3..0000000 --- a/ANNOUNCE.1.3 +++ /dev/null @@ -1,41 +0,0 @@ -GNUstep Objective-C Runtime 1.3 -=============================== - -This is the fourth official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. - -This release contains several bug fixes and includes a unified exception -model, providing the same features as Apple's Modern runtime for Objective-C++ -code, specifically the ability to throw Objective-C objects with @throw() or -throw() and catch them with @catch() or catch(). The new unified exception -model is supported by Clang 2.9 and is compatible with Apple's Objective-C++ -exception behaviour. Another enhancement in this release is the addition of -support for class aliases. This provides a run-time equivalent of -@compatibility_alias, allowing a class to show up in class lookup searching for -its alias. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.3 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.3.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully compatible with the -FSF's GCC Objective-C ABI and also implements a new ABI that is supported by -Clang and Étoilé's LanguageKit and is required for some of the newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is entirely new (MIT licensed) code and -may still contain bugs. If you come across any problems, please report them to -the GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.4 b/ANNOUNCE.1.4 deleted file mode 100644 index 0a723a1..0000000 --- a/ANNOUNCE.1.4 +++ /dev/null @@ -1,79 +0,0 @@ -GNUstep Objective-C Runtime 1.4 -=============================== - -This is the fifth official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. Highlights of this -release include: - -- Support for the associated reference APIs introduced with OS X 10.6. This - allows storing arbitrary objects associated with another object. - -- Concurrent, thread-safe, +initialize. The runtime will now send +initialize - messages to different classes concurrently in multiple threads, but still - ensures that no class receives another message until it has returned from - +initialize. This mirrors OS X behaviour. Care must be taken that - +initialize methods do not deadlock - if two classes are simultaneously - initialised from two different threads, and there +initialize methods call - methods in the other class, then deadlock will result. - -- Exceptions can now safely propagate out of +initialize methods. - -- Better hiding of local symbols. Now the internal runtime functions are not - visible from outside of the runtime. This may break code that attempts to - use private APIs, but means that it is now impossible to accidentally use - private APIs. - -- Dispatch table updates have been improved. Category loading now longer - triggers dtable creation and partial dtable updates are faster. - -- Improvements to the low memory profile. Uses 5-10% less (total) memory - running Gorm, and now passes the entire GNUstep and EtoileFoundation test - suites. Build with [g]make low_memory=yes to enable this mode. Note that - the low memory profile trades some CPU time for memory usage, so don't use it - for CPU-bound tasks. - -- The class lookup cache optimisation (LLVM) now caches lookups irrespective of - the ABI. It will insert direct references to the class structures if - possible (i.e. if the symbol is visible). If not, then it will cache the - result of objc_class_lookup(). The cache is shared across the module (the - library, if run as a link-time optimisation), so the lookup only needs to be - run once. This eliminates the need for explicit class lookup caching in the - source code (when using LLVM-based compilers, such as Clang, LanguageKit, or - DragonEgg). - -- Added some missing runtime API functions, such as those for setting and - getting instance variables. These are required by some language bridges, - although using them safely is not actually possible with the non-fragile ABI - (also true on OS X), since instance variables are no longer uniquely - identified by name. - -- Added support for accessing property type encodings. These are extended type - encodings, allowing code introspecting the properties to learn if they are - read-only, their assignment policy, the methods used to implement them, and - so on. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.4 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.4.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully compatible with the -FSF's GCC Objective-C ABI and also implements a new ABI that is supported by -Clang and Étoilé's LanguageKit and is required for some of the newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is entirely new (MIT licensed) code and -may still contain bugs. If you come across any problems, please report them to -the GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.5 b/ANNOUNCE.1.5 deleted file mode 100644 index bdf8d81..0000000 --- a/ANNOUNCE.1.5 +++ /dev/null @@ -1,55 +0,0 @@ -GNUstep Objective-C Runtime 1.5 -=============================== - -This is the sixth official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. Highlights of this -release include: - -- Support for Apple-compatible garbage collection APIs, along with extensions - to support CoreFoundation-style explicit reference counting in a garbage - collected environment. This uses the Boehm garbage collector and is enabled - by specifying boehm_gc=yes when building. This requires version 7.1 or later - of libgc. Code compiled with -fobjc-gc can be mixed with code that - implements normal reference counting and with code compiled with - -fobjc-gc-only. The runtime supports both GC and non-GC code when compiled - with GC support and will automatically select the correct behavior depending - on the loaded code. - -- The runtime will now use Boehm GC for several internal data structures, if it - is built with GC enabled. This avoids the need for defensive programming - with respect to thread safety in several places. - -- This is the first release to provide a superset of the functionality provided - by the Mac Objective-C runtime, as shipped with OS X 10.6. - -- Full support for Automatic Reference Counting (ARC), compatible with OS X - 10.7 and iOS 5, including support for __weak references. - -- The LLVM optimisation passes have been improved and better tested. Code - compiled with them now passes the EtoileFoundation test suite. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.5 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.5.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully compatible with the -FSF's GCC Objective-C ABI and also implements a new ABI that is supported by -Clang and Étoilé's LanguageKit and is required for some of the newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is entirely new (MIT licensed) code and -may still contain bugs. If you come across any problems, please report them to -the GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.6 b/ANNOUNCE.1.6 deleted file mode 100644 index 618ab6c..0000000 --- a/ANNOUNCE.1.6 +++ /dev/null @@ -1,64 +0,0 @@ -GNUstep Objective-C Runtime 1.6 -=============================== - -This is the seventh official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of Objective-C 2 -for use with GNUstep and other Objective-C programs. Highlights of this -release include: - -- Compatibility with the new runtime APIs introduced with Mac OS X 10.7 / iOS 5. - -- Support for small objects (ones hidden inside a pointer). On 32-bit systems, - the runtime permits one small object class, on 64-bit systems it permits 4. - This is used by GNUstep for small NSNumber and NSString instances, and these - are used by LanguageKit for message sending to small integers. - -- Support for prototype-style object orientation. You can now add methods, as - well as associated references, to individual objects, and clone them. The - runtime now supports everything required for the JavaScript object model, - including the ability to use blocks as methods on x86, x86-64 and ARM. - -- Support for Apple-compatible objc_msgSend() functions for x86, x86-64, and - ARM. Using these approximately halves the cost of message sending operations - and results in a 10% smaller total binary size. - -- A fully maintained POSIX Makefile to make bootstrapping builds and packaging - easier. This will be used automatically if GNUstep Make is not installed. - -- Improvements to the included LLVM optimisation passes. Testing on a 2.8GHz - Xeon, a loop of 200,000,000 class messages took 0.8 seconds with all - optimisations enabled (including speculative inlining). With -Os, the test - took 2 seconds. With explicit IMP caching in the source code, the test took - 1.2 seconds. For reference, the same test using the GCC Objective-C runtime - took 11 seconds (when compiled with either Clang/LLVM or GCC). - -Various features of this release required some per-platform assembly code. For -the 1.6.0 release, ARM, x86 and x86-64 (with the SysV ABI, not with the Win64 -ABI) are supported. Future releases in the 1.6.x series will extend this to -other architectures. - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.6 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.6.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully backwards compatible -with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is -supported by Clang and Étoilé's LanguageKit and is required for some of the -newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is relatively new code and may still -contain bugs. If you come across any problems, please report them to the -GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.6.1 b/ANNOUNCE.1.6.1 deleted file mode 100644 index 03f6ba0..0000000 --- a/ANNOUNCE.1.6.1 +++ /dev/null @@ -1,41 +0,0 @@ -GNUstep Objective-C Runtime 1.6.1 -================================= - -This is a point release to the seventh official release of the GNUstep -Objective-C runtime (a.k.a. libobjc2). This runtime was designed to support -the features of Objective-C 2 for use with GNUstep and other Objective-C -programs. Highlights of this release include: - -- Improved support for ARC autorelease pools. - -- Some small bug fixes in blocks support. - -- Improvements to the Objective-C++ unified exception model support. - -- Updated optimisation passes to work with LLVM 3.1 - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.6.1 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.6.1.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully backwards compatible -with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is -supported by Clang and Étoilé's LanguageKit and is required for some of the -newer features. - -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is relatively new code and may still -contain bugs. If you come across any problems, please report them to the -GNUstep Developer mailing list . diff --git a/ANNOUNCE.1.7 b/ANNOUNCE.1.7 deleted file mode 100644 index 8d09630..0000000 --- a/ANNOUNCE.1.7 +++ /dev/null @@ -1,68 +0,0 @@ -GNUstep Objective-C Runtime 1.7 -=============================== - -This is a point release to the eighth official release of the GNUstep -Objective-C runtime (a.k.a. libobjc2). This runtime was designed to support -the features of modern dialects of Objective-C for use with GNUstep and other -Objective-C programs. Highlights of this release include: - -- A new CMake-based build system. This makes all of the configurable options - available via a clean interface. CPack is supported for building RPM and DEB - packages out of the box. - -- A new CTest-based test suite, replacing the old ad-hoc tests. - -- Build a single libobjc with support for Objective-C++ on platforms where a - C++ ABI library (libcxxrt or libsupc++) is installed as a shared library. - -- Added specialised property accessor functions and support for atomic - properties with C++ non-POD types. - -- Significant improvements in property introspection and an exhaustive test - suite. - -- Improved integration with libdispatch. The runtime will correctly register - work queues with the garbage collector or create autorelease pools around - block invocations. - -- A new exception implementation providing better integration with foreign - exceptions (e.g. C++ exceptions). The new ABI is supported by clang 3.3 when - compiling with -fobjc-runtime=gnustep-1.7 (or higher). The old ABI is still - supported and both can be used within the same program, however code compiled - with the old ABI remains unreliable in the presence of foreign exceptions. - It is strongly recommended that anyone using exceptions with Objective-C++ - switches to the new version. - -- MIPS64 support in the assembly routines. Currently these are only tested - with the n64 ABI. They are believed to work with n32 and o32, but should be - considered unsupported on these platforms. - -- Small algorithmic improvement to the objc_msgSend() implementation, giving - approximately a 10% speedup (architecture-dependent) on message sends. - -- Updated optimisation passes to work with LLVM 3.2 and recent LLVM trunk. - - -You may obtain the code for this release from subversion at the following -subversion branch: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/releases/1.7 - -Alternatively, a tarball is available from: - -http://download.gna.org/gnustep/libobjc2-1.7.tar.bz2 - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully backwards compatible -with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is -supported by Clang and Étoilé's LanguageKit and is required for some of the -newer features. - -If you come across any problems, please report them to the GNUstep Developer -mailing list . diff --git a/ANNOUNCE.1.8 b/ANNOUNCE.1.8 deleted file mode 100644 index 9d1029b..0000000 --- a/ANNOUNCE.1.8 +++ /dev/null @@ -1,37 +0,0 @@ -GNUstep Objective-C Runtime 1.8 -=============================== - -This the ninth official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of modern -dialects of Objective-C for use with GNUstep and other Objective-C programs. -Highlights of this release include: - -- Added API for tracing, allowing interposition on all message sends matching a - given selector. - -- Numerous bug fixes and stability improvements. - -You may obtain the code for this release from git and use the 1.8 branch: - -https://github.com/gnustep/libobjc2.git - -Alternatively, a tarball is available from: - -https://github.com/gnustep/libobjc2/archive/v1.8.zip -https://github.com/gnustep/libobjc2/archive/v1.8.tar.gz - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully backwards compatible -with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is -supported by Clang and Étoilé's LanguageKit and is required for some of the -newer features. - -If you come across any problems, please file them in the issue tracker: - -https://github.com/gnustep/libobjc2/issues diff --git a/ANNOUNCE.1.8.1 b/ANNOUNCE.1.8.1 deleted file mode 100644 index 17e83ae..0000000 --- a/ANNOUNCE.1.8.1 +++ /dev/null @@ -1,37 +0,0 @@ -GNUstep Objective-C Runtime 1.8.1 -================================= - -This a bugfix release for the ninth official release of the GNUstep Objective-C -runtime (a.k.a. libobjc2). This runtime was designed to support the features -of modern dialects of Objective-C for use with GNUstep and other Objective-C -programs. Highlights of this release include: - -- Better build system detection of LLVM not being present - -- Fix for a bug causing corruption of runtime state when hidden classes are - deallocated. - -You may obtain the code for this release from git and use the 1.8 branch: - -https://github.com/gnustep/libobjc2.git - -Alternatively, a tarball is available from: - -https://github.com/gnustep/libobjc2/archive/v1.8.zip -https://github.com/gnustep/libobjc2/archive/v1.8.tar.gz - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully backwards compatible -with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is -supported by Clang and Étoilé's LanguageKit and is required for some of the -newer features. - -If you come across any problems, please file them in the issue tracker: - -https://github.com/gnustep/libobjc2/issues diff --git a/ANNOUNCE.1.9 b/ANNOUNCE.1.9 deleted file mode 100644 index e705ee7..0000000 --- a/ANNOUNCE.1.9 +++ /dev/null @@ -1,77 +0,0 @@ -GNUstep Objective-C Runtime 1.9 -=============================== - -This the ninth official release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of modern -dialects of Objective-C for use with GNUstep and other Objective-C programs. -Highlights of this release include: - -- Support for 64-bit ARM (AArch64) processors, including assembly fast paths - for message sending. - -- Improved the dispatch table representation to improve performance and cache - usage on the fast path. - -- The implementation of `imp_implementationWithBlock`, the function that allows - blocks to be used as methods, no longer requires physical pages to be mapped - both writeable and executable. - -- Numerous improvements to the interaction between runtime functions and ARC. - -- Support for Thumb-2 interworking on ARM. Note that the library must be - compiled for ARMv7 or ARMv6T2 for this code to be enabled. Once it has been, - other Objective-C binaries linked with the library can be compiled as ARM or - Thumb-2 code. This will also generate Thumb-2 message send functions, - improving instruction cache usage. - -- Significant improvements to ARC, including - - * The runtime no longer acquires a global lock on every object deallocation (a - global lock is still used for objects that have weak references). *NOTE:* - This is incompatible with other code directly inspecting the reference - count and will break with older versions of GNUstep Base! - - * Weak references use a scheme closer to C++ `std::weak_pointer` and are - lazily zeroed on access. This reduces the space overheads for weak - references. - - * Some additional helper functions are added for use in `NSObject` and other - root classes, which simplifies the layering between the runtime and the - Foundation (or equivalent) implementation. - -- Improvements to how the runtime handles layout of ivars with strong alignment - requirements, which should fix issues relating to using vector types in - Objective-C objects. - -- The option to build a separate libobjcxx has been removed. The runtime will - now depend on the C++ standard library implementation if no useable C++ - runtime is available. Note that C++ exception interworking does not work - because LLVM's libc++abi (shipped by Apple) does not provide GNU-compatible - hooks and so Objective-C++ exception support will be automatically disabled - on this platform. Any other platforms shipping libc++abi should consider - either GNU libsupc++ or libcxxrt as an alternative. - -You may obtain the code for this release from git and use the 1.9 branch: - -https://github.com/gnustep/libobjc2.git - -Alternatively, a tarball is available from: - -https://github.com/gnustep/libobjc2/archive/v1.9.zip -https://github.com/gnustep/libobjc2/archive/v1.9.tar.gz - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number -of GCC APIs for legacy compatibility. - -This library is based on the Étoilé Objective-C Runtime, an earlier research -prototype, and includes support for non-fragile instance variables, -type-dependent dispatch, and object planes. It is fully backwards compatible -with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is -supported by Clang and Étoilé's LanguageKit and is required for some of the -newer features. - -If you come across any problems, please file them in the issue tracker: - -https://github.com/gnustep/libobjc2/issues diff --git a/ANNOUNCE.2.0 b/ANNOUNCE.2.0 deleted file mode 100644 index 5adc4e9..0000000 --- a/ANNOUNCE.2.0 +++ /dev/null @@ -1,88 +0,0 @@ -GNUstep Objective-C Runtime 2.0 -=============================== - -This the second major release of the GNUstep Objective-C runtime (a.k.a. -libobjc2). This runtime was designed to support the features of modern -dialects of Objective-C for use with GNUstep and other Objective-C programs. - -This release introduces a new Objective-C ABI, which is designed to be forward -compatible with future changes and removes a large number of hacks that were -required for compatibility with the prior ABI that included features introduced -in the 1980s. Highlights include: - -- The linker now removes duplicate selectors within a library. - -- The linker will remove duplicate protocol definitions within a library. - -- Protocols and classes are now always accessed via an indirection layer, so - they can be extended by future versions of the ABI without breaking - compatibility. - -- Categories can include property metadata and both categories. - -- Constant strings are now emitted with a richer structure, which provides - faster hashing and better unicode support. - -- Instance variable metadata includes size and alignment, allowing better object - layout and more accurate introspection. Instance variable metadata now - includes more accurate information about ARC ownership semantics. - -- Method metadata now includes extended type information, allowing bridged - languages to understand the class that methods expect and not just the fact - that they take an object. This was previously an obstacle to supporting - JavaScriptCore's Objective-C bridge with GNUstep. - -In addition to the new ABI, there are several improvements on Windows: - -- The runtime now uses SEH-based exception handling on Windows. This - interoperates with C++ exceptions in libraries compiled with MSVC or - MSVC-compatible compilers. - -- All of the assembly code paths now fully support i386 and x64 Windows. - -- Object allocations on 32-bit Windows now use `_aligned_malloc` to guarantee - sufficient alignment for AVX vectors in instance variables. - -- The runtime now uses fibre-local storage and critical sections on Windows, - improving performance in multithreaded workloads on Windows. - -- Public runtime functions are now marked dllexport, so the runtime can be built - as objc.dll on Windows. - -**WARNING**: The new ABI is currently incompatible with incremental linking on -Windows, so projects built with Microsoft's linker must disable incremental -linking. - -Note: Microsoft's WinObjC project contains a friendly fork of this library that -includes a work around for the incremental linking issue. If you wish to use -incremental linking on Windows, please use that version. A subsequent version -of clang and link.exe should include a fix that will make it possible to use -this version with incremental linking. - -The runtime will now use the new ABI's data structures internally and will -automatically upgrade on-disk structures from old ABIs when used with the old -ABI. As a result, memory usage will be higher when using the old ABI and users -who are unable to recompile their code may prefer to stick with the 1.9.x -release series. Mixing libraries compiled with the old and new ABIs is not -supported and will abort at run time. - -The new ABI provides better run-time introspection metadata and smaller -binaries. When used with the new ABI, this version of the runtime will consume -less memory than the previous release. - -You may obtain the code for this release from git and use the 2.0 branch: - -https://github.com/gnustep/libobjc2.git - -Alternatively, a tarball is available from: - -https://github.com/gnustep/libobjc2/archive/v2.0.zip -https://github.com/gnustep/libobjc2/archive/v2.0.tar.gz - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements a superset of Apple's Objective-C Runtime APIs. - -If you come across any problems, please file them in the issue tracker: - -https://github.com/gnustep/libobjc2/issues diff --git a/ANNOUNCE.2.1 b/ANNOUNCE.2.1 deleted file mode 100644 index e5e8a1a..0000000 --- a/ANNOUNCE.2.1 +++ /dev/null @@ -1,79 +0,0 @@ -GNUstep Objective-C Runtime 2.1 -================================= - -This is the first update to the second major release of the GNUstep Objective-C -runtime (a.k.a. libobjc2). This runtime was designed to support the features -of modern dialects of Objective-C for use with GNUstep and other Objective-C -programs. - -*NOTE:* This is the first release to use submodules. If you are downloading -the sources from git, please make sure that you do a recursive clone. If you -forget, the build system will give you instructions to correct this. -Tarballs from GitHub do not include submodules, so if you are downloading the -tarball then you will need to download the submodule separately. - - -Highlights of this release include: - -- Numerous improvements to the Objective-C++ exception interoperation code. - The runtime now dynamically detects whether the libcxxrt or libsupc++ variant - of the Itanium C++ Exception ABI is being used - -- Sending a message to `super` where the corresponding method did not exist was - silently ignored in previous versions of the runtime. This now correctly - invokes the forwarding hooks and so (with an implementation of the Foundation - framework, such as GNUstep Base or WinObjC) will trigger an exception or - invoke `forwardInvocation:`. - -- The checks for overloaded memory management methods were incorrect, causing - some classes to be incorrectly opted into ARC fast paths. These checks are - now correct. - -- Several memory management bugs in corner cases of weak reference management - were fixed. - -- The ARM assembly implementation of `objc_msgSend` now correctly restores the - stack after calling a forwarding implementation. This bug caused stack - corruption and usually crashing on ARM. - -- The ARC code has been rewritten as C++, using a well-tested third-party - Robin-Hood hash table to store weak references, replacing the home-grown - version. This improves performance and reduces the likelihood of bugs - arising from the hash table implementation. - -- Control Flow Guard (CGF) checks were added on Windows on x86 (32- and - 64-bit). If Objective-C code is compiled with CFG enabled then - `objc_msgSend` will crash if it attempts to jump to an address that is not a - valid function entry point. - -- The function signatures in the blocks headers were updated for compatibility - with recent macOS releases. - -- Support for the C11 _Atomic type qualifier in property metadata was added. - -You may obtain the code for this release from git and use the 2.1 branch: - -https://github.com/gnustep/libobjc2.git - -Alternatively, a tarball is available from: - -https://github.com/gnustep/libobjc2/archive/v2.1.zip -https://github.com/gnustep/libobjc2/archive/v2.1.tar.gz - -The submodule is available from: - -https://github.com/Tessil/robin-map/archive/757de82.zip -https://github.com/Tessil/robin-map/archive/757de82.tar.gz - -This will extract as robin-map-757de829927489bee55ab02147484850c687b620. -You must move the contents of that directory into third_party/robin_map in the -libobjc2 tree. - - -The runtime library is responsible for implementing the core features of the -object model, as well as exposing introspection features to the user. The -GNUstep runtime implements a superset of Apple's Objective-C Runtime APIs. - -If you come across any problems, please file them in the issue tracker: - -https://github.com/gnustep/libobjc2/issues diff --git a/Block.h b/Block.h deleted file mode 100644 index 253e365..0000000 --- a/Block.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/Block_private.h b/Block_private.h deleted file mode 100644 index c565ce7..0000000 --- a/Block_private.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt deleted file mode 100644 index a16bfff..0000000 --- a/CMake/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.2) -project(test_cxx_runtime) - -add_executable(test_cxx_runtime typeinfo_test.cc) -add_executable(test_cxx_stdlib typeinfo_test.cc) -if (CXX_RUNTIME) - if (CXX_RUNTIME MATCHES ".*libc\\+\\+abi.*") - find_library(M_LIBRARY m) - if (M_LIBRARY) - target_link_libraries(test_cxx_runtime ${M_LIBRARY}) - endif() - endif() - set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") - target_link_libraries(test_cxx_runtime ${CXX_RUNTIME}) - set_target_properties(test_cxx_runtime PROPERTIES - LINKER_LANGUAGE C) -endif() diff --git a/CMake/typeinfo_test.cc b/CMake/typeinfo_test.cc deleted file mode 100644 index 1d5536b..0000000 --- a/CMake/typeinfo_test.cc +++ /dev/null @@ -1,62 +0,0 @@ -#include - -namespace __cxxabiv1 -{ - struct __class_type_info; -} - -using __cxxabiv1::__class_type_info; - -namespace std -{ - /** - * std::type_info defined with the GCC ABI. This may not be exposed in - * public headers, but is required for correctly implementing the unified - * exception model. - */ - class type_info - { - public: - virtual ~type_info(); - bool operator==(const type_info &) const; - bool operator!=(const type_info &) const; - bool before(const type_info &) const; - private: - type_info(const type_info& rhs); - type_info& operator= (const type_info& rhs); - const char *__type_name; - protected: - type_info(const char *name): __type_name(name) { } - public: - const char* name() const { return __type_name; } - virtual bool __is_pointer_p() const; - virtual bool __is_function_p() const; - virtual bool __do_catch(const type_info *thrown_type, - void **thrown_object, - unsigned outer) const; - virtual bool __do_upcast( - const __class_type_info *target, - void **thrown_object) const; - }; -} - -class type_info2 : public std::type_info -{ - public: - type_info2() : type_info("foo") {} - virtual bool __is_pointer_p() const; - virtual bool __is_function_p() const { return true; } - virtual bool __do_catch(const type_info *thrown_type, - void **thrown_object, - unsigned outer) const { return true; } - virtual bool __do_upcast( - const __class_type_info *target, - void **thrown_object) const { return true; }; -}; -bool type_info2::__is_pointer_p() const { return true; } - -int main() -{ - type_info2 s; - return s.__is_pointer_p(); -} diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 0242d97..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,454 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(libobjc C ASM CXX) - -if (NOT "${CMAKE_C_COMPILER_ID}" MATCHES Clang*) - message(WARNING "WARNING: It is strongly recommended that you compile with clang") -elseif (WIN32 AND "${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC") - message(WARNING "WARNING: It is strongly recommended that you compile with clang (clang-cl is not supported)") -endif() - -# fix up CMake Objective-C compiler detection on Windows before enabling languages below -if (WIN32) - foreach(lang IN ITEMS C CXX) - set(CMAKE_OBJ${lang}_COMPILER_FORCED ON) - foreach(runtimeLibrary IN ITEMS MultiThreaded MultiThreadedDLL MultiThreadedDebug MultiThreadedDebugDLL) - set(CMAKE_OBJ${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_${runtimeLibrary} ${CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_${runtimeLibrary}}) - endforeach() - endforeach() -endif() - -enable_language(OBJC OBJCXX) - -if (MINGW) - # Make sure ObjC++ source code uses the C++ implicit include directories. This is needed, for example, to make sure we use the right - # C++ headers when using clang but linking with libstdc++. - set(CMAKE_OBJCXX_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) -endif () - -INCLUDE (CheckCXXSourceCompiles) -INCLUDE (FetchContent) -INCLUDE (CheckSymbolExists) - -set(libobjc_VERSION 4.6) - - -if (MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /EHas") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHas") - set(CMAKE_C_FLAGS_DEBUG "/Z7 ${CMAKE_C_FLAGS_DEBUG}") - set(CMAKE_SHARED_LINKER_FLAGS "/DEBUG /INCREMENTAL:NO ${CMAKE_SHARED_LINKER_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "/DEBUG /INCREMENTAL:NO ${CMAKE_EXE_LINKER_FLAGS}") - set(objc_LINK_FLAGS "/DEBUG /INCREMENTAL:NO ${objc_LINK_FLAGS}") -endif() - -message(STATUS "Architecture as detected by CMake: ${CMAKE_SYSTEM_PROCESSOR}") - -# Build configuration -add_compile_definitions(GNUSTEP __OBJC_RUNTIME_INTERNAL__=1) - -set(CMAKE_CXX_STANDARD 17) - -set(libobjc_ASM_SRCS - block_trampolines.S - objc_msgSend.S) -set(libobjc_OBJCXX_SRCS - arc.mm - ) -set(libobjc_OBJC_SRCS - NSBlocks.m - Protocol2.m - associate.m - blocks_runtime_np.m - properties.m) -set(libobjc_C_SRCS - alias_table.c - block_to_imp.c - caps.c - category_loader.c - class_table.c - dtable.c - encoding2.c - gc_none.c - hooks.c - ivar.c - loader.c - mutation.m - protocol.c - runtime.c - sarray2.c - sendmsg2.c - fast_paths.m - ) -set(libobjc_HDRS - objc/Availability.h - objc/Object.h - objc/Protocol.h - objc/capabilities.h - objc/developer.h - objc/encoding.h - objc/hooks.h - objc/message.h - objc/objc-api.h - objc/objc-arc.h - objc/objc-auto.h - objc/objc-class.h - objc/objc-exception.h - objc/objc-runtime.h - objc/objc-visibility.h - objc/objc.h - objc/runtime-deprecated.h - objc/runtime.h - objc/slot.h) - -set(libobjc_CXX_SRCS - selector_table.cc - ) -# Windows does not use DWARF EH, except when using the GNU ABI (MinGW) -if (WIN32 AND NOT MINGW) - list(APPEND libobjc_CXX_SRCS eh_win32_msvc.cc) -elseif (NOT MINGW) - list(APPEND libobjc_C_SRCS eh_personality.c) -endif () - -find_package(tsl-robin-map) - -if (NOT tsl-robin-map_FOUND) - FetchContent_Declare( - robinmap - GIT_REPOSITORY https://github.com/Tessil/robin-map/ - GIT_TAG v1.2.1) - - FetchContent_MakeAvailable(robinmap) -endif() - -if (WIN32) - set(OLD_ABI_COMPAT_DEFAULT false) -else() - set(OLD_ABI_COMPAT_DEFAULT true) -endif() - -option(TYPE_DEPENDENT_DISPATCH "Enable type-dependent dispatch" ON) -option(ENABLE_TRACING - "Enable tracing support (slower, not recommended for deployment)" OFF) -option(OLDABI_COMPAT - "Enable compatibility with GCC and old GNUstep ABIs" - ${OLD_ABI_COMPAT_DEFAULT}) -option(LEGACY_COMPAT "Enable legacy compatibility features" OFF) -option(DEBUG_ARC_COMPAT - "Log warnings for classes that don't hit ARC fast paths" OFF) -option(ENABLE_OBJCXX "Enable support for Objective-C++" ON) -option(TESTS "Enable building the tests") -option(EMBEDDED_BLOCKS_RUNTIME "Include an embedded blocks runtime, rather than relying on libBlocksRuntime to supply it" ON) - -# For release builds, we disable spamming the terminal with warnings about -# selector type mismatches -add_compile_definitions($<$:NO_SELECTOR_MISMATCH_WARNINGS>) -add_compile_definitions($<$:TYPE_DEPENDENT_DISPATCH>) -add_compile_definitions($<$:WITH_TRACING=1>) -add_compile_definitions($<$:DEBUG_ARC_COMPAT>) - -if (OLDABI_COMPAT) - list(APPEND libobjc_C_SRCS legacy.c abi_version.c statics_loader.c) - add_definitions(-DOLDABI_COMPAT=1) -endif() - -if (LEGACY_COMPAT) - list(APPEND libobjc_C_SRCS legacy_malloc.c) -else () - add_definitions(-DNO_LEGACY) -endif () - -set(LIBOBJC_NAME "objc" CACHE STRING - "Name of the Objective-C runtime library (e.g. objc2 for libobjc2)") - -set(INCLUDE_DIRECTORY "objc" CACHE STRING - "Subdirectory of the include path to install the headers.") - -add_compile_options($<$:-march=i586>) - -# PowerPC 32-bit does not support native 64-bit atomic operations, -# which is used in safe caching. -# You must also update the guard in objc/runtime.h, when updating -# this macro. -if (CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppcle") - add_definitions(-DNO_SAFE_CACHING) -endif() - -set(INSTALL_TARGETS objc) - -if(WIN32) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(ASM_TARGET -m64) - else() - set(ASM_TARGET -m32) - endif() -endif() - - -if (WIN32 AND NOT MINGW) - set(ASSEMBLER ${CMAKE_ASM_COMPILER} CACHE STRING "Assembler to use with Visual Studio (must be gcc / clang!)") - message(STATUS "Using custom build commands to work around CMake bugs") - message(STATUS "ASM compiler: ${ASSEMBLER}") - # CMake is completely broken when you try to build assembly files on Windows. - add_custom_command(OUTPUT block_trampolines.obj - COMMAND echo ${ASSEMBLER} ${ASM_TARGET} -c "${CMAKE_SOURCE_DIR}/block_trampolines.S" -o "${CMAKE_BINARY_DIR}/block_trampolines.obj" - COMMAND ${ASSEMBLER} ${ASM_TARGET} -c "${CMAKE_SOURCE_DIR}/block_trampolines.S" -o "${CMAKE_BINARY_DIR}/block_trampolines.obj" - MAIN_DEPENDENCY block_trampolines.S - ) - add_custom_command(OUTPUT objc_msgSend.obj - COMMAND echo ${ASSEMBLER} ${ASM_TARGET} -c "${CMAKE_SOURCE_DIR}/objc_msgSend.S" -o "${CMAKE_BINARY_DIR}/objc_msgSend.obj" - COMMAND ${ASSEMBLER} ${ASM_TARGET} -c "${CMAKE_SOURCE_DIR}/objc_msgSend.S" -o "${CMAKE_BINARY_DIR}/objc_msgSend.obj" - MAIN_DEPENDENCY objc_msgSend.S - DEPENDS objc_msgSend.aarch64.S objc_msgSend.arm.S objc_msgSend.mips.S objc_msgSend.x86-32.S objc_msgSend.x86-64.S - ) - set(libobjc_ASM_OBJS block_trampolines.obj objc_msgSend.obj) -endif() - - - -if (WIN32 AND NOT MINGW) - message(STATUS "Using MSVC-compatible exception model") -elseif (MINGW) - message(STATUS "Using MinGW-compatible exception model") - list(APPEND libobjc_CXX_SRCS objcxx_eh.cc objcxx_eh_mingw.cc) -else () - set(EH_PERSONALITY_FLAGS "") - if (CMAKE_CXX_COMPILER_TARGET) - list(APPEND EH_PERSONALITY_FLAGS "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") - endif () - add_custom_command(OUTPUT eh_trampoline.S - COMMAND ${CMAKE_CXX_COMPILER} ARGS ${EH_PERSONALITY_FLAGS} -fPIC -S "${CMAKE_SOURCE_DIR}/eh_trampoline.cc" -o - -fexceptions -fno-inline | sed "s/__gxx_personality_v0/test_eh_personality/g" > "${CMAKE_BINARY_DIR}/eh_trampoline.S" - MAIN_DEPENDENCY eh_trampoline.cc) - list(APPEND libobjc_ASM_SRCS eh_trampoline.S) - list(APPEND libobjc_CXX_SRCS objcxx_eh.cc) - # Find libm for linking, as some versions of libc++ don't link against it - find_library(M_LIBRARY m) -endif () - -if (EMBEDDED_BLOCKS_RUNTIME) - set(libBlocksRuntime_COMPATIBILITY_HDRS - Block.h - Block_private.h - ) - list(APPEND libobjc_OBJC_SRCS blocks_runtime.m) - list(APPEND libobjc_HDRS objc/blocks_private.h) - list(APPEND libobjc_HDRS objc/blocks_runtime.h) - add_definitions(-DEMBEDDED_BLOCKS_RUNTIME) -else () - find_library(BLOCKS_RUNTIME_LIBRARY BlocksRuntime) - if (BLOCKS_RUNTIME_LIBRARY) - set(CMAKE_REQUIRED_LIBRARIES ${BLOCKS_RUNTIME_LIBRARY}) - check_symbol_exists(_Block_use_RR2 "Block_private.h" HAVE_BLOCK_USE_RR2) - if (HAVE_BLOCK_USE_RR2) - add_definitions(-DHAVE_BLOCK_USE_RR2) - else () - message(FATAL_ERROR "libBlocksRuntime does not contain _Block_use_RR2(). Enable EMBEDDED_BLOCKS_RUNTIME to use the built-in blocks runtime.") - endif () - unset(CMAKE_REQUIRED_LIBRARIES) - else () - message(FATAL_ERROR "libBlocksRuntime not found. Enable EMBEDDED_BLOCKS_RUNTIME to use the built-in blocks runtime.") - endif () -endif () - -add_library(objc SHARED ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_OBJCXX_SRCS} ${libobjc_ASM_OBJS}) -target_compile_options(objc PRIVATE "$<$,$>:-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$:-Xclang;-fexceptions>") - -list(APPEND libobjc_CXX_SRCS ${libobjcxx_CXX_SRCS}) -target_sources(objc PRIVATE ${libobjc_CXX_SRCS}) - -include(FindThreads) -target_link_libraries(objc PUBLIC Threads::Threads) -# Link against ntdll.dll for RtlRaiseException -if (WIN32 AND NOT MINGW) - target_link_libraries(objc PUBLIC ntdll.dll) -endif() - -target_link_libraries(objc PRIVATE tsl::robin_map) - -set_target_properties(objc PROPERTIES - LINKER_LANGUAGE C - SOVERSION ${libobjc_VERSION} - OUTPUT_NAME ${LIBOBJC_NAME} - LINK_FLAGS "${objc_LINK_FLAGS}" - ) - -set_property(TARGET PROPERTY NO_SONAME true) - -option(BUILD_STATIC_LIBOBJC "Build the static version of libobjc" OFF) -if (BUILD_STATIC_LIBOBJC) - add_library(objc-static STATIC ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_CXX_SRCS}) - set_target_properties(objc-static PROPERTIES - POSITION_INDEPENDENT_CODE true - OUTPUT_NAME ${LIBOBJC_NAME}) - list(APPEND INSTALL_TARGETS objc-static) -endif () - -# Explicitly link libm, as an implicit dependency of the C++ runtime -if (M_LIBRARY) - target_link_libraries(objc PUBLIC ${M_LIBRARY}) -endif () - -if (BLOCKS_RUNTIME_LIBRARY) - target_link_libraries(objc PUBLIC ${BLOCKS_RUNTIME_LIBRARY}) -endif () - -# Make weak symbols work on OS X -if (APPLE) - set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS - "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup") - set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-undefined,dynamic_lookup") - set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -Wl,-undefined,dynamic_lookup") -endif () - -# -# Installation -# - - -find_program(GNUSTEP_CONFIG gnustep-config) -if (GNUSTEP_CONFIG) - EXEC_PROGRAM(gnustep-config - ARGS "--installation-domain-for=libobjc2" - OUTPUT_VARIABLE DEFAULT_INSTALL_TYPE) -endif () - - -# If we have GNUstep environment variables, then default to installing in the -# GNUstep local environment. -if (DEFAULT_INSTALL_TYPE) -else () - set(DEFAULT_INSTALL_TYPE "NONE") -endif () - -if (NOT CMAKE_INSTALL_LIBDIR) - set(CMAKE_INSTALL_LIBDIR lib) -endif () - -if (NOT CMAKE_INSTALL_BINDIR) - set(CMAKE_INSTALL_BINDIR bin) -endif () - -set(GNUSTEP_INSTALL_TYPE ${DEFAULT_INSTALL_TYPE} CACHE STRING - "GNUstep installation type. Options are NONE, SYSTEM, NETWORK or LOCAL.") -if (${GNUSTEP_INSTALL_TYPE} STREQUAL "NONE") - SET(LIB_INSTALL_PATH "${CMAKE_INSTALL_LIBDIR}" CACHE STRING - "Subdirectory of the root prefix where libraries are installed.") - SET(BIN_INSTALL_PATH "${CMAKE_INSTALL_BINDIR}" CACHE STRING - "Subdirectory of the root prefix where libraries are installed.") - SET(HEADER_INSTALL_PATH "include") - SET(PC_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) -else () - EXEC_PROGRAM(gnustep-config - ARGS "--variable=GNUSTEP_${GNUSTEP_INSTALL_TYPE}_LIBRARIES" - OUTPUT_VARIABLE LIB_INSTALL_PATH) - EXEC_PROGRAM(gnustep-config - ARGS "--variable=GNUSTEP_${GNUSTEP_INSTALL_TYPE}_LIBRARIES" - OUTPUT_VARIABLE BIN_INSTALL_PATH) - EXEC_PROGRAM(gnustep-config - ARGS "--variable=GNUSTEP_${GNUSTEP_INSTALL_TYPE}_HEADERS" - OUTPUT_VARIABLE HEADER_INSTALL_PATH) - SET(PC_INSTALL_PREFIX "/") -endif () -message(STATUS "GNUstep install type set to ${GNUSTEP_INSTALL_TYPE}") - -target_include_directories( - objc - INTERFACE - $ - $) -install(TARGETS ${INSTALL_TARGETS} - EXPORT libobjcTargets - RUNTIME DESTINATION ${BIN_INSTALL_PATH} - LIBRARY DESTINATION ${LIB_INSTALL_PATH} - ARCHIVE DESTINATION ${LIB_INSTALL_PATH}) - -install(FILES ${libobjc_HDRS} - DESTINATION "${HEADER_INSTALL_PATH}/${INCLUDE_DIRECTORY}") - -if (EMBEDDED_BLOCKS_RUNTIME) - install(FILES ${libBlocksRuntime_COMPATIBILITY_HDRS} - DESTINATION "${HEADER_INSTALL_PATH}") -endif () - - -set(CPACK_GENERATOR TGZ CACHE STRING - "Installer types to generate. Sensible options include TGZ, RPM and DEB") - -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GNUstep Objective-C Runtime") -set(CPACK_PACKAGE_VENDOR "The GNUstep Project") -set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") -set(CPACK_PACKAGE_VERSION_MAJOR "2") -set(CPACK_PACKAGE_VERSION_MINOR "2") -set(CPACK_PACKAGE_VERSION_PATCH "0") -set(CPACK_PACKAGE_CONTACT "GNUstep Developer ") -set(CPACK_PACKAGE_INSTALL_DIRECTORY "CMake ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}") -if (UNIX) - set(CPACK_STRIP_FILES true CACHE BOOL "Strip libraries when packaging") -endif () -include (CPack) - -# CMake Configuration File - -install(EXPORT libobjcTargets - FILE libobjcTargets.cmake - DESTINATION ${LIB_INSTALL_PATH}/cmake/libobjc) -include(CMakePackageConfigHelpers) -configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/libobjcConfig.cmake" - INSTALL_DESTINATION "${LIB_INSTALL_PATH}/cmake/libobjc" - NO_SET_AND_CHECK_MACRO - NO_CHECK_REQUIRED_COMPONENTS_MACRO) -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/libobjcConfigVersion.cmake" - VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" - COMPATIBILITY AnyNewerVersion) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/libobjcConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/libobjcConfigVersion.cmake - DESTINATION ${LIB_INSTALL_PATH}/cmake/libobjc) - -# pkg-config descriptor - -set(PC_LIBS_PRIVATE ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES}) -if (M_LIBRARY) - list(APPEND PC_LIBS_PRIVATE ${M_LIBRARY}) -endif () -if (BLOCKS_RUNTIME_LIBRARY) - list(APPEND PC_LIBS_PRIVATE ${BLOCKS_RUNTIME_LIBRARY}) -endif () -list(REMOVE_DUPLICATES PC_LIBS_PRIVATE) -string(REPLACE ";" " -l" PC_LIBS_PRIVATE "${PC_LIBS_PRIVATE}") -set(PC_LIBS_PRIVATE "Libs.private: -l${PC_LIBS_PRIVATE}") - -configure_file("libobjc.pc.in" "libobjc.pc" @ONLY) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libobjc.pc" - DESTINATION "${LIB_INSTALL_PATH}/pkgconfig" -) - - -# uninstall target -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) - -add_custom_target(uninstall - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) - -if (TESTS) - enable_testing() - add_subdirectory(Test) -endif (TESTS) - -CHECK_CXX_SOURCE_COMPILES(" - #include - extern \"C\" { - __attribute__((weak)) - void *__cxa_allocate_exception(size_t thrown_size) noexcept; - } - #include - int main() { return 0; }" CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES) - -add_compile_definitions($,CXA_ALLOCATE_EXCEPTION_SPECIFIER=noexcept,CXA_ALLOCATE_EXCEPTION_SPECIFIER>) diff --git a/Config.cmake.in b/Config.cmake.in deleted file mode 100644 index 8b40091..0000000 --- a/Config.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -@PACKAGE_INIT@ - -include ( "${CMAKE_CURRENT_LIST_DIR}/libobjcTargets.cmake" ) diff --git a/NSBlocks.m b/NSBlocks.m deleted file mode 100644 index 49eee9d..0000000 --- a/NSBlocks.m +++ /dev/null @@ -1,79 +0,0 @@ -#include "objc/runtime.h" -#include "class.h" -#include "loader.h" -#include "lock.h" -#include "objc/blocks_runtime.h" -#include "dtable.h" -#include - -#ifdef EMBEDDED_BLOCKS_RUNTIME -#define BLOCK_STORAGE OBJC_PUBLIC -#else -#define BLOCK_STORAGE extern -#endif - -BLOCK_STORAGE struct objc_class _NSConcreteGlobalBlock; -BLOCK_STORAGE struct objc_class _NSConcreteStackBlock; -BLOCK_STORAGE struct objc_class _NSConcreteMallocBlock; -BLOCK_STORAGE struct objc_class _NSConcreteAutoBlock; -BLOCK_STORAGE struct objc_class _NSConcreteFinalizingBlock; - -static struct objc_class _NSConcreteGlobalBlockMeta; -static struct objc_class _NSConcreteStackBlockMeta; -static struct objc_class _NSConcreteMallocBlockMeta; -static struct objc_class _NSConcreteAutoBlockMeta; -static struct objc_class _NSConcreteFinalizingBlockMeta; - -static struct objc_class _NSBlock; -static struct objc_class _NSBlockMeta; - -static void createNSBlockSubclass(Class superclass, Class newClass, - Class metaClass, char *name) -{ - // Initialize the metaclass - //metaClass->class_pointer = superclass->class_pointer; - //metaClass->super_class = superclass->class_pointer; - metaClass->info = objc_class_flag_meta; - metaClass->dtable = uninstalled_dtable; - - // Set up the new class - newClass->isa = metaClass; - newClass->super_class = superclass; - newClass->name = name; - newClass->dtable = uninstalled_dtable; - newClass->info = objc_class_flag_is_block; - - LOCK_RUNTIME_FOR_SCOPE(); - objc_load_class(newClass); - -} - -#define NEW_CLASS(super, sub) \ - createNSBlockSubclass(super, &sub, &sub ## Meta, #sub) - -OBJC_PUBLIC -BOOL objc_create_block_classes_as_subclasses_of(Class super) -{ - if (_NSBlock.super_class != NULL) { return NO; } - - NEW_CLASS(super, _NSBlock); - NEW_CLASS(&_NSBlock, _NSConcreteStackBlock); - NEW_CLASS(&_NSBlock, _NSConcreteGlobalBlock); - NEW_CLASS(&_NSBlock, _NSConcreteMallocBlock); - NEW_CLASS(&_NSBlock, _NSConcreteAutoBlock); - NEW_CLASS(&_NSBlock, _NSConcreteFinalizingBlock); - // Global blocks never need refcount manipulation. - objc_set_class_flag(&_NSConcreteGlobalBlock, - objc_class_flag_permanent_instances); - return YES; -} - -PRIVATE void init_early_blocks(void) -{ - if (_NSBlock.super_class != NULL) { return; } - _NSConcreteStackBlock.info = objc_class_flag_is_block; - _NSConcreteGlobalBlock.info = objc_class_flag_is_block | objc_class_flag_permanent_instances; - _NSConcreteMallocBlock.info = objc_class_flag_is_block; - _NSConcreteAutoBlock.info = objc_class_flag_is_block; - _NSConcreteFinalizingBlock.info = objc_class_flag_is_block; -} \ No newline at end of file diff --git a/Protocol2.m b/Protocol2.m deleted file mode 100644 index 154403b..0000000 --- a/Protocol2.m +++ /dev/null @@ -1,45 +0,0 @@ -#include "objc/runtime.h" -#include "protocol.h" -#include "class.h" -#include -#include - -@implementation Protocol -// FIXME: This needs removing, but it's included for now because GNUstep's -// implementation of +[NSObject conformsToProtocol:] calls it. -- (BOOL)conformsTo: (Protocol*)p -{ - return protocol_conformsToProtocol(self, p); -} -- (id)retain -{ - return self; -} -- (void)release {} -+ (Class)class { return self; } -- (id)self { return self; } -@end -@interface __IncompleteProtocol : Protocol @end -@implementation __IncompleteProtocol @end - -/** - * This class exists for the sole reason that the legacy GNU ABI did not - * provide a way of registering protocols with the runtime. With the new ABI, - * every protocol in a compilation unit that is not referenced should be added - * in a category on this class. This ensures that the runtime sees every - * protocol at least once and can perform uniquing. - */ -@interface __ObjC_Protocol_Holder_Ugly_Hack { id isa; } @end -@implementation __ObjC_Protocol_Holder_Ugly_Hack @end - -@implementation Object @end - -@implementation ProtocolGCC @end -@implementation ProtocolGSv1 @end - -PRIVATE void link_protocol_classes(void) -{ - [Protocol class]; - [ProtocolGCC class]; - [ProtocolGSv1 class]; -} diff --git a/README.windows b/README.windows deleted file mode 100644 index 6341fca..0000000 --- a/README.windows +++ /dev/null @@ -1,11 +0,0 @@ -Building on Windows -=================== - -The runtime can build on Windows with Ninja: - -``` -> mkdir build -> cd build -> cmake .. -G Ninja -DCMAKE_C_COMPILER=path/to/clang.exe -DCMAKE_CXX_COMPILER=path/to/clang.exe -> ninja -``` diff --git a/Test/ARCTest_arc.m b/Test/ARCTest_arc.m deleted file mode 100644 index 4fd2a65..0000000 --- a/Test/ARCTest_arc.m +++ /dev/null @@ -1,135 +0,0 @@ -#import "Test.h" - -// Checks using non-portable APIs -#ifdef GNUSTEP_RUNTIME -void check_retain_count(id obj, size_t rc) -{ - assert(object_getRetainCount_np(obj) == rc); -} -void check_autorelease_count(id obj, size_t rc) -{ - assert(objc_arc_autorelease_count_for_object_np(obj) == rc); -} -#else -void check_retain_count(id obj, size_t rc) -{ -} -void check_autorelease_count(id obj, size_t rc) -{ -} -#endif - -id __weak var; - -@interface ARC : Test @end -@implementation ARC -- (id __autoreleasing)loadWeakAutoreleasing -{ - return var; -} -- (id)loadWeak -{ - return var; -} -- (void)setWeakFromWeak: (id __weak)anObject -{ - var = anObject; - anObject = nil; -} -- (void)setWeak: (id)anObject -{ - var = anObject; -} -@end - -@interface CheckDealloc : Test -@end -@implementation CheckDealloc -{ - BOOL *flag; -} -- (id)initWithFlag: (BOOL*)aFlag -{ - flag = aFlag; - *flag = NO; - return self; -} -- (void)dealloc -{ - *flag = YES; -} -@end - -static __weak id weakRef; - -@interface CheckDeallocWeakRef : Test -@end -@implementation CheckDeallocWeakRef -- (void)dealloc -{ - weakRef = self; -} -@end - - -int main(void) -{ - ARC *obj = [ARC new]; - BOOL f1; - BOOL f2; - // Check that storing weak references works. - { - id o1 = [[CheckDealloc new] initWithFlag: &f1]; - id o2 = [[CheckDealloc new] initWithFlag: &f2]; - [obj setWeak: o1]; - assert([obj loadWeak] == o1); - [obj setWeakFromWeak: o2]; - assert([obj loadWeak] == o2); - @autoreleasepool - { - id __autoreleasing o2a = [obj loadWeakAutoreleasing]; - assert(o2a == o2); - } - } - assert(f1); - assert(f2); - assert([obj loadWeak] == nil); - @autoreleasepool - { - id __autoreleasing o1a; - { - id o1 = [[CheckDealloc new] initWithFlag: &f1]; - assert(!f1); - [obj setWeak: o1]; - assert([obj loadWeak] == o1); - o1a = [obj loadWeakAutoreleasing]; - assert(o1a == o1); - check_autorelease_count(o1a, 1); - check_retain_count(o1a, 1); - } - assert(o1a == [obj loadWeak]); - } - assert(f1); - assert([obj loadWeak] == nil); - // Try to trigger an objc_moveWeak call - { - id o1 = [Test new]; - { - id __weak x = o1; - var = x; - } - } - assert([obj loadWeak] == nil); - // Now check what happens with a constant string in a weak variable. - { - id x = @"foo"; - [obj setWeak: x]; - } - assert([obj loadWeak] != nil); - // Check setting weak references during dealloc - { - [CheckDeallocWeakRef new]; - } - assert(weakRef == nil); - return 0; -} diff --git a/Test/AllocatePair.m b/Test/AllocatePair.m deleted file mode 100644 index cb39cba..0000000 --- a/Test/AllocatePair.m +++ /dev/null @@ -1,51 +0,0 @@ -#include "Test.h" -#include - -// Regression test for a bug where allocating a class as a subclass of an -// unresolved class failed. - -static int loaded; - -static void load(Class self, SEL _cmd) -{ - loaded++; -} - -int main() -{ - Class a, b, c, d, e; - - assert(class_getInstanceSize(objc_allocateClassPair(Nil, "Empty", 0)) == sizeof(Class)); - a = objc_allocateClassPair([Test class], "A", 0); - objc_registerClassPair(a); - - b = objc_allocateClassPair(a, "B", 0); - class_addMethod(object_getClass(b), @selector(load), (IMP)load, "@:"); - - class_addIvar(b, "anIvar", 4, 2, "i"); - objc_registerClassPair(b); - - Ivar iv = class_getInstanceVariable(b, "anIvar"); - size_t superSize = class_getInstanceSize([Test class]); - assert(ivar_getOffset(iv) == superSize); - - class_getSuperclass(b); - - - c = objc_allocateClassPair(b, "C", 0); - objc_registerClassPair(c); - d = objc_allocateClassPair(c, "D", 0); - objc_registerClassPair(d); - e = objc_allocateClassPair(d, "E", 0); - objc_registerClassPair(e); - assert(loaded == 0); - assert(objc_getClass("C") == c); - assert(objc_getClass("D") == d); - assert(objc_getClass("E") == e); - objc_disposeClassPair(e); - assert(objc_getClass("E") == nil); - - return 0; -} - - diff --git a/Test/AssociatedObject.m b/Test/AssociatedObject.m deleted file mode 100644 index d22044b..0000000 --- a/Test/AssociatedObject.m +++ /dev/null @@ -1,61 +0,0 @@ -#include "Test.h" -#include -#include - -BOOL deallocCalled = NO; -static const char* objc_setAssociatedObjectKey = "objc_setAssociatedObjectKey"; - -@interface Associated : Test -@end - -@implementation Associated --(void) dealloc -{ - deallocCalled = YES; - [super dealloc]; -} -@end - -int main(void) -{ - @autoreleasepool { - Associated *object = [Associated new]; - Test *holder = [[Test new] autorelease]; - objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN); - [object release]; - assert(!deallocCalled); - } - // dealloc should be called when holder is released during pool drain - assert(deallocCalled); - - deallocCalled = NO; - - Associated *object = [Associated new]; - Test *holder = [Test new]; - objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN); - [object release]; // commuted into associated object storage - objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, nil, OBJC_ASSOCIATION_ASSIGN); - [holder release]; - - assert(deallocCalled); - - object = [Associated new]; - holder = [Test new]; - for (uintptr_t i = 1; i <= 20; ++i) - { - objc_setAssociatedObject(holder, (void*)i, object, OBJC_ASSOCIATION_RETAIN); - } - int lost = 0; - for (uintptr_t i = 1; i <= 20; ++i) - { - if (object != objc_getAssociatedObject(holder, (const void*)i)) - { - fprintf(stderr, "lost object %" PRIuPTR "\n", i); - ++lost; - } - } - [holder release]; - [object release]; - assert(0 == lost); - return 0; -} diff --git a/Test/AssociatedObject2.m b/Test/AssociatedObject2.m deleted file mode 100644 index 0d958c0..0000000 --- a/Test/AssociatedObject2.m +++ /dev/null @@ -1,37 +0,0 @@ -#include "Test.h" - -@interface MLTestClass : Test { -@public -} -- (void)someF; -@end - -@implementation MLTestClass -- (void)someF -{ -} - -@end - -static void ff(id obj, SEL _cmd) -{ -} - - -int main() -{ - static char static_char; - MLTestClass * tc; - tc = [MLTestClass new]; - objc_setAssociatedObject(tc, &static_char, (id)1223, OBJC_ASSOCIATION_ASSIGN); - [tc release]; - tc = [MLTestClass new]; - objc_setAssociatedObject(tc, &static_char, (id)1223, OBJC_ASSOCIATION_ASSIGN); - SEL some_sel = sel_registerName(".some_sel"); - const char *types = "v@:"; - class_addMethod(object_getClass(tc), some_sel, - (IMP)ff, types); - int j = (int)objc_getAssociatedObject(tc, &static_char); - assert(j == 1223); - [tc release]; -} diff --git a/Test/BlockImpTest.m b/Test/BlockImpTest.m deleted file mode 100644 index aa07499..0000000 --- a/Test/BlockImpTest.m +++ /dev/null @@ -1,66 +0,0 @@ -#include "../objc/runtime.h" -#include "../objc/blocks_runtime.h" -#include -#include -#include - -struct big -{ - int a, b, c, d, e; -}; - -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface Foo @end -@implementation Foo @end -@interface Foo (Dynamic) -+(int)count: (int)i; -+(struct big)sret; -@end - - -int main(void) -{ - __block Class cls = objc_getClass("Foo"); - __block int b = 0; - void* blk = ^(id self, int a) { - assert(self == cls); - b += a; - return b; }; - blk = Block_copy(blk); - IMP imp = imp_implementationWithBlock(blk); - char *type = block_copyIMPTypeEncoding_np(blk); - assert(NULL != type); - class_addMethod((objc_getMetaClass("Foo")), @selector(count:), imp, type); - assert(2 == ((int(*)(id,SEL,int))imp)(cls, @selector(count:), 2)); - free(type); - assert(4 == [Foo count: 2]); - assert(6 == [Foo count: 2]); - assert(imp_getBlock(imp) == (blk)); - IMP imp2 = imp_implementationWithBlock(blk); - assert(imp != imp2); - imp_removeBlock(imp); - assert(imp_getBlock(imp) != (blk)); - - blk = ^(id self) { - assert(self == cls); - struct big b = {1, 2, 3, 4, 5}; - return b; - }; - imp = imp_implementationWithBlock(blk); - assert(imp && "Can't make sret IMP"); - type = block_copyIMPTypeEncoding_np(blk); - assert(NULL != type); - class_addMethod((objc_getMetaClass("Foo")), @selector(sret), imp, type); - free(type); - struct big s = [Foo sret]; - assert(s.a == 1); - assert(s.b == 2); - assert(s.c == 3); - assert(s.d == 4); - assert(s.e == 5); - return 0; -} diff --git a/Test/BlockTest_arc.m b/Test/BlockTest_arc.m deleted file mode 100644 index 4e338d2..0000000 --- a/Test/BlockTest_arc.m +++ /dev/null @@ -1,17 +0,0 @@ -#include - -int foo() { - __block id x; - void (^hello)(void) = ^(void) { - printf("hello is running, %p\n", x); - }; - printf("1\n"); - hello(); - printf("2\n"); - hello = 0; // Here ARC is releasing the block, that's why we don't see '3' printed. - printf("3\n"); - return 0; -} -int main() { - return foo(); -} diff --git a/Test/BoxedForeignException.m b/Test/BoxedForeignException.m deleted file mode 100644 index 7ef5863..0000000 --- a/Test/BoxedForeignException.m +++ /dev/null @@ -1,105 +0,0 @@ -#define _GNU_SOURCE -#include "../unwind.h" -#include "Test.h" -#include "../objc/hooks.h" -#include - - -struct foreign_exception -{ - struct _Unwind_Exception header; - int x; -}; - -BOOL finally_called = NO; - - -int throw(void) -{ - struct foreign_exception *foreign_exception = calloc(sizeof(struct foreign_exception), 1); - foreign_exception->header.exception_class = 42; - foreign_exception->x = 12; - _Unwind_RaiseException(&foreign_exception->header); - assert(0); -} - -void finally(void) -{ - @try - { - throw(); - } - @finally - { - finally_called = YES; - } - finally_called = NO; -} -@interface BoxedException : Test -{ - struct foreign_exception *exception; -} -- (int)value; -@end -@implementation BoxedException -+ (id) exceptionWithForeignException: (struct _Unwind_Exception*)ex -{ - BoxedException *b = [BoxedException new]; - b->exception = (struct foreign_exception*)ex; - return b; -} -- (void)dealloc -{ - free(exception); - [super dealloc]; -} -- (int)value -{ - if (exception) - { - return exception->x; - } - return -1; -} -- (void)rethrow -{ - struct _Unwind_Exception *ex = &exception->header; - exception = 0; - [self dealloc]; - _Unwind_Resume_or_Rethrow(ex); - abort(); -} -@end - - -Class boxer(int64_t class) -{ - assert(class == 42); - return [BoxedException class]; -} - -int main(void) -{ - _objc_class_for_boxing_foreign_exception = boxer; - BOOL catchall = NO; - BOOL catchboxed = NO; - @try - { - finally(); - } - @catch (BoxedException *x) - { - assert(x != nil); - assert([x value] == 12); - [x dealloc]; - catchboxed = YES; - } - @catch(...) - { - catchall = YES; - } - assert(finally_called == YES); - assert(catchall == NO); - assert(catchboxed == YES); - return 0; -} diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt deleted file mode 100644 index f2161e2..0000000 --- a/Test/CMakeLists.txt +++ /dev/null @@ -1,188 +0,0 @@ - -# Clear the LD_LIBRARY_PATH if GNUstep set it so that we don't accidentally use -# the installed version - - -set(INCREMENTAL " ") -if (MSVC) - set(CMAKE_EXE_LINKER_FLAGS "/DEBUG /INCREMENTAL:NO ${CMAKE_EXE_LINKER_FLAGS}") - set(INCREMENTAL "/INCREMENTAL:NO") -endif () - -# List of single-file tests. -set(TESTS - alias.m - alignTest.m - AllocatePair.m - AssociatedObject.m - AssociatedObject2.m - BlockImpTest.m - BlockTest_arc.m - ConstantString.m - Category.m - ExceptionTest.m - FastARC.m - FastARCPool.m - FastRefCount.m - Forward.m - ManyManySelectors.m - NestedExceptions.m - PropertyAttributeTest.m - ProtocolExtendedProperties.m - PropertyIntrospectionTest.m - ProtocolCreation.m - ResurrectInDealloc_arc.m - RuntimeTest.m - SuperMethodMissing.m - WeakBlock_arc.m - WeakRefLoad.m - WeakReferences_arc.m - WeakImportClass.m - ivar_arc.m - ivar_atomic.m - IVarOverlap.m - IVarSuperclassOverlap.m - objc_msgSend.m - msgInterpose.m - NilException.m - MethodArguments.m - zeroSizedIVar.m - exchange.m - hash_table_delete.c - hash_test.c - setSuperclass.m - UnexpectedException.m -) - -set(ENABLE_ALL_OBJC_ARC_TESTS On) - -if (WIN32) - # On Windows these tests work only with more recent versions of Clang - set(ENABLE_ALL_OBJC_ARC_TESTS Off) - if (CMAKE_C_COMPILER_ID STREQUAL Clang) - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0.0) - set(ENABLE_ALL_OBJC_ARC_TESTS On) - endif() - endif() - - if (CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) - list(APPEND TESTS - objc_msgSend_WoA64.mm - ) - endif() -else () - # Don't run the tests that are specific to Itanium-style exceptions on - # Windows. - list(APPEND TESTS - BoxedForeignException.m - ForeignException.m - ) -endif () - -if (ENABLE_ALL_OBJC_ARC_TESTS) - list(APPEND TESTS - ARCTest_arc.m - PropertyIntrospectionTest2_arc.m - ) -endif() - -# List of single-file tests that won't work with the legacy ABI and so -# shouldn't be run in legacy mode. -set(NEW_TESTS - category_properties.m - DirectMethods.m - FastPathAlloc.m -) - -remove_definitions(-D__OBJC_RUNTIME_INTERNAL__=1) - -add_library(test_runtime_legacy OBJECT Test.m) -set_target_properties(test_runtime_legacy PROPERTIES - INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}" - COMPILE_FLAGS "-Xclang -fblocks -fobjc-runtime=gnustep-1.7" - LINKER_LANGUAGE C -) - -add_library(test_runtime OBJECT Test.m) -set_target_properties(test_runtime PROPERTIES - INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}" - COMPILE_FLAGS "-Xclang -fblocks -fobjc-runtime=gnustep-2.0" - LINKER_LANGUAGE C -) - - -# Function for adding a test. This takes the name of the test and the list of -# source files as arguments. -function(addtest_flags TEST_NAME FLAGS TEST_SOURCE) - if (${TEST_NAME} MATCHES ".*_arc") - # Only compile the main file with ARC - set_source_files_properties(${TEST_SOURCE} - COMPILE_FLAGS "-Xclang -fobjc-arc") - endif() - add_executable(${TEST_NAME} ${TEST_SOURCE}) - add_test(${TEST_NAME} ${TEST_NAME}) - set(ARC "") - set_target_properties(${TEST_NAME} PROPERTIES - INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}" - COMPILE_FLAGS "-Xclang -fblocks -Xclang -fobjc-exceptions ${FLAGS}" - LINK_FLAGS ${INCREMENTAL} - LINKER_LANGUAGE C - ) - set_property(TEST ${TEST_NAME} PROPERTY SKIP_RETURN_CODE 77) - set_property(TEST ${TEST_NAME} PROPERTY - ENVIRONMENT "LD_LIBRARY_PATH=" "LLVM_PROFILE_FILE=${TEST_NAME}.profraw" - ) - if(WIN32) - set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_append:${CMAKE_BINARY_DIR}") - endif() - target_link_libraries(${TEST_NAME} objc) -endfunction(addtest_flags) - -function(addtest_variants TEST TEST_SOURCE LEGACY) - addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.2 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}") - target_sources(${TEST} PRIVATE $) - addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.2 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}") - target_sources("${TEST}_optimised" PRIVATE $) - - # -fobjc-arc is not supported on platforms using the legacy runtime - if (${LEGACY} AND ${OLDABI_COMPAT} AND NOT ${TEST} MATCHES ".*_arc") - addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}") - target_sources("${TEST}_legacy" PRIVATE $) - addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}") - target_sources("${TEST}_legacy_optimised" PRIVATE $) - endif() -endfunction(addtest_variants) - -foreach(TEST_SOURCE ${TESTS}) - get_filename_component(TEST ${TEST_SOURCE} NAME_WE) - addtest_variants(${TEST} ${TEST_SOURCE} true) -endforeach() - -foreach(TEST_SOURCE ${NEW_TESTS}) - get_filename_component(TEST ${TEST_SOURCE} NAME_WE) - addtest_variants(${TEST} ${TEST_SOURCE} false) -endforeach() - -# Tests that are more than a single file. -addtest_variants("CXXExceptions" "CXXException.m;CXXException.cc" true) -addtest_variants("ForwardDeclareProtocolAccess" "ForwardDeclareProtocolAccess.m;ForwardDeclareProtocol.m" true) -if (ENABLE_OBJCXX) - addtest_variants(ObjCXXEHInterop "ObjCXXEHInterop.mm;ObjCXXEHInterop.m" true) - addtest_variants(ObjCXXEHInteropTwice "ObjCXXEHInteropTwice.mm" true) - if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0.0) - addtest_variants(ObjCXXEHInterop_arc "ObjCXXEHInterop_arc.mm;ObjCXXEHInterop_arc.m" true) - endif() - else() - addtest_variants(ObjCXXEHInterop_arc "ObjCXXEHInterop_arc.mm;ObjCXXEHInterop_arc.m" true) - endif() -endif() - -# Some tests use enough memory that they fail on CI intermittently if they -# happen to run in parallel with each other. -set_tests_properties(ManyManySelectors PROPERTIES PROCESSORS 3) -set_tests_properties(ManyManySelectors_optimised PROPERTIES PROCESSORS 3) -if (${LEGACY}) - set_tests_properties(ManyManySelectors_legacy PROPERTIES PROCESSORS 3) - set_tests_properties(ManyManySelectors_legacy_optimised PROPERTIES PROCESSORS 3) -endif () diff --git a/Test/CXXException.cc b/Test/CXXException.cc deleted file mode 100644 index 3c2347d..0000000 --- a/Test/CXXException.cc +++ /dev/null @@ -1,21 +0,0 @@ - -extern "C" void throw_int() -{ - throw 12; -} - -extern "C" void throw_id(); - - -extern "C" int catchall() -{ - try - { - throw_id(); - } - catch(...) - { - throw; - } - __builtin_trap(); -} diff --git a/Test/CXXException.m b/Test/CXXException.m deleted file mode 100644 index b0423e5..0000000 --- a/Test/CXXException.m +++ /dev/null @@ -1,70 +0,0 @@ -#include "Test.h" -#include "../unwind.h" - -#if __cplusplus -#error This is not an ObjC++ test! -#endif - -struct -{ - struct _Unwind_Exception header; - id x; -} foreign_exception; - -BOOL finally_called = NO; - -id e1; -void throw_id(void) -{ - @throw e1; -} - -void throw_int(void); -int catchall(void); - - -void finally(void) -{ - @try - { - throw_int(); - } - @finally - { - finally_called = YES; - } - finally_called = NO; -} - - -int main(void) -{ - BOOL catchall_entered = NO; - BOOL catchid = YES; - e1 = [Test new]; - @try - { - finally(); - } - @catch (id x) - { - assert(0); - } - @catch(...) - { - catchall_entered = YES; - } - assert(finally_called == YES); - assert(catchall_entered == YES); - @try - { - catchall(); - } - @catch (id x) - { - assert(x == e1); - } - assert(catchid == YES); - [e1 dealloc]; - return 0; -} diff --git a/Test/Category.m b/Test/Category.m deleted file mode 100644 index 44df27e..0000000 --- a/Test/Category.m +++ /dev/null @@ -1,26 +0,0 @@ -#include "Test.h" -#include "../objc/runtime.h" - -#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" -@interface Foo : Test -+ (int)replaced; -@end -@implementation Foo -+ (int)replaced -{ - return 1; -} -@end - -@implementation Foo (bar) -+ (int)replaced -{ - return 2; -} -@end - -int main (void) -{ - assert([Foo replaced] == 2); - return 0; -} diff --git a/Test/ConstantString.m b/Test/ConstantString.m deleted file mode 100644 index 034ba70..0000000 --- a/Test/ConstantString.m +++ /dev/null @@ -1,21 +0,0 @@ -#import "Test.h" -#include - -@implementation NSConstantString (Test) -- (unsigned int)length -{ - return length; -} -- (const char*)cString -{ - return str; -} -@end - - -int main(void) -{ - assert([@"1234567890" length] == 10); - assert(strcmp([@"123456789" cString], "123456789") == 0); - return 0; -} diff --git a/Test/DirectMethods.m b/Test/DirectMethods.m deleted file mode 100644 index 129959a..0000000 --- a/Test/DirectMethods.m +++ /dev/null @@ -1,45 +0,0 @@ -#include "Test.h" - -#if !__has_attribute(objc_direct) -int main() -{ - return 77; -} -#else - -static BOOL initializeCalled; -static BOOL directMethodCalled; - -@interface HasDirect : Test -+ (void)clsDirect __attribute__((objc_direct)); -- (int)instanceDirect __attribute__((objc_direct)); -@end -@implementation HasDirect -+ (void)initialize -{ - initializeCalled = YES; -} -+ (void)clsDirect -{ - directMethodCalled = YES; -} -- (int)instanceDirect -{ - return 42; -} -@end - -int main(void) -{ - [HasDirect clsDirect]; - assert(directMethodCalled); - assert(initializeCalled); - HasDirect *obj = [HasDirect new]; - assert([obj instanceDirect] == 42); - obj = nil; - assert([obj instanceDirect] == 0); - return 0; -} - - -#endif diff --git a/Test/ExceptionTest.m b/Test/ExceptionTest.m deleted file mode 100644 index 5a7295c..0000000 --- a/Test/ExceptionTest.m +++ /dev/null @@ -1,101 +0,0 @@ -#include "Test.h" - -#if __cplusplus -#error This is not an ObjC++ test! -#endif - -BOOL finallyEntered = NO; -BOOL cleanupRun = NO; -BOOL idRethrown = NO; -BOOL catchallRethrown = NO; -BOOL testCaught = NO; -BOOL wrongMatch = NO; - -@interface NSString : Test @end -void runCleanup(void *x) -{ - assert(cleanupRun == NO); - cleanupRun = YES; -} - -int throw(void) -{ - @throw [Test new]; -} - -int finally(void) -{ - __attribute__((cleanup(runCleanup))) - int x; - (void)x; - @try { throw(); } - @finally { finallyEntered = YES; } - return 0; -} -int rethrow_id(void) -{ - @try { finally(); } - @catch(id x) - { - assert(object_getClass(x) == [Test class]); - idRethrown = YES; - @throw; - } - return 0; -} -int rethrow_test(void) -{ - @try { rethrow_id(); } - @catch (Test *t) - { - testCaught = YES; - @throw; - } - @catch (id x) - { - assert(0 && "should not be reached!"); - } - @catch (...) - { - assert(0 && "should not be reached!"); - } -} -int rethrow_catchall(void) -{ - @try { rethrow_test(); } - @catch(...) - { - assert(testCaught); - catchallRethrown = YES; - @throw; - } - return 0; -} -int not_matched_catch(void) -{ - @try { rethrow_catchall(); } - @catch(NSString *s) - { - wrongMatch = YES; - } - return 0; -} - -int main(void) -{ - @try - { - rethrow_catchall(); - } - @catch (id x) - { - assert(finallyEntered == YES); - assert(cleanupRun == YES); - assert(idRethrown == YES); - assert(catchallRethrown == YES); - assert(wrongMatch == NO); - assert(object_getClass(x) == [Test class]); - [x dealloc]; - } - return 0; -} diff --git a/Test/FastARC.m b/Test/FastARC.m deleted file mode 100644 index d7c6d6a..0000000 --- a/Test/FastARC.m +++ /dev/null @@ -1,104 +0,0 @@ -#include "Test.h" -#include - -static BOOL called; - -@interface AllUnsafe : Test @end -@implementation AllUnsafe -- (id)retain -{ - return self; -} -- (void)release {} -- (id)autorelease -{ - return self; -} -@end - -@interface Retain : AllUnsafe @end -@implementation Retain -- (id)retain -{ - called = YES; - return self; -} -@end - -@interface RetainSafe : AllUnsafe @end -@implementation RetainSafe -- (id)retain -{ - return self; -} -- (void)_ARCCompliantRetainRelease {} -@end - -@interface Release : AllUnsafe @end -@implementation Release -- (void)release -{ - called = YES; -} -@end - -@interface ReleaseSafe : AllUnsafe @end -@implementation ReleaseSafe -- (void)release -{ -} -- (void)_ARCCompliantRetainRelease {} -@end - -@interface Autorelease : AllUnsafe @end -@implementation Autorelease -- (id)autorelease -{ - called = YES; - return self; -} -@end - -@interface AutoreleaseSafe : AllUnsafe @end -@implementation AutoreleaseSafe -- (id)autorelease -{ - return self; -} -- (void)_ARCCompliantRetainRelease {} -@end - -void check(id obj, BOOL expected) -{ - fprintf(stderr, "Checking %s\n", class_getName(object_getClass(obj))); -} - -int main() -{ - called = NO; - objc_retain([Retain new]); - assert(called == YES); - - called = NO; - objc_retain([RetainSafe new]); - assert(called == NO); - - called = NO; - objc_release([Release new]); - assert(called == YES); - - called = NO; - objc_release([ReleaseSafe new]); - assert(called == NO); - - called = NO; - objc_autorelease([Autorelease new]); - assert(called == YES); - - called = NO; - objc_autorelease([AutoreleaseSafe new]); - assert(called == NO); - - return 0; -} - diff --git a/Test/FastARCPool.m b/Test/FastARCPool.m deleted file mode 100644 index 0debe28..0000000 --- a/Test/FastARCPool.m +++ /dev/null @@ -1,41 +0,0 @@ -#include "Test.h" - -#define POOL_SIZE (4096 / sizeof(void*)) - -static BOOL called; - -@interface Canary : Test -@end -@implementation Canary -- (void)dealloc -{ - called = YES; - [super dealloc]; -} -@end - -@interface Creator : Test -@end -@implementation Creator -- (void)dealloc -{ - // Add a new page of autorelease references to see if we can still release - // the reference on the canary object. - for (int i = 0; i < POOL_SIZE; i++) - [[Test new] autorelease]; - [super dealloc]; -} -@end - -int main() -{ - called = NO; - @autoreleasepool - { - [[Canary new] autorelease]; - [[Creator new] autorelease]; - } - assert(called == YES); - - return 0; -} diff --git a/Test/FastPathAlloc.m b/Test/FastPathAlloc.m deleted file mode 100644 index f09aedd..0000000 --- a/Test/FastPathAlloc.m +++ /dev/null @@ -1,129 +0,0 @@ -#if __clang_major__ < 18 -// Skip this test if clang is too old to support it. -int main(void) -{ - return 77; -} -#else -#include "Test.h" -#include - -static BOOL called; - -typedef struct _NSZone NSZone; - -@interface ShouldAlloc : Test @end -@interface ShouldAllocWithZone : Test @end -@interface ShouldInit : Test @end -@interface ShouldInit2 : Test @end - -@interface NoAlloc : Test @end -@interface NoInit : Test @end -@interface NoInit2 : NoInit @end - -@implementation ShouldAlloc -+ (instancetype)alloc -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return [super alloc]; -} -@end -@implementation ShouldAllocWithZone -+ (instancetype)allocWithZone: (NSZone*)aZone -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return [super alloc]; -} -@end -@implementation ShouldInit -- (instancetype)init -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return self; -} -@end -@implementation ShouldInit2 -+ (instancetype)alloc -{ - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return [super alloc]; -} -- (instancetype)init -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return self; -} -@end - -@implementation NoAlloc -+ (void)_TrivialAllocInit{} -+ (instancetype)alloc -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return [super alloc]; -} -+ (instancetype)allocWithZone: (NSZone*)aZone -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return [super alloc]; -} -@end -@implementation NoInit -+ (void)_TrivialAllocInit{} -- (instancetype)init -{ - called = YES; - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return self; -} -@end -@implementation NoInit2 -+ (instancetype)alloc -{ - fprintf(stderr, "[%s %s] called\n", class_getName(object_getClass(self)), sel_getName(_cmd)); - return [super alloc]; -} -@end - -int main(void) -{ - called = NO; - [ShouldAlloc alloc]; - assert(called); - - [ShouldAllocWithZone allocWithZone: NULL]; - assert(called); - called = NO; - - called = NO; - [[ShouldInit alloc] init]; - assert(called); - - called = NO; - [[ShouldInit2 alloc] init]; - assert(called); - - called = NO; - [NoAlloc alloc]; - assert(!called); - - [NoAlloc allocWithZone: NULL]; - assert(!called); - called = NO; - - called = NO; - [[NoInit alloc] init]; - assert(!called); - - called = NO; - [[NoInit2 alloc] init]; - assert(!called); -} - -#endif diff --git a/Test/FastRefCount.m b/Test/FastRefCount.m deleted file mode 100644 index 0154a10..0000000 --- a/Test/FastRefCount.m +++ /dev/null @@ -1,167 +0,0 @@ -#include "Test.h" - -void direct_saturation_test(); - -@interface TestWithDelloc : Test -@end - -@implementation TestWithDelloc -- (void)dealloc -{ - id obj = nil; - objc_storeStrong(&obj, self); - assert(obj == self); - assert(object_getRetainCount_np(obj) == 0); - [super dealloc]; -} -@end - -int main() -{ - id obj = [Test new]; - assert(object_getRetainCount_np(obj) == 1); - - for (int i = 0; i < 2; i++) - { - size_t count = object_getRetainCount_np(obj); - id ret = objc_retain_fast_np(obj); - assert(ret == obj); - assert(object_getRetainCount_np(obj) == ++count); - } - - for (int i = 0; i < 2; i++) - { - size_t count = object_getRetainCount_np(obj); - BOOL destroy = objc_release_fast_no_destroy_np(obj); - assert(destroy == NO); - assert(object_getRetainCount_np(obj) == --count); - } - - { - // Final release should prevent further retains and releases. - assert(objc_release_fast_no_destroy_np(obj) == YES); - assert(object_getRetainCount_np(obj) == 0); - assert(objc_retain_fast_np(obj) == obj); - assert(object_getRetainCount_np(obj) == 0); - assert(objc_release_fast_no_destroy_np(obj) == NO); - assert(object_getRetainCount_np(obj) == 0); - } - - object_dispose(obj); - obj = [Test new]; - - { - // Should not be able to delete weak refs until final release. - id weak; - assert(objc_initWeak(&weak, obj) == obj); - assert(weak != nil); - assert(objc_loadWeakRetained(&weak) == obj); - assert(objc_release_fast_no_destroy_np(obj) == NO); - // Assumes a return of NO means no effect on obj at all. - assert(objc_delete_weak_refs(obj) == NO); - assert(objc_loadWeakRetained(&weak) == obj); - assert(objc_release_fast_no_destroy_np(obj) == NO); - // This will also call objc_delete_weak_refs() and succeed. - assert(objc_release_fast_no_destroy_np(obj) == YES); - objc_destroyWeak(&weak); - // Check what happens when the weak refs were already deleted. - assert(objc_delete_weak_refs(obj) == YES); - } - - object_dispose(obj); - // Check we can use strong references inside a dealloc method. - obj = [TestWithDelloc new]; - [obj release]; - obj = nil; - - direct_saturation_test(); - return 0; -} - - -// ---------------- -// This test has knowledge of the implementation details of the ARC -// reference counting and may need modification if the details change. - -const long refcount_shift = 1; -const size_t weak_mask = ((size_t)1)<<((sizeof(size_t)*8)-refcount_shift); -const size_t refcount_mask = ~weak_mask; -const size_t refcount_max = refcount_mask - 1; - -size_t get_refcount(id obj) -{ - size_t *refCount = ((size_t*)obj) - 1; - return *refCount & refcount_mask; -} - -void set_refcount(id obj, size_t count) -{ - size_t *refCount = ((size_t*)obj) - 1; - *refCount = (*refCount & weak_mask) | (count & refcount_mask); -} - -void direct_saturation_test() -{ - { - id obj = [Test new]; - // sanity check - objc_retain_fast_np(obj); - assert(object_getRetainCount_np(obj) == 2); - assert(get_refcount(obj) == 1); - - // Check the behaviour close to the maximum refcount. - set_refcount(obj, refcount_max - 3); - assert(object_getRetainCount_np(obj) == refcount_max - 2); - - assert(objc_retain_fast_np(obj) == obj); - assert(object_getRetainCount_np(obj) == refcount_max - 1); - - id weak; - assert(objc_initWeak(&weak, obj) == obj); - assert(weak != nil); - assert(objc_loadWeakRetained(&weak) == obj); - assert(object_getRetainCount_np(obj) == refcount_max); - - // This retain should cause the count to saturate. - assert(objc_retain_fast_np(obj) == obj); - assert(object_getRetainCount_np(obj) == refcount_max + 1); - - // A saturated count is no longer affected by retains or releases. - assert(objc_release_fast_no_destroy_np(obj) == NO); - assert(object_getRetainCount_np(obj) == refcount_max + 1); - assert(objc_retain_fast_np(obj) == obj); - assert(object_getRetainCount_np(obj) == refcount_max + 1); - - // Nor should any weak refs be deleted. - assert(objc_delete_weak_refs(obj) == NO); - assert(objc_loadWeakRetained(&weak) == obj); - assert(object_getRetainCount_np(obj) == refcount_max + 1); - - // Cleanup (can skip this if it becomes an issue) - objc_destroyWeak(&weak); - set_refcount(obj, 0); - objc_release_fast_no_destroy_np(obj); - object_dispose(obj); - } - - { - id obj = [Test new]; - set_refcount(obj, refcount_max - 2); - assert(objc_retain_fast_np(obj) == obj); - assert(objc_retain_fast_np(obj) == obj); - assert(object_getRetainCount_np(obj) == refcount_max + 1); - - // Check we can init a weak ref to an object with a saturated count. - id weak; - assert(objc_initWeak(&weak, obj) == obj); - assert(weak != nil); - assert(objc_loadWeakRetained(&weak) == obj); - assert(object_getRetainCount_np(obj) == refcount_max + 1); - - // Cleanup (can skip this if it becomes an issue) - objc_destroyWeak(&weak); - set_refcount(obj, 0); - objc_release_fast_no_destroy_np(obj); - object_dispose(obj); - } -} diff --git a/Test/ForeignException.m b/Test/ForeignException.m deleted file mode 100644 index 73f50e8..0000000 --- a/Test/ForeignException.m +++ /dev/null @@ -1,65 +0,0 @@ -#include "Test.h" -#include "../unwind.h" - -#if __cplusplus -#error This is not an ObjC++ test! -#endif - -struct -{ - struct _Unwind_Exception header; - id x; -} foreign_exception; - -BOOL cleanup_called = NO; -BOOL finally_called = NO; - -static void cleanup(_Unwind_Reason_Code i,struct _Unwind_Exception *e) -{ - assert(e == &foreign_exception.header); - cleanup_called = YES; -} - -int throw(void) -{ - foreign_exception.header.exception_class = 42; - foreign_exception.header.exception_cleanup = cleanup; - foreign_exception.x = (id)12; - _Unwind_RaiseException(&foreign_exception.header); - assert(0); -} - -void finally(void) -{ - @try - { - throw(); - } - @finally - { - finally_called = YES; - } - finally_called = NO; -} - - -int main(void) -{ - BOOL catchall = NO; - @try - { - finally(); - } - @catch (id x) - { - assert(0); - } - @catch(...) - { - catchall = YES; - } - assert(finally_called == YES); - assert(catchall == YES); - assert(cleanup_called == YES); - return 0; -} diff --git a/Test/Forward.m b/Test/Forward.m deleted file mode 100644 index f998dae..0000000 --- a/Test/Forward.m +++ /dev/null @@ -1,71 +0,0 @@ -#include "Test.h" -#include "../objc/hooks.h" - -@interface Forward : Test -- (id)forwardingTargetForSelector: (SEL)sel; -@end - -id target; - -@implementation Forward -- (id)forwardingTargetForSelector: (SEL)sel -{ - return target; -} -@end - -@interface Forward2 : Test -@end - -@interface ForwardingTarget : Test -@end - -BOOL forwardingTargetCalled; - -@implementation ForwardingTarget -- (void)foo: (int)x -{ - assert(x == 42); - forwardingTargetCalled = YES; -} -@end -@implementation Forward2 -- (void)forward: (int)x -{ - [target foo: x]; -} -@end - -static id proxy_lookup(id receiver, SEL selector) -{ - if (class_respondsToSelector(object_getClass(receiver), @selector(forwardingTargetForSelector:))) - { - return [receiver forwardingTargetForSelector: selector]; - } - return nil; -} - -static IMP forward(id receiver, SEL selector) -{ - if (class_respondsToSelector(object_getClass(receiver), @selector(forward:))) - { - return class_getMethodImplementation(object_getClass(receiver), @selector(forward:)); - } - assert(0); -} - -int main(void) -{ - objc_proxy_lookup = proxy_lookup; - __objc_msg_forward2 = forward; - target = [ForwardingTarget new]; - id proxy = [Forward new]; - [proxy foo: 42]; - [proxy dealloc]; - assert(forwardingTargetCalled == YES); - forwardingTargetCalled = NO; - proxy = [Forward2 new]; - [proxy foo: 42]; - [proxy dealloc]; - assert(forwardingTargetCalled == YES); -} diff --git a/Test/ForwardDeclareProtocol.m b/Test/ForwardDeclareProtocol.m deleted file mode 100644 index 5ee02db..0000000 --- a/Test/ForwardDeclareProtocol.m +++ /dev/null @@ -1,16 +0,0 @@ -#pragma clang diagnostic ignored "-Wat-protocol" -@protocol P; - -Protocol *getProtocol(void) -{ -// Don't try to compile this on known-broken compilers. -#if !defined(__clang__) - return @protocol(P); - // Clang versions before 7 are broken, clang versions after 7 regard this - // as a hard error and will refuse to compile it. -#elif __clang_major__ == 7 - return @protocol(P); -#else - return 0; -#endif -} diff --git a/Test/ForwardDeclareProtocolAccess.m b/Test/ForwardDeclareProtocolAccess.m deleted file mode 100644 index e214647..0000000 --- a/Test/ForwardDeclareProtocolAccess.m +++ /dev/null @@ -1,28 +0,0 @@ -#include "Test.h" - -@protocol P -- (void)someMethod; -@end - -// Defined in another compilation unit. Returns @protocol(P). -Protocol *getProtocol(void); - -int main(void) -{ - Protocol *p1 = @protocol(P); - Protocol *p2 = getProtocol(); - if (p2 == NULL) - { - return 0; - } - assert(protocol_isEqual(p1, p2)); -#ifdef GS_RUNTIME_V2 - // With the new ABI, these should be precisely the same object. - assert(p1 == p2); - unsigned int count; - protocol_copyMethodDescriptionList(p2, YES, YES, &count); - // We did get the correct protocol! - assert(count == 1); -#endif - return 0; -} diff --git a/Test/GNUmakefile b/Test/GNUmakefile deleted file mode 100644 index 94e24fd..0000000 --- a/Test/GNUmakefile +++ /dev/null @@ -1,8 +0,0 @@ -include $(GNUSTEP_MAKEFILES)/common.make - -TOOL_NAME = RuntimeTest - -RuntimeTest_OBJC_FILES=\ - RuntimeTest.m - -include $(GNUSTEP_MAKEFILES)/tool.make diff --git a/Test/IVarOverlap.m b/Test/IVarOverlap.m deleted file mode 100644 index 4dc7332..0000000 --- a/Test/IVarOverlap.m +++ /dev/null @@ -1,28 +0,0 @@ -#import -#import "../objc/runtime.h" -#include "Test.h" - -#import -#import - -@interface Dummy : Test -{ - id objOne; - struct stat statBuf; - BOOL flagOne; -} -@end - -@implementation Dummy -- (void)test -{ - assert((char*)&statBuf+sizeof(struct stat) <= (char*)&flagOne); -} -@end - - -int main(int argc, char *argv[]) -{ - [[Dummy new] test]; - return 0; -} diff --git a/Test/IVarSuperclassOverlap.m b/Test/IVarSuperclassOverlap.m deleted file mode 100644 index 3a10f3f..0000000 --- a/Test/IVarSuperclassOverlap.m +++ /dev/null @@ -1,99 +0,0 @@ -#import "../objc/runtime.h" -#include "Test.h" - -@interface Space : Test -{ - uint16_t baseSmall; -} -@end - -@interface StartsWithChar : Space -{ - char c1; - char c2; - char c3; -} -@end -@interface StartsWithBitfield : Space -{ - @public - uint16_t b1:4; - uint16_t b2:4; - uint16_t b3:4; - uint16_t b4:4; - uint16_t notBitfield; -} -@end - -@implementation Space @end -@implementation StartsWithChar @end -@implementation StartsWithBitfield @end - -int main(int argc, char *argv[]) -{ - Class s = objc_getClass("Space"); - assert(s); - Class swc = objc_getClass("StartsWithChar"); - assert(swc); - Class swb = objc_getClass("StartsWithBitfield"); - assert(swb); - Ivar baseSmall = class_getInstanceVariable(s, "baseSmall"); - Ivar c1 = class_getInstanceVariable(swc, "c1"); - Ivar c2 = class_getInstanceVariable(swc, "c2"); - Ivar c3 = class_getInstanceVariable(swc, "c3"); - Ivar b1 = class_getInstanceVariable(swb, "b1"); - Ivar b2 = class_getInstanceVariable(swb, "b2"); - Ivar b3 = class_getInstanceVariable(swb, "b3"); - Ivar b4 = class_getInstanceVariable(swb, "b4"); - Ivar notBitfield = class_getInstanceVariable(swb, "notBitfield"); - assert(baseSmall); - assert(c1); - assert(c2); - assert(c3); - assert(b1); - assert(b2); - assert(b3); - assert(b4); - assert(notBitfield); - StartsWithBitfield *swbi = [StartsWithBitfield new]; - - // Alternating 01 bit pattern, should catch small overwrites. - swbi->notBitfield = 0x5555; - swbi->b1 = 5; - swbi->b2 = 11; - swbi->b3 = 11; - swbi->b4 = 5; - assert(swbi->b1 == 5); - assert(swbi->b2 == 11); - assert(swbi->b3 == 11); - assert(swbi->b4 == 5); - assert(swbi->notBitfield == 0x5555); - swbi->notBitfield = 0xaaaa; - swbi->b1 = 5; - swbi->b2 = 11; - swbi->b3 = 5; - swbi->b4 = 11; - assert(swbi->notBitfield == 0xaaaa); - assert(swbi->b1 == 5); - assert(swbi->b2 == 11); - assert(swbi->b3 == 5); - assert(swbi->b4 == 11); - -#ifdef NEW_ABI - ptrdiff_t baseSmallOffset = ivar_getOffset(baseSmall); - // These should pass with the old ABI, but they don't at the moment. The - // way that they don't is not very harmful though: we just get a bit of - // redundant padding, so I don't consider a fix a very high priority. - assert(ivar_getOffset(c1) == baseSmallOffset + 2); - assert(ivar_getOffset(c2) == baseSmallOffset + 3); - assert(ivar_getOffset(c3) == baseSmallOffset + 4); - assert(ivar_getOffset(b1) == baseSmallOffset + 2); - assert(ivar_getOffset(b2) == baseSmallOffset + 2); - assert(ivar_getOffset(b3) == baseSmallOffset + 3); - assert(ivar_getOffset(b4) == baseSmallOffset + 3); - assert(ivar_getOffset(notBitfield) == baseSmallOffset + 4); -#endif - - - return 0; -} diff --git a/Test/ManyManySelectors.m b/Test/ManyManySelectors.m deleted file mode 100644 index b2fad79..0000000 --- a/Test/ManyManySelectors.m +++ /dev/null @@ -1,55 +0,0 @@ -#include "Test.h" -#include -#include -#include -#include "../selector.h" - -#include - - -static BOOL methodCalled = NO; - -static char selBuffer[] = "XXXXXXXselectorXXXXXXXX"; - -static id x(id self, SEL _cmd) -{ - methodCalled = YES; - if (strcmp(selBuffer, sel_getName(_cmd)) != 0) - { - fprintf(stderr, "'%s' != '%s'\n", selBuffer, sel_getName(_cmd)); - } - assert(strcmp(selBuffer, sel_getName(_cmd)) == 0); - return self; -} - -int main(void) -{ - - SEL nextSel; - Class cls = [Test class]; - assert(cls != Nil); - int sel_size = 0; - for (uint32_t i=0 ; i<0xf0000 ; i++) - { - snprintf(selBuffer, sizeof(selBuffer), "%" PRId32 "selector%" PRIx32, i, i); - nextSel = sel_registerName(selBuffer); - char *registeredName = sel_getName(nextSel); - if (strcmp(selBuffer, registeredName) != 0) - { - fprintf(stderr, "'%s' != '%s' (%p)\n", selBuffer, registeredName, nextSel->index); - } - assert(strcmp(selBuffer, sel_getName(nextSel)) == 0); - sel_size += strlen(selBuffer); - } - assert(class_addMethod(object_getClass([Test class]), nextSel, (IMP)x, "@@:")); - assert(cls == [Test class]); - // Test both the C and assembly code paths. - objc_msg_lookup(cls, nextSel)(cls, nextSel); - assert(methodCalled == YES); - methodCalled = NO; -#ifdef __GNUSTEP_MSGSEND__ - objc_msgSend([Test class], nextSel); - assert(methodCalled == YES); -#endif - return 0; -} diff --git a/Test/MethodArguments.m b/Test/MethodArguments.m deleted file mode 100644 index eaf9004..0000000 --- a/Test/MethodArguments.m +++ /dev/null @@ -1,91 +0,0 @@ -#import "Test.h" -#include -#include -#include -#include - -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface Foo --(id)bar; --(void)setBar:(id)b; -@end -@implementation Foo -- (id)bar -{ - return nil; -} - -- (void)setBar: (id)b -{ - return; -} -@end - -int main(void) -{ - Class foo = objc_getClass("Foo"); - Method barMethod = class_getInstanceMethod(foo, @selector(bar)); - Method setBarMethod = class_getInstanceMethod(foo,@selector(setBar:)); - char arg[16]; - - memset(&arg[0], '\0', 16 * sizeof(char)); - method_getReturnType(barMethod, &arg[0], 16); - assert(0 == strcmp(&arg[0],"@")); - - char* expected[3] = {"@", ":", "" }; - for (int i = 0; i < 3; i++) - { - memset(&arg[0], '\0', 16 * sizeof(char)); - method_getArgumentType(barMethod, i, &arg[0], 16); - assert(0 == strcmp(&arg[0],expected[i])); - } - - - memset(&arg[0], '\0', 16 * sizeof(char)); - method_getReturnType(setBarMethod, &arg[0], 16); - assert(0 == strcmp(&arg[0],"v")); - - expected[2] = "@"; - - for (int i = 0; i < 3; i++) - { - memset(&arg[0], '\0', 16 * sizeof(char)); - method_getArgumentType(setBarMethod, i, &arg[0], 16); - assert(0 == strcmp(&arg[0],expected[i])); - } - - char *arg_copied = method_copyReturnType(barMethod); - assert(0 == strcmp(arg_copied,"@")); - free(arg_copied); - arg_copied = NULL; - - for (int i = 0; i < 2; i++) - { - arg_copied = method_copyArgumentType(barMethod, i); - assert(0 == strcmp(arg_copied,expected[i])); - free(arg_copied); - } - - arg_copied = method_copyArgumentType(barMethod, 2); - assert(NULL == arg_copied); - - - - arg_copied = method_copyReturnType(setBarMethod); - assert(0 == strcmp(arg_copied,"v")); - free(arg_copied); - - for (int i = 0; i < 3; i++) - { - arg_copied = method_copyArgumentType(setBarMethod, i); - assert(0 == strcmp(arg_copied,expected[i])); - free(arg_copied); - } - - - return 0; -} diff --git a/Test/NestedExceptions.m b/Test/NestedExceptions.m deleted file mode 100644 index 1399c68..0000000 --- a/Test/NestedExceptions.m +++ /dev/null @@ -1,39 +0,0 @@ -#include "Test.h" - -#if __cplusplus -#error This is not an ObjC++ test! -#endif - - -id a; -int throw(void) -{ - @throw a; -} - - -int main(void) -{ - id e1 = [Test new]; - id e2 = [Test new]; - @try - { - a = e1; - throw(); - } - @catch (id x) - { - assert(x == e1); - @try { - a = e2; - @throw a; - } - @catch (id y) - { - assert(y == e2); - } - } - [e1 dealloc]; - [e2 dealloc]; - return 0; -} diff --git a/Test/NilException.m b/Test/NilException.m deleted file mode 100644 index de85663..0000000 --- a/Test/NilException.m +++ /dev/null @@ -1,49 +0,0 @@ -#include "Test.h" - - -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface NSObject -{ - Class isa; -} -@end - -@implementation NSObject -+ (id)new -{ - return class_createInstance(self, 0); -} -@end -int main(void) -{ - BOOL caught_exception = NO; - @try - { - @throw(nil); - } - @catch (NSObject* o) - { - assert(0); - } - @catch (id x) - { - assert(nil == x); - caught_exception = YES; - } - assert(caught_exception == YES); - caught_exception = NO; - @try - { - @throw(nil); - } - @catch (...) - { - caught_exception = YES; - } - assert(caught_exception == YES); - return 0; -} diff --git a/Test/ObjCXXEHInterop.m b/Test/ObjCXXEHInterop.m deleted file mode 100644 index 36be8b9..0000000 --- a/Test/ObjCXXEHInterop.m +++ /dev/null @@ -1,24 +0,0 @@ -#import "Test.h" - -#import "stdio.h" - -void poke_objcxx(void); -void check_uncaught_count(void); - -void rethrow(id x) -{ - @throw x; -} - -int main(void) -{ - @try { - printf("Poking from minRepM\n"); - poke_objcxx(); - printf("Poked from minRepM\n"); - } @catch (Test *localException) { - printf("In NS_HANDLER block, %p\n", localException); - } - check_uncaught_count(); -} - diff --git a/Test/ObjCXXEHInterop.mm b/Test/ObjCXXEHInterop.mm deleted file mode 100644 index 37527d9..0000000 --- a/Test/ObjCXXEHInterop.mm +++ /dev/null @@ -1,41 +0,0 @@ -#import "Test.h" -#import "stdio.h" - -#ifdef __unix__ -// Declare these inline. The libsupc++ version of cxxabi.h does not include -// __cxa_eh_globls, even though it's mandated by the ABI. -namespace __cxxabiv1 -{ - struct __cxa_exception; - struct __cxa_eh_globals - { - __cxa_exception *caughtExceptions; - unsigned int uncaughtExceptions; - }; - extern "C" __cxa_eh_globals *__cxa_get_globals(); -} -extern "C" void check_uncaught_count(void) -{ - assert(__cxxabiv1::__cxa_get_globals()->uncaughtExceptions == 0); -} -#else -extern "C" void check_uncaught_count(void) {} -#endif - -extern "C" void rethrow(id); - - -extern "C" void poke_objcxx(void) -{ - @try { - printf("Raising MyException\n"); - Test *e = [Test new]; - @throw e; - } @catch (Test *localException) { - printf("Caught - re-raising\n"); - [localException retain]; - localException = [localException autorelease];; - rethrow(localException); - } -} - diff --git a/Test/ObjCXXEHInteropTwice.mm b/Test/ObjCXXEHInteropTwice.mm deleted file mode 100644 index 11d542d..0000000 --- a/Test/ObjCXXEHInteropTwice.mm +++ /dev/null @@ -1,22 +0,0 @@ -#import "Test.h" - -#import "stdio.h" - - -void excerciseExceptionCXX(Test *e) { - @try { - printf("Raising Test\n"); - @throw e; - } @catch (Test *localException) { - printf("Caught\n"); - } -} - -int main(void) -{ - Test *e = [Test new]; - excerciseExceptionCXX(e); - excerciseExceptionCXX(e); - [e release]; -} - diff --git a/Test/ObjCXXEHInterop_arc.m b/Test/ObjCXXEHInterop_arc.m deleted file mode 100644 index 8d4d275..0000000 --- a/Test/ObjCXXEHInterop_arc.m +++ /dev/null @@ -1,22 +0,0 @@ -#import "Test.h" - -#import "stdio.h" - -void poke_objcxx(void); - -void rethrow(id x) -{ - @throw x; -} - -int main(void) -{ - @try { - printf("Poking from minRepM\n"); - poke_objcxx(); - printf("Poked from minRepM\n"); - } @catch (Test *localException) { - printf("In NS_HANDLER block, %p\n", localException); - } -} - diff --git a/Test/ObjCXXEHInterop_arc.mm b/Test/ObjCXXEHInterop_arc.mm deleted file mode 100644 index f5f47ad..0000000 --- a/Test/ObjCXXEHInterop_arc.mm +++ /dev/null @@ -1,18 +0,0 @@ -#import "Test.h" -#import "stdio.h" - -extern "C" void rethrow(id); - - -extern "C" void poke_objcxx(void) -{ - @try { - printf("Raising MyException\n"); - Test *e = [Test new]; - @throw e; - } @catch (Test *localException) { - printf("Caught - re-raising\n"); - rethrow(localException); - } -} - diff --git a/Test/PropertyAttributeTest.m b/Test/PropertyAttributeTest.m deleted file mode 100644 index 637a658..0000000 --- a/Test/PropertyAttributeTest.m +++ /dev/null @@ -1,77 +0,0 @@ -#include -#import "Test.h" -#include -#include -#include - -@protocol X -@optional -@property (readonly) int x; -@end - -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif - -@interface helloclass { - @private int varName; -} -@property (class, retain) id clsProp; -@property (readwrite,assign) int propName; -@end - -@implementation helloclass -@synthesize propName = varName; -+ (id)class { return self; } -+ (id)clsProp { return nil; } -+ (void)setClsProp: (id)arg {} -@end - -int main() -{ - unsigned int outCount; - objc_property_t *properties = class_copyPropertyList([helloclass class], &outCount); - assert(outCount == 1); - objc_property_t property = properties[0]; - assert(strcmp(property_getName(property), "propName") == 0); - assert(strcmp(property_getAttributes(property), "Ti,VvarName") == 0); - free(properties); - - properties = class_copyPropertyList(object_getClass([helloclass class]), &outCount); - assert(outCount == 1); - property = properties[0]; - assert(strcmp(property_getName(property), "clsProp") == 0); - fprintf(stderr, "%s\n", property_getAttributes(property)); - assert(strcmp(property_getAttributes(property), "T@,&") == 0); - free(properties); - - Method* methods = class_copyMethodList([helloclass class], &outCount); - // This metadata was buggy in clang versions prior to clang 11. -#if __clang_major__ > 10 - assert(outCount == 2); -#endif - free(methods); - - objc_property_attribute_t a = { "V", "varName" }; - assert(class_addProperty([helloclass class], "propName2", &a, 1)); - properties = class_copyPropertyList([helloclass class], &outCount); - assert(outCount == 2); - int found = 0; - for (int i=0 ; i<2 ; i++) - { - property = properties[i]; - fprintf(stderr, "Name: %s\n", property_getName(property)); - fprintf(stderr, "Attrs: %s\n", property_getAttributes(property)); - if (strcmp(property_getName(property), "propName2") == 0) - { - assert(strcmp(property_getAttributes(property), "VvarName") == 0); - found++; - } - } - assert(found == 1); - return 0; -} - - diff --git a/Test/PropertyIntrospectionTest.m b/Test/PropertyIntrospectionTest.m deleted file mode 100644 index 20fbc9c..0000000 --- a/Test/PropertyIntrospectionTest.m +++ /dev/null @@ -1,41 +0,0 @@ -#import "Test.h" -#include -#include -#include - -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface Foo -@property (getter=bar, setter=setBar:, nonatomic, copy) id foo; -@end -@interface Foo(Bar) --(id)bar; --(void)setBar:(id)b; -@end -@implementation Foo -@synthesize foo; -@end - -int main(void) -{ - objc_property_t p = class_getProperty(objc_getClass("Foo"), "foo"); - unsigned int count; - objc_property_attribute_t *l = property_copyAttributeList(p, &count); - for (unsigned int i=0 ; i -#include -#include -#include -#include - -#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" - -// Clang < 3 doesn't exist usefully, so we can skip tests for it. Clang 3.5 -// adds proper metadata for weak properties, earlier ones don't, so don't fail -// the tests because of known compiler bugs. -#ifndef __clang_minor__ -#define WEAK_ATTR ATTR("W", ""), -#define WEAK_STR "W," -#elif (__clang_major__ < 4) && (__clang_minor__ < 5) -#define WEAK_ATTR -#define WEAK_STR -#else -#define WEAK_ATTR ATTR("W", ""), -#define WEAK_STR "W," -#endif - -enum FooManChu { FOO, MAN, CHU }; -struct YorkshireTeaStruct { int pot; signed char lady; }; -typedef struct YorkshireTeaStruct YorkshireTeaStructType; -union MoneyUnion { float alone; double down; }; - -#ifndef __has_attribute -#define __has_attribute(x) 0 -#endif - -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -@interface PropertyTest -{ -@public - Class isa; - atomic_bool atomicBoolDefault; - signed char charDefault; - double doubleDefault; - enum FooManChu enumDefault; - float floatDefault; - int intDefault; - long longDefault; - short shortDefault; - signed signedDefault; - struct YorkshireTeaStruct structDefault; - YorkshireTeaStructType typedefDefault; - union MoneyUnion unionDefault; - unsigned unsignedDefault; - int (*functionPointerDefault)(char *); - int *intPointer; - void *voidPointerDefault; - int intSynthEquals; - int intSetterGetter; - int intReadonly; - int intReadonlyGetter; - int intReadwrite; - int intAssign; - __unsafe_unretained id idDefault; - id idRetain; - id idCopy; - __weak id idWeak; - id idStrong; - int intNonatomic; - id idReadonlyCopyNonatomic; - id idReadonlyRetainNonatomic; - __weak id idReadonlyWeakNonatomic; - id _idOther; -} -@property atomic_bool atomicBoolDefault; -@property signed char charDefault; -@property double doubleDefault; -@property enum FooManChu enumDefault; -@property float floatDefault; -@property int intDefault; -@property long longDefault; -@property short shortDefault; -@property signed signedDefault; -@property struct YorkshireTeaStruct structDefault; -@property YorkshireTeaStructType typedefDefault; -@property union MoneyUnion unionDefault; -@property unsigned unsignedDefault; -@property int (*functionPointerDefault)(char *); -@property int *intPointer; -@property void *voidPointerDefault; -@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; -@property(readonly) int intReadonly; -@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; -@property(readwrite) int intReadwrite; -@property(assign) int intAssign; -@property(unsafe_unretained) id idDefault; -@property(retain) id idRetain; -@property(copy) id idCopy; -@property(weak) id idWeak; -@property(strong) id idStrong; -@property(nonatomic) int intNonatomic; -@property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; -@property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; -@property(nonatomic, readonly, weak) id idReadonlyWeakNonatomic; -@property(retain) id idOther; -@property(retain) id idDynamic; -@property(retain, nonatomic, getter=dynamicGetterSetter, setter=setDynamicGetterSetter:) id idDynamicGetterSetter; -@end - -@interface PropertyTest (Informal) -- (void)setStructDefault2: (struct YorkshireTeaStruct)tp; -- (void)setIntDefault2: (int)i; -- (struct YorkshireTeaStruct)structDefault2; -- (int)intDefault2; -@end - - -@implementation PropertyTest -@synthesize atomicBoolDefault; -@synthesize charDefault; -@synthesize doubleDefault; -@synthesize enumDefault; -@synthesize floatDefault; -@synthesize intDefault; -@synthesize longDefault; -@synthesize shortDefault; -@synthesize signedDefault; -@synthesize structDefault; -@synthesize typedefDefault; -@synthesize unionDefault; -@synthesize unsignedDefault; -@synthesize functionPointerDefault; -@synthesize intPointer; -@synthesize voidPointerDefault; -@synthesize intSetterGetter; -@synthesize intReadonly; -@synthesize intReadonlyGetter; -@synthesize intReadwrite; -@synthesize intAssign; -@synthesize idDefault; -@synthesize idRetain; -@synthesize idCopy; -@synthesize idWeak; -@synthesize idStrong; -@synthesize intNonatomic; -@synthesize idReadonlyCopyNonatomic; -@synthesize idReadonlyRetainNonatomic; -@synthesize idReadonlyWeakNonatomic; -@synthesize idOther = _idOther; -@dynamic idDynamic; -@dynamic idDynamicGetterSetter; -- (void)_ARCCompliantRetainRelease {} -@end - -@protocol ProtocolTest -@property atomic_bool atomicBoolDefault; -@property signed char charDefault; -@property double doubleDefault; -@property enum FooManChu enumDefault; -@property float floatDefault; -@property int intDefault; -@property long longDefault; -@property short shortDefault; -@property signed signedDefault; -@property struct YorkshireTeaStruct structDefault; -@property YorkshireTeaStructType typedefDefault; -@property union MoneyUnion unionDefault; -@property unsigned unsignedDefault; -@property int (*functionPointerDefault)(char *); -@property int *intPointer; -@property void *voidPointerDefault; -@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; -@property(readonly) int intReadonly; -@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; -@property(readwrite) int intReadwrite; -@property(assign) int intAssign; -@property(unsafe_unretained) id idDefault; -@property(retain) id idRetain; -@property(copy) id idCopy; -@property(weak) id idWeak; -@property(strong) id idStrong; -@property(nonatomic) int intNonatomic; -@property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; -@property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; -@property(nonatomic, readonly, weak) id idReadonlyWeakNonatomic; -@property(retain) id idOther; -@property(retain) id idDynamic; -@property(retain, nonatomic, getter=dynamicGetterSetter, setter=setDynamicGetterSetter:) id idDynamicGetterSetter; -@end - -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -@interface PropertyProtocolTest -{ - Class isa; - atomic_bool atomicBoolDefault; - signed char charDefault; - double doubleDefault; - enum FooManChu enumDefault; - float floatDefault; - int intDefault; - long longDefault; - short shortDefault; - signed signedDefault; - struct YorkshireTeaStruct structDefault; - YorkshireTeaStructType typedefDefault; - union MoneyUnion unionDefault; - unsigned unsignedDefault; - int (*functionPointerDefault)(char *); - int *intPointer; - void *voidPointerDefault; - int intSynthEquals; - int intSetterGetter; - int intReadonly; - int intReadonlyGetter; - int intReadwrite; - int intAssign; - __unsafe_unretained id idDefault; - id idRetain; - id idCopy; - __weak id idWeak; - id idStrong; - int intNonatomic; - id idReadonlyCopyNonatomic; - id idReadonlyRetainNonatomic; - __weak id idReadonlyWeakNonatomic; - id _idOther; -} -@end - -@implementation PropertyProtocolTest -@synthesize atomicBoolDefault; -@synthesize charDefault; -@synthesize doubleDefault; -@synthesize enumDefault; -@synthesize floatDefault; -@synthesize intDefault; -@synthesize longDefault; -@synthesize shortDefault; -@synthesize signedDefault; -@synthesize structDefault; -@synthesize typedefDefault; -@synthesize unionDefault; -@synthesize unsignedDefault; -@synthesize functionPointerDefault; -@synthesize intPointer; -@synthesize voidPointerDefault; -@synthesize intSetterGetter; -@synthesize intReadonly; -@synthesize intReadonlyGetter; -@synthesize intReadwrite; -@synthesize intAssign; -@synthesize idDefault; -@synthesize idRetain; -@synthesize idCopy; -@synthesize idWeak; -@synthesize idStrong; -@synthesize intNonatomic; -@synthesize idReadonlyCopyNonatomic; -@synthesize idReadonlyRetainNonatomic; -@synthesize idReadonlyWeakNonatomic; -@synthesize idOther = _idOther; -@dynamic idDynamic; -@dynamic idDynamicGetterSetter; -- (void)_ARCCompliantRetainRelease {} -@end - -#define ATTR(n, v) (objc_property_attribute_t){(n), (v)} -#define ATTRS(...) (objc_property_attribute_t[]){ __VA_ARGS__ }, \ - sizeof((objc_property_attribute_t[]){ __VA_ARGS__ }) / sizeof(objc_property_attribute_t) -#define OPT_ASSERT(stmt) if (abort) { \ - assert(stmt);\ -} else { \ - if (!(stmt)) { return NO; } \ -} - -static BOOL testPropertyForProperty_alt(objc_property_t p, - const char *name, - const char *types, - objc_property_attribute_t* list, - unsigned int size, BOOL abort) -{ - OPT_ASSERT(0 != p); - OPT_ASSERT(strcmp(name, property_getName(p)) == 0); - const char *attrs = property_getAttributes(p); - OPT_ASSERT(0 != attrs); - OPT_ASSERT(strcmp(types, attrs) == 0); - unsigned int attrsCount = 0; - objc_property_attribute_t *attrsList = property_copyAttributeList(p, &attrsCount); - OPT_ASSERT(0 != attrsList); - OPT_ASSERT(attrsCount == size); - for (unsigned int index=0; indexname != NULL) && (attrsCount < size); attrsCount++, ra++) {} - OPT_ASSERT(attrsCount == size); - free(attrsList); - for (unsigned int index=0; indexidRetain == testValue); - assert(t->_idOther == nil); - - Method idRetainSetter = class_getInstanceMethod(testClass, @selector(setIdRetain:)); - Method idOtherSetter = class_getInstanceMethod(testClass, @selector(setIdOther:)); - method_setImplementation(idRetainSetter, method_getImplementation(idOtherSetter)); - idRetainSetter = class_getInstanceMethod(testClass, @selector(setIdRetain:)); - - id testValue2 = [Test new]; - t.idRetain = testValue2; - assert(t->idRetain == testValue); - assert(t->_idOther == testValue2); - return 0; -} diff --git a/Test/ProtocolCreation.m b/Test/ProtocolCreation.m deleted file mode 100644 index fb933e7..0000000 --- a/Test/ProtocolCreation.m +++ /dev/null @@ -1,115 +0,0 @@ -#import "Test.h" -#include -#include -#include - -@protocol Test2 @end -@protocol Test3 @end -@protocol Test4 @end - -void checkProtocolMethod(Protocol *p, SEL sel, BOOL isClass, BOOL isOptional) -{ - struct objc_method_description d = protocol_getMethodDescription(p, sel, isClass, isOptional); - assert(sel_isEqual(d.name, sel)); - assert(strcmp((d.types), "@:") == 0); - d = protocol_getMethodDescription(p, sel, !isClass, isOptional); - assert(d.name == NULL); - assert(d.types == NULL); - d = protocol_getMethodDescription(p, sel, isClass, !isOptional); - assert(d.name == NULL); - assert(d.types == NULL); - d = protocol_getMethodDescription(p, sel, !isClass, !isOptional); - assert(d.name == NULL); - assert(d.types == NULL); -} - -int main(void) -{ - __attribute__((unused)) - Protocol *force_reference = @protocol(Test2); - Protocol *p = objc_allocateProtocol("Test"); - protocol_addMethodDescription(p, @selector(someClassMethod), "@:", YES, NO); - protocol_addMethodDescription(p, @selector(someOptionalClassMethod), "@:", YES, YES); - protocol_addMethodDescription(p, @selector(someMethod), "@:", NO, NO); - protocol_addMethodDescription(p, @selector(someOtherMethod), "@:", NO, NO); - protocol_addMethodDescription(p, @selector(someOptionalMethod), "@:", NO, YES); - assert(objc_getProtocol("Test2")); - protocol_addProtocol(p, objc_getProtocol("Test2")); - protocol_addProtocol(p, @protocol(Test3)); - - // Check that this don't crash - protocol_addProtocol(p, NULL); - protocol_addProtocol(NULL, p); - protocol_addProtocol(NULL, NULL); - - objc_property_attribute_t attrs[] = { {"T", "@" }, {"V", "optional"} }; - protocol_addProperty(p, "optional", attrs, 2, NO, YES); - attrs[1].value = "required"; - protocol_addProperty(p, "required", attrs, 2, YES, YES); - attrs[1].value = "required2"; - protocol_addProperty(p, "required2", attrs, 2, YES, YES); - protocol_addProperty(p, "classOptional", attrs, 1, NO, NO); - protocol_addProperty(p, "classRequired", attrs, 1, YES, NO); - - checkProtocolMethod(p, @selector(someClassMethod), YES, NO); - checkProtocolMethod(p, @selector(someOptionalClassMethod), YES, YES); - checkProtocolMethod(p, @selector(someMethod), NO, NO); - checkProtocolMethod(p, @selector(someOtherMethod), NO, NO); - checkProtocolMethod(p, @selector(someOptionalMethod), NO, YES); - objc_registerProtocol(p); - // Modifying protocols after they've been registered is not permitted. - protocol_addProtocol(p, @protocol(Test4)); - protocol_addMethodDescription(p, @selector(someUnsupportedMethod), "@:", NO, NO); - protocol_addProperty(p, "classRequired2", attrs, 1, NO, NO); - - Protocol *p1 = objc_getProtocol("Test"); - assert(p == p1); - - checkProtocolMethod(p1, @selector(someClassMethod), YES, NO); - checkProtocolMethod(p1, @selector(someOptionalClassMethod), YES, YES); - checkProtocolMethod(p1, @selector(someMethod), NO, NO); - checkProtocolMethod(p1, @selector(someOtherMethod), NO, NO); - checkProtocolMethod(p1, @selector(someOptionalMethod), NO, YES); - // Added after the protocol was registered, shouldn't have been allowed. - struct objc_method_description d = protocol_getMethodDescription(p1, @selector(someUnsupportedMethod), NO, NO); - assert(d.name == NULL); - assert(d.types == NULL); - - assert(protocol_conformsToProtocol(p1, objc_getProtocol("Test2"))); - assert(protocol_conformsToProtocol(p1, objc_getProtocol("Test3"))); - // Added after the protocol was registered, shouldn't have been allowed. - assert(!protocol_conformsToProtocol(p1, objc_getProtocol("Test4"))); - unsigned int count; - protocol_copyPropertyList(p1, &count); - assert(count == 2); - protocol_copyPropertyList2(p1, &count, YES, YES); - assert(count == 2); - objc_property_t *props = protocol_copyPropertyList2(p1, &count, NO, YES); - assert(count == 1); - assert(strcmp("T@,Voptional", property_getAttributes(*props)) == 0); - - - Protocol **list = objc_copyProtocolList(&count); - assert(count >= 4); - Protocol *expected[4] = {@protocol(Test2), @protocol(Test3), @protocol(Test4), p}; - const char *expectedNames[4] = {"Test2", "Test3", "Test4", "Test"}; - BOOL found[4]; - for (unsigned i=0 ; i - -@class NSString; -@protocol Foo -- (NSString*)aMethod: (void(^)(int))aBlock; -@end - -int main(void) -{ - const char *encoding = _protocol_getMethodTypeEncoding(@protocol(Foo), @selector(aMethod:), YES, YES); -#ifdef GS_RUNTIME_V2 - // We expect something like this (LP64): @"NSString"24@0:8@?16 - assert(strstr(encoding, "@\"NSString\"") == encoding); - assert(strstr(encoding, "@?") != NULL); -#else - assert(strstr(encoding, "@?") != NULL); -#endif -} diff --git a/Test/ResurrectInDealloc_arc.m b/Test/ResurrectInDealloc_arc.m deleted file mode 100644 index c73d0ca..0000000 --- a/Test/ResurrectInDealloc_arc.m +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include "Test.h" - -@interface DieStrong : Test @end -@interface DieWeak : Test @end -@implementation DieStrong -- (void)dealloc -{ - fprintf(stderr, "Killing strong\n"); - void (^myBlock)() = ^() - { - id foo = self; - fprintf(stderr, "strong self: %p\n", foo); - }; - myBlock(); -} -@end - -@implementation DieWeak -- (void)dealloc -{ - fprintf(stderr, "Killing weak\n"); - __weak id this = self; - void (^myBlock)() = ^() - { - id foo = this; - fprintf(stderr, "weak self: %p\n", foo); - }; - myBlock(); -} -@end - -int main(void) -{ - fprintf(stderr, "Test running?\n"); - id a = [DieStrong new], b = [DieWeak new]; - a = nil; - b = nil; -} diff --git a/Test/RuntimeTest.m b/Test/RuntimeTest.m deleted file mode 100644 index 1a49609..0000000 --- a/Test/RuntimeTest.m +++ /dev/null @@ -1,343 +0,0 @@ -#include "Test.h" -#include -#include -#ifdef _WIN32 -# include "../safewindows.h" -# define sleep(x) Sleep(1000 * x) -#else -# include -#endif - -static int exitStatus = 0; - -static void _test(BOOL X, char *expr, int line) -{ - if (!X) - { - exitStatus = 1; - fprintf(stderr, "ERROR: Test failed: '%s' on %s:%d\n", expr, __FILE__, line); - } -} -#define test(X) _test(X, #X, __LINE__) - -static int stringsEqual(const char *a, const char *b) -{ - return 0 == strcmp(a,b); -} - -@protocol NSCoding -@end - -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface NSObject -{ - id isa; - int refcount; -} -@end -@implementation NSObject -- (id)class -{ - return object_getClass(self); -} -+ (id)class -{ - return self; -} -+ (id)new -{ - return class_createInstance(self, 0); -} -- (void)release -{ - if (refcount == 0) - { - object_dispose(self); - } - refcount--; -} -- (id)retain -{ - refcount++; - return self; -} -@end - - -@interface Foo : NSObject -{ - id a; -} -- (void) aMethod; -+ (void) aMethod; -- (int) manyTypes; -- (void) synchronizedCode; -+ (void) synchronizedCode; -+ (id) shared; -- (BOOL) basicThrowAndCatchException; -@end - -@interface Bar : Foo -{ - id b; -} -- (void) anotherMethod; -+ (void) anotherMethod; -- (id) manyTypes; -- (id) aBool: (BOOL)d andAnInt: (int) w; -@end - -id exceptionObj = @"Exception"; - -@implementation Foo -- (void) aMethod -{ -} -+ (void) aMethod -{ -} -- (int) manyTypes -{ - return YES; -} -- (void) synchronizedCode -{ - @synchronized(self) { [[self class] synchronizedCode]; } -} -+ (void) synchronizedCode -{ - @synchronized(self) { } -} -+ (id) shared -{ - @synchronized(self) { } - return nil; -} -- (void) throwException -{ - @throw exceptionObj; -} -- (BOOL) basicThrowAndCatchException -{ - @try - { - [self throwException]; - } - @catch (id e) - { - test(e == exceptionObj); - return YES; - } - @catch(...) - { - return NO; - } - return NO; -} -@end - -@implementation Bar -- (void) anotherMethod -{ -} -+ (void) anotherMethod -{ -} -- (id) manyTypes -{ - return @"Hello"; -} -- (id) aBool: (BOOL)d andAnInt: (int) w -{ - return @"Hello"; -} -@end - - -void testInvalidArguments() -{ - test(NO == class_conformsToProtocol([NSObject class], NULL)); - test(NO == class_conformsToProtocol(Nil, NULL)); - test(NO == class_conformsToProtocol(Nil, @protocol(NSCoding))); - test(NULL == class_copyIvarList(Nil, NULL)); - test(NULL == class_copyMethodList(Nil, NULL)); - test(NULL == class_copyPropertyList(Nil, NULL)); - test(NULL == class_copyProtocolList(Nil, NULL)); - test(nil == class_createInstance(Nil, 0)); - test(0 == class_getVersion(Nil)); - test(NO == class_isMetaClass(Nil)); - test(Nil == class_getSuperclass(Nil)); - - test(NULL == method_getName(NULL)); - test(NULL == method_copyArgumentType(NULL, 0)); - test(NULL == method_copyReturnType(NULL)); - method_exchangeImplementations(NULL, NULL); - test((IMP)NULL == method_setImplementation(NULL, (IMP)NULL)); - test((IMP)NULL == method_getImplementation(NULL)); - method_getArgumentType(NULL, 0, NULL, 0); - test(0 == method_getNumberOfArguments(NULL)); - test(NULL == method_getTypeEncoding(NULL)); - method_getReturnType(NULL, NULL, 0); - - test(NULL == ivar_getName(NULL)); - test(0 == ivar_getOffset(NULL)); - test(NULL == ivar_getTypeEncoding(NULL)); - - test(nil == objc_getProtocol(NULL)); - - test(stringsEqual("", sel_getName((SEL)0))); - test((SEL)0 == sel_getUid(NULL)); - test(0 != sel_getUid("")); // the empty string is permitted as a selector - test(stringsEqual("", sel_getName(sel_getUid("")))); - test(YES == sel_isEqual((SEL)0, (SEL)0)); - - //test(NULL == property_getName(NULL)); - - printf("testInvalidArguments() ran\n"); -} - -void testAMethod(Method m) -{ - test(NULL != m); - test(stringsEqual("aMethod", sel_getName(method_getName(m)))); - - printf("testAMethod() ran\n"); -} - -void testGetMethod() -{ - testAMethod(class_getClassMethod([Bar class], @selector(aMethod))); - testAMethod(class_getClassMethod([Bar class], sel_getUid("aMethod"))); - - printf("testGetMethod() ran\n"); -} - -void testProtocols() -{ - test(protocol_isEqual(@protocol(NSCoding), objc_getProtocol("NSCoding"))); - - printf("testProtocols() ran\n"); -} - -void testMultiTypedSelector() -{ - test(sel_isEqual(@selector(manyTypes),sel_getUid("manyTypes"))); - - Method intMethod = class_getInstanceMethod([Foo class], @selector(manyTypes)); - Method idMethod = class_getInstanceMethod([Bar class], @selector(manyTypes)); - - test(sel_isEqual(method_getName(intMethod), @selector(manyTypes))); - test(sel_isEqual(method_getName(idMethod), @selector(manyTypes))); - - char ret[10]; - method_getReturnType(intMethod, ret, 10); - test(stringsEqual(ret, "i")); - method_getReturnType(idMethod, ret, 10); - test(stringsEqual(ret, "@")); - - printf("testMultiTypedSelector() ran\n"); -} - -void testClassHierarchy() -{ - Class nsProxy = objc_getClass("NSProxy"); - Class nsObject = objc_getClass("NSObject"); - Class nsProxyMeta = object_getClass(nsProxy); - Class nsObjectMeta = object_getClass(nsObject); - - test(object_getClass(nsProxyMeta) == nsProxyMeta); - test(object_getClass(nsObjectMeta) == nsObjectMeta); - - test(Nil == class_getSuperclass(nsProxy)); - test(Nil == class_getSuperclass(nsObject)); - - test(nsObject == class_getSuperclass(nsObjectMeta)); - test(nsProxy == class_getSuperclass(nsProxyMeta)); - printf("testClassHierarchy() ran\n"); -} - -void testAllocateClass() -{ - Class newClass = objc_allocateClassPair(objc_lookUpClass("NSObject"), "UserAllocated", 0); - test(Nil != newClass); - // class_getSuperclass() will call objc_resolve_class(). - // Although we have not called objc_registerClassPair() yet, this works with - // the Apple runtime and GNUstep Base relies on this behavior in - // GSObjCMakeClass(). - test(objc_lookUpClass("NSObject") == class_getSuperclass(newClass)); - printf("testAllocateClass() ran\n"); -} - -void testSynchronized() -{ - Foo *foo = [Foo new]; - printf("Enter synchronized code\n"); - [foo synchronizedCode]; - [foo release]; - [Foo shared]; - printf("testSynchronized() ran\n"); -} - -void testExceptions() -{ - Foo *foo = [Foo new]; - test([foo basicThrowAndCatchException]); - [foo release]; - printf("testExceptions() ran\n"); - -} - -void testRegisterAlias() -{ - class_registerAlias_np([NSObject class], "AliasObject"); - test([NSObject class] == objc_getClass("AliasObject")); - printf("testRegisterAlias() ran\n"); -} - -@interface SlowInit1 : NSObject -+ (void)doNothing; -@end -@interface SlowInit2 : NSObject -+ (void)doNothing; -@end - -@implementation SlowInit1 -+ (void)initialize -{ - sleep(1); - [SlowInit2 doNothing]; -} -+ (void)doNothing {} -@end -static int initCount; -@implementation SlowInit2 -+ (void)initialize -{ - sleep(1); - __sync_fetch_and_add(&initCount, 1); -} -+ (void)doNothing {} -@end - - - -int main (int argc, const char * argv[]) -{ - testInvalidArguments(); - testGetMethod(); - testProtocols(); - testMultiTypedSelector(); - testClassHierarchy(); - testAllocateClass(); - printf("Instance of NSObject: %p\n", class_createInstance([NSObject class], 0)); - - testSynchronized(); - testExceptions(); - testRegisterAlias(); - - return exitStatus; -} diff --git a/Test/RuntimeTest.xcodeproj/project.pbxproj b/Test/RuntimeTest.xcodeproj/project.pbxproj deleted file mode 100644 index 29fc106..0000000 --- a/Test/RuntimeTest.xcodeproj/project.pbxproj +++ /dev/null @@ -1,203 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 8DD76F9A0486AA7600D96B5E /* RuntimeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* RuntimeTest.m */; settings = {ATTRIBUTES = (); }; }; - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 08FB7796FE84155DC02AAC07 /* RuntimeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuntimeTest.m; sourceTree = ""; }; - 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 8DD76FA10486AA7600D96B5E /* RuntimeTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = RuntimeTest; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* RuntimeTest */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = RuntimeTest; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 08FB7796FE84155DC02AAC07 /* RuntimeTest.m */, - ); - name = Source; - sourceTree = ""; - }; - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { - isa = PBXGroup; - children = ( - 08FB779EFE84155DC02AAC07 /* Foundation.framework */, - ); - name = "External Frameworks and Libraries"; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8DD76FA10486AA7600D96B5E /* RuntimeTest */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8DD76F960486AA7600D96B5E /* RuntimeTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "RuntimeTest" */; - buildPhases = ( - 8DD76F990486AA7600D96B5E /* Sources */, - 8DD76F9B0486AA7600D96B5E /* Frameworks */, - 8DD76F9E0486AA7600D96B5E /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RuntimeTest; - productInstallPath = "$(HOME)/bin"; - productName = RuntimeTest; - productReference = 8DD76FA10486AA7600D96B5E /* RuntimeTest */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "RuntimeTest" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* RuntimeTest */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8DD76F960486AA7600D96B5E /* RuntimeTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 8DD76F990486AA7600D96B5E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9A0486AA7600D96B5E /* RuntimeTest.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB927508733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = RuntimeTest; - }; - name = Debug; - }; - 1DEB927608733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = RuntimeTest; - }; - name = Release; - }; - 1DEB927908733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Debug; - }; - 1DEB927A08733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "RuntimeTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927508733DD40010E9CD /* Debug */, - 1DEB927608733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "RuntimeTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927908733DD40010E9CD /* Debug */, - 1DEB927A08733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/Test/SuperMethodMissing.m b/Test/SuperMethodMissing.m deleted file mode 100644 index 8ea581c..0000000 --- a/Test/SuperMethodMissing.m +++ /dev/null @@ -1,46 +0,0 @@ -#include "Test.h" -#include "objc/hooks.h" -#include - -@interface Test (DoesNotExist) -- (void)run; -@end - -@interface Foo : Test -@end - -@implementation Foo -- (void)run -{ - [super run]; -} -@end - -static int missing_methods; - -id forward(id self, SEL cmd, ...) -{ - Class cls = object_getClass(self); - missing_methods++; - fprintf(stderr, "Missing method: %c[%s %s]\n", class_isMetaClass(cls) ? '+' : '-', class_getName(cls),sel_getName(cmd)); - return nil; -} - -IMP no_method(id self, SEL cmd) -{ - return forward; -} - - -int -main() -{ - __objc_msg_forward2 = no_method; - Test *t = [Test new]; - [t run]; - assert(missing_methods == 1); - Foo *f = [Foo new]; - [f run]; - assert(missing_methods == 2); - //[Test run]; -} diff --git a/Test/Test.h b/Test/Test.h deleted file mode 100644 index 04b12fb..0000000 --- a/Test/Test.h +++ /dev/null @@ -1,51 +0,0 @@ -#import "../objc/runtime.h" -#import "../objc/objc-arc.h" -#ifdef NDEBUG -#undef NDEBUG -#endif -#include - -#ifndef __has_attribute -#define __has_attribute(x) 0 -#endif - -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -@interface Test { id isa; } -+ (Class)class; -+ (id)new; -+ (id)alloc; -#if !__has_feature(objc_arc) -- (void)dealloc; -- (id)autorelease; -- (id)retain; -- (void)release; -#endif -@end - -#ifdef __OBJC_GNUSTEP_RUNTIME_ABI__ -# if __OBJC_GNUSTEP_RUNTIME_ABI__ >= 20 -# define NEW_ABI -# endif -#endif - -@interface NSConstantString : Test -{ -#ifdef NEW_ABI - uint32_t flags; - uint32_t length; - uint32_t size; - uint32_t hash; - const char * const str; -#else - const char * const str; - const unsigned int length; -#endif -} -@end - -@interface NSAutoreleasePool : Test -@end - - diff --git a/Test/Test.m b/Test/Test.m deleted file mode 100644 index c8c9b4a..0000000 --- a/Test/Test.m +++ /dev/null @@ -1,71 +0,0 @@ -#import "../objc/runtime.h" -#import "../objc/objc-arc.h" -#ifdef NDEBUG -#undef NDEBUG -#endif -#include -#include "Test.h" - -@implementation NSConstantString -- (void)dealloc -{ - // Silence a warning - if (0) - { - [super dealloc]; - } -} -@end - -@interface NSTinyString : NSConstantString @end -@implementation NSTinyString -+ (void)load -{ - if (sizeof(void*) > 4) - { - objc_registerSmallObjectClass_np(self, 4); - } -} -- (Class)class { return [NSTinyString class]; } -- (id)retain { return self; } -- (id)autorelease { return self; } -- (void)release {} -@end - -@implementation Test -+ (Class)class { return self; } -+ (id)new -{ - return class_createInstance(self, 0); -} -+ (id)alloc -{ - return class_createInstance(self, 0); -} -- (void)dealloc -{ - object_dispose(self); -} -- (id)autorelease -{ - return objc_autorelease(self); -} -- (id)retain -{ - return objc_retain(self); -} -- (void)release -{ - objc_release(self); -} -- (void)_ARCCompliantRetainRelease {} -+ (void)_TrivialAllocInit{} -@end - -@implementation NSAutoreleasePool -- (void)_ARCCompatibleAutoreleasePool {} -+ (void)addObject:(id)anObject -{ - objc_autorelease(anObject); -} -@end diff --git a/Test/UnexpectedException.m b/Test/UnexpectedException.m deleted file mode 100644 index 9f2a2ba..0000000 --- a/Test/UnexpectedException.m +++ /dev/null @@ -1,56 +0,0 @@ -#include "Test.h" -#include "../objc/hooks.h" -#include "../objc/objc-exception.h" - -#include - -#ifdef _WIN32 -#include -#endif - -id expectedExceptionObj = @"ExpectedException"; -id unexpectedExceptionObj = @"UnexpectedException"; - -void _UncaughtExceptionHandler(id exception) -{ - assert(exception == unexpectedExceptionObj); -#if defined(_WIN32) && !defined(__MINGW32__) - // on Windows we will exit in _UnhandledExceptionFilter() below -#else - exit(0); -#endif -} - -#if defined(_WIN32) && !defined(__MINGW32__) -LONG WINAPI _UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) -{ - assert(exceptionInfo != NULL); - exit(0); -} -#endif - -int main(void) -{ -#if !(defined(__arm__) || defined(__ARM_ARCH_ISA_A64)) && !defined(__powerpc__) -#if defined(_WIN32) && !defined(__MINGW32__) - // also verify that an existing handler still gets called after we set ours - SetUnhandledExceptionFilter(&_UnhandledExceptionFilter); -#endif - @try - { - @throw expectedExceptionObj; - } - @catch(id exception) - { - assert(exception == expectedExceptionObj); - } - - objc_setUncaughtExceptionHandler(_UncaughtExceptionHandler); - @throw unexpectedExceptionObj; - assert(0 && "should not be reached!"); - - return -1; -#endif - // FIXME: Test currently fails on ARM and AArch64 - return 77; // Skip test -} diff --git a/Test/WeakBlock_arc.m b/Test/WeakBlock_arc.m deleted file mode 100644 index c21fe43..0000000 --- a/Test/WeakBlock_arc.m +++ /dev/null @@ -1,15 +0,0 @@ -#include "Test.h" - -int main(int argc, const char * argv[]) -{ - id __weak ref; - @autoreleasepool { - __block int val; - id block = ^() { return val++; }; - assert(block != nil); - ref = block; - assert(ref != nil); - } - assert(ref == nil); - return 0; -} diff --git a/Test/WeakImportClass.m b/Test/WeakImportClass.m deleted file mode 100644 index 2d6c932..0000000 --- a/Test/WeakImportClass.m +++ /dev/null @@ -1,12 +0,0 @@ -#include "Test.h" - - -__attribute__((weak_import)) -@interface WeakClass -- (id)class; -@end - -int main(void) -{ - assert([WeakClass class] == nil); -} diff --git a/Test/WeakRefLoad.m b/Test/WeakRefLoad.m deleted file mode 100644 index c851638..0000000 --- a/Test/WeakRefLoad.m +++ /dev/null @@ -1,22 +0,0 @@ -#include "Test.h" - -#define SIZE 5000 - -int main(int argc, const char * argv[]) -{ - id t = [Test new]; - id w1; - id w2; - objc_initWeak(&w1, t); - objc_initWeak(&w2, t); - [t release]; - assert(objc_loadWeakRetained(&w1) == nil); - assert(objc_loadWeakRetained(&w2) == nil); - assert(w1 == nil); - assert(w2 == nil); - assert(objc_loadWeakRetained(&w1) == nil); - assert(objc_loadWeakRetained(&w2) == nil); - assert(w1 == nil); - assert(w2 == nil); - return 0; -} diff --git a/Test/WeakReferences_arc.m b/Test/WeakReferences_arc.m deleted file mode 100644 index ff15bc6..0000000 --- a/Test/WeakReferences_arc.m +++ /dev/null @@ -1,45 +0,0 @@ -#include "Test.h" - -#define SIZE 5000 - -int main(int argc, const char * argv[]) -{ - @autoreleasepool { - id __weak refs[SIZE]; - id values[SIZE]; - - // Setup - for (int i=0; ix = (v4d){1,2,3,4}; - return v; -} -- (void)permute -{ - // This will become a sequence of one or more vector operations. We must - // have the correct alignment for x, even after the instance variable - // munging, or this will break. - x *= (v4d){2,3,4,5}; -} -@end - -typedef int v4si __attribute__ ((vector_size (16))); -@interface Foo : Test -{ - v4si var; -} -- (void)check; -@end -@implementation Foo -- (void)check -{ - size_t addr = (size_t)&var; - fprintf(stderr, "self: %p Addr: %p\n", self, &var); - assert(addr % __alignof__(v4si) == 0); -} -@end - -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -@interface StringLikeTest -{ - Class isa; - char* c_string; - int len; -} -@end - -@implementation StringLikeTest -+ (Class)class -{ - return self; -} -@end - -int main(void) -{ - [[Vector alloc] permute]; - [[Foo new] check]; - - Ivar v_isa = class_getInstanceVariable([StringLikeTest class], "isa"); - Ivar v_c_string = class_getInstanceVariable([StringLikeTest class], "c_string"); - Ivar v_len = class_getInstanceVariable([StringLikeTest class], "len"); - ptrdiff_t o_isa = ivar_getOffset(v_isa); - ptrdiff_t o_c_string = ivar_getOffset(v_c_string); - assert(o_isa == 0); - assert(o_c_string == sizeof(Class)); - assert(o_isa < o_c_string); - assert(o_c_string < ivar_getOffset(v_len)); -} diff --git a/Test/category_properties.m b/Test/category_properties.m deleted file mode 100644 index f758f20..0000000 --- a/Test/category_properties.m +++ /dev/null @@ -1,41 +0,0 @@ -#include "Test.h" -#include -#include - -@interface Test (Property) -@property (readonly) int val; -@property (class, readonly) int val2; -@end - -@interface Test (Property2) -@property (readonly) int val2; -@end - - -@implementation Test (Property) -@dynamic val2; -- (int)val { return 0; } -@end - -@implementation Test (Property2) -@dynamic val2; -- (int)val2 { return 0; } -@end - -int main(int argc, char** argv) -{ - Class test = objc_getClass("Test"); - objc_property_t prop = class_getProperty(test, "val"); - assert(prop); - assert(strcmp("Ti,R", property_getAttributes(prop)) == 0); - prop = class_getProperty(test, "val2"); - assert(prop); - assert(strcmp("Ti,R,D", property_getAttributes(prop)) == 0); -#ifdef GS_RUNTIME_V2 - test = object_getClass(test); - prop = class_getProperty(test, "val2"); - assert(prop); - assert(strcmp("Ti,R,D", property_getAttributes(prop)) == 0); -#endif -} - diff --git a/Test/exchange.m b/Test/exchange.m deleted file mode 100644 index 10b8676..0000000 --- a/Test/exchange.m +++ /dev/null @@ -1,23 +0,0 @@ -#include "Test.h" - -@interface Exchange : Test -+ (int)test1; -+ (int)test2; -@end - -@implementation Exchange -+ (void)noop { } - -+ (int)test1 { return 1024; } -+ (int)test2 { return 2048; } -@end - -int main(int argc, char** argv) { - [Exchange noop]; - Class i32meta = object_getClass(objc_getClass("Exchange")); - Method m1 = class_getInstanceMethod(i32meta, @selector(test1)); - Method m2 = class_getInstanceMethod(i32meta, @selector(test2)); - method_exchangeImplementations(m1, m2); - assert(2048 == [Exchange test1]); -} - diff --git a/Test/hash_table_delete.c b/Test/hash_table_delete.c deleted file mode 100644 index ad7af89..0000000 --- a/Test/hash_table_delete.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -struct test_struct { - uintptr_t key; -}; - -struct test_struct null_placeholder = {0}; - -static int test_compare(const void *key, const struct test_struct test) { - return (uintptr_t)key == test.key; -} - -// force hash collisions -static uint32_t test_key_hash(const void *ptr) { - return ((uint32_t)(uintptr_t)ptr)>>2; -} - -static uint32_t test_value_hash(const struct test_struct test) { - return test.key>>2; -} - -static int test_is_null(const struct test_struct test) { - return test.key == 0; -} - -#define MAP_TABLE_NAME test -#define MAP_TABLE_COMPARE_FUNCTION test_compare -#define MAP_TABLE_VALUE_TYPE struct test_struct -#define MAP_TABLE_VALUE_NULL test_is_null -#define MAP_TABLE_HASH_KEY test_key_hash -#define MAP_TABLE_HASH_VALUE test_value_hash -#define MAP_TABLE_VALUE_PLACEHOLDER null_placeholder -#define MAP_TABLE_ACCESS_BY_REFERENCE 1 -#define MAP_TABLE_SINGLE_THREAD 1 -#define MAP_TABLE_NO_LOCK 1 - -#include "../hash_table.h" - -int main(int argc, char *argv[]) -{ - test_table *testTable; - test_initialize(&testTable, 128); - - struct test_struct one, two, three; - one.key = 1; - two.key = 2; - three.key = 3; - - test_insert(testTable, one); - test_insert(testTable, two); - test_insert(testTable, three); - - test_remove(testTable, (void*)2); - test_remove(testTable, (void*)1); - - struct test_struct *pthree = test_table_get(testTable, (void*)3); - if (!pthree) { - fprintf(stderr, "failed to find value (key=3) inserted into hash table\n"); - return 1; - } - - return 0; -} diff --git a/Test/hash_test.c b/Test/hash_test.c deleted file mode 100644 index 7c6e9c7..0000000 --- a/Test/hash_test.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include - -static int compare(const void *i1, uint32_t i2) -{ - return ((uint32_t)(uintptr_t)i1) == i2; -} - -static uint32_t hash_int(uint32_t i) -{ - return i; -} -static uint32_t hash_key(const void *i) -{ - return hash_int((uint32_t)(uintptr_t)i); -} - -static int is_null(uint32_t i) -{ - return i == 0; -} - -#define MAP_TABLE_NAME test -#define MAP_TABLE_COMPARE_FUNCTION compare -#define MAP_TABLE_VALUE_TYPE uint32_t -#define MAP_TABLE_VALUE_PLACEHOLDER 0 -#define MAP_TABLE_VALUE_NULL is_null -#define MAP_TABLE_HASH_KEY hash_key -#define MAP_TABLE_HASH_VALUE hash_int -#define MAP_TABLE_SINGLE_THREAD 1 -#define MAP_TABLE_NO_LOCK 1 - -#include "../hash_table.h" - -static test_table *table; - - -void check_table() -{ - int count = 0; - for (int i=0 ; itable_size ; i++) - { - struct test_table_cell_struct *s = &table->table[i]; - uint32_t v = ((uint32_t)s->value); - if (v != 0) - { - count++; - assert(v == test_table_get(table, (void*)(uintptr_t)v)); - } - else - { - assert(s->secondMaps == 0); - } - } - assert(count == table->table_used); -} - -int main(void) -{ - test_initialize(&table, 128); - const int step = 2; - // 10 iterations was enough to hit the failing case previously. For - // extra paranoia, we can run this test a lot. - const int max = -#ifdef SLOW_TESTS - 8096; -#else - 10; -#endif - for (int seed = 0 ; seed < max ; seed++) - { - fprintf(stderr, "Seed: %d\n", seed); - srand(seed); - for (uint32_t i=1 ; i<5000 ; i+=step) - { - int x = rand(); - if (x == 0) - { - continue; - } - test_insert(table, x); - check_table(); - } - srand(seed); - for (uint32_t i=1 ; i<5000 ; i+=step) - { - int x = rand(); - if (x == 0) - { - continue; - } - test_remove(table, (void*)(uintptr_t)x); - check_table(); - } - assert(table->table_used == 0); - } -} diff --git a/Test/ivar_arc.m b/Test/ivar_arc.m deleted file mode 100644 index 0f719e5..0000000 --- a/Test/ivar_arc.m +++ /dev/null @@ -1,77 +0,0 @@ -#include "Test.h" -#include "../objc/runtime.h" - -@interface Foo : Test -{ - @public - __weak id w; - __unsafe_unretained id u; - __strong id s; -} -@end -@implementation Foo @end -@interface Dealloc : Test -@end -int dealloc = 0; -@implementation Dealloc -- (void)dealloc -{ - dealloc++; -} -@end - -@interface EmptySubFoo : Foo -@end - -@implementation EmptySubFoo -@end - -@interface NonEmptySubFoo : Foo -{ - __strong id ignored; -} -@end - -@implementation NonEmptySubFoo -@end - -void setIvar(id obj, const char * name, id val) -{ - object_setIvar(obj, class_getInstanceVariable(object_getClass(obj), name), val); -} - -void testIvarsOn(Foo* f) -{ - dealloc = 0; - Dealloc *d = [Dealloc new]; - __unsafe_unretained Dealloc *dead; - setIvar(f, "w", d); - assert(f->w == d); - assert(dealloc == 0); - d = 0; - assert(dealloc == 1); - assert(f->w == nil); - dealloc = 0; - d = [Dealloc new]; - dead = d; - setIvar(f, "s", d); - assert(dealloc == 0); - assert(f->s == d); - d = nil; - assert(dealloc == 0); - assert(f->s == dead); - setIvar(f, "s", nil); - assert(dealloc == 1); - assert(f->s == nil); -} - -int main (void) -{ - /* Test for ivars in the same class */ - testIvarsOn([Foo new]); - /* Test for ivars in the superclass (receiver ivar list empty) */ - testIvarsOn([EmptySubFoo new]); - /* Test for ivars in the superclass (receiver ivar list non-empty) */ - testIvarsOn([NonEmptySubFoo new]); - return 0; -} diff --git a/Test/ivar_atomic.m b/Test/ivar_atomic.m deleted file mode 100644 index 6dd8371..0000000 --- a/Test/ivar_atomic.m +++ /dev/null @@ -1,24 +0,0 @@ -#include "Test.h" - -#import - -@interface Dummy : Test -{ - atomic_bool atomicBool; -} -@end - -@implementation Dummy -- (void)test -{ - int value = 1; - object_setIvar(self, class_getInstanceVariable(object_getClass(self), "atomicBool"), (__bridge id)(void*)(intptr_t)value); -} -@end - - -int main(int argc, char *argv[]) -{ - [[Dummy new] test]; - return 0; -} diff --git a/Test/minRep1.mm b/Test/minRep1.mm deleted file mode 100644 index ab87270..0000000 --- a/Test/minRep1.mm +++ /dev/null @@ -1,15 +0,0 @@ -#import "Test.h" - -#import "minRep1.h" - -#import "stdio.h" - -@implementation MinRep1 - -- (void)poke -{ - printf("Poking from minRep1\n"); - poke_objcxx(); -} - -@end diff --git a/Test/msgInterpose.m b/Test/msgInterpose.m deleted file mode 100644 index 908332f..0000000 --- a/Test/msgInterpose.m +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include "Test.h" -#include "../objc/hooks.h" -#include "../objc/capabilities.h" - - -int count; -static const int loops = 50000; -static const int depth = 42; -static const int calls = loops * (depth+1); -static int d = depth; -Class TestCls; -int tracecount; - -@implementation Test (Nothing) -+ nothing -{ - count++; - if (d > 0) - { - d--; - [self nothing]; - d++; - } - return 0; -} -@end -__thread IMP real; - -static int interposecount; -id interpose(id self, SEL _cmd) { - interposecount++; - return real(self, _cmd); -} - -//IMP hook(id object, SEL selector, IMP method, int isReturn, void *wordReturn) { tracecount++; return (IMP)logExit; } -IMP hook(id object, SEL selector, IMP method, int isReturn, void *wordReturn) { tracecount++; real = method; return (IMP)0; } -IMP hook0(id object, SEL selector, IMP method, int isReturn, void *wordReturn) { tracecount++; real = method; return (IMP)1; } -IMP hook1(id object, SEL selector, IMP method, int isReturn, void *wordReturn) { tracecount++; real = method; return (IMP)interpose; } - -int main(void) -{ - if (!objc_test_capability(OBJC_CAP_TRACING)) - { - fprintf(stderr, "Tracing support not compiled into runtime\n"); - return 0; - } - TestCls = objc_getClass("Test"); - objc_registerTracingHook(@selector(nothing), hook); - interposecount = 0; - count = 0; - tracecount = 0; - for (int i=0 ; i -#include -#include -#include -#include -#include "../objc/runtime.h" -#include "../objc/hooks.h" - -//#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); } - -typedef void (*fwdManyFunc)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, float, float, float, float, float, float, float, float, float, float, float); - -typedef struct { int a,b,c,d,e; } s; -@interface Fake -- (int)izero; -- (float)fzero; -- (double)dzero; -- (long double)ldzero; -@end - -Class TestCls; -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface MsgTest { id isa; } @end -@interface MsgTest (Dynamic) -+ (void)manyArgs: (int)a0 - : (int) a1 - : (int) a2 - : (int) a3 - : (int) a4 - : (int) a5 - : (int) a6 - : (int) a7 - : (int) a8 - : (int) a9 - : (int) a10 - : (float) f0 - : (float) f1 - : (float) f2 - : (float) f3 - : (float) f4 - : (float) f5 - : (float) f6 - : (float) f7 - : (float) f8 - : (float) f9 - : (float) f10; -@end -@implementation MsgTest -- foo -{ - assert((id)1 == self); - assert(strcmp("foo", sel_getName(_cmd)) == 0); - return (id)0x42; -} -+ foo -{ - assert(TestCls == self); - assert(strcmp("foo", sel_getName(_cmd)) == 0); - return (id)0x42; -} -+ (s)sret -{ - assert(TestCls == self); - assert(strcmp("sret", sel_getName(_cmd)) == 0); - s st = {1,2,3,4,5}; - return st; -} -- (s)sret -{ - assert((id)3 == self); - assert(strcmp("sret", sel_getName(_cmd)) == 0); - s st = {1,2,3,4,5}; - return st; -} -+ (void)printf: (const char*)str, ... -{ - va_list ap; - char s[100]; - - va_start(ap, str); - - vsnprintf(s, 100, str, ap); - va_end(ap); - assert(strcmp(s, "Format string 42 42.000000\n") ==0); -} -+ (void)initialize -{ - [self printf: "Format %s %d %f%c", "string", 42, 42.0, '\n']; - @throw self; -} -+ nothing { return 0; } -@end - -int forwardcalls; -void fwdMany(id self, - SEL _cmd, - int a0, - int a1, - int a2, - int a3, - int a4, - int a5, - int a6, - int a7, - int a8, - int a9, - int a10, - float f0, - float f1, - float f2, - float f3, - float f4, - float f5, - float f6, - float f7, - float f8, - float f9, - float f10) -{ - forwardcalls++; - assert(self == objc_getClass("MsgTest")); - if (sel_isEqual(_cmd, sel_registerName("manyArgs:::::::::::::::::::::"))) - assert(a0 == 0); - assert(a1 == 1); - assert(a2 == 2); - assert(a3 == 3); - assert(a4 == 4); - assert(a5 == 5); - assert(a6 == 6); - assert(a7 == 7); - assert(a8 == 8); - assert(a9 == 9); - assert(a10 == 10); - assert(f0 == 0); - assert(f1 == 1); - assert(f2 == 2); - assert(f3 == 3); - assert(f4 == 4); - assert(f5 == 5); - assert(f6 == 6); - assert(f7 == 7); - assert(f8 == 8); - assert(f9 == 9); - assert(f10 == 10); -} - -void fwd(void) -{ - forwardcalls++; -} - -IMP forward(id o, SEL s) -{ - assert(o == objc_getClass("MsgTest")); - if (sel_isEqual(s, sel_registerName("missing"))) - { - return (IMP)fwd; - } - return (IMP)fwdMany; -} - -static struct objc_slot slot; -struct objc_slot *forward_slot(id o, SEL s) -{ - slot.method = (IMP)fwd; - return &slot; -} - - - -int main(void) -{ -#ifdef __GNUSTEP_MSGSEND__ - __objc_msg_forward2 = forward; - __objc_msg_forward3 = forward_slot; - TestCls = objc_getClass("MsgTest"); - int exceptionThrown = 0; - @try { - objc_msgSend(TestCls, @selector(foo)); - } @catch (id e) - { - assert((TestCls == e) && "Exceptions propagate out of +initialize"); - exceptionThrown = 1; - } - assert(exceptionThrown && "An exception was thrown"); - assert((id)0x42 == objc_msgSend(TestCls, @selector(foo))); - objc_msgSend(TestCls, @selector(nothing)); - objc_msgSend(TestCls, @selector(missing)); - assert(forwardcalls == 1); - assert(0 == objc_msgSend(0, @selector(nothing))); - id a = objc_msgSend(objc_getClass("MsgTest"), @selector(foo)); - assert((id)0x42 == a); - a = objc_msgSend(TestCls, @selector(foo)); - assert((id)0x42 == a); - assert(objc_registerSmallObjectClass_np(objc_getClass("MsgTest"), 1)); - a = objc_msgSend((id)01, @selector(foo)); - assert((id)0x42 == a); - s ret = ((s(*)(id, SEL))objc_msgSend_stret)(TestCls, @selector(sret)); - assert(ret.a == 1); - assert(ret.b == 2); - assert(ret.c == 3); - assert(ret.d == 4); - assert(ret.e == 5); - if (sizeof(id) == 8) - { - assert(objc_registerSmallObjectClass_np(objc_getClass("MsgTest"), 3)); - ret = ((s(*)(id, SEL))objc_msgSend_stret)((id)3, @selector(sret)); - assert(ret.a == 1); - assert(ret.b == 2); - assert(ret.c == 3); - assert(ret.d == 4); - assert(ret.e == 5); - } - Fake *f = nil; - assert(0 == [f izero]); - assert(0 == [f dzero]); - assert(0 == [f ldzero]); - assert(0 == [f fzero]); - // Call manyArgs with objc_msgSend explicitly to test the slow lookup path - SEL manyArgsSel = sel_registerName("manyArgs::::::::::::::::::::::"); - ((fwdManyFunc)objc_msgSend)(TestCls, manyArgsSel, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - assert(forwardcalls == 2); - [TestCls manyArgs: 0 : 1 : 2 : 3: 4: 5: 6: 7: 8: 9: 10 : 0 : 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10]; - assert(forwardcalls == 3); -#ifdef BENCHMARK - const int iterations = 1000000000; - double times[3]; - clock_t c1, c2; - c1 = clock(); - for (int i=0 ; i -#include -#include "../objc/runtime.h" -#include "../objc/hooks.h" - -// Pass and return for type size <= 8 bytes. -struct S1 { - int a[2]; -}; - -// Pass and return hfa <= 8 bytes -struct F1 { - float a[2]; -}; - -// Pass and return type size <= 16 bytes -struct S2 { - int a[4]; -}; - -// Pass and return for type size > 16 bytes. -struct S3 { - int a[5]; -}; - -// Pass and return aggregate (of size < 16 bytes) with non-trivial destructor. -// Sret and inreg: Returned in x0 -struct S4 { - int a[3]; - ~S4(); -}; -S4::~S4() { -} - -// Pass and return an object with a user-provided constructor (passed directly, -// returned indirectly) -struct S5 { - S5(); - int x; -}; -S5::S5() { - x = 42; -} - -Class TestCls; -#ifdef __has_attribute -#if __has_attribute(objc_root_class) -__attribute__((objc_root_class)) -#endif -#endif -@interface MsgTest { id isa; } @end -@implementation MsgTest -+ (S1) smallS1 { - assert(TestCls == self); - assert(strcmp("smallS1", sel_getName(_cmd)) == 0); - - S1 x; - x.a[0] = 0; - x.a[1] = 1; - return x; - -} -+ (F1) smallF1 { - assert(TestCls == self); - assert(strcmp("smallF1", sel_getName(_cmd)) == 0); - - F1 x; - x.a[0] = 0.2f; - x.a[1] = 0.5f; - return x; -} -+ (S2) smallS2 { - assert(TestCls == self); - assert(strcmp("smallS2", sel_getName(_cmd)) == 0); - - S2 x; - for (int i = 0; i < 4; i++) { - x.a[i] = i; - } - return x; -} -+ (S3) stretS3 { - assert(TestCls == self); - assert(strcmp("stretS3", sel_getName(_cmd)) == 0); - - S3 x; - for (int i = 0; i < 5; i++) { - x.a[i] = i; - } - return x; -} -+ (S4) stretInRegS4 { - assert(TestCls == self); - assert(strcmp("stretInRegS4", sel_getName(_cmd)) == 0); - - S4 x; - for (int i = 0; i < 3; i++) { - x.a[i] = i; - } - return x; -} -+ (S5) stretInRegS5 { - assert(TestCls == self); - assert(strcmp("stretInRegS5", sel_getName(_cmd)) == 0); - - return S5(); -} -@end - -int main(int argc, char *argv[]) { - #ifdef __GNUSTEP_MSGSEND__ - TestCls = objc_getClass("MsgTest"); - - // Returned in x0 - S1 ret = ((S1(*)(id, SEL))objc_msgSend)(TestCls, @selector(smallS1)); - assert(ret.a[0] == 0); - assert(ret.a[1] == 1); - - F1 retF1 = ((F1(*)(id, SEL))objc_msgSend)(TestCls, @selector(smallF1)); - assert(retF1.a[0] == 0.2f); - assert(retF1.a[1] == 0.5f); - - // Returned in x0 and x1 - S2 ret2 = ((S2(*)(id, SEL))objc_msgSend)(TestCls, @selector(smallS2)); - for (int i = 0; i < 4; i++) { - assert(ret2.a[i] == i); - } - - // Indirect result register x8 used - S3 ret3 = ((S3(*)(id, SEL))objc_msgSend_stret)(TestCls, @selector(stretS3)); - for (int i = 0; i < 5; i++) { - assert(ret3.a[i] == i); - } - - // Stret with inreg. Returned in x0. - S4 ret4 = ((S4(*)(id, SEL))objc_msgSend_stret2)(TestCls, @selector(stretInRegS4)); - for (int i = 0; i < 3; i++) { - assert(ret4.a[i] == i); - } - - // Stret with inreg. Returned in x0. - S5 ret5 = ((S5(*)(id, SEL))objc_msgSend_stret2)(TestCls, @selector(stretInRegS5)); - assert(ret5.x == 42); - - return 0; - #endif // __GNUSTEP_MSGSEND__ - return 77; -} diff --git a/Test/setSuperclass.m b/Test/setSuperclass.m deleted file mode 100644 index ba2f4d7..0000000 --- a/Test/setSuperclass.m +++ /dev/null @@ -1,328 +0,0 @@ -#include "../objc/runtime.h" -#include -#include - -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -__attribute__((objc_root_class)) -@interface Root -{ - id isa; -} -@end -@interface DefaultSuperclass: Root @end - -// test: new superclass when not initialized at the time of class_setSuperclass -@interface NotInitializedSuperclass1: Root @end -@interface Subclass1: DefaultSuperclass @end - -// test: new superclass when already initialized at the time of class_setSuperclass -@interface NotInitializedSuperclass2: Root @end -@interface Subclass2: DefaultSuperclass @end -@interface Subclass2Subclass: Subclass2 @end - -@interface ChangesDuringInitialize: DefaultSuperclass @end - -// test: class gets reparented under its parent's parent. -@interface RemovedFromHierarchy: DefaultSuperclass @end -@interface MovesUpwardsInHierarchy: RemovedFromHierarchy @end - -// test: one class initializes anotherwhile initializing during class_setSuperclass -@interface OtherInitializedClass: Root @end -@interface InitializesOneClassWhileBeingInitialized: NotInitializedSuperclass2 @end -@interface Subclass3: DefaultSuperclass @end - -// test: transitioning an initialized class to an initialized superclass -// Test4Subclass only inherits access to "onlyExistsOnFinalSuperclass" from -// its new superclass's superclass (Test4FinalSuperclass) -@interface Test4Subclass: Root @end -@interface Test4FinalSuperclass: DefaultSuperclass @end - -@implementation Root -+ (Class)class { return self; } -+ (BOOL)respondsToSelector:(SEL)selector { - return class_respondsToSelector(object_getClass(self), selector); -} -+ (BOOL)instancesRespondToSelector:(SEL)selector { - return class_respondsToSelector(self, selector); -} -@end - -@implementation NotInitializedSuperclass1 -static BOOL _notInitializedSuperclass1Initialized = NO; -+ (void)initialize { - _notInitializedSuperclass1Initialized = YES; -} -+ (void)existsOnNotInitializedSuperclassMeta { }; -+ (int)sameNameMeta { return 12; } -+ (int)overriddenMeta { return 12; } - -- (BOOL)existsOnNotInitializedSuperclass { return YES; } -- (int)sameName { return 2; } -- (int)overridden { return 2; } -@end - -@implementation NotInitializedSuperclass2 -static BOOL _notInitializedSuperclass2Initialized = NO; -+ (void)initialize { - _notInitializedSuperclass2Initialized = YES; -} -+ (void)existsOnNotInitializedSuperclassMeta { }; -+ (int)sameNameMeta { return 13; } -+ (int)overriddenMeta { return 13; } -- (BOOL)existsOnNotInitializedSuperclass { return YES; } -- (int)sameName { return 3; } -- (int)overridden { return 3; } -@end - -@implementation DefaultSuperclass -static BOOL _alreadyInitializedSuperclassInitialized = NO; -+ (void)initialize { - _alreadyInitializedSuperclassInitialized = YES; -} -+ (void)existsOnDefaultSuperclassMeta { }; -+ (int)sameNameMeta { return 14; } -+ (int)overriddenMeta { return 14; } -- (BOOL)existsOnDefaultSuperclass { return YES; } -- (int)sameName { return 4; } -- (int)overridden { return 4; } -@end - -@implementation Subclass1 -static BOOL _subclass1Initialized = NO; -+ (void)initialize { - _subclass1Initialized = YES; -} -+ (int)overriddenMeta { return 15; } // shadows 14 -- (BOOL)existsOnSubclass1 { return YES; } -- (int)overridden { return 5; } // shadows 4 -@end - -@implementation Subclass2 -static BOOL _subclass2Initialized = NO; -+ (void)initialize { - _subclass2Initialized = YES; -} -+ (int)overriddenMeta { return 16; } // shadows 14 -- (BOOL)existsOnSubclass2 { return YES; } -- (int)overridden { return 6; } // shadows 4 -- (int)intermediateOverride { return 100; } -@end - -@implementation Subclass2Subclass -- (int)intermediateOverride { return 200; } -@end - -@implementation ChangesDuringInitialize -+ (void)initialize { - class_setSuperclass(self, objc_getClass("NotInitializedSuperclass1")); -} -+ (int)overriddenMeta { return 18; } -@end - -@implementation RemovedFromHierarchy -+ (int)overriddenMeta { return 19; } // shadows 14 on DefaultSuperClass -+ (int)sameNameMeta { return 19; } // shadows 14 on DefaultSuperClass -+ (void)onlyExistsOnRemovedClassMeta { } -- (void)onlyExistsOnRemovedClass { } -@end - -@implementation MovesUpwardsInHierarchy -+ (int)overriddenMeta { return 20; } // shadows 19 on RemovedFromHierarchy or 14 on DefaultSuperClass -@end - -@implementation OtherInitializedClass -static BOOL _otherInitializedClassInitialized = NO; -+ (void)initialize { - _otherInitializedClassInitialized = YES; -} -@end - -@implementation InitializesOneClassWhileBeingInitialized -+ (void)initialize { - [OtherInitializedClass class]; -} -@end - -@implementation Subclass3 -@end - -@implementation Test4Subclass -@end -@implementation Test4FinalSuperclass -+ (int)onlyExistsOnFinalSuperclassMeta { return 501; } -- (int)onlyExistsOnFinalSuperclass { return 500; } -@end - -static int failures = 0; - -#define expect(x) do \ -{ \ - if (!(x)) \ - { \ - fprintf(stderr, "expectation FAILED: %s\n", #x); \ - ++failures; \ - } \ -} while(0) - -int main(int argc, char **argv) { - /* Transitioning to a new superclass before +initialize has been called */ - { - Class subclass1 = objc_getClass("Subclass1"); - Class secondSuperclass = objc_getClass("NotInitializedSuperclass1"); - - assert(!_notInitializedSuperclass1Initialized); - assert(!_subclass1Initialized); - - class_setSuperclass(subclass1, secondSuperclass); - - // assert: dtable has not been installed; new superclass is still not initialized - assert(!_notInitializedSuperclass1Initialized); - - [Subclass1 class]; - // initialization and dtable installation has taken place - assert(_notInitializedSuperclass1Initialized); - - Subclass1 *subclass1instance1 = class_createInstance(subclass1, 0); - - // CLASS - // can call method on subclass - expect([subclass1instance1 existsOnSubclass1]); - // can call method on _new_ superclass - expect([(id)subclass1instance1 existsOnNotInitializedSuperclass]); - // does not respond to selector from original superclass - expect(![subclass1 instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]); - // *does* respond to selector from new superclass - expect([subclass1 instancesRespondToSelector:@selector(existsOnNotInitializedSuperclass)]); - // method existing on both old and new superclass kept, IMP updated - expect(2 == [subclass1instance1 sameName]); - // method existing on subclass, old and new superclass kept, IMP kept - expect(5 == [subclass1instance1 overridden]); - - - // METACLASS - // metaclass does not respond to selector from original meta superclass - expect(![subclass1 respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]); - // metaclass *does* respond to selector from new meta superclass - expect([subclass1 respondsToSelector:@selector(existsOnNotInitializedSuperclassMeta)]); - // method existing on both old and new superclass kept, IMP updated - expect(12 == [subclass1 sameNameMeta]); - // method existing on subclass, old and new superclass kept, IMP kept - expect(15 == [subclass1 overriddenMeta]); - } - - /* Transitioning to a new superclass when +initialize has already been called */ - { - Class subclass2 = objc_getClass("Subclass2"); - Class secondSuperclass = objc_getClass("NotInitializedSuperclass2"); - assert(!_notInitializedSuperclass2Initialized); - assert(!_subclass2Initialized); - - [Subclass2 class]; - [Subclass2Subclass class]; // Make sure the subclass is initialized too. - assert(_alreadyInitializedSuperclassInitialized); - assert(_subclass2Initialized); - - Subclass2 *subclass2instance1 = class_createInstance(subclass2, 0); - assert([subclass2instance1 existsOnSubclass2]); - - class_setSuperclass(subclass2, secondSuperclass); - assert(_notInitializedSuperclass2Initialized); - - // CLASS - // can call method on subclass - expect([subclass2instance1 existsOnSubclass2]); - // can call method on _new_ superclass - expect([(id)subclass2instance1 existsOnNotInitializedSuperclass]); - // does not respond to selector from original superclass - expect(![subclass2 instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]); - // *does* respond to selector from new superclass - expect([subclass2 instancesRespondToSelector:@selector(existsOnNotInitializedSuperclass)]); - - // method existing on both old and new superclass kept, IMP updated - expect(3 == [subclass2instance1 sameName]); - // method existing on subclass, old and new superclass kept, IMP kept - expect(6 == [subclass2instance1 overridden]); - // method existing only on subclass preserved - expect(100 == [subclass2instance1 intermediateOverride]); - - // METACLASS - // metaclass does not respond to selector from original meta superclass - expect(![subclass2 respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]); - // metaclass *does* respond to selector from new meta superclass - expect([subclass2 respondsToSelector:@selector(existsOnNotInitializedSuperclassMeta)]); - // method existing on both old and new superclass kept, IMP updated - expect(13 == [subclass2 sameNameMeta]); - // method existing on subclass, old and new superclass kept, IMP kept - expect(16 == [subclass2 overriddenMeta]); - - // SUBCLASS - Subclass2 *subclass2subclassInstance = class_createInstance([Subclass2Subclass class], 0); - expect(![Subclass2Subclass instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]); - expect(![Subclass2Subclass respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]); - expect(3 == [subclass2subclassInstance sameName]); - expect(6 == [subclass2subclassInstance overridden]); - expect(200 == [subclass2subclassInstance intermediateOverride]); - expect(13 == [Subclass2Subclass sameNameMeta]); - expect(16 == [Subclass2Subclass overriddenMeta]); - } - - /* Transitioning ourselves to a new superclass while +initialize is running */ - { - expect(12 == [ChangesDuringInitialize sameNameMeta]); - expect(18 == [ChangesDuringInitialize overriddenMeta]); - } - - /* Transitioning to a superclass that's in our inheritance hierarchy already */ - { - assert(20 == [MovesUpwardsInHierarchy overriddenMeta]); - assert(19 == [MovesUpwardsInHierarchy sameNameMeta]); - assert([MovesUpwardsInHierarchy respondsToSelector:@selector(onlyExistsOnRemovedClassMeta)]); - assert([MovesUpwardsInHierarchy instancesRespondToSelector:@selector(onlyExistsOnRemovedClass)]); - - class_setSuperclass([MovesUpwardsInHierarchy class], [DefaultSuperclass class]); - - expect(20 == [MovesUpwardsInHierarchy overriddenMeta]); // still overridden - expect(14 == [MovesUpwardsInHierarchy sameNameMeta]); // falls back to DefaultSuperclass - expect(![MovesUpwardsInHierarchy respondsToSelector:@selector(onlyExistsOnRemovedClassMeta)]); - expect(![MovesUpwardsInHierarchy instancesRespondToSelector:@selector(onlyExistsOnRemovedClass)]); - } - - /* Transitioning to a superclass that may cause initialize lock contention */ - { - assert(!_otherInitializedClassInitialized); - expect(14 == [Subclass3 sameNameMeta]); - expect(14 == [Subclass3 overriddenMeta]); - - class_setSuperclass([Subclass3 class], objc_getClass("InitializesOneClassWhileBeingInitialized")); - - expect(_otherInitializedClassInitialized); - expect(13 == [Subclass3 sameNameMeta]); - expect(13 == [Subclass3 overriddenMeta]); - } - - /* Transitioning an initialized class to an initialized superclass. */ - { - Class test4subclass = objc_getClass("Test4Subclass"); - Class newSuperclass = objc_getClass("Test4FinalSuperclass"); - - // Make sure every class in the hierarchy is initialized. - [Test4Subclass class]; - [Test4FinalSuperclass class]; - - expect(![test4subclass respondsToSelector:@selector(onlyExistsOnFinalSuperclassMeta)]); - expect(![test4subclass instancesRespondToSelector:@selector(onlyExistsOnFinalSuperclass)]); - - class_setSuperclass(test4subclass, newSuperclass); - - Test4Subclass *test4instance = class_createInstance(test4subclass, 0); - - expect([test4subclass respondsToSelector:@selector(onlyExistsOnFinalSuperclassMeta)]); - expect([test4subclass instancesRespondToSelector:@selector(onlyExistsOnFinalSuperclass)]); - - expect(501 == [(id)test4subclass onlyExistsOnFinalSuperclassMeta]); - expect(500 == [(id)test4instance onlyExistsOnFinalSuperclass]); - } - - return failures; -} diff --git a/Test/zeroSizedIVar.m b/Test/zeroSizedIVar.m deleted file mode 100644 index 2200536..0000000 --- a/Test/zeroSizedIVar.m +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include "Test.h" - - -typedef uintptr_t NSUInteger; - -@interface NSArray : Test -{ - NSUInteger count; - id objects[0]; -} -@end - -@implementation NSArray @end - -@interface BitfieldTest : Test -{ - BOOL flag1:1; - BOOL flag2:1; - BOOL flag3:1; -} -@end - -@implementation BitfieldTest @end - -@interface BitfieldTest2 : Test -{ - BOOL flag1:1; - BOOL flag2:1; - BOOL flag3:1; - int x; -} -@end - -@implementation BitfieldTest2 @end - - -int main() -{ - Class nsarray = objc_getClass("NSArray"); - assert(nsarray); - assert(class_getInstanceSize(nsarray) == (sizeof(Class) + sizeof(NSUInteger))); - Ivar count = class_getInstanceVariable(nsarray, "count"); - assert(ivar_getOffset(count) == sizeof(id)); - - Class bitfield = objc_getClass("BitfieldTest"); - assert(bitfield); - Ivar flag1 = class_getInstanceVariable(bitfield, "flag1"); - assert(flag1); - assert(ivar_getOffset(flag1) == sizeof(id)); - Ivar flag2 = class_getInstanceVariable(bitfield, "flag2"); - assert(flag2); - assert(ivar_getOffset(flag2) == sizeof(id)); - Ivar flag3 = class_getInstanceVariable(bitfield, "flag3"); - assert(flag3); - assert(ivar_getOffset(flag3) == sizeof(id)); - assert(ivar_getOffset(flag3) + sizeof(BOOL) <= class_getInstanceSize(bitfield)); - - bitfield = objc_getClass("BitfieldTest2"); - flag1 = class_getInstanceVariable(bitfield, "flag1"); - flag3 = class_getInstanceVariable(bitfield, "flag3"); - Ivar x = class_getInstanceVariable(bitfield, "x"); - assert(ivar_getOffset(flag1) == ivar_getOffset(flag3)); - assert(ivar_getOffset(x) > ivar_getOffset(flag3)); - assert(ivar_getOffset(x) + sizeof(int) <= class_getInstanceSize(bitfield)); -} - diff --git a/abi_version.c b/abi_version.c deleted file mode 100644 index cfddfe0..0000000 --- a/abi_version.c +++ /dev/null @@ -1,130 +0,0 @@ -#include "visibility.h" -#include "objc/runtime.h" -#include "module.h" -#include "gc_ops.h" -#include -#include -#include - -/** - * The smallest ABI version number of loaded modules. - */ -static unsigned long min_loaded_version; -/** - * The largest ABI version number of loaded modules. - */ -static unsigned long max_loaded_version; - -/** - * Structure defining the compatibility between Objective-C ABI versions. - */ -struct objc_abi_version -{ - /** Version of this ABI. */ - unsigned long version; - /** Lowest ABI version that this is compatible with. */ - unsigned long min_compatible_version; - /** Highest ABI version compatible with this. */ - unsigned long max_compatible_version; - /** Size of the module structure for this ABI version. */ - unsigned long module_size; -}; - -enum -{ - gcc_abi = 8, - gnustep_abi = 9, - gc_abi = 10 -}; - -/** - * List of supported ABIs. - */ -static struct objc_abi_version known_abis[] = -{ - /* GCC ABI. */ - {gcc_abi, gcc_abi, gnustep_abi, sizeof(struct objc_module_abi_8)}, - /* Non-fragile ABI. */ - {gnustep_abi, gcc_abi, gc_abi, sizeof(struct objc_module_abi_8)}, - /* GC ABI. Adds a field describing the GC mode. */ - {gc_abi, gcc_abi, gc_abi, sizeof(struct objc_module_abi_10)} -}; - -static int known_abi_count = - (sizeof(known_abis) / sizeof(struct objc_abi_version)); - -#define FAIL_IF(x, msg) do {\ - if (x)\ - {\ - fprintf(stderr, "Objective-C ABI Error: %s while loading %s\n", msg, module->name);\ - return NO;\ - }\ -} while(0) - -static BOOL endsWith(const char *string, const char *suffix) -{ - if (NULL == string) { return NO; } - char *interior = strstr(string, suffix); - return (interior && (strlen(suffix) == strlen(interior))); -} - -PRIVATE BOOL objc_check_abi_version(struct objc_module_abi_8 *module) -{ - static int runtime_modules = 5; - // As a quick and ugly hack, skip these three tests for the .m files in the - // runtime. They should (in theory, at least) be aware of the GC mode and - // behave accordingly. - if (runtime_modules > 0) - { - if (endsWith(module->name, "properties.m") || - endsWith(module->name, "associate.m") || - endsWith(module->name, "arc.m") || - endsWith(module->name, "blocks_runtime.m") || - endsWith(module->name, "Protocol2.m")) - { - runtime_modules--; - return YES; - } - } - unsigned long version = module->version; - unsigned long module_size = module->size; - enum objc_gc_mode gc_mode = (version < gc_abi) ? GC_None - : ((struct objc_module_abi_10*)module)->gc_mode; - struct objc_abi_version *v = NULL; - for (int i=0 ; imodule_size != module_size), "Incorrect module size"); - // Only check for ABI compatibility if - if (min_loaded_version > 0) - { - FAIL_IF((v->min_compatible_version > min_loaded_version), - "Loading modules from incompatible ABIs"); - FAIL_IF((v->max_compatible_version < max_loaded_version), - "Loading modules from incompatible ABIs"); - if (min_loaded_version > version) - { - min_loaded_version = version; - } - if (max_loaded_version < version) - { - max_loaded_version = version; - } - } - else - { - min_loaded_version = version; - max_loaded_version = version; - } - - // We can't mix GC_None and GC_Required code, but we can mix any other - // combination - FAIL_IF((gc_mode == GC_Required), "GC code is no longer supported!"); - return YES; -} diff --git a/alias.h b/alias.h deleted file mode 100644 index fbd4d94..0000000 --- a/alias.h +++ /dev/null @@ -1,27 +0,0 @@ -/** Declaration of a helper function for getting class references from aliases. - Copyright (c) 2011 Free Software Foundation, Inc. - - Written by: Niels Grewe - Created: March 2011 - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ -#include "objc/runtime.h" - -OBJC_PUBLIC Class alias_getClass(const char *alias_name); diff --git a/alias_table.c b/alias_table.c deleted file mode 100644 index e6d1071..0000000 --- a/alias_table.c +++ /dev/null @@ -1,128 +0,0 @@ -/** A hash table for mapping compatibility aliases to classes. - Copyright (c) 2011 Free Software Foundation, Inc. - - Written by: Niels Grewe - Created: March 2011 - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#include "visibility.h" -#include "objc/runtime.h" -#include "class.h" -#include "lock.h" -#include "string_hash.h" - -#include - -struct objc_alias -{ - const char* name; - Class class; -}; - -typedef struct objc_alias Alias; - -static int alias_compare(const char *name, const Alias alias) -{ - return string_compare(name, alias.name); -} - -static int alias_hash(const Alias alias) -{ - return string_hash(alias.name); -} -static int alias_is_null(const Alias alias) -{ - return alias.name == NULL; -} -static Alias NullAlias; -#define MAP_TABLE_NAME alias_table_internal -#define MAP_TABLE_COMPARE_FUNCTION alias_compare -#define MAP_TABLE_HASH_KEY string_hash -#define MAP_TABLE_HASH_VALUE alias_hash -#define MAP_TABLE_VALUE_TYPE struct objc_alias -#define MAP_TABLE_VALUE_NULL alias_is_null -#define MAP_TABLE_VALUE_PLACEHOLDER NullAlias - -#include "hash_table.h" - -static alias_table_internal_table *alias_table; - -PRIVATE void init_alias_table(void) -{ - alias_table_internal_initialize(&alias_table, 128); -} - - -static Alias alias_table_get_safe(const char *alias_name) -{ - return alias_table_internal_table_get(alias_table, alias_name); -} - - -OBJC_PUBLIC Class alias_getClass(const char *alias_name) -{ - if (NULL == alias_name) - { - return NULL; - } - - Alias alias = alias_table_get_safe(alias_name); - - if (NULL == alias.name) - { - return NULL; - } - - return alias.class; -} - -PRIVATE void alias_table_insert(Alias alias) -{ - alias_table_internal_insert(alias_table, alias); -} - -OBJC_PUBLIC BOOL class_registerAlias_np(Class class, const char *alias) -{ - if ((NULL == alias) || (NULL == class)) - { - return 0; - } - - class = (Class)objc_getClass(class->name); - - /* - * If there already exists a matching alias, determine whether we the existing - * alias is the correct one. Please note that objc_getClass() goes through the - * alias lookup and will create the alias table if necessary. - */ - Class existingClass = (Class)objc_getClass(alias); - if (NULL != existingClass) - { - /* - * Return YES if the alias has already been registered for this very - * class, and NO if the alias is already used for another class. - */ - return (class == existingClass); - } - Alias newAlias = { strdup(alias), class }; - alias_table_insert(newAlias); - return 1; -} diff --git a/arc.mm b/arc.mm deleted file mode 100644 index ee3efbf..0000000 --- a/arc.mm +++ /dev/null @@ -1,1057 +0,0 @@ -#define _LIBCPP_NO_EXCEPTIONS 1 -#define TSL_NO_EXCEPTIONS 1 -// Libc++ < 13 requires this for to be header only. It is ignored in -// libc++ >= 14 -#define _LIBCPP_DISABLE_EXTERN_TEMPLATE 1 -#include -#include -#include -#include -#include -#import "lock.h" -#import "objc/runtime.h" -#ifdef EMBEDDED_BLOCKS_RUNTIME -#import "objc/blocks_private.h" -#import "objc/blocks_runtime.h" -#else -#include -#include -#endif -#import "nsobject.h" -#import "class.h" -#import "selector.h" -#import "visibility.h" -#import "objc/hooks.h" -#import "objc/objc-arc.h" -#include "objc/message.h" - -/** - * Helper to send a manual message for retain / release. - * We cannot use [object retain] and friends because recent clang will turn - * that into a call to `objc_retain`, causing infinite recursion. - */ -#ifdef __GNUSTEP_MSGSEND__ -#define ManualRetainReleaseMessage(object, selName, types) \ - ((types)objc_msgSend)(object, @selector(selName)) -#else -#define ManualRetainReleaseMessage(object, selName, types) \ - ((types)(objc_msg_lookup(object, @selector(selName))))(object, @selector(selName)) -#endif - -extern "C" id (*_objc_weak_load)(id object); - -#if defined(_WIN32) -// We're using the Fiber-Local Storage APIs on Windows -// because the TLS APIs won't pass app certification. -// Additionally, the FLS API surface is 1:1 mapped to -// the TLS API surface when fibers are not in use. -# include "safewindows.h" -# define arc_tls_store FlsSetValue -# define arc_tls_load FlsGetValue -# define TLS_CALLBACK(name) void WINAPI name - -typedef DWORD arc_tls_key_t; -typedef void WINAPI(*arc_cleanup_function_t)(void*); -static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFunction) -{ - return FlsAlloc(cleanupFunction); -} - -#else // if defined(_WIN32) - -# ifndef NO_PTHREADS -# include -# define arc_tls_store pthread_setspecific -# define arc_tls_load pthread_getspecific -# define TLS_CALLBACK(name) void name - -typedef pthread_key_t arc_tls_key_t; -typedef void (*arc_cleanup_function_t)(void*); -static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFunction) -{ - pthread_key_t key; - pthread_key_create(&key, cleanupFunction); - return key; -} -# endif -#endif - -#ifdef arc_tls_store -arc_tls_key_t ARCThreadKey; -#endif - -#ifndef HAVE_BLOCK_USE_RR2 -extern "C" -{ - extern struct objc_class _NSConcreteMallocBlock; - extern struct objc_class _NSConcreteStackBlock; - extern struct objc_class _NSConcreteGlobalBlock; - extern struct objc_class _NSConcreteAutoBlock; - extern struct objc_class _NSConcreteFinalizingBlock; -} -#endif - -@interface NSAutoreleasePool -+ (Class)class; -+ (id)new; -- (void)release; -@end - -#define POOL_SIZE (4096 / sizeof(void*) - (2 * sizeof(void*))) -/** - * Structure used for ARC-managed autorelease pools. This structure should be - * exactly one page in size, so that it can be quickly allocated. This does - * not correspond directly to an autorelease pool. The 'pool' returned by - * objc_autoreleasePoolPush() may be an interior pointer to one of these - * structures. - */ -struct arc_autorelease_pool -{ - /** - * Pointer to the previous autorelease pool structure in the chain. Set - * when pushing a new structure on the stack, popped during cleanup. - */ - struct arc_autorelease_pool *previous; - /** - * The current insert point. - */ - id *insert; - /** - * The remainder of the page, an array of object pointers. - */ - id pool[POOL_SIZE]; -}; - -struct arc_tls -{ - struct arc_autorelease_pool *pool; - id returnRetained; -}; - -/** - * Type-safe wrapper around calloc. - */ -template -static inline T* new_zeroed() -{ - return static_cast(calloc(sizeof(T), 1)); -} - -static inline struct arc_tls* getARCThreadData(void) -{ -#ifndef arc_tls_store - return NULL; -#else // !defined arc_tls_store - auto tls = static_cast(arc_tls_load(ARCThreadKey)); - if (NULL == tls) - { - tls = new_zeroed(); - arc_tls_store(ARCThreadKey, tls); - } - return tls; -#endif -} -static inline void release(id obj); - -/** - * Empties objects from the autorelease pool, stating at the head of the list - * specified by pool and continuing until it reaches the stop point. If the stop point is NULL then - */ -static void emptyPool(struct arc_tls *tls, void *stop) -{ - struct arc_autorelease_pool *stopPool = NULL; - if (NULL != stop) - { - stopPool = tls->pool; - while (1) - { - // Invalid stop location - if (NULL == stopPool) - { - return; - } - // NULL is the placeholder for the top-level pool - if (NULL == stop && stopPool->previous == NULL) - { - break; - } - // Stop location was found in this pool - if ((stop >= stopPool->pool) && (stop < &stopPool->pool[POOL_SIZE])) - { - break; - } - stopPool = stopPool->previous; - } - } - do { - while (tls->pool != stopPool) - { - while (tls->pool->insert > tls->pool->pool) - { - tls->pool->insert--; - // This may autorelease some other objects, so we have to work in - // the case where the autorelease pool is extended during a -release. - release(*tls->pool->insert); - } - void *old = tls->pool; - tls->pool = tls->pool->previous; - free(old); - } - if (NULL == tls->pool) break; - while ((stop == NULL || (tls->pool->insert > stop)) && - (tls->pool->insert > tls->pool->pool)) - { - tls->pool->insert--; - release(*tls->pool->insert); - } - } while (tls->pool != stopPool); - //fprintf(stderr, "New insert: %p. Stop: %p\n", tls->pool->insert, stop); -} - -#ifdef arc_tls_store -static TLS_CALLBACK(cleanupPools)(struct arc_tls* tls) -{ - if (tls->returnRetained) - { - release(tls->returnRetained); - tls->returnRetained = nil; - } - if (NULL != tls->pool) - { - emptyPool(tls, NULL); - assert(NULL == tls->pool); - } - if (tls->returnRetained) - { - cleanupPools(tls); - } - free(tls); -} -#endif - - -static Class AutoreleasePool; -static IMP NewAutoreleasePool; -static IMP DeleteAutoreleasePool; -static IMP AutoreleaseAdd; - -static BOOL useARCAutoreleasePool; - -static const long refcount_shift = 1; -/** - * We use the top bit of the reference count to indicate whether an object has - * ever had a weak reference taken. This lets us avoid acquiring the weak - * table lock for most objects on deallocation. - */ -static const size_t weak_mask = ((size_t)1)<<((sizeof(size_t)*8)-refcount_shift); -/** - * All of the bits other than the top bit are the real reference count. - */ -static const size_t refcount_mask = ~weak_mask; -static const size_t refcount_max = refcount_mask - 1; - -extern "C" OBJC_PUBLIC size_t object_getRetainCount_np(id obj) -{ - uintptr_t *refCount = ((uintptr_t*)obj) - 1; - uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); - size_t realCount = refCountVal & refcount_mask; - return realCount == refcount_mask ? 0 : realCount + 1; -} - -static id retain_fast(id obj, BOOL isWeak) -{ - uintptr_t *refCount = ((uintptr_t*)obj) - 1; - uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); - uintptr_t newVal = refCountVal; - do { - refCountVal = newVal; - size_t realCount = refCountVal & refcount_mask; - // If this object's reference count is already less than 0, then - // this is a spurious retain. This can happen when one thread is - // attempting to acquire a strong reference from a weak reference - // and the other thread is attempting to destroy it. The - // deallocating thread will decrement the reference count with no - // locks held and will then acquire the weak ref table lock and - // attempt to zero the weak references. The caller of this will be - // `objc_loadWeakRetained`, which will also hold the lock. If the - // serialisation is such that the locked retain happens after the - // decrement, then we return nil here so that the weak-to-strong - // transition doesn't happen and the object is actually destroyed. - // If the serialisation happens the other way, then the locked - // check of the reference count will happen after we've referenced - // this and we don't zero the references or deallocate. - if (realCount == refcount_mask) - { - return isWeak ? nil : obj; - } - // If the reference count is saturated, don't increment it. - if (realCount == refcount_max) - { - return obj; - } - realCount++; - realCount |= refCountVal & weak_mask; - uintptr_t updated = (uintptr_t)realCount; - newVal = __sync_val_compare_and_swap(refCount, refCountVal, updated); - } while (newVal != refCountVal); - return obj; -} - -extern "C" OBJC_PUBLIC id objc_retain_fast_np(id obj) -{ - return retain_fast(obj, NO); -} - -__attribute__((always_inline)) -static inline BOOL isPersistentObject(id obj) -{ - // No reference count manipulations on nil objects. - if (obj == nil) - { - return YES; - } - // Small objects are never accessibly by reference - if (isSmallObject(obj)) - { - return YES; - } - // Persistent objects are persistent. Safe to access isa directly here - // because we've already handled the small object case separately. - return objc_test_class_flag(obj->isa, objc_class_flag_permanent_instances); -} - -static inline id retain(id obj, BOOL isWeak) -{ - if (isPersistentObject(obj)) { return obj; } - Class cls = obj->isa; - if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) - { - return Block_copy(obj); - } - if (objc_test_class_flag(cls, objc_class_flag_fast_arc)) - { - return retain_fast(obj, isWeak); - } - return ManualRetainReleaseMessage(obj, retain, id(*)(id, SEL)); -} - -extern "C" OBJC_PUBLIC BOOL objc_release_fast_no_destroy_np(id obj) -{ - uintptr_t *refCount = ((uintptr_t*)obj) - 1; - uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); - uintptr_t newVal = refCountVal; - bool isWeak; - bool shouldFree; - do { - refCountVal = newVal; - size_t realCount = refCountVal & refcount_mask; - // If the reference count is saturated or deallocating, don't decrement it. - if (realCount >= refcount_max) - { - return NO; - } - realCount--; - isWeak = (refCountVal & weak_mask) == weak_mask; - shouldFree = realCount == -1; - realCount |= refCountVal & weak_mask; - uintptr_t updated = (uintptr_t)realCount; - newVal = __sync_val_compare_and_swap(refCount, refCountVal, updated); - } while (newVal != refCountVal); - - if (shouldFree) - { - if (isWeak) - { - if (!objc_delete_weak_refs(obj)) - { - return NO; - } - } - return YES; - } - return NO; -} - -extern "C" OBJC_PUBLIC void objc_release_fast_np(id obj) -{ - if (objc_release_fast_no_destroy_np(obj)) - { - [obj dealloc]; - } -} - -static inline void release(id obj) -{ - if (isPersistentObject(obj)) { return; } - Class cls = obj->isa; - if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) - { - if (cls == static_cast(&_NSConcreteStackBlock)) - { - return; - } - _Block_release(obj); - return; - } - if (objc_test_class_flag(cls, objc_class_flag_fast_arc)) - { - objc_release_fast_np(obj); - return; - } - return ManualRetainReleaseMessage(obj, release, void(*)(id, SEL)); -} - -static inline void initAutorelease(void) -{ - if (Nil == AutoreleasePool) - { - AutoreleasePool = objc_getClass("NSAutoreleasePool"); - if (Nil == AutoreleasePool) - { - useARCAutoreleasePool = YES; - } - else - { - useARCAutoreleasePool = (0 != class_getInstanceMethod(AutoreleasePool, - SELECTOR(_ARCCompatibleAutoreleasePool))); - if (!useARCAutoreleasePool) - { - [AutoreleasePool class]; - NewAutoreleasePool = class_getMethodImplementation(object_getClass(AutoreleasePool), - SELECTOR(new)); - DeleteAutoreleasePool = class_getMethodImplementation(AutoreleasePool, - SELECTOR(release)); - AutoreleaseAdd = class_getMethodImplementation(object_getClass(AutoreleasePool), - SELECTOR(addObject:)); - } - } - } -} - -static inline id autorelease(id obj) -{ - //fprintf(stderr, "Autoreleasing %p\n", obj); - if (useARCAutoreleasePool) - { - struct arc_tls *tls = getARCThreadData(); - if (NULL != tls) - { - struct arc_autorelease_pool *pool = tls->pool; - if (NULL == pool || (pool->insert >= &pool->pool[POOL_SIZE])) - { - pool = new_zeroed(); - pool->previous = tls->pool; - pool->insert = pool->pool; - tls->pool = pool; - } - *pool->insert = obj; - pool->insert++; - return obj; - } - } - if (objc_test_class_flag(classForObject(obj), objc_class_flag_fast_arc)) - { - initAutorelease(); - if (0 != AutoreleaseAdd) - { - AutoreleaseAdd(AutoreleasePool, SELECTOR(addObject:), obj); - } - return obj; - } - return ManualRetainReleaseMessage(obj, autorelease, id(*)(id, SEL)); -} - -extern "C" OBJC_PUBLIC unsigned long objc_arc_autorelease_count_np(void) -{ - struct arc_tls* tls = getARCThreadData(); - unsigned long count = 0; - if (!tls) { return 0; } - - for (struct arc_autorelease_pool *pool=tls->pool ; - NULL != pool ; - pool = pool->previous) - { - count += (((intptr_t)pool->insert) - ((intptr_t)pool->pool)) / sizeof(id); - } - return count; -} -extern "C" OBJC_PUBLIC unsigned long objc_arc_autorelease_count_for_object_np(id obj) -{ - struct arc_tls* tls = getARCThreadData(); - unsigned long count = 0; - if (!tls) { return 0; } - - for (struct arc_autorelease_pool *pool=tls->pool ; - NULL != pool ; - pool = pool->previous) - { - for (id* o = pool->insert-1 ; o >= pool->pool ; o--) - { - if (*o == obj) - { - count++; - } - } - } - return count; -} - -extern "C" OBJC_PUBLIC void *objc_autoreleasePoolPush(void) -{ - initAutorelease(); - struct arc_tls* tls = getARCThreadData(); - // If there is an object in the return-retained slot, then we need to - // promote it to the real autorelease pool BEFORE pushing the new - // autorelease pool. If we don't, then it may be prematurely autoreleased. - if ((NULL != tls) && (nil != tls->returnRetained)) - { - autorelease(tls->returnRetained); - tls->returnRetained = nil; - } - if (useARCAutoreleasePool) - { - if (NULL != tls) - { - struct arc_autorelease_pool *pool = tls->pool; - if (NULL == pool || (pool->insert >= &pool->pool[POOL_SIZE])) - { - pool = new_zeroed(); - pool->previous = tls->pool; - pool->insert = pool->pool; - tls->pool = pool; - } - // If there is no autorelease pool allocated for this thread, then - // we lazily allocate one the first time something is autoreleased. - return (NULL != tls->pool) ? tls->pool->insert : NULL; - } - } - initAutorelease(); - if (0 == NewAutoreleasePool) { return NULL; } - return NewAutoreleasePool(AutoreleasePool, SELECTOR(new)); -} -extern "C" OBJC_PUBLIC void objc_autoreleasePoolPop(void *pool) -{ - if (useARCAutoreleasePool) - { - struct arc_tls* tls = getARCThreadData(); - if (NULL != tls) - { - if (NULL != tls->pool) - { - emptyPool(tls, pool); - } - return; - } - } - DeleteAutoreleasePool(static_cast(pool), SELECTOR(release)); - struct arc_tls* tls = getARCThreadData(); - if (tls && tls->returnRetained) - { - release(tls->returnRetained); - tls->returnRetained = nil; - } -} - -extern "C" OBJC_PUBLIC id objc_autorelease(id obj) -{ - if (nil != obj) - { - obj = autorelease(obj); - } - return obj; -} - -extern "C" OBJC_PUBLIC id objc_autoreleaseReturnValue(id obj) -{ - if (!useARCAutoreleasePool) - { - struct arc_tls* tls = getARCThreadData(); - if (NULL != tls) - { - objc_autorelease(tls->returnRetained); - tls->returnRetained = obj; - return obj; - } - } - return objc_autorelease(obj); -} - -extern "C" OBJC_PUBLIC id objc_retainAutoreleasedReturnValue(id obj) -{ - // If the previous object was released with objc_autoreleaseReturnValue() - // just before return, then it will not have actually been autoreleased. - // Instead, it will have been stored in TLS. We just remove it from TLS - // and undo the fake autorelease. - // - // If the object was not returned with objc_autoreleaseReturnValue() then - // we actually autorelease the fake object. and then retain the argument. - // In tis case, this is equivalent to objc_retain(). - struct arc_tls* tls = getARCThreadData(); - if (NULL != tls) - { - // If we're using our own autorelease pool, just pop the object from the top - if (useARCAutoreleasePool) - { - if ((NULL != tls->pool) && - (*(tls->pool->insert-1) == obj)) - { - tls->pool->insert--; - return obj; - } - } - else if (obj == tls->returnRetained) - { - tls->returnRetained = NULL; - return obj; - } - } - return objc_retain(obj); -} - -extern "C" OBJC_PUBLIC id objc_retain(id obj) -{ - if (nil == obj) { return nil; } - return retain(obj, NO); -} - -extern "C" OBJC_PUBLIC id objc_retainAutorelease(id obj) -{ - return objc_autorelease(objc_retain(obj)); -} - -extern "C" OBJC_PUBLIC id objc_retainAutoreleaseReturnValue(id obj) -{ - if (nil == obj) { return obj; } - return objc_autoreleaseReturnValue(retain(obj, NO)); -} - - -extern "C" OBJC_PUBLIC id objc_retainBlock(id b) -{ - return static_cast(_Block_copy(b)); -} - -extern "C" OBJC_PUBLIC void objc_release(id obj) -{ - if (nil == obj) { return; } - release(obj); -} - -extern "C" OBJC_PUBLIC id objc_storeStrong(id *addr, id value) -{ - value = objc_retain(value); - id oldValue = *addr; - *addr = value; - objc_release(oldValue); - return value; -} - -//////////////////////////////////////////////////////////////////////////////// -// Weak references -//////////////////////////////////////////////////////////////////////////////// - -static int weakref_class; - -namespace { - -struct WeakRef -{ - void *isa = &weakref_class; - id obj = nullptr; - size_t weak_count = 1; - WeakRef(id o) : obj(o) {} -}; - -template -struct malloc_allocator -{ - typedef T value_type; - T* allocate(std::size_t n) - { - return static_cast(malloc(sizeof(T) * n)); - } - - void deallocate(T* p, std::size_t) - { - free(p); - } - - template - malloc_allocator &operator=(const malloc_allocator&) const - { - return *this; - } - - bool operator==(const malloc_allocator &) const - { - return true; - } - - template - operator malloc_allocator() const - { - return malloc_allocator(); - } -}; - -using weak_ref_table = tsl::robin_pg_map, - std::equal_to, - malloc_allocator>>; - -weak_ref_table &weakRefs() -{ - static weak_ref_table w{128}; - return w; -} - -mutex_t weakRefLock; - -} - -#ifdef HAVE_BLOCK_USE_RR2 -static const struct Block_callbacks_RR blocks_runtime_callbacks = { - sizeof(Block_callbacks_RR), - (void (*)(const void*))objc_retain, - (void (*)(const void*))objc_release, - (void (*)(const void*))objc_delete_weak_refs - }; -#endif - -PRIVATE extern "C" void init_arc(void) -{ - INIT_LOCK(weakRefLock); -#ifdef arc_tls_store - ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools); -#endif -#ifdef HAVE_BLOCK_USE_RR2 - _Block_use_RR2(&blocks_runtime_callbacks); -#endif -} - -/** - * Load from a weak pointer and return whether this really was a weak - * reference or a strong (not deallocatable) object in a weak pointer. The - * object will be stored in `obj` and the weak reference in `ref`, if one - * exists. - */ -__attribute__((always_inline)) -static BOOL loadWeakPointer(id *addr, id *obj, WeakRef **ref) -{ - id oldObj = *addr; - if (oldObj == nil) - { - *ref = NULL; - *obj = nil; - return NO; - } - if (classForObject(oldObj) == (Class)&weakref_class) - { - *ref = (WeakRef*)oldObj; - *obj = (*ref)->obj; - return YES; - } - *ref = NULL; - *obj = oldObj; - return NO; -} - -__attribute__((always_inline)) -static inline BOOL weakRefRelease(WeakRef *ref) -{ - ref->weak_count--; - if (ref->weak_count == 0) - { - weakRefs().erase(ref->obj); - delete ref; - return YES; - } - return NO; -} - -extern "C" void* block_load_weak(void *block); - -static BOOL setObjectHasWeakRefs(id obj) -{ - BOOL isGlobalObject = isPersistentObject(obj); - Class cls = isGlobalObject ? Nil : obj->isa; - if (obj && cls && objc_test_class_flag(cls, objc_class_flag_fast_arc)) - { - uintptr_t *refCount = ((uintptr_t*)obj) - 1; - if (obj) - { - uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); - uintptr_t newVal = refCountVal; - do { - refCountVal = newVal; - size_t realCount = refCountVal & refcount_mask; - // If this object has already been deallocated (or is in the - // process of being deallocated) then don't bother storing it. - if (realCount == refcount_mask) - { - obj = nil; - cls = Nil; - break; - } - // The weak ref flag is monotonic (it is set, never cleared) so - // don't bother trying to re-set it. - if ((refCountVal & weak_mask) == weak_mask) - { - break; - } - // Set the flag in the reference count to indicate that a weak - // reference has been taken. - // - // We currently hold the weak ref lock, so another thread - // racing to deallocate this object will have to wait to do so - // if we manage to do the reference count update first. This - // shouldn't be possible, because `obj` should be a strong - // reference and so it shouldn't be possible to deallocate it - // while we're assigning it. - uintptr_t updated = ((uintptr_t)realCount | weak_mask); - newVal = __sync_val_compare_and_swap(refCount, refCountVal, updated); - } while (newVal != refCountVal); - } - } - return isGlobalObject; -} - -WeakRef *incrementWeakRefCount(id obj) -{ - WeakRef *&ref = weakRefs()[obj]; - if (ref == nullptr) - { - ref = new WeakRef(obj); - } - else - { - assert(ref->obj == obj); - ref->weak_count++; - } - return ref; -} - -extern "C" OBJC_PUBLIC id objc_storeWeak(id *addr, id obj) -{ - LOCK_FOR_SCOPE(&weakRefLock); - WeakRef *oldRef; - id old; - loadWeakPointer(addr, &old, &oldRef); - // If the old and new values are the same, then we don't need to do anything. - if (old == obj) - { - return obj; - } - BOOL isGlobalObject = setObjectHasWeakRefs(obj); - // If we old ref exists, decrement its reference count. This may also - // delete the weak reference control block. - if (oldRef != NULL) - { - weakRefRelease(oldRef); - } - // If we're storing nil, then just write a null pointer. - if (nil == obj) - { - *addr = obj; - return nil; - } - if (isGlobalObject) - { - // If this is a global object, it's never deallocated, so secretly make - // this a strong reference. - *addr = obj; - return obj; - } - Class cls = classForObject(obj); - if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) - { - // Check whether the block is being deallocated and return nil if so - if (_Block_isDeallocating(obj)) { - *addr = nil; - return nil; - } - } - else if (object_getRetainCount_np(obj) == 0) - { - // If the object is being deallocated return nil. - *addr = nil; - return nil; - } - if (nil != obj) - { - *addr = (id)incrementWeakRefCount(obj); - } - return obj; -} - -extern "C" OBJC_PUBLIC BOOL objc_delete_weak_refs(id obj) -{ - LOCK_FOR_SCOPE(&weakRefLock); - if (objc_test_class_flag(classForObject(obj), objc_class_flag_fast_arc)) - { - // Don't proceed if the object isn't deallocating. - uintptr_t *refCount = ((uintptr_t*)obj) - 1; - uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); - size_t realCount = refCountVal & refcount_mask; - if (realCount != refcount_mask) - { - return NO; - } - } - auto &table = weakRefs(); - auto old = table.find(obj); - if (old != table.end()) - { - WeakRef *oldRef = old->second; - // The address of obj is likely to be reused, so remove it from - // the table so that we don't accidentally alias weak - // references - table.erase(old); - // Zero the object pointer. This prevents any other weak - // accesses from loading from this. This must be done after - // removing the ref from the table, because the compare operation - // tests the obj field. - oldRef->obj = nil; - // If the weak reference count is zero, then we should have - // already removed this. - assert(oldRef->weak_count > 0); - } - return YES; -} - -extern "C" OBJC_PUBLIC id objc_loadWeakRetained(id* addr) -{ - LOCK_FOR_SCOPE(&weakRefLock); - id obj; - WeakRef *ref; - // If this is really a strong reference (nil, or an non-deallocatable - // object), just return it. - if (!loadWeakPointer(addr, &obj, &ref)) - { - return obj; - } - // The object cannot be deallocated while we hold the lock (release - // will acquire the lock before attempting to deallocate) - if (obj == nil) - { - // If the object is destroyed, drop this reference to the WeakRef - // struct. - if (ref != NULL) - { - weakRefRelease(ref); - *addr = nil; - } - return nil; - } - Class cls = classForObject(obj); - if (objc_test_class_flag(cls, objc_class_flag_permanent_instances)) - { - return obj; - } - else if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block))) - { - obj = static_cast(block_load_weak(obj)); - if (obj == nil) - { - return nil; - } - // This is a defeasible retain operation that protects against another thread concurrently - // starting to deallocate the block. - if (_Block_tryRetain(obj)) - { - return obj; - } - return nil; - - } - else if (!objc_test_class_flag(cls, objc_class_flag_fast_arc)) - { - obj = _objc_weak_load(obj); - } - // _objc_weak_load() can return nil - if (obj == nil) { return nil; } - return retain(obj, YES); -} - -extern "C" OBJC_PUBLIC id objc_loadWeak(id* object) -{ - return objc_autorelease(objc_loadWeakRetained(object)); -} - -extern "C" OBJC_PUBLIC void objc_copyWeak(id *dest, id *src) -{ - // Don't retain or release. - // `src` is a valid pointer to a __weak pointer or nil. - // `dest` is a valid pointer to uninitialised memory. - // After this operation, `dest` should contain whatever `src` contained. - LOCK_FOR_SCOPE(&weakRefLock); - id obj; - WeakRef *srcRef; - loadWeakPointer(src, &obj, &srcRef); - *dest = *src; - if (srcRef) - { - srcRef->weak_count++; - } -} - -extern "C" OBJC_PUBLIC void objc_moveWeak(id *dest, id *src) -{ - // Don't retain or release. - // `dest` is a valid pointer to uninitialized memory. - // `src` is a valid pointer to a __weak pointer. - // This operation moves from *src to *dest and must be atomic with respect - // to other stores to *src via `objc_storeWeak`. - // - // Acquire the lock so that we guarantee the atomicity. We could probably - // optimise this by doing an atomic exchange of `*src` with `nil` and - // storing the result in `dest`, but it's probably not worth it unless weak - // references are a bottleneck. - LOCK_FOR_SCOPE(&weakRefLock); - *dest = *src; - *src = nil; -} - -extern "C" OBJC_PUBLIC void objc_destroyWeak(id* obj) -{ - LOCK_FOR_SCOPE(&weakRefLock); - WeakRef *oldRef; - id old; - loadWeakPointer(obj, &old, &oldRef); - // If the old ref exists, decrement its reference count. This may also - // delete the weak reference control block. - if (oldRef != NULL) - { - weakRefRelease(oldRef); - } -} - -extern "C" OBJC_PUBLIC id objc_initWeak(id *addr, id obj) -{ - if (obj == nil) - { - *addr = nil; - return nil; - } - LOCK_FOR_SCOPE(&weakRefLock); - BOOL isGlobalObject = setObjectHasWeakRefs(obj); - if (isGlobalObject) - { - // If this is a global object, it's never deallocated, so secretly make - // this a strong reference. - *addr = obj; - return obj; - } - // If the object is being deallocated return nil. - if (object_getRetainCount_np(obj) == 0) - { - *addr = nil; - return nil; - } - if (nil != obj) - { - *(WeakRef**)addr = incrementWeakRefCount(obj); - } - return obj; -} diff --git a/asmconstants.h b/asmconstants.h deleted file mode 100644 index d15581c..0000000 --- a/asmconstants.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifdef __LP64__ -#define DTABLE_OFFSET 64 -#define SMALLOBJ_BITS 3 -#define SHIFT_OFFSET 0 -#define DATA_OFFSET 8 -#define SLOT_OFFSET 0 -#elif defined(_WIN64) -// long is 32 bits on Win64, so struct objc_class is smaller. All other offsets are the same. -#define DTABLE_OFFSET 56 -#define SMALLOBJ_BITS 3 -#define SHIFT_OFFSET 0 -#define DATA_OFFSET 8 -#define SLOT_OFFSET 0 -#else -#define DTABLE_OFFSET 32 -#define SMALLOBJ_BITS 1 -#define SHIFT_OFFSET 0 -#define DATA_OFFSET 8 -#define SLOT_OFFSET 0 -#endif -#define SMALLOBJ_MASK ((1< -#include -#include -#include -#include "objc/runtime.h" -#include "objc/objc-arc.h" -#include "nsobject.h" -#include "spinlock.h" -#include "class.h" -#include "dtable.h" -#include "selector.h" -#include "lock.h" -#include "gc_ops.h" - -/** - * A single associative reference. Contains the key, value, and association - * policy. - */ -struct reference -{ - /** - * The key used for identifying this object. Opaque pointer, should be set - * to 0 when this slot is unused. - */ - const void *key; - /** - * The associated object. Note, if the policy is assign then this may be - * some other type of pointer... - */ - void *object; - /** - * Association policy. - */ - uintptr_t policy; -}; - -#define REFERENCE_LIST_SIZE 10 - -/** - * Linked list of references associated with an object. We assume that there - * won't be very many, so we don't bother with a proper hash table, and just - * iterate over a list. - */ -struct reference_list -{ - /** - * Next group of references. This is only ever used if we have more than - * 10 references associated with an object, which seems highly unlikely. - */ - struct reference_list *next; - /** - * Mutex. Only set for the first reference list in a chain. Used for - * @syncronize(). - */ - mutex_t lock; - /** - * Garbage collection type. This stores the location of all of the - * instance variables in the object that may contain pointers. - */ - void *gc_type; - /** - * Array of references. - */ - struct reference list[REFERENCE_LIST_SIZE]; -}; -enum -{ - OBJC_ASSOCIATION_ATOMIC = 0x300, -}; - -static BOOL isAtomic(uintptr_t policy) -{ - return (policy & OBJC_ASSOCIATION_ATOMIC) == OBJC_ASSOCIATION_ATOMIC; -} - -static struct reference* findReference(struct reference_list *list, const void *key) -{ - while (list) - { - for (int i=0 ; ilist[i].key == key) - { - return &list->list[i]; - } - } - list = list->next; - } - return NULL; -} -static void cleanupReferenceList(struct reference_list *list) -{ - if (NULL == list) { return; } - - cleanupReferenceList(list->next); - - for (int i=0 ; ilist[i]; - if (0 != r->key) - { - r->key = 0; - if (OBJC_ASSOCIATION_ASSIGN != r->policy) - { - // Full barrier - ensure that we've zero'd the key before doing - // this! - __sync_synchronize(); - objc_release(r->object); - } - r->object = 0; - r->policy = 0; - } - } -} - -static void freeReferenceList(struct reference_list *l) -{ - if (NULL == l) { return; } - freeReferenceList(l->next); - gc->free(l); -} - -static void setReference(struct reference_list *list, - const void *key, - void *obj, - uintptr_t policy) -{ - switch (policy) - { - // Ignore any unknown association policies - default: return; - case OBJC_ASSOCIATION_COPY_NONATOMIC: - case OBJC_ASSOCIATION_COPY: - obj = [(id)obj copy]; - break; - case OBJC_ASSOCIATION_RETAIN_NONATOMIC: - case OBJC_ASSOCIATION_RETAIN: - obj = objc_retain(obj); - case OBJC_ASSOCIATION_ASSIGN: - break; - } - // While inserting into the list, we need to lock it temporarily. - volatile int *lock = lock_for_pointer(list); - lock_spinlock(lock); - struct reference *r = findReference(list, key); - // If there's an existing reference, then we can update it, otherwise we - // have to install a new one - if (NULL == r) - { - // Search for an unused slot - r = findReference(list, 0); - if (NULL == r) - { - struct reference_list *l = list; - - while (NULL != l->next) { l = l->next; } - - l->next = gc->malloc(sizeof(struct reference_list)); - r = &l->next->list[0]; - } - r->key = key; - } - unlock_spinlock(lock); - // Now we only need to lock if the old or new property is atomic - BOOL needLock = isAtomic(r->policy) || isAtomic(policy); - if (needLock) - { - lock = lock_for_pointer(r); - lock_spinlock(lock); - } - @try - { - if (OBJC_ASSOCIATION_ASSIGN != r->policy) - { - objc_release(r->object); - } - } - @finally - { - r->policy = policy; - r->object = obj; - } - if (needLock) - { - unlock_spinlock(lock); - } -} - -static void deallocHiddenClass(id obj, SEL _cmd); - -static inline Class findHiddenClass(id obj) -{ - Class cls = obj->isa; - while (Nil != cls && - !objc_test_class_flag(cls, objc_class_flag_assoc_class)) - { - cls = class_getSuperclass(cls); - } - return cls; -} - -static Class allocateHiddenClass(Class superclass) -{ - Class newClass = - calloc(1, sizeof(struct objc_class) + sizeof(struct reference_list)); - - if (Nil == newClass) { return Nil; } - - // Set up the new class - newClass->isa = superclass->isa; - newClass->name = superclass->name; - // Uncomment this for debugging: it makes it easier to track which hidden - // class is which - // static int count; - //asprintf(&newClass->name, "%s%d", superclass->name, count++); - newClass->info = objc_class_flag_resolved | objc_class_flag_user_created | - objc_class_flag_hidden_class | objc_class_flag_assoc_class; - newClass->super_class = superclass; - newClass->dtable = uninstalled_dtable; - newClass->instance_size = superclass->instance_size; - - LOCK_RUNTIME_FOR_SCOPE(); - newClass->sibling_class = superclass->subclass_list; - superclass->subclass_list = newClass; - - return newClass; -} - -static inline Class initHiddenClassForObject(id obj) -{ - Class hiddenClass = allocateHiddenClass(obj->isa); - assert(!class_isMetaClass(obj->isa)); - static SEL cxx_destruct; - if (NULL == cxx_destruct) - { - cxx_destruct = sel_registerName(".cxx_destruct"); - } - const char *types = sizeof(void*) == 4 ? "v8@0:4" : "v16@0:8"; - class_addMethod(hiddenClass, cxx_destruct, - (IMP)deallocHiddenClass, types); - obj->isa = hiddenClass; - return hiddenClass; -} - -static void deallocHiddenClass(id obj, SEL _cmd) -{ - LOCK_RUNTIME_FOR_SCOPE(); - Class hiddenClass = findHiddenClass(obj); - // After calling [super dealloc], the object will no longer exist. - // Free the hidden class. - struct reference_list *list = object_getIndexedIvars(hiddenClass); - DESTROY_LOCK(&list->lock); - cleanupReferenceList(list); - freeReferenceList(list->next); - //fprintf(stderr, "Deallocating dtable %p\n", hiddenClass->dtable); - free_dtable(hiddenClass->dtable); - // We shouldn't have any subclasses left at this point - assert(hiddenClass->subclass_list == 0); - // Remove the class from the subclass list of its superclass - Class sub = hiddenClass->super_class->subclass_list; - if (sub == hiddenClass) - { - hiddenClass->super_class->subclass_list = hiddenClass->sibling_class; - } - else - { - while (sub != NULL) - { - if ((Class)sub->sibling_class == hiddenClass) - { - sub->sibling_class = hiddenClass->sibling_class; - break; - } - sub = sub->sibling_class; - } - } - obj->isa = hiddenClass->super_class; - // Free the introspection structures: - freeMethodLists(hiddenClass); - freeIvarLists(hiddenClass); - // Free the class - free(hiddenClass); -} - -static struct reference_list* referenceListForObject(id object, BOOL create) -{ - if (class_isMetaClass(object->isa)) - { - Class cls = (Class)object; - if ((NULL == cls->extra_data) && create) - { - volatile int *lock = lock_for_pointer(cls); - struct reference_list *list = gc->malloc(sizeof(struct reference_list)); - lock_spinlock(lock); - if (NULL == cls->extra_data) - { - INIT_LOCK(list->lock); - cls->extra_data = list; - unlock_spinlock(lock); - } - else - { - unlock_spinlock(lock); - gc->free(list); - } - } - return cls->extra_data; - } - Class hiddenClass = findHiddenClass(object); - if ((NULL == hiddenClass) && create) - { - volatile int *lock = lock_for_pointer(object); - lock_spinlock(lock); - hiddenClass = findHiddenClass(object); - if (NULL == hiddenClass) - { - hiddenClass = initHiddenClassForObject(object); - struct reference_list *list = object_getIndexedIvars(hiddenClass); - INIT_LOCK(list->lock); - } - unlock_spinlock(lock); - } - return hiddenClass ? object_getIndexedIvars(hiddenClass) : NULL; -} - -void objc_setAssociatedObject(id object, - const void *key, - id value, - objc_AssociationPolicy policy) -{ - if (isSmallObject(object)) { return; } - struct reference_list *list = referenceListForObject(object, YES); - setReference(list, key, value, policy); -} - -id objc_getAssociatedObject(id object, const void *key) -{ - if (isSmallObject(object)) { return nil; } - struct reference_list *list = referenceListForObject(object, NO); - if (NULL == list) { return nil; } - struct reference *r = findReference(list, key); - if (NULL != r) - { - return r->object; - } - if (class_isMetaClass(object->isa)) - { - return nil; - } - Class cls = object->isa; - while (Nil != cls) - { - while (Nil != cls && - !objc_test_class_flag(cls, objc_class_flag_assoc_class)) - { - cls = class_getSuperclass(cls); - } - if (Nil != cls) - { - struct reference_list *next_list = object_getIndexedIvars(cls); - if (list != next_list) - { - list = next_list; - struct reference *r = findReference(list, key); - if (NULL != r) - { - return r->object; - } - } - cls = class_getSuperclass(cls); - } - } - return nil; -} - - -void objc_removeAssociatedObjects(id object) -{ - if (isSmallObject(object)) { return; } - cleanupReferenceList(referenceListForObject(object, NO)); -} - -PRIVATE void *gc_typeForClass(Class cls) -{ - struct reference_list *list = referenceListForObject(cls, YES); - return list->gc_type; -} -PRIVATE void gc_setTypeForClass(Class cls, void *type) -{ - struct reference_list *list = referenceListForObject(cls, YES); - list->gc_type = type; -} - -OBJC_PUBLIC -int objc_sync_enter(id object) -{ - if ((object == 0) || isSmallObject(object)) { return 0; } - struct reference_list *list = referenceListForObject(object, YES); - LOCK(&list->lock); - return 0; -} - -OBJC_PUBLIC -int objc_sync_exit(id object) -{ - if ((object == 0) || isSmallObject(object)) { return 0; } - struct reference_list *list = referenceListForObject(object, NO); - if (NULL != list) - { - UNLOCK(&list->lock); - return 0; - } - return 1; -} - -static Class hiddenClassForObject(id object) -{ - if (isSmallObject(object)) { return nil; } - if (class_isMetaClass(object->isa)) - { - return object->isa; - } - Class hiddenClass = findHiddenClass(object); - if (NULL == hiddenClass) - { - volatile int *lock = lock_for_pointer(object); - lock_spinlock(lock); - hiddenClass = findHiddenClass(object); - if (NULL == hiddenClass) - { - hiddenClass = initHiddenClassForObject(object); - struct reference_list *list = object_getIndexedIvars(hiddenClass); - INIT_LOCK(list->lock); - } - unlock_spinlock(lock); - } - return hiddenClass; -} - -BOOL object_addMethod_np(id object, SEL name, IMP imp, const char *types) -{ - return class_addMethod(hiddenClassForObject(object), name, imp, types); -} - -IMP object_replaceMethod_np(id object, SEL name, IMP imp, const char *types) -{ - return class_replaceMethod(hiddenClassForObject(object), name, imp, types); -} -static char prototypeKey; - -id object_clone_np(id object) -{ - if (isSmallObject(object)) { return object; } - // Make sure that the prototype has a hidden class, so that methods added - // to it will appear in the clone. - referenceListForObject(object, YES); - id new = class_createInstance(object->isa, 0); - Class hiddenClass = initHiddenClassForObject(new); - struct reference_list *list = object_getIndexedIvars(hiddenClass); - INIT_LOCK(list->lock); - objc_setAssociatedObject(new, &prototypeKey, object, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return new; -} - -id object_getPrototype_np(id object) -{ - return objc_getAssociatedObject(object, &prototypeKey); -} diff --git a/block_to_imp.c b/block_to_imp.c deleted file mode 100644 index e3aa00a..0000000 --- a/block_to_imp.c +++ /dev/null @@ -1,338 +0,0 @@ -// On some platforms, we need _GNU_SOURCE to expose asprintf() -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#include -#include -#else -#include "safewindows.h" -#endif -#include "objc/runtime.h" -#include "objc/blocks_runtime.h" -#include "blocks_runtime.h" -#include "lock.h" -#include "visibility.h" -#include "asmconstants.h" // For PAGE_SIZE - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#if defined(_WIN32) && (defined(__arm__) || defined(__aarch64__)) - static inline void __clear_cache(void* start, void* end) { - FlushInstructionCache(GetCurrentProcess(), start, end - start); - } - #define clear_cache __clear_cache -#elif __has_builtin(__builtin___clear_cache) - #define clear_cache __builtin___clear_cache -#else - void __clear_cache(void* start, void* end); - #define clear_cache __clear_cache -#endif - - -/* QNX needs a special header for asprintf() */ -#ifdef __QNXNTO__ -#include -#endif - -#ifdef _WIN32 -#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP && _WIN32_WINNT >= 0x0A00 -// Prefer the *FromApp versions when we're being built in a Windows Store App context on -// Windows >= 10. *FromApp require the application to be manifested for "codeGeneration". -#define VirtualAlloc VirtualAllocFromApp -#define VirtualProtect VirtualProtectFromApp -#endif // App family partition - -#ifndef PROT_READ -#define PROT_READ 0x4 -#endif - -#ifndef PROT_WRITE -#define PROT_WRITE 0x2 -#endif - -#ifndef PROT_EXEC -#define PROT_EXEC 0x1 -#endif - -static int mprotect(void *buffer, size_t len, int prot) -{ - DWORD oldProt = 0, newProt = PAGE_NOACCESS; - // Windows doesn't offer values that can be ORed together... - if ((prot & PROT_WRITE)) - { - // promote to readwrite as there's no writeonly protection constant - newProt = PAGE_READWRITE; - } - else if ((prot & PROT_READ)) - { - newProt = PAGE_READONLY; - } - - if ((prot & PROT_EXEC)) - { - switch (newProt) - { - case PAGE_NOACCESS: newProt = PAGE_EXECUTE; break; - case PAGE_READONLY: newProt = PAGE_EXECUTE_READ; break; - case PAGE_READWRITE: newProt = PAGE_EXECUTE_READWRITE; break; - } - } - - return 0 != VirtualProtect(buffer, len, newProt, &oldProt); -} -#else -# ifndef MAP_ANONYMOUS -# define MAP_ANONYMOUS MAP_ANON -# endif -#endif - -struct block_header -{ - void *block; - void(*fnptr)(void); - /** - * On 64-bit platforms, we have 16 bytes for instructions, which ought to - * be enough without padding. - * Note: If we add too much padding, then we waste space but have no other - * ill effects. If we get this too small, then the assert in - * `init_trampolines` will fire on library load. - * - * PowerPC: We need INSTR_CNT * INSTR_LEN = 7*4 = 28 bytes - * for instruction. sizeof(block_header) must be a divisor of - * PAGE_SIZE, so we need to pad block_header to 32 bytes. - * On PowerPC 64-bit where sizeof(void *) = 8 bytes, we - * add 16 bytes of padding. - */ -#if defined(__i386__) || (defined(__mips__) && !defined(__mips_n64)) || (defined(__powerpc__) && !defined(__powerpc64__)) - uint64_t padding[3]; -#elif defined(__mips__) || defined(__powerpc64__) - uint64_t padding[2]; -#elif defined(__arm__) - uint64_t padding; -#endif -}; - -#define HEADERS_PER_PAGE (PAGE_SIZE/sizeof(struct block_header)) - -/** - * Structure containing a two pages of block trampolines. Each trampoline - * loads its block and target method address from the corresponding - * block_header (one page before the start of the block structure). - */ -struct trampoline_buffers -{ - struct block_header headers[HEADERS_PER_PAGE]; - char rx_buffer[PAGE_SIZE]; -}; -_Static_assert(__builtin_offsetof(struct trampoline_buffers, rx_buffer) == PAGE_SIZE, - "Incorrect offset for read-execute buffer"); -_Static_assert(sizeof(struct trampoline_buffers) == 2*PAGE_SIZE, - "Incorrect size for trampoline buffers"); - -struct trampoline_set -{ - struct trampoline_buffers *buffers; - struct trampoline_set *next; - int first_free; -}; - -static mutex_t trampoline_lock; - -struct wx_buffer -{ - void *w; - void *x; -}; -extern char __objc_block_trampoline; -extern char __objc_block_trampoline_end; -extern char __objc_block_trampoline_sret; -extern char __objc_block_trampoline_end_sret; - -PRIVATE void init_trampolines(void) -{ - assert(&__objc_block_trampoline_end - &__objc_block_trampoline <= sizeof(struct block_header)); - assert(&__objc_block_trampoline_end_sret - &__objc_block_trampoline_sret <= sizeof(struct block_header)); - INIT_LOCK(trampoline_lock); -} - -static id invalid(id self, SEL _cmd) -{ - fprintf(stderr, "Invalid block method called for [%s %s]\n", - class_getName(object_getClass(self)), sel_getName(_cmd)); - return nil; -} - -static struct trampoline_set *alloc_trampolines(char *start, char *end) -{ - struct trampoline_set *metadata = calloc(1, sizeof(struct trampoline_set)); -#if _WIN32 - metadata->buffers = VirtualAlloc(NULL, sizeof(struct trampoline_buffers), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -#else - metadata->buffers = mmap(NULL, sizeof(struct trampoline_buffers), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); -#endif - for (int i=0 ; ibuffers->headers[i].fnptr = (void(*)(void))invalid; - metadata->buffers->headers[i].block = &metadata->buffers->headers[i+1].block; - char *block = metadata->buffers->rx_buffer + (i * sizeof(struct block_header)); - memcpy(block, start, end-start); - } - metadata->buffers->headers[HEADERS_PER_PAGE-1].block = NULL; - mprotect(metadata->buffers->rx_buffer, PAGE_SIZE, PROT_READ | PROT_EXEC); - clear_cache(metadata->buffers->rx_buffer, &metadata->buffers->rx_buffer[PAGE_SIZE]); - - return metadata; -} - -static struct trampoline_set *sret_trampolines; -static struct trampoline_set *trampolines; - -IMP imp_implementationWithBlock(id block) -{ - struct Block_layout *b = (struct Block_layout *)block; - void *start; - void *end; - LOCK_FOR_SCOPE(&trampoline_lock); - struct trampoline_set **setptr; - - if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET) - { - setptr = &sret_trampolines; - start = &__objc_block_trampoline_sret; - end = &__objc_block_trampoline_end_sret; - } - else - { - setptr = &trampolines; - start = &__objc_block_trampoline; - end = &__objc_block_trampoline_end; - } - size_t trampolineSize = end - start; - // If we don't have a trampoline intrinsic for this architecture, return a - // null IMP. - if (0 >= trampolineSize) { return 0; } - block = Block_copy(block); - // Allocate some trampolines if this is the first time that we need to do this. - if (*setptr == NULL) - { - *setptr = alloc_trampolines(start, end); - } - for (struct trampoline_set *set=*setptr ; set!=NULL ; set=set->next) - { - if (set->first_free != -1) - { - int i = set->first_free; - struct block_header *h = &set->buffers->headers[i]; - struct block_header *next = h->block; - set->first_free = next ? (next - set->buffers->headers) : -1; - assert(set->first_free < HEADERS_PER_PAGE); - assert(set->first_free >= -1); - h->fnptr = (void(*)(void))b->invoke; - h->block = b; - uintptr_t addr = (uintptr_t)&set->buffers->rx_buffer[i*sizeof(struct block_header)]; -#if (__ARM_ARCH_ISA_THUMB == 2) - // If the trampoline is Thumb-2 code, then we must set the low bit - // to 1 so that b[l]x instructions put the CPU in the correct mode. - addr |= 1; -#endif - return (IMP)addr; - } - } - UNREACHABLE("Failed to allocate block"); -} - -static int indexForIMP(IMP anIMP, struct trampoline_set **setptr) -{ - for (struct trampoline_set *set=*setptr ; set!=NULL ; set=set->next) - { - if (((char*)anIMP >= set->buffers->rx_buffer) && - ((char*)anIMP < &set->buffers->rx_buffer[PAGE_SIZE])) - { - *setptr = set; - ptrdiff_t offset = (char*)anIMP - set->buffers->rx_buffer; - return offset / sizeof(struct block_header); - } - } - return -1; -} - -id imp_getBlock(IMP anImp) -{ - LOCK_FOR_SCOPE(&trampoline_lock); - struct trampoline_set *set = trampolines; - int idx = indexForIMP(anImp, &set); - if (idx == -1) - { - set = sret_trampolines; - indexForIMP(anImp, &set); - } - if (idx == -1) - { - return NULL; - } - return set->buffers->headers[idx].block; -} - -BOOL imp_removeBlock(IMP anImp) -{ - LOCK_FOR_SCOPE(&trampoline_lock); - struct trampoline_set *set = trampolines; - int idx = indexForIMP(anImp, &set); - if (idx == -1) - { - set = sret_trampolines; - indexForIMP(anImp, &set); - } - if (idx == -1) - { - return NO; - } - struct block_header *h = &set->buffers->headers[idx]; - Block_release(h->block); - h->fnptr = (void(*)(void))invalid; - h->block = set->first_free == -1 ? NULL : &set->buffers->headers[set->first_free]; - set->first_free = h - set->buffers->headers; - return YES; -} - -PRIVATE size_t lengthOfTypeEncoding(const char *types); - -char *block_copyIMPTypeEncoding_np(id block) -{ - char *buffer = strdup(block_getType_np(block)); - if (NULL == buffer) { return NULL; } - char *replace = buffer; - // Skip the return type - replace += lengthOfTypeEncoding(replace); - while (isdigit(*replace)) { replace++; } - // The first argument type should be @? (block), and we need to transform - // it to @, so we have to delete the ?. Assert here because this isn't a - // block encoding at all if the first argument is not a block, and since we - // got it from block_getType_np(), this means something is badly wrong. - assert('@' == *replace); - replace++; - assert('?' == *replace); - // Use strlen(replace) not replace+1, because we want to copy the NULL - // terminator as well. - memmove(replace, replace+1, strlen(replace)); - // The next argument should be an object, and we want to replace it with a - // selector - while (isdigit(*replace)) { replace++; } - if ('@' != *replace) - { - free(buffer); - return NULL; - } - *replace = ':'; - return buffer; -} diff --git a/block_trampolines.S b/block_trampolines.S deleted file mode 100644 index e943818..0000000 --- a/block_trampolines.S +++ /dev/null @@ -1,232 +0,0 @@ -#include "common.S" -#include "asmconstants.h" - -# -# This file defines some trampolines for calling blocks. A block function -# looks like this: -# -# retType blockFn(block*, ...) -# -# An IMP looks like this: -# -# retType imp(id, SEL,...) -# -# The trampoline must find the block pointer and then call the block function -# with the correct first argument, the self pointer moved to the second real -# argument (the first block argument) and the _cmd parameter excised - -.file "block_trampolines.S" - - -#if __x86_64 -//////////////////////////////////////////////////////////////////////////////// -// x86-64 trampoline -//////////////////////////////////////////////////////////////////////////////// -.macro trampoline arg0, arg1 - mov -0x1007(%rip), \arg1 # Load the block pointer into the second argument - xchg \arg1, \arg0 # Swap the first and second arguments - jmp *-0x1008(%rip) # Call the block function -.endm -// The Win64 and SysV x86-64 ABIs use different registers -# ifdef _WIN64 -# define ARG0 %rcx -# define ARG1 %rdx -# define SARG1 %r8 -# else -# define ARG0 %rdi -# define ARG1 %rsi -# define SARG1 %rdx -# endif -# define SARG0 ARG1 - -#elif __i386 -//////////////////////////////////////////////////////////////////////////////// -// x86-32 trampoline -//////////////////////////////////////////////////////////////////////////////// - -#ifdef _WIN32 -// Mark this compilation unit as SEH-safe -.text -.def @feat.00; -.scl 3; -.type 0; -.endef -.globl @feat.00 -.set @feat.00, 1 -.data -#endif - -.macro trampoline arg0, arg1 - call 1f # Store the instruction pointer on the stack -1: - pop %eax # Load the old instruction pointer - mov \arg0(%esp), %ebx # Load the self parameter - mov %ebx, \arg1(%esp) # Store self as the second argument - mov -0x1005(%eax), %ebx # Load the block pointer to %ebx - mov %ebx, \arg0(%esp) # Store the block pointer in the first argument - jmp *-0x1001(%eax) # Call the block function -.endm -// All arguments on i386 are passed on the stack. These values are stack -// offsets - on other platforms they're register values. -# define ARG0 4 -# define ARG1 8 -# define SARG0 8 -# define SARG1 12 - -#elif __mips__ -//////////////////////////////////////////////////////////////////////////////// -// MIPS trampoline -//////////////////////////////////////////////////////////////////////////////// -# ifdef _ABI64 -.macro trampoline arg0, arg1 - move \arg1, \arg0 - ld \arg0, -4096($25) - ld $25, -4088($25) - jr $25 -.endm -# else -// 32-bit variant. This ought to work with both n32 and o32, because they both -// use 32-bit pointers and both use the same registers for the first four -// arguments (and we only care about the first three). -.macro trampoline arg0, arg1 - move \arg1, \arg0 - lw \arg0, -4096($25) - lw $25, -4092($25) - jr $25 -.endm -# endif -#define ARG0 $a0 -#define ARG1 $a1 -#define ARG2 $a2 - -#elif defined(__powerpc__) -//////////////////////////////////////////////////////////////////////////////// -// PowerPC trampoline -//////////////////////////////////////////////////////////////////////////////// - -#if defined(__powerpc64__) -#define LOAD ld -#define OFFSET 8 -#else -#define LOAD lwz -#define OFFSET 4 -#endif - -.macro trampoline arg0, arg1 - mfctr %r12 # The block trampoline is always called - # via a function pointer. We can thus - # assume that ctr contains the trampline - # entry point address from the previous - # branch to this trampoline (bctrl). - - #if PAGE_SHIFT < 16 - addi %r12, %r12, -PAGE_SIZE # Substract page size from entry point - #else - addis %r12, %r12, (-0x1 << (PAGE_SHIFT - 16)) - #endif - - mr \arg1, \arg0 - LOAD \arg0, 0(%r12) - LOAD %r12, OFFSET(%r12) - mtctr %r12 # Move block function pointer into ctr - bctr # Branch to block function -.endm - -#define ARG0 %r3 -#define ARG1 %r4 -#define ARG2 %r5 -#define SARG0 ARG1 -#define SARG1 ARG2 - -#elif defined(__riscv) && (__riscv_xlen == 64) -//////////////////////////////////////////////////////////////////////////////// -// RISC-V trampoline -//////////////////////////////////////////////////////////////////////////////// -.macro trampoline arg0, arg1 - auipc t6, 0xFFFFF // pc + -0x1000 - mv \arg1, \arg0 - ld \arg0, 0(t6) - ld t6, 8(t6) - jr t6 -.endm - -#define ARG0 a0 -#define ARG1 a1 -#define ARG2 a2 -#define SARG0 ARG1 -#define SARG1 ARG2 - -#elif defined(__ARM_ARCH_ISA_A64) -//////////////////////////////////////////////////////////////////////////////// -// AArch64 (ARM64) trampoline -//////////////////////////////////////////////////////////////////////////////// -.macro trampoline arg0, arg1 - adr x17, #-4096 - mov \arg1, \arg0 - ldp \arg0, x17, [x17] - br x17 -.endm -#define ARG0 x0 -#define ARG1 x1 -#define SARG0 x0 -#define SARG1 x1 - -#elif __arm__ -//////////////////////////////////////////////////////////////////////////////// -// AArch32 (ARM) trampoline -//////////////////////////////////////////////////////////////////////////////// - -# if (__ARM_ARCH_ISA_THUMB == 2) -// If we're on a target that supports Thumb 2, then we need slightly more -// instructions to support Thumb/ARM code for the IMP and so we need to make -// the trampolines thumb to be able to fit them in 16 bytes (they fit exactly -// when assembled as Thumb-2). -.thumb -.macro trampoline arg0, arg1 - sub r12, pc, #4095 - mov \arg1, \arg0 // Move self over _cmd - ldr \arg0, [r12, #-5] // Load the block pointer over self - ldr r12, [r12, #-1] // Jump to the block function - bx r12 -.endm -# else -.macro trampoline arg0, arg1 - sub r12, pc, #4096 - mov \arg1, \arg0 // Move self over _cmd - ldr \arg0, [r12, #-8] // Load the block pointer over self - ldr pc, [r12, #-4] // Jump to the block function -.endm -# endif // (__ARM_ARCH_ISA_THUMB == 2) -#define ARG0 r0 -#define ARG1 r1 -#define SARG0 r1 -#define SARG1 r2 - -#else - -#warning imp_implementationWithBlock() not implemented for your architecture -.macro trampoline arg0, arg1 -.endm -#define ARG0 0 -#define ARG1 0 -#define SARG0 0 -#define SARG1 0 - -#endif - - -.globl CDECL(__objc_block_trampoline) -CDECL(__objc_block_trampoline): - trampoline ARG0, ARG1 -.globl CDECL(__objc_block_trampoline_end) -CDECL(__objc_block_trampoline_end): -.globl CDECL(__objc_block_trampoline_sret) -CDECL(__objc_block_trampoline_sret): - trampoline SARG0, SARG1 -.globl CDECL(__objc_block_trampoline_end_sret) -CDECL(__objc_block_trampoline_end_sret): - - -#ifdef __ELF__ -.section .note.GNU-stack,"",%progbits -#endif diff --git a/blocks_runtime.h b/blocks_runtime.h deleted file mode 100644 index f69490a..0000000 --- a/blocks_runtime.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Block descriptor flags. - */ -enum block_flags -{ - /** - * The block descriptor contains copy and dispose helpers. - */ - BLOCK_HAS_COPY_DISPOSE = (1 << 25), - /** - * The helpers have C++ code. - */ - BLOCK_HAS_CTOR = (1 << 26), - /** - * Block is stored in global memory and does not need to be copied. - */ - BLOCK_IS_GLOBAL = (1 << 28), - /** - * Block function uses a calling convention that returns a structure via a - * pointer passed in by the caller. - */ - BLOCK_USE_SRET = (1 << 29), - /** - * Block has an Objective-C type encoding. - */ - BLOCK_HAS_SIGNATURE = (1 << 30), - /** - * Mask for the reference count in byref structure's flags field. The low - * 3 bytes are reserved for the reference count, the top byte for the - * flags. - */ - BLOCK_REFCOUNT_MASK = 0x00ffffff -}; - -/** - * Flags used in the final argument to _Block_object_assign() and - * _Block_object_dispose(). These indicate the type of copy or dispose to - * perform. - */ -enum -{ - /** - * The value is of some id-like type, and should be copied as an - * Objective-C object: i.e. by sending -retain or via the GC assign - * functions in GC mode (not yet supported). - */ - BLOCK_FIELD_IS_OBJECT = 3, - /** - * The field is a block. This must be copied by the block copy functions. - */ - BLOCK_FIELD_IS_BLOCK = 7, - /** - * The field is an indirect reference to a variable declared with the - * __block storage qualifier. - */ - BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable - - BLOCK_FIELD_IS_WEAK = 16, // declared __weak - - BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers -}; -#define IS_SET(x, y) ((x & y) == y) - -/* - * Include the block_descriptor_copydispose and block_literal definitions that - * are also made public under different names for use in libdispatch. - */ -#include "objc/blocks_private.h" - -/** - * Block descriptor that does not contain copy and dispose helper functions. - */ -struct Block_descriptor_basic -{ - /** - * Reserved for future use, currently always 0. - */ - unsigned long int reserved; - /** Size of the block. */ - unsigned long int size; - /** - * Objective-C type encoding of the block. - */ - const char *encoding; -}; - - -/** - * Structure used for on-stack variables that are referenced by blocks. - */ -struct block_byref_obj -{ - /** - * Class pointer. Currently unused and always NULL. Could be used in the - * future to support introspection. - */ - void *isa; - /** - * The pointer to the structure that contains the real version of the data. - * All accesses go via this pointer. If an on-stack byref structure is - * copied to the heap, then its forwarding pointer should point to the heap - * version. Otherwise it should point to itself. - */ - struct block_byref_obj *forwarding; - /** - * Flags and reference count. - */ - int flags; //refcount; - /** - * Size of this structure. - */ - int size; - /** - * Copy function. - */ - void (*byref_keep)(struct block_byref_obj *dst, const struct block_byref_obj *src); - /** - * Dispose function. - */ - void (*byref_dispose)(struct block_byref_obj *); - /** - * __block-qualified variables are copied here. - */ -}; - diff --git a/blocks_runtime.m b/blocks_runtime.m deleted file mode 100644 index 91ab85d..0000000 --- a/blocks_runtime.m +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2009 Remy Demarest - * Portions Copyright (c) 2009 David Chisnall - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ -#import "objc/blocks_runtime.h" -#include "objc/blocks_private.h" -#import "objc/runtime.h" -#import "objc/objc-arc.h" -#include "blocks_runtime.h" -#include "gc_ops.h" -#include "visibility.h" -#include -#include -#include -#include -#include - - -static void *_HeapBlockByRef = (void*)1; - - -OBJC_PUBLIC bool _Block_has_signature(void *b) -{ - const struct Block_layout *block = (struct Block_layout*)b; - return ((NULL != block) && (block->flags & BLOCK_HAS_SIGNATURE)); -} -/** - * Returns the Objective-C type encoding for the block. - */ -OBJC_PUBLIC const char * _Block_signature(void *b) -{ - const struct Block_layout *block = (struct Block_layout*)b; - if ((NULL == block) || !(block->flags & BLOCK_HAS_SIGNATURE)) - { - return NULL; - } - if (!(block->flags & BLOCK_HAS_COPY_DISPOSE)) - { - return ((struct Block_descriptor_basic*)block->descriptor)->encoding; - } - return block->descriptor->encoding; -} - -static int increment24(int *ref) -{ - int old = *ref; - int val = old & BLOCK_REFCOUNT_MASK; - if (val == BLOCK_REFCOUNT_MASK) - { - return val; - } - assert(val < BLOCK_REFCOUNT_MASK); - if (!__sync_bool_compare_and_swap(ref, old, old+1)) - { - return increment24(ref); - } - return val + 1; -} - -static int decrement24(int *ref) -{ - int old = *ref; - int val = old & BLOCK_REFCOUNT_MASK; - if (val == BLOCK_REFCOUNT_MASK) - { - return val; - } - assert(val > 0); - if (!__sync_bool_compare_and_swap(ref, old, old-1)) - { - return decrement24(ref); - } - return val - 1; -} - -// This is a really ugly hack that works around a buggy register allocator in -// GCC. Compiling nontrivial code using __sync_bool_compare_and_swap() with -// GCC (4.2.1, at least), causes the register allocator to run out of registers -// and fall over and die. We work around this by wrapping this CAS in a -// function, which means the register allocator can trivially handle it. Do -// not remove the noinline attribute - without it, gcc will inline it early on -// and then crash later. -#ifndef __clang__ -__attribute__((noinline)) -static int cas(void *ptr, void *old, void *new) -{ - return __sync_bool_compare_and_swap((void**)ptr, old, new); -} -#define __sync_bool_compare_and_swap cas -#endif - -/* Certain field types require runtime assistance when being copied to the - * heap. The following function is used to copy fields of types: blocks, - * pointers to byref structures, and objects (including - * __attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to - * the other choices which are mutually exclusive. Only in a Block copy helper - * will one see BLOCK_FIELD_IS_BYREF. - */ -OBJC_PUBLIC void _Block_object_assign(void *destAddr, const void *object, const int flags) -{ - //printf("Copying %x to %x with flags %x\n", object, destAddr, flags); - // FIXME: Needs to be implemented - //if(flags & BLOCK_FIELD_IS_WEAK) - { - } - //else - { - if (IS_SET(flags, BLOCK_FIELD_IS_BYREF)) - { - struct block_byref_obj *src = (struct block_byref_obj *)object; - struct block_byref_obj **dst = destAddr; - src = src->forwarding; - - if ((src->flags & BLOCK_REFCOUNT_MASK) == 0) - { - *dst = gc->malloc(src->size); - memcpy(*dst, src, src->size); - (*dst)->isa = _HeapBlockByRef; - // Refcount must be two; one for the copy and one for the - // on-stack version that will point to it. - (*dst)->flags += 2; - if (IS_SET(src->flags, BLOCK_HAS_COPY_DISPOSE)) - { - src->byref_keep(*dst, src); - } - (*dst)->forwarding = *dst; - // Concurrency. If we try copying the same byref structure - // from two threads simultaneously, we could end up with two - // versions on the heap that are unaware of each other. That - // would be bad. So we first set up the copy, then try to do - // an atomic compare-and-exchange to point the old version at - // it. If the forwarding pointer in src has changed, then we - // recover - clean up and then return the structure that the - // other thread created. - if (!__sync_bool_compare_and_swap(&src->forwarding, src, *dst)) - { - if((size_t)src->size >= sizeof(struct block_byref_obj)) - { - src->byref_dispose(*dst); - } - gc->free(*dst); - *dst = src->forwarding; - } - } - else - { - *dst = (struct block_byref_obj*)src; - increment24(&(*dst)->flags); - } - } - else if (IS_SET(flags, BLOCK_FIELD_IS_BLOCK)) - { - struct Block_layout *src = (struct Block_layout*)object; - struct Block_layout **dst = destAddr; - - *dst = Block_copy(src); - } - else if (IS_SET(flags, BLOCK_FIELD_IS_OBJECT) && - !IS_SET(flags, BLOCK_BYREF_CALLER)) - { - id src = (id)object; - void **dst = destAddr; - *dst = src; - *dst = objc_retain(src); - } - } -} - -/* Similarly a compiler generated dispose helper needs to call back for each - * field of the byref data structure. (Currently the implementation only packs - * one field into the byref structure but in principle there could be more). - * The same flags used in the copy helper should be used for each call - * generated to this function: - */ -OBJC_PUBLIC void _Block_object_dispose(const void *object, const int flags) -{ - // FIXME: Needs to be implemented - //if(flags & BLOCK_FIELD_IS_WEAK) - { - } - //else - { - if (IS_SET(flags, BLOCK_FIELD_IS_BYREF)) - { - struct block_byref_obj *src = - (struct block_byref_obj*)object; - src = src->forwarding; - if (src->isa == _HeapBlockByRef) - { - int refcount = (src->flags & BLOCK_REFCOUNT_MASK) == 0 ? 0 : decrement24(&src->flags); - if (refcount == 0) - { - if(IS_SET(src->flags, BLOCK_HAS_COPY_DISPOSE) && (0 != src->byref_dispose)) - { - src->byref_dispose(src); - } - gc->free(src); - } - } - } - else if (IS_SET(flags, BLOCK_FIELD_IS_BLOCK)) - { - struct Block_layout *src = (struct Block_layout*)object; - Block_release(src); - } - else if (IS_SET(flags, BLOCK_FIELD_IS_OBJECT) && - !IS_SET(flags, BLOCK_BYREF_CALLER)) - { - id src = (id)object; - objc_release(src); - } - } -} - - -// Copy a block to the heap if it's still on the stack or increments its retain count. -OBJC_PUBLIC void *_Block_copy(const void *src) -{ - if (NULL == src) { return NULL; } - struct Block_layout *self = (struct Block_layout*)src; - struct Block_layout *ret = self; - - extern void _NSConcreteStackBlock; - extern void _NSConcreteMallocBlock; - - // If the block is Global, there's no need to copy it on the heap. - if(self->isa == &_NSConcreteStackBlock) - { - ret = gc->malloc(self->descriptor->size); - memcpy(ret, self, self->descriptor->size); - ret->isa = &_NSConcreteMallocBlock; - if(self->flags & BLOCK_HAS_COPY_DISPOSE) - { - self->descriptor->copy_helper(ret, self); - } - // We don't need any atomic operations here, because on-stack blocks - // can not be aliased across threads (unless you've done something - // badly wrong). - ret->reserved = 1; - } - else if (self->isa == &_NSConcreteMallocBlock) - { - // We need an atomic increment for malloc'd blocks, because they may be - // shared. - __sync_fetch_and_add(&ret->reserved, 1); - } - return ret; -} - -// Release a block and frees the memory when the retain count hits zero. -OBJC_PUBLIC void _Block_release(const void *src) -{ - if (NULL == src) { return; } - struct Block_layout *self = (struct Block_layout*)src; - - extern void _NSConcreteStackBlock; - extern void _NSConcreteMallocBlock; - - if (&_NSConcreteStackBlock == self->isa) - { - fprintf(stderr, "Block_release called upon a stack Block: %p, ignored\n", self); - } - else if (&_NSConcreteMallocBlock == self->isa) - { - if (__sync_sub_and_fetch(&self->reserved, 1) == 0) - { - if(self->flags & BLOCK_HAS_COPY_DISPOSE) - self->descriptor->dispose_helper(self); - objc_delete_weak_refs((id)self); - gc->free(self); - } - } -} - -OBJC_PUBLIC bool _Block_isDeallocating(const void* arg) -{ - struct Block_layout *block = (struct Block_layout*)arg; - int *refCountPtr = &((struct Block_layout*)arg)->reserved; - int refCount = __sync_fetch_and_add(refCountPtr, 0); - return refCount == 0; -} - -OBJC_PUBLIC bool _Block_tryRetain(const void* arg) -{ - /* This is used by the weak reference management in ARC. The implementation - * follows the reasoning of `retain_fast()` in arc.mm: We want to abandon the - * retain operation if another thread has started deallocating the object between - * loading the weak pointer and executing the retain operation. - */ - struct Block_layout *block = (struct Block_layout*)arg; - int *refCountPtr = &block->reserved; - int refCountVal = __sync_fetch_and_add(refCountPtr, 0); - int newVal = refCountVal; - do { - refCountVal = newVal; - if (refCountVal <= 0) - { - return false; - } - newVal = __sync_val_compare_and_swap(refCountPtr, refCountVal, newVal + 1); - } while (newVal != refCountVal); - return true; -} \ No newline at end of file diff --git a/blocks_runtime_np.m b/blocks_runtime_np.m deleted file mode 100644 index 789539b..0000000 --- a/blocks_runtime_np.m +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2009 Remy Demarest - * Portions Copyright (c) 2009 David Chisnall - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - - -#ifdef EMBEDDED_BLOCKS_RUNTIME -#import "objc/blocks_runtime.h" -#include "blocks_runtime.h" -#else -#import -#import -#endif -#include "visibility.h" - - -OBJC_PUBLIC const char *block_getType_np(const void *b) -{ - return _Block_signature((void*)b); -} - -/** - * Returns the block pointer, or NULL if the block is already - * being deallocated. The implementation does not employ atomic - * operations, so this function must only be called by the ARC - * subsystem after obtaining the weak-reference lock. - */ -PRIVATE void* block_load_weak(void *block) -{ - struct Block_layout *self = block; - #ifdef EMBEDDED_BLOCKS_RUNTIME - return (self->reserved) > 0 ? block : 0; - #else - return (self->flags) & BLOCK_REFCOUNT_MASK ? block : 0; - #endif -} diff --git a/buffer.h b/buffer.h deleted file mode 100644 index 1282274..0000000 --- a/buffer.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * buffer.h defines a simple dynamic array that is used to store temporary - * values for later processing. Define BUFFER_TYPE before including this file. - */ - -#include - -#define BUFFER_SIZE 128 -static BUFFER_TYPE buffered_object_buffer[BUFFER_SIZE]; -static BUFFER_TYPE *buffered_object_overflow; -static int buffered_objects; -static int buffered_object_overflow_space; - -static void set_buffered_object_at_index(BUFFER_TYPE cat, unsigned int i) -{ - if (i < BUFFER_SIZE) - { - buffered_object_buffer[i] = cat; - } - else - { - i -= BUFFER_SIZE; - if (NULL == buffered_object_overflow) - { - buffered_object_overflow = - calloc(BUFFER_SIZE, sizeof(BUFFER_TYPE)); - buffered_object_overflow_space = BUFFER_SIZE; - } - while (i >= buffered_object_overflow_space) - { - buffered_object_overflow_space <<= 1; - buffered_object_overflow = realloc(buffered_object_overflow, - buffered_object_overflow_space * sizeof(BUFFER_TYPE)); - } - buffered_object_overflow[i] = cat; - } -} - -static BUFFER_TYPE buffered_object_at_index(unsigned int i) -{ - if (i - -/** - * Bitmask of all of the capabilities compiled into this version of the - * runtime. - */ -static const int32_t caps = - (1<= 32) { return 0; } - if (caps & (1< -#include "objc/runtime.h" -#include "visibility.h" -#include "loader.h" -#include "dtable.h" -#include "properties.h" - -#define BUFFER_TYPE struct objc_category * -#include "buffer.h" - -void objc_send_load_message(Class class); - -static void register_methods(struct objc_class *cls, struct objc_method_list *l) -{ - if (NULL == l) { return; } - - // Add the method list at the head of the list of lists. - l->next = cls->methods; - cls->methods = l; - // Update the dtable to catch the new methods, if the dtable has been - // created (don't bother creating dtables for classes when categories are - // loaded if the class hasn't received any messages yet. - if (classHasDtable(cls)) - { - add_method_list_to_class(cls, l); - } -} - -static void load_category(struct objc_category *cat, struct objc_class *class) -{ - register_methods(class, cat->instance_methods); - register_methods(class->isa, cat->class_methods); - //fprintf(stderr, "Loading %s (%s)\n", cat->class_name, cat->name); - - if (cat->protocols) - { - objc_init_protocols(cat->protocols); - cat->protocols->next = class->protocols; - class->protocols = cat->protocols; - } - if (cat->properties) - { - cat->properties->next = class->properties; - class->properties = cat->properties; - } - if (cat->class_properties) - { - cat->class_properties->next = class->isa->properties; - class->isa->properties = cat->class_properties; - } -} - -static BOOL try_load_category(struct objc_category *cat) -{ - Class class = (Class)objc_getClass(cat->class_name); - //fprintf(stderr, "Trying to load %s (%s)\n", cat->class_name, cat->name); - if (Nil != class) - { - load_category(cat, class); - return YES; - } - //fprintf(stderr, "waiting to load %s (%s)\n", cat->class_name, cat->name); - return NO; -} - -/** - * Attaches a category to its class, if the class is already loaded. Buffers - * it for future resolution if not. - */ -PRIVATE void objc_try_load_category(struct objc_category *cat) -{ - if (!try_load_category(cat)) - { - set_buffered_object_at_index(cat, buffered_objects++); - } -} - -PRIVATE void objc_load_buffered_categories(void) -{ - BOOL shouldReshuffle = NO; - - for (unsigned i=0 ; i - -#ifdef __cplusplus -extern "C" -{ -#endif - -/** - * Overflow bitfield. Used for bitfields that are more than 63 bits. - */ -struct objc_bitfield -{ - /** - * The number of elements in the values array. - */ - int32_t length; - /** - * An array of values. Each 32 bits is stored in the native endian for the - * platform. - */ - int32_t values[0]; -}; - -static inline BOOL objc_bitfield_test(uintptr_t bitfield, uint64_t field) -{ - if (bitfield & 1) - { - uint64_t bit = 1<<(field+1); - return (bitfield & bit) == bit; - } - struct objc_bitfield *bf = (struct objc_bitfield*)bitfield; - uint64_t byte = field / 32; - if (byte >= bf->length) - { - return NO; - } - uint64_t bit = 1<<(field%32); - return (bf->values[byte] & bit) == bit; -} - -// begin: objc_class -struct objc_class -{ - /** - * Pointer to the metaclass for this class. The metaclass defines the - * methods use when a message is sent to the class, rather than an - * instance. - */ - Class isa; - /** - * Pointer to the superclass. The compiler will set this to the name of - * the superclass, the runtime will initialize it to point to the real - * class. - */ - Class super_class; - /** - * The name of this class. Set to the same value for both the class and - * its associated metaclass. - */ - const char *name; - /** - * The version of this class. This is not used by the language, but may be - * set explicitly at class load time. - */ - long version; - /** - * A bitfield containing various flags. See the objc_class_flags - * enumerated type for possible values. - */ - unsigned long info; - /** - * The size of this class. For classes using the non-fragile ABI, the - * compiler will set this to a negative value The absolute value will be - * the size of the instance variables defined on just this class. When - * using the fragile ABI, the instance size is the size of instances of - * this class, including any instance variables defined on superclasses. - * - * In both cases, this will be set to the size of an instance of the class - * after the class is registered with the runtime. - */ - long instance_size; - /** - * Metadata describing the instance variables in this class. - */ - struct objc_ivar_list *ivars; - /** - * Metadata for for defining the mappings from selectors to IMPs. Linked - * list of method list structures, one per class and one per category. - */ - struct objc_method_list *methods; - /** - * The dispatch table for this class. Intialized and maintained by the - * runtime. - */ - void *dtable; - /** - * A pointer to the first subclass for this class. Filled in by the - * runtime. - */ - Class subclass_list; - /** - * Pointer to the .cxx_construct method if one exists. This method needs - * to be called outside of the normal dispatch mechanism. - */ - IMP cxx_construct; - /** - * Pointer to the .cxx_destruct method if one exists. This method needs to - * be called outside of the normal dispatch mechanism. - */ - IMP cxx_destruct; - /** - * A pointer to the next sibling class to this. You may find all - * subclasses of a given class by following the subclass_list pointer and - * then subsequently following the sibling_class pointers in the - * subclasses. - */ - Class sibling_class; - - /** - * Metadata describing the protocols adopted by this class. Not used by - * the runtime. - */ - struct objc_protocol_list *protocols; - /** - * Linked list of extra data attached to this class. - */ - struct reference_list *extra_data; - /** - * The version of the ABI used for this class. Currently always zero for v2 - * ABI classes. - */ - long abi_version; - /** - * List of declared properties on this class (NULL if none). - */ - struct objc_property_list *properties; -}; -// end: objc_class - -struct objc_class_gsv1 -{ - /** - * Pointer to the metaclass for this class. The metaclass defines the - * methods use when a message is sent to the class, rather than an - * instance. - */ - Class isa; - /** - * Pointer to the superclass. The compiler will set this to the name of - * the superclass, the runtime will initialize it to point to the real - * class. - */ - Class super_class; - /** - * The name of this class. Set to the same value for both the class and - * its associated metaclass. - */ - const char *name; - /** - * The version of this class. This is not used by the language, but may be - * set explicitly at class load time. - */ - long version; - /** - * A bitfield containing various flags. See the objc_class_flags - * enumerated type for possible values. - */ - unsigned long info; - /** - * The size of this class. For classes using the non-fragile ABI, the - * compiler will set this to a negative value The absolute value will be - * the size of the instance variables defined on just this class. When - * using the fragile ABI, the instance size is the size of instances of - * this class, including any instance variables defined on superclasses. - * - * In both cases, this will be set to the size of an instance of the class - * after the class is registered with the runtime. - */ - long instance_size; - /** - * Metadata describing the instance variables in this class. - */ - struct objc_ivar_list_gcc *ivars; - /** - * Metadata for for defining the mappings from selectors to IMPs. Linked - * list of method list structures, one per class and one per category. - */ - struct objc_method_list_gcc *methods; - /** - * The dispatch table for this class. Intialized and maintained by the - * runtime. - */ - void *dtable; - /** - * A pointer to the first subclass for this class. Filled in by the - * runtime. - */ - Class subclass_list; - /** - * A pointer to the next sibling class to this. You may find all - * subclasses of a given class by following the subclass_list pointer and - * then subsequently following the sibling_class pointers in the - * subclasses. - */ - Class sibling_class; - - /** - * Metadata describing the protocols adopted by this class. Not used by - * the runtime. - */ - struct objc_protocol_list *protocols; - /** - * Linked list of extra data attached to this class. - */ - struct reference_list *extra_data; - /** - * New ABI. The following fields are only available with classes compiled to - * support the new ABI. You may test whether any given class supports this - * ABI by using the CLS_ISNEW_ABI() macro. - */ - - /** - * The version of the ABI used for this class. Zero indicates the ABI first - * implemented by clang 1.0. One indicates the presence of bitmaps - * indicating the offsets of strong, weak, and unretained ivars. Two - * indicates that the new ivar structure is used. - */ - long abi_version; - - /** - * Array of pointers to variables where the runtime will store the ivar - * offset. These may be used for faster access to non-fragile ivars if all - * of the code is compiled for the new ABI. Each of these pointers should - * have the mangled name __objc_ivar_offset_value_{class name}.{ivar name} - * - * When using the compatible non-fragile ABI, this faster form should only be - * used for classes declared in the same compilation unit. - * - * The compiler should also emit symbols of the form - * __objc_ivar_offset_{class name}.{ivar name} which are pointers to the - * offset values. These should be emitted as weak symbols in every module - * where they are used. The legacy-compatible ABI uses these with a double - * layer of indirection. - */ - int **ivar_offsets; - /** - * List of declared properties on this class (NULL if none). This contains - * the accessor methods for each property. - */ - struct objc_property_list_gsv1 *properties; - - /** - * GC / ARC ABI: Fields below this point only exist if abi_version is >= 1. - */ - - /** - * The location of all strong pointer ivars declared by this class. - * - * If the low bit of this field is 0, then this is a pointer to an - * objc_bitfield structure. If the low bit is 1, then the remaining 63 - * bits are set, from low to high, for each ivar in the object that is a - * strong pointer. - */ - uintptr_t strong_pointers; - /** - * The location of all zeroing weak pointer ivars declared by this class. - * The format of this field is the same as the format of the - * strong_pointers field. - */ - uintptr_t weak_pointers; -}; - -/** - * Structure representing the GCC ABI class structure. This is only ever - * required so that we can take its size - struct objc_class begins with the - * same fields, and you can test the new abi flag to tell whether it is safe to - * access the subsequent fields. - */ -struct objc_class_gcc -{ - Class isa; - Class super_class; - const char *name; - long version; - unsigned long info; - long instance_size; - struct objc_ivar_list_gcc *ivars; - struct objc_method_list *methods; - void *dtable; - Class subclass_list; - Class sibling_class; - struct objc_protocol_list *protocols; - void *gc_object_type; -}; - - -/** - * An enumerated type describing all of the valid flags that may be used in the - * info field of a class. - */ -enum objc_class_flags -{ - /** This class structure represents a metaclass. */ - objc_class_flag_meta = (1<<0), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved1 = (1<<1), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved2 = (1<<2), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved3 = (1<<3), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved4 = (1<<4), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved5 = (1<<5), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved6 = (1<<6), - /** Reserved for future ABI versions. */ - objc_class_flag_reserved7 = (1<<7), - /** - * This class has been sent a +initalize message. This message is sent - * exactly once to every class that is sent a message by the runtime, just - * before the first other message is sent. - * - * For direct method support, this is now part of the public ABI. - */ - objc_class_flag_initialized = (1<<8), - /** - * The class has been initialized by the runtime. Its super_class pointer - * should now point to a class, rather than a C string containing the class - * name, and its subclass and sibling class links will have been assigned, - * if applicable. - */ - objc_class_flag_resolved = (1<<9), - /** - * This class was created at run time and may be freed. - */ - objc_class_flag_user_created = (1<<10), - /** - * Instances of this class are provide ARC-safe retain / release / - * autorelease implementations. - */ - objc_class_flag_fast_arc = (1<<11), - /** - * This class is a hidden class (should not be registered in the class - * table nor returned from object_getClass()). - */ - objc_class_flag_hidden_class = (1<<12), - /** - * This class is a hidden class used to store associated values. - */ - objc_class_flag_assoc_class = (1<<13), - /** - * This class has instances that are never deallocated and are therefore - * safe to store directly into weak variables and to skip all reference - * count manipulations. - */ - objc_class_flag_permanent_instances = (1<<14), - /** - * On a metaclass, guarantees that `+alloc` and `+allocWithZone:` are - * trivial wrappers around `class_createInstance`. - * - * On a class, guarantees that `+init` is trivial. - */ - objc_class_flag_fast_alloc_init = (1<<15), - /** - * The class is a block class. Reference count management must be done by - * the underlying blocks runtime. - */ - objc_class_flag_is_block = (1 << 16), -}; - -/** - * Sets the specific class flag. Note: This is not atomic. - */ -static inline void objc_set_class_flag(Class aClass, - enum objc_class_flags flag) -{ - aClass->info |= (unsigned long)flag; -} -/** - * Unsets the specific class flag. Note: This is not atomic. - */ -static inline void objc_clear_class_flag(Class aClass, - enum objc_class_flags flag) -{ - aClass->info &= ~(unsigned long)flag; -} -/** - * Checks whether a specific class flag is set. - */ -static inline BOOL objc_test_class_flag(Class aClass, - enum objc_class_flags flag) -{ - return (aClass->info & (unsigned long)flag) == (unsigned long)flag; -} - - -/** - * Adds a class to the class table. - */ -void class_table_insert(Class cls); - -/** - * Removes a class from the class table. Must be called with the runtime lock - * held! - */ -void class_table_remove(Class cls); - -/** - * Array of classes used for small objects. Small objects are embedded in - * their pointer. In 32-bit mode, we have one small object class (typically - * used for storing 31-bit signed integers. In 64-bit mode then we can have 7, - * because classes are guaranteed to be word aligned. - */ -extern Class SmallObjectClasses[7]; - -static BOOL isSmallObject(id obj) -{ - uintptr_t addr = ((uintptr_t)obj); - return (addr & OBJC_SMALL_OBJECT_MASK) != 0; -} - -__attribute__((always_inline)) -static inline Class classForObject(id obj) -{ - if (UNLIKELY(isSmallObject(obj))) - { - if (sizeof(Class) == 4) - { - return SmallObjectClasses[0]; - } - else - { - uintptr_t addr = ((uintptr_t)obj); - return SmallObjectClasses[(addr & OBJC_SMALL_OBJECT_MASK)]; - } - } - return obj->isa; -} - -static inline BOOL classIsOrInherits(Class cls, Class base) -{ - for (Class c = cls ; - Nil != c ; - c = c->super_class) - { - if (c == base) { return YES; } - } - return NO; -} - -/** - * Free the instance variable lists associated with a class. - */ -void freeIvarLists(Class aClass); -/** - * Free the method lists associated with a class. - */ -void freeMethodLists(Class aClass); - -#ifdef __cplusplus -} // extern "C" -#endif -#endif //__OBJC_CLASS_H_INCLUDED diff --git a/class_table.c b/class_table.c deleted file mode 100644 index 3a6f1aa..0000000 --- a/class_table.c +++ /dev/null @@ -1,602 +0,0 @@ -#include "objc/runtime.h" -#include "objc/hooks.h" -#include "objc/developer.h" -#include "alias.h" -#include "class.h" -#include "method.h" -#include "selector.h" -#include "lock.h" -#include "dtable.h" -#include "legacy.h" -#include "visibility.h" -#include -#include - -void objc_init_protocols(struct objc_protocol_list *protos); -void objc_compute_ivar_offsets(Class class); - -//////////////////////////////////////////////////////////////////////////////// -// +load method hash table -//////////////////////////////////////////////////////////////////////////////// -static int imp_compare(const void *i1, void *i2) -{ - return i1 == i2; -} -static int32_t imp_hash(const void *imp) -{ - return (int32_t)(((uintptr_t)imp) >> 4); -} -#define MAP_TABLE_NAME load_messages -#define MAP_TABLE_COMPARE_FUNCTION imp_compare -#define MAP_TABLE_HASH_KEY imp_hash -#define MAP_TABLE_HASH_VALUE imp_hash -#include "hash_table.h" - -static load_messages_table *load_table; - -SEL loadSel; - -PRIVATE void objc_init_load_messages_table(void) -{ - load_messages_initialize(&load_table, 4096); - loadSel = sel_registerName("load"); -} - -PRIVATE void objc_send_load_message(Class class) -{ - Class meta = class->isa; - for (struct objc_method_list *l=meta->methods ; NULL!=l ; l=l->next) - { - for (int i=0 ; icount ; i++) - { - Method m = method_at_index(l, i); - if (sel_isEqual(m->selector, loadSel)) - { - if (load_messages_table_get(load_table, m->imp) == 0) - { - m->imp((id)class, loadSel); - load_messages_insert(load_table, m->imp); - } - } - } - } -} - -// Get the functions for string hashing -#include "string_hash.h" - -static int class_compare(const char *name, const Class class) -{ - return string_compare(name, class->name); -} -static int class_hash(const Class class) -{ - return string_hash(class->name); -} -#define MAP_TABLE_NAME class_table_internal -#define MAP_TABLE_COMPARE_FUNCTION class_compare -#define MAP_TABLE_HASH_KEY string_hash -#define MAP_TABLE_HASH_VALUE class_hash -// This defines the maximum number of classes that the runtime supports. -/* -#define MAP_TABLE_STATIC_SIZE 2048 -#define MAP_TABLE_STATIC_NAME class_table -*/ -#include "hash_table.h" - -static class_table_internal_table *class_table; - - -#define unresolved_class_next subclass_list -#define unresolved_class_prev sibling_class -/** - * Linked list using the subclass_list pointer in unresolved classes. - */ -static Class unresolved_class_list; - -static enum objc_developer_mode_np mode; - -void objc_setDeveloperMode_np(enum objc_developer_mode_np newMode) -{ - mode = newMode; -} - -//////////////////////////////////////////////////////////////////////////////// -// Class table manipulation -//////////////////////////////////////////////////////////////////////////////// - -PRIVATE Class zombie_class; - -PRIVATE void class_table_insert(Class class) -{ - if (!objc_test_class_flag(class, objc_class_flag_resolved)) - { - if (Nil != unresolved_class_list) - { - unresolved_class_list->unresolved_class_prev = class; - } - class->unresolved_class_next = unresolved_class_list; - unresolved_class_list = class; - } - if ((0 == zombie_class) && (strcmp("NSZombie", class->name) == 0)) - { - zombie_class = class; - } - class_table_internal_insert(class_table, class); -} - -PRIVATE Class class_table_get_safe(const char *class_name) -{ - if (NULL == class_name) { return Nil; } - return class_table_internal_table_get(class_table, class_name); -} - -PRIVATE Class class_table_next(void **e) -{ - return class_table_internal_next(class_table, - (struct class_table_internal_table_enumerator**)e); -} - -PRIVATE void init_class_tables(void) -{ - class_table_internal_initialize(&class_table, 4096); - objc_init_load_messages_table(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Loader functions -//////////////////////////////////////////////////////////////////////////////// - -PRIVATE BOOL objc_resolve_class(Class cls) -{ - // Skip this if the class is already resolved. - if (objc_test_class_flag(cls, objc_class_flag_resolved)) { return YES; } - - // We can only resolve the class if its superclass is resolved. - if (cls->super_class) - { - Class super = cls->super_class; - - if (!objc_test_class_flag(super, objc_class_flag_resolved)) - { - if (!objc_resolve_class(super)) - { - return NO; - } - } - } -#ifdef OLDABI_COMPAT - else - { - struct objc_class_gsv1 *ocls = objc_legacy_class_for_class(cls); - if (ocls != NULL) - { - const char *super_name = (const char*)ocls->super_class; - if (super_name) - { - Class super = (Class)objc_getClass(super_name); - if (super == Nil) - { - return NO; - } - cls->super_class = super; - return objc_resolve_class(cls); - } - } - } -#endif - - - // Remove the class from the unresolved class list - if (Nil == cls->unresolved_class_prev) - { - unresolved_class_list = cls->unresolved_class_next; - } - else - { - cls->unresolved_class_prev->unresolved_class_next = - cls->unresolved_class_next; - } - if (Nil != cls->unresolved_class_next) - { - cls->unresolved_class_next->unresolved_class_prev = - cls->unresolved_class_prev; - } - cls->unresolved_class_prev = Nil; - cls->unresolved_class_next = Nil; - - // The superclass for the metaclass. This is the metaclass for the - // superclass if one exists, otherwise it is the root class itself - Class superMeta = Nil; - // The metaclass for the metaclass. This is always the root class's - // metaclass. - Class metaMeta = Nil; - - // Resolve the superclass pointer - - if (NULL == cls->super_class) - { - superMeta = cls; - metaMeta = cls->isa; - } - else - { - // Resolve the superclass if it isn't already resolved - Class super = cls->super_class; - if (!objc_test_class_flag(super, objc_class_flag_resolved)) - { - objc_resolve_class(super); - } - superMeta = super->isa; - // Set the superclass pointer for the class and the superclass - do - { - metaMeta = super->isa; - super = super->super_class; - } while (Nil != super); - } - Class meta = cls->isa; - - // Make the root class the superclass of the metaclass (e.g. NSObject is - // the superclass of all metaclasses in classes that inherit from NSObject) - meta->super_class = superMeta; - meta->isa = metaMeta; - - // Don't register root classes as children of anything - if (Nil != cls->super_class) - { - // Set up the class links - cls->sibling_class = cls->super_class->subclass_list; - cls->super_class->subclass_list = cls; - } - // Set up the metaclass links - meta->sibling_class = superMeta->subclass_list; - superMeta->subclass_list = meta; - - // Mark this class (and its metaclass) as resolved - objc_set_class_flag(cls, objc_class_flag_resolved); - objc_set_class_flag(cls->isa, objc_class_flag_resolved); - - - // Fix up the ivar offsets - objc_compute_ivar_offsets(cls); -#ifdef OLDABI_COMPAT - struct objc_class_gsv1 *oldCls = objc_legacy_class_for_class(cls); - if (oldCls) - { - oldCls->super_class = cls->super_class; - oldCls->isa->super_class = cls->isa->super_class; - } -#endif - // Send the +load message, if required - if (!objc_test_class_flag(cls, objc_class_flag_user_created)) - { - objc_send_load_message(cls); - } - if (_objc_load_callback) - { - _objc_load_callback(cls, 0); - } - return YES; -} - -PRIVATE void objc_resolve_class_links(void) -{ - LOCK_RUNTIME_FOR_SCOPE(); - BOOL resolvedClass; - do - { - Class class = unresolved_class_list; - resolvedClass = NO; - while ((Nil != class)) - { - Class next = class->unresolved_class_next; - // If the class has been resolved, then this means that the last - // call to objc_resolve_class resolved it as part of resolving - // superclasses and removed it from the list. We now don't have a - // pointer into the linked list, so abort and try again from the - // start. - if (objc_test_class_flag(class, objc_class_flag_resolved)) - { - assert(resolvedClass); - break; - } - objc_resolve_class(class); - if (resolvedClass || - objc_test_class_flag(class, objc_class_flag_resolved)) - { - resolvedClass = YES; - } - class = next; - } - } while (resolvedClass); -} -PRIVATE void __objc_resolve_class_links(void) -{ - static BOOL warned = NO; - if (!warned) - { - fprintf(stderr, - "Warning: Calling deprecated private ObjC runtime function %s\n", __func__); - warned = YES; - } - objc_resolve_class_links(); -} - -static void reload_class(struct objc_class *class, struct objc_class *old) -{ - const char *superclassName = (char*)class->super_class; - class->super_class = class_table_get_safe(superclassName); - // Checking the instance sizes are equal here is a quick-and-dirty test. - // It's not actually needed, because we're testing the ivars are at the - // same locations next, but it lets us skip those tests if the total size - // is different. - BOOL equalLayouts = (class->super_class == old->super_class) && - (class->instance_size == old->instance_size); - // If either of the classes has an empty ivar list, then the other one must too. - if ((NULL == class->ivars) || (NULL == old->ivars)) - { - equalLayouts &= (class->ivars == old->ivars); - } - else - { - // If the class sizes are the same, ensure that the ivars have the same - // types, names, and offsets. Note: Renaming an ivar is treated as a - // conflict because name changes are often accompanied by semantic - // changes. For example, an object ivar at offset 16 goes from being - // called 'delegate' to being called 'view' - we almost certainly don't - // want methods that expect to be working with the delegate ivar to - // work with the view ivar now! - for (int i=0 ; equalLayouts && (iivars->count) ; i++) - { - struct objc_ivar *oldIvar = ivar_at_index(old->ivars, i); - struct objc_ivar *newIvar = ivar_at_index(class->ivars, i); - equalLayouts &= strcmp(oldIvar->name, newIvar->name) == 0; - equalLayouts &= strcmp(oldIvar->type, newIvar->type) == 0; - equalLayouts &= (oldIvar->offset == newIvar->offset); - } - } - - // If the layouts are equal, then we can simply tack the class's method - // list on to the front of the old class and update the dtable. - if (equalLayouts) - { - class->methods->next = old->methods; - old->methods = class->methods; - objc_update_dtable_for_class(old); - return; - } - - // If we get to here, then we are adding a new class. This is where things - // start to get a bit tricky... - - // Ideally, we'd want to capture the subclass list here. Unfortunately, - // this is not possible because the subclass will contain methods that - // refer to ivars in the superclass. - // - // We can't use the non-fragile ABI's offset facility easily, because we'd - // have to have two (or more) offsets for the same ivar. This gets messy - // very quickly. Ideally, we'd want every class to include ivar offsets - // for every single (public) ivar in its superclasses. These could then be - // updated by copies of the class. Defining a development ABI is something - // to consider for a future release. - class->subclass_list = NULL; - - // Replace the old class with this one in the class table. New lookups for - // this class will now return this class. - class_table_internal_table_set(class_table, (void*)class->name, class); - - // Set the uninstalled dtable. The compiler could do this as well. - class->dtable = uninstalled_dtable; - class->isa->dtable = uninstalled_dtable; - - // If this is a root class, make the class into the metaclass's superclass. - // This means that all instance methods will be available to the class. - if (NULL == superclassName) - { - class->isa->super_class = class; - } - - if (class->protocols) - { - objc_init_protocols(class->protocols); - } -} - -/** - * Loads a class. This function assumes that the runtime mutex is locked. - */ -PRIVATE void objc_load_class(struct objc_class *class) -{ - struct objc_class *existingClass = class_table_get_safe(class->name); - if (Nil != existingClass) - { - if (objc_developer_mode_developer != mode) - { - fprintf(stderr, - "Loading two versions of %s. The class that will be used is undefined\n", - class->name); - return; - } - reload_class(class, existingClass); - return; - } - -#ifdef _WIN32 - // On Windows, the super_class pointer may point to the local __imp_ - // symbol, rather than to the external symbol. The runtime must remove the - // extra indirection. - if (class->super_class) - { - Class superMeta = class->super_class->isa; - if (!class_isMetaClass(superMeta)) - { - class->super_class = superMeta; - } - } -#endif - - // Work around a bug in some versions of GCC that don't initialize the - // class structure correctly. - class->subclass_list = NULL; - - // Insert the class into the class table - class_table_insert(class); - - // Set the uninstalled dtable. The compiler could do this as well. - class->dtable = uninstalled_dtable; - class->isa->dtable = uninstalled_dtable; - - // Mark constant string instances as never needing refcount manipulation. - if (strcmp(class->name, "NSConstantString") == 0) - { - objc_set_class_flag(class, objc_class_flag_permanent_instances); - } - - // If this is a root class, make the class into the metaclass's superclass. - // This means that all instance methods will be available to the class. - if (NULL == class->super_class) - { - class->isa->super_class = class; - } - - if (class->protocols) - { - objc_init_protocols(class->protocols); - } -} - -PRIVATE Class SmallObjectClasses[7]; - -BOOL objc_registerSmallObjectClass_np(Class class, uintptr_t mask) -{ - if ((mask & OBJC_SMALL_OBJECT_MASK) != mask) - { - return NO; - } - if (sizeof(void*) == 4) - { - if (Nil == SmallObjectClasses[0]) - { - SmallObjectClasses[0] = class; - return YES; - } - return NO; - } - if (Nil != SmallObjectClasses[mask]) - { - return NO; - } - SmallObjectClasses[mask] = class; - return YES; -} - -PRIVATE void class_table_remove(Class cls) -{ - assert(objc_test_class_flag(cls, objc_class_flag_user_created)); - class_table_internal_remove(class_table, (void*)cls->name); -} - - -//////////////////////////////////////////////////////////////////////////////// -// Public API -//////////////////////////////////////////////////////////////////////////////// - -int objc_getClassList(Class *buffer, int bufferLen) -{ - if (buffer == NULL || bufferLen == 0) - { - return class_table->table_used; - } - int count = 0; - struct class_table_internal_table_enumerator *e = NULL; - Class next; - while (count < bufferLen && - (next = class_table_internal_next(class_table, &e))) - { - buffer[count++] = next; - } - return count; -} -Class *objc_copyClassList(unsigned int *outCount) -{ - int count = class_table->table_used; - Class *buffer = calloc(sizeof(Class), count); - if (NULL != outCount) - { - *outCount = count; - } - objc_getClassList(buffer, count); - return buffer; -} - -Class class_getSuperclass(Class cls) -{ - if (Nil == cls) { return Nil; } - if (!objc_test_class_flag(cls, objc_class_flag_resolved)) - { - objc_resolve_class(cls); - } - return cls->super_class; -} - - -id objc_getClass(const char *name) -{ - id class = (id)class_table_get_safe(name); - - if (nil != class) { return class; } - - // Second chance lookup via @compatibilty_alias: - class = (id)alias_getClass(name); - if (nil != class) { return class; } - - // Third chance lookup via the hook: - if (0 != _objc_lookup_class) - { - class = (id)_objc_lookup_class(name); - } - - return class; -} - -id objc_lookUpClass(const char *name) -{ - return (id)class_table_get_safe(name); -} - - -id objc_getMetaClass(const char *name) -{ - Class cls = (Class)objc_getClass(name); - return cls == Nil ? nil : (id)cls->isa; -} - -// Legacy interface compatibility - -id objc_get_class(const char *name) -{ - return objc_getClass(name); -} - -id objc_lookup_class(const char *name) -{ - return objc_getClass(name); -} - -id objc_get_meta_class(const char *name) -{ - return objc_getMetaClass(name); -} - -Class objc_next_class(void **enum_state) -{ - return class_table_next ( enum_state); -} - -Class class_pose_as(Class impostor, Class super_class) -{ - fprintf(stderr, "Class posing is no longer supported.\n"); - fprintf(stderr, "Please use class_replaceMethod() instead.\n"); - abort(); -} diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in deleted file mode 100644 index c6d8094..0000000 --- a/cmake_uninstall.cmake.in +++ /dev/null @@ -1,22 +0,0 @@ -if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") -endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - -file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) -string(REGEX REPLACE "\n" ";" files "${files}") -list(REVERSE files) -foreach (file ${files}) - message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") - if (EXISTS "$ENV{DESTDIR}${file}") - execute_process( - COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" - OUTPUT_VARIABLE rm_out - RESULT_VARIABLE rm_retval - ) - if(NOT ${rm_retval} EQUAL 0) - message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") - endif (NOT ${rm_retval} EQUAL 0) - else (EXISTS "$ENV{DESTDIR}${file}") - message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") - endif (EXISTS "$ENV{DESTDIR}${file}") -endforeach(file) diff --git a/common.S b/common.S deleted file mode 100644 index 182d967..0000000 --- a/common.S +++ /dev/null @@ -1,18 +0,0 @@ -#if ((defined(_WIN32) || defined(__CYGWIN__)) && defined(__i386__)) || defined(__APPLE__) -#define CDECL(symbol) _##symbol -#else -#define CDECL(symbol) symbol -#endif - -#if __ELF__ -#define TYPE_DIRECTIVE(symbol, symboltype) .type symbol, symboltype -#else -#define TYPE_DIRECTIVE(symbol, symboltype) -#endif - -#if defined(_MSC_VER) && defined(__i386__) -#define STRINGIFY(a) #a -#define EXPORT_SYMBOL(symbol) .ascii " " STRINGIFY(/EXPORT:_##symbol) -#else -#define EXPORT_SYMBOL(symbol) .ascii " /EXPORT:" #symbol -#endif diff --git a/constant_string.h b/constant_string.h deleted file mode 100644 index 781a491..0000000 --- a/constant_string.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CONSTANT_STRING_CLASS -# ifdef GNUSTEP -# define CONSTANT_STRING_CLASS "NSConstantString" -# else -# define CONSTANT_STRING_CLASS "NXConstantString" -# endif -#endif diff --git a/dtable.c b/dtable.c deleted file mode 100644 index a6f1641..0000000 --- a/dtable.c +++ /dev/null @@ -1,861 +0,0 @@ -#define __BSD_VISIBLE 1 -#include -#include -#include -#include -#include "objc/runtime.h" -#include "objc/hooks.h" -#include "sarray2.h" -#include "selector.h" -#include "class.h" -#include "lock.h" -#include "method.h" -#include "dtable.h" -#include "visibility.h" -#include "asmconstants.h" - -_Static_assert(__builtin_offsetof(struct objc_class, dtable) == DTABLE_OFFSET, - "Incorrect dtable offset for assembly"); -_Static_assert(__builtin_offsetof(SparseArray, shift) == SHIFT_OFFSET, - "Incorrect shift offset for assembly"); -_Static_assert(__builtin_offsetof(SparseArray, data) == DATA_OFFSET, - "Incorrect data offset for assembly"); -// Slots are now a public interface to part of the method structure, so make -// sure that it's safe to use method and slot structures interchangeably. -_Static_assert(__builtin_offsetof(struct objc_slot2, method) == SLOT_OFFSET, - "Incorrect slot offset for assembly"); -_Static_assert(__builtin_offsetof(struct objc_method, imp) == SLOT_OFFSET, - "Incorrect slot offset for assembly"); - -PRIVATE dtable_t uninstalled_dtable; -#if defined(WITH_TRACING) && defined (__x86_64) -PRIVATE dtable_t tracing_dtable; -#endif -#ifndef ENOTSUP -# define ENOTSUP -1 -#endif - -/** Head of the list of temporary dtables. Protected by initialize_lock. */ -PRIVATE InitializingDtable *temporary_dtables; -/** Lock used to protect the temporary dtables list. */ -PRIVATE mutex_t initialize_lock; -/** The size of the largest dtable. This is a sparse array shift value, so is - * 2^x in increments of 8. */ -static uint32_t dtable_depth = 8; - -#ifndef NO_SAFE_CACHING -_Atomic(uint64_t) objc_method_cache_version; -#endif - -/** - * Starting at `cls`, finds the class that provides the implementation of the - * method identified by `sel`. - */ -static Class ownerForMethod(Class cls, SEL sel) -{ - struct objc_slot2 *slot = objc_get_slot2(cls, sel, NULL); - if (slot == NULL) - { - return Nil; - } - if (cls->super_class == NULL) - { - return cls; - } - if (objc_get_slot2(cls->super_class, sel, NULL) == slot) - { - return ownerForMethod(cls->super_class, sel); - } - return cls; -} - -/** - * Returns YES if the class implements a method for the specified selector, NO - * otherwise. - */ -static BOOL ownsMethod(Class cls, SEL sel) -{ - return ownerForMethod(cls, sel) == cls; -} - - -#ifdef DEBUG_ARC_COMPAT -#define ARC_DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) -#else -#define ARC_DEBUG_LOG(...) do {} while(0) -#endif - -/** - * Check whether this class pair implement or override `+alloc`, - * `+allocWithZone`, or `-init` in a way that requires the methods to be - * called. - */ -static void checkFastAllocInit(Class cls) -{ - // This needs to be called on the class, not the metaclass - if (class_isMetaClass(cls)) - { - return; - } - static SEL alloc, allocWithZone, init, isTrivialAllocInit; - if (NULL == alloc) - { - alloc = sel_registerName("alloc"); - allocWithZone = sel_registerName("allocWithZone:"); - init = sel_registerName("init"); - isTrivialAllocInit = sel_registerName("_TrivialAllocInit"); - } - Class metaclass = cls->isa; - Class isTrivialOwner = ownerForMethod(metaclass, isTrivialAllocInit); - // If nothing in this hierarchy opts in to trivial alloc / init behaviour, give up. - if (isTrivialOwner == nil) - { - objc_clear_class_flag(cls, objc_class_flag_fast_alloc_init); - objc_clear_class_flag(metaclass, objc_class_flag_fast_alloc_init); - return; - } - // Check for overrides of alloc or allocWithZone:. - // This check has some false negatives. If you override only one of alloc - // or allocWithZone, both will hit the slow path. That's fine because the - // fast path is an optimisation, not a guarantee. - Class allocOwner = ownerForMethod(metaclass, alloc); - Class allocWithZoneOwner = ownerForMethod(metaclass, allocWithZone); - if (((allocOwner == nil) || (allocOwner == isTrivialOwner)) && - ((allocWithZoneOwner == nil) || (allocWithZoneOwner == isTrivialOwner))) - { - objc_set_class_flag(metaclass, objc_class_flag_fast_alloc_init); - } - else - { - objc_clear_class_flag(metaclass, objc_class_flag_fast_alloc_init); - } - Class initOwner = ownerForMethod(cls, init); - if ((initOwner == nil) || (initOwner->isa == isTrivialOwner)) - { - objc_set_class_flag(cls, objc_class_flag_fast_alloc_init); - } - else - { - objc_clear_class_flag(cls, objc_class_flag_fast_alloc_init); - } -} - -/** - * Checks whether the class implements memory management methods, and whether - * they are safe to use with ARC. - */ -static void checkARCAccessors(Class cls) -{ - checkFastAllocInit(cls); - static SEL retain, release, autorelease, isARC; - if (NULL == retain) - { - retain = sel_registerName("retain"); - release = sel_registerName("release"); - autorelease = sel_registerName("autorelease"); - isARC = sel_registerName("_ARCCompliantRetainRelease"); - } - Class owner = ownerForMethod(cls, retain); - if ((NULL != owner) && !ownsMethod(owner, isARC)) - { - ARC_DEBUG_LOG("%s does not support ARC correctly (implements retain)\n", cls->name); - objc_clear_class_flag(cls, objc_class_flag_fast_arc); - return; - } - owner = ownerForMethod(cls, release); - if ((NULL != owner) && !ownsMethod(owner, isARC)) - { - ARC_DEBUG_LOG("%s does not support ARC correctly (implements release)\n", cls->name); - objc_clear_class_flag(cls, objc_class_flag_fast_arc); - return; - } - owner = ownerForMethod(cls, autorelease); - if ((NULL != owner) && !ownsMethod(owner, isARC)) - { - ARC_DEBUG_LOG("%s does not support ARC correctly (implements autorelease)\n", cls->name); - objc_clear_class_flag(cls, objc_class_flag_fast_arc); - return; - } - objc_set_class_flag(cls, objc_class_flag_fast_arc); -} - -static BOOL selEqualUnTyped(SEL expected, SEL untyped) -{ - return (expected->index == untyped->index) -#ifdef TYPE_DEPENDENT_DISPATCH - || (get_untyped_idx(expected) == untyped->index) -#endif - ; -} - -PRIVATE void checkARCAccessorsSlow(Class cls) -{ - if (cls->dtable != uninstalled_dtable) - { - return; - } - static SEL retain, release, autorelease, isARC; - if (NULL == retain) - { - retain = sel_registerName("retain"); - release = sel_registerName("release"); - autorelease = sel_registerName("autorelease"); - isARC = sel_registerName("_ARCCompliantRetainRelease"); - } - BOOL superIsFast = YES; - if (cls->super_class != Nil) - { - checkARCAccessorsSlow(cls->super_class); - superIsFast = objc_test_class_flag(cls->super_class, objc_class_flag_fast_arc); - } - BOOL selfImplementsRetainRelease = NO; - for (struct objc_method_list *l=cls->methods ; l != NULL ; l= l->next) - { - for (int i=0 ; icount ; i++) - { - SEL s = method_at_index(l, i)->selector; - if (selEqualUnTyped(s, retain) || - selEqualUnTyped(s, release) || - selEqualUnTyped(s, autorelease)) - { - selfImplementsRetainRelease = YES; - } - else if (selEqualUnTyped(s, isARC)) - { - objc_set_class_flag(cls, objc_class_flag_fast_arc); - return; - } - } - } - if (superIsFast && !selfImplementsRetainRelease) - { - objc_set_class_flag(cls, objc_class_flag_fast_arc); - } -} - -static void collectMethodsForMethodListToSparseArray( - struct objc_method_list *list, - SparseArray *sarray, - BOOL recurse) -{ - if (recurse && (NULL != list->next)) - { - collectMethodsForMethodListToSparseArray(list->next, sarray, YES); - } - for (unsigned i=0 ; icount ; i++) - { - SparseArrayInsert(sarray, method_at_index(list, i)->selector->index, - (void*)method_at_index(list, i)); - } -} - - -PRIVATE void init_dispatch_tables () -{ - INIT_LOCK(initialize_lock); - uninstalled_dtable = SparseArrayNewWithDepth(dtable_depth); -#if defined(WITH_TRACING) && defined (__x86_64) - tracing_dtable = SparseArrayNewWithDepth(dtable_depth); -#endif -} - -#if defined(WITH_TRACING) && defined (__x86_64) -static int init; - -static void free_thread_stack(void* x) -{ - free(*(void**)x); -} -static pthread_key_t thread_stack_key; -static void alloc_thread_stack(void) -{ - pthread_key_create(&thread_stack_key, free_thread_stack); - init = 1; -} - -PRIVATE void* pushTraceReturnStack(void) -{ - static pthread_once_t once_control = PTHREAD_ONCE_INIT; - if (!init) - { - pthread_once(&once_control, alloc_thread_stack); - } - void **stack = pthread_getspecific(thread_stack_key); - if (stack == 0) - { - stack = malloc(4096*sizeof(void*)); - } - pthread_setspecific(thread_stack_key, stack + 5); - return stack; -} - -PRIVATE void* popTraceReturnStack(void) -{ - void **stack = pthread_getspecific(thread_stack_key); - stack -= 5; - pthread_setspecific(thread_stack_key, stack); - return stack; -} -#endif - -int objc_registerTracingHook(SEL aSel, objc_tracing_hook aHook) -{ -#if defined(WITH_TRACING) && defined (__x86_64) - // If this is an untyped selector, register it for every typed variant - if (sel_getType_np(aSel) == 0) - { - SEL buffer[16]; - SEL *overflow = 0; - int count = sel_copyTypedSelectors_np(sel_getName(aSel), buffer, 16); - if (count > 16) - { - overflow = calloc(count, sizeof(SEL)); - sel_copyTypedSelectors_np(sel_getName(aSel), buffer, 16); - for (int i=0 ; iindex, aHook); - } - free(overflow); - } - else - { - for (int i=0 ; iindex, aHook); - } - } - } - SparseArrayInsert(tracing_dtable, aSel->index, aHook); - return 0; -#else - return ENOTSUP; -#endif -} - -/** - * Installs a new method in the dtable for `class`. If `replaceMethod` is - * `YES` then this will replace any dtable entry where the original is - * `method_to_replace`. This is used when a superclass method is replaced, to - * replace all subclass dtable entries that are inherited, but not ones that - * are overridden. - */ -static BOOL installMethodInDtable(Class class, - SparseArray *dtable, - struct objc_method *method, - struct objc_method *method_to_replace, - BOOL replaceExisting) -{ - ASSERT(uninstalled_dtable != dtable); - uint32_t sel_id = method->selector->index; - struct objc_method *oldMethod = SparseArrayLookup(dtable, sel_id); - // If we're being asked to replace an existing method, don't if it's the - // wrong one. - if ((replaceExisting) && (method_to_replace != oldMethod)) - { - return NO; - } - // If we're not being asked to replace existing methods and there is an - // existing one, don't replace it. - if (!replaceExisting && (oldMethod != NULL)) - { - return NO; - } - // If this method is the one already installed, pretend to install it again. - if (NULL != oldMethod && (oldMethod->imp == method->imp)) - { - return NO; - } - SparseArrayInsert(dtable, sel_id, method); - // In TDD mode, we also register the first typed method that we - // encounter as the untyped version. -#ifdef TYPE_DEPENDENT_DISPATCH - uint32_t untyped_idx = get_untyped_idx(method->selector); - SparseArrayInsert(dtable, untyped_idx, method); -#endif - - static SEL cxx_construct, cxx_destruct; - if (NULL == cxx_construct) - { - cxx_construct = sel_registerName(".cxx_construct"); - cxx_destruct = sel_registerName(".cxx_destruct"); - } - if (selEqualUnTyped(method->selector, cxx_construct)) - { - class->cxx_construct = method->imp; - } - else if (selEqualUnTyped(method->selector, cxx_destruct)) - { - class->cxx_destruct = method->imp; - } - - for (struct objc_class *subclass=class->subclass_list ; - Nil != subclass ; subclass = subclass->sibling_class) - { - // Don't bother updating dtables for subclasses that haven't been - // initialized yet - if (!classHasDtable(subclass)) { continue; } - - // Recursively install this method in all subclasses - installMethodInDtable(subclass, - dtable_for_class(subclass), - method, - oldMethod, - YES); - } - - // Invalidate the old slot, if there is one. - if (NULL != oldMethod) - { -#ifndef NO_SAFE_CACHING - objc_method_cache_version++; -#endif - } - return YES; -} - -static void installMethodsInClass(Class cls, - SparseArray *methods_to_replace, - SparseArray *methods, - BOOL replaceExisting) -{ - SparseArray *dtable = dtable_for_class(cls); - assert(uninstalled_dtable != dtable); - - uint32_t idx = 0; - struct objc_method *m; - while ((m = SparseArrayNext(methods, &idx))) - { - struct objc_method *method_to_replace = methods_to_replace - ? SparseArrayLookup(methods_to_replace, m->selector->index) - : NULL; - if (!installMethodInDtable(cls, dtable, m, method_to_replace, replaceExisting)) - { - // Remove this method from the list, if it wasn't actually installed - SparseArrayInsert(methods, idx, 0); - } - } -} - -Class class_getSuperclass(Class); - -PRIVATE void objc_update_dtable_for_class(Class cls) -{ - // Only update real dtables - if (!classHasDtable(cls)) { return; } - - LOCK_RUNTIME_FOR_SCOPE(); - - SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); - collectMethodsForMethodListToSparseArray((void*)cls->methods, methods, YES); - SparseArray *super_dtable = cls->super_class ? dtable_for_class(cls->super_class) - : NULL; - installMethodsInClass(cls, super_dtable, methods, YES); - SparseArrayDestroy(methods); - checkARCAccessors(cls); -} - -static void rebaseDtableRecursive(Class cls, Class newSuper) -{ - dtable_t parentDtable = dtable_for_class(newSuper); - // Collect all of the methods for this class: - dtable_t temporaryDtable = SparseArrayNewWithDepth(dtable_depth); - - for (struct objc_method_list *list = cls->methods ; list != NULL ; list = list->next) - { - for (unsigned i=0 ; icount ; i++) - { - struct objc_method *m = method_at_index(list, i); - uint32_t idx = m->selector->index; - // Don't replace existing methods - we're doing the traversal - // pre-order so we'll see methods from categories first. - if (SparseArrayLookup(temporaryDtable, idx) == NULL) - { - SparseArrayInsert(temporaryDtable, idx, m); - } - } - } - - - dtable_t dtable = dtable_for_class(cls); - uint32_t idx = 0; - struct objc_method *method; - // Install all methods from the parent that aren't overridden here. - while ((method = SparseArrayNext(parentDtable, &idx))) - { - if (SparseArrayLookup(temporaryDtable, idx) == NULL) - { - SparseArrayInsert(dtable, idx, method); - SparseArrayInsert(temporaryDtable, idx, method); - } - } - idx = 0; - // Now look at all of the methods in the dtable. If they're not ones from - // the dtable that we've just created, then they must have come from the - // original superclass, so remove them by replacing them with NULL. - while ((method = SparseArrayNext(dtable, &idx))) - { - if (SparseArrayLookup(temporaryDtable, idx) == NULL) - { - SparseArrayInsert(dtable, idx, NULL); - } - } - SparseArrayDestroy(temporaryDtable); - - // merge can make a class ARC-compatible. - checkARCAccessors(cls); - - // Now visit all of our subclasses and propagate the changes downwards. - for (struct objc_class *subclass=cls->subclass_list ; - Nil != subclass ; subclass = subclass->sibling_class) - { - // Don't bother updating dtables for subclasses that haven't been - // initialized yet - if (!classHasDtable(subclass)) { continue; } - rebaseDtableRecursive(subclass, cls); - } - -} - -PRIVATE void objc_update_dtable_for_new_superclass(Class cls, Class newSuper) -{ - // Only update real dtables - if (!classHasDtable(cls)) { return; } - - LOCK_RUNTIME_FOR_SCOPE(); - rebaseDtableRecursive(cls, newSuper); - // Invalidate all caches after this operation. -#ifndef NO_SAFE_CACHING - objc_method_cache_version++; -#endif - - return; -} - -PRIVATE void add_method_list_to_class(Class cls, - struct objc_method_list *list) -{ - // Only update real dtables - if (!classHasDtable(cls)) { return; } - - LOCK_RUNTIME_FOR_SCOPE(); - - SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); - SparseArray *super_dtable = cls->super_class ? dtable_for_class(cls->super_class) - : NULL; - collectMethodsForMethodListToSparseArray(list, methods, NO); - installMethodsInClass(cls, super_dtable, methods, YES); - // Methods now contains only the new methods for this class. - SparseArrayDestroy(methods); - checkARCAccessors(cls); -} - -PRIVATE dtable_t create_dtable_for_class(Class class, dtable_t root_dtable) -{ - // Don't create a dtable for a class that already has one - if (classHasDtable(class)) { return dtable_for_class(class); } - - LOCK_RUNTIME_FOR_SCOPE(); - - // Make sure that another thread didn't create the dtable while we were - // waiting on the lock. - if (classHasDtable(class)) { return dtable_for_class(class); } - - Class super = class_getSuperclass(class); - dtable_t dtable; - dtable_t super_dtable = NULL; - - if (Nil == super) - { - dtable = SparseArrayNewWithDepth(dtable_depth); - } - else - { - super_dtable = dtable_for_class(super); - if (super_dtable == uninstalled_dtable) - { - if (super->isa == class) - { - super_dtable = root_dtable; - } - else - { - abort(); - } - } - dtable = SparseArrayCopy(super_dtable); - } - - // When constructing the initial dtable for a class, we iterate along the - // method list in forward-traversal order. The first method that we - // encounter is always the one that we want to keep, so we instruct - // installMethodInDtable() to replace only methods that are inherited from - // the superclass. - struct objc_method_list *list = (void*)class->methods; - - while (NULL != list) - { - for (unsigned i=0 ; icount ; i++) - { - struct objc_method *super_method = super_dtable - ? SparseArrayLookup(super_dtable, method_at_index(list, i)->selector->index) - : NULL; - installMethodInDtable(class, dtable, method_at_index(list, i), super_method, YES); - } - list = list->next; - } - - return dtable; -} - - -Class class_table_next(void **e); - -PRIVATE void objc_resize_dtables(uint32_t newSize) -{ - // If dtables already have enough space to store all registered selectors, do nothing - if (1< newSize) { return; } - - LOCK_RUNTIME_FOR_SCOPE(); - - if (1< newSize) { return; } - - dtable_depth += 8; - - uint32_t oldShift = uninstalled_dtable->shift; - dtable_t old_uninstalled_dtable = uninstalled_dtable; - - uninstalled_dtable = SparseArrayExpandingArray(uninstalled_dtable, dtable_depth); -#if defined(WITH_TRACING) && defined (__x86_64) - tracing_dtable = SparseArrayExpandingArray(tracing_dtable, dtable_depth); -#endif - { - LOCK_FOR_SCOPE(&initialize_lock); - for (InitializingDtable *buffer = temporary_dtables ; NULL != buffer ; buffer = buffer->next) - { - buffer->dtable = SparseArrayExpandingArray(buffer->dtable, dtable_depth); - } - } - // Resize all existing dtables - void *e = NULL; - struct objc_class *next; - while ((next = class_table_next(&e))) - { - if (next->dtable == old_uninstalled_dtable) - { - next->dtable = uninstalled_dtable; - next->isa->dtable = uninstalled_dtable; - continue; - } - if (NULL != next->dtable && - ((SparseArray*)next->dtable)->shift == oldShift) - { - next->dtable = SparseArrayExpandingArray((void*)next->dtable, dtable_depth); - next->isa->dtable = SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth); - } - } -} - -PRIVATE dtable_t objc_copy_dtable_for_class(dtable_t old, Class cls) -{ - return SparseArrayCopy(old); -} - -PRIVATE void free_dtable(dtable_t dtable) -{ - SparseArrayDestroy(dtable); -} - -LEGACY void update_dispatch_table_for_class(Class cls) -{ - static BOOL warned = NO; - if (!warned) - { - fprintf(stderr, - "Warning: Calling deprecated private ObjC runtime function %s\n", __func__); - warned = YES; - } - objc_update_dtable_for_class(cls); -} - -void objc_resolve_class(Class); - -__attribute__((unused)) static void objc_release_object_lock(id *x) -{ - objc_sync_exit(*x); -} -/** - * Macro that is equivalent to @synchronize, for use in C code. - */ -#define LOCK_OBJECT_FOR_SCOPE(obj) \ - __attribute__((cleanup(objc_release_object_lock)))\ - __attribute__((unused)) id lock_object_pointer = obj;\ - objc_sync_enter(obj); - -/** - * Remove a buffer from an entry in the initializing dtables list. This is - * called as a cleanup to ensure that it runs even if +initialize throws an - * exception. - */ -static void remove_dtable(InitializingDtable* meta_buffer) -{ - LOCK(&initialize_lock); - InitializingDtable *buffer = meta_buffer->next; - // Install the dtable: - meta_buffer->class->dtable = meta_buffer->dtable; - buffer->class->dtable = buffer->dtable; - // Remove the look-aside buffer entry. - if (temporary_dtables == meta_buffer) - { - temporary_dtables = buffer->next; - } - else - { - InitializingDtable *prev = temporary_dtables; - while (prev->next->class != meta_buffer->class) - { - prev = prev->next; - } - prev->next = buffer->next; - } - UNLOCK(&initialize_lock); -} - -/** - * Send a +initialize message to the receiver, if required. - */ -OBJC_PUBLIC void objc_send_initialize(id object) -{ - Class class = classForObject(object); - // If the first message is sent to an instance (weird, but possible and - // likely for things like NSConstantString, make sure +initialize goes to - // the class not the metaclass. - if (objc_test_class_flag(class, objc_class_flag_meta)) - { - class = (Class)object; - } - Class meta = class->isa; - - - // Make sure that the class is resolved. - objc_resolve_class(class); - - // Make sure that the superclass is initialized first. - if (Nil != class->super_class) - { - objc_send_initialize((id)class->super_class); - } - - // Lock the runtime while we're creating dtables and before we acquire the - // init lock. This prevents a lock-order reversal when dtable_for_class is - // called from something holding the runtime lock while we're still holding - // the initialize lock. We should ensure that we never acquire the runtime - // lock after acquiring the initialize lock. - LOCK_RUNTIME(); - - // Superclass +initialize might possibly send a message to this class, in - // which case this method would be called again. See NSObject and - // NSAutoreleasePool +initialize interaction in GNUstep. - if (objc_test_class_flag(class, objc_class_flag_initialized)) - { - // We know that initialization has started because the flag is set. - // Check that it's finished by grabbing the class lock. This will be - // released once the class has been fully initialized. The runtime - // lock needs to be released first to prevent a deadlock between the - // runtime lock and the class-specific lock. - UNLOCK_RUNTIME(); - - objc_sync_enter((id)meta); - objc_sync_exit((id)meta); - assert(dtable_for_class(class) != uninstalled_dtable); - return; - } - - // We should try to acquire the class lock before any runtime/init locks. - // If another thread is in the middle of running `allocateHiddenClass()` it - // has acquired a spinlock and will be trying to acquire the runtime lock. - // When this happens there is a small chance we could hit the same spinlock - // and deadlock the process (as any further attempts to acquire the runtime - // will also block forever). - UNLOCK_RUNTIME(); - - LOCK_OBJECT_FOR_SCOPE((id)meta); - LOCK_RUNTIME(); - LOCK(&initialize_lock); - if (objc_test_class_flag(class, objc_class_flag_initialized)) - { - UNLOCK(&initialize_lock); - UNLOCK_RUNTIME(); - return; - } - BOOL skipMeta = objc_test_class_flag(meta, objc_class_flag_initialized); - // Mark metaclasses as never needing refcount manipulation for their - // instances (classes). - if (!skipMeta) - { - objc_set_class_flag(meta, objc_class_flag_permanent_instances); - } - - // Set the initialized flag on both this class and its metaclass, to make - // sure that +initialize is only ever sent once. - objc_set_class_flag(class, objc_class_flag_initialized); - objc_set_class_flag(meta, objc_class_flag_initialized); - - dtable_t class_dtable = create_dtable_for_class(class, uninstalled_dtable); - dtable_t dtable = skipMeta ? 0 : create_dtable_for_class(meta, class_dtable); - // Now we've finished doing things that may acquire the runtime lock, so we - // can hold onto the initialise lock to make anything doing - // dtable_for_class block until we've finished updating temporary dtable - // lists. - // If another thread holds the runtime lock, it can now proceed until it - // gets into a dtable_for_class call, and then block there waiting for us - // to finish setting up the temporary dtable. - UNLOCK_RUNTIME(); - - static SEL initializeSel = 0; - if (0 == initializeSel) - { - initializeSel = sel_registerName("initialize"); - } - - struct objc_method *initializeSlot = skipMeta ? 0 : - objc_dtable_lookup(dtable, initializeSel->index); - - // If there's no initialize method, then don't bother installing and - // removing the initialize dtable, just install both dtables correctly now - if (0 == initializeSlot) - { - if (!skipMeta) - { - meta->dtable = dtable; - } - class->dtable = class_dtable; - checkARCAccessors(class); - UNLOCK(&initialize_lock); - return; - } - - - - // Create an entry in the dtable look-aside buffer for this. When sending - // a message to this class in future, the lookup function will check this - // buffer if the receiver's dtable is not installed, and block if - // attempting to send a message to this class. - InitializingDtable buffer = { class, class_dtable, temporary_dtables }; - __attribute__((cleanup(remove_dtable))) - InitializingDtable meta_buffer = { meta, dtable, &buffer }; - temporary_dtables = &meta_buffer; - // We now release the initialize lock. We'll reacquire it later when we do - // the cleanup, but at this point we allow other threads to get the - // temporary dtable and call +initialize in other threads. - UNLOCK(&initialize_lock); - // We still hold the class lock at this point. dtable_for_class will block - // there after acquiring the temporary dtable. - - checkARCAccessors(class); - - // Store the buffer in the temporary dtables list. Note that it is safe to - // insert it into a global list, even though it's a temporary variable, - // because we will clean it up after this function. - initializeSlot->imp((id)class, initializeSel); -} - diff --git a/dtable.h b/dtable.h deleted file mode 100644 index f69ee77..0000000 --- a/dtable.h +++ /dev/null @@ -1,141 +0,0 @@ -#include "lock.h" -#include "class.h" -#include "sarray2.h" -#include "objc/slot.h" -#include "visibility.h" -#include -#include - -#ifdef __OBJC_LOW_MEMORY__ -typedef struct objc_dtable* dtable_t; -struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid); -#else -typedef SparseArray* dtable_t; -# define objc_dtable_lookup SparseArrayLookup -#endif - -/** - * Pointer to the sparse array representing the pretend (uninstalled) dtable. - */ -PRIVATE extern dtable_t uninstalled_dtable; -/** - * Structure for maintaining a linked list of temporary dtables. When sending - * an +initialize message to a class, we create a temporary dtables and store - * it in a linked list. This is then used when sending other messages to - * instances of classes in the middle of initialisation. - */ -typedef struct _InitializingDtable -{ - /** The class that owns the dtable. */ - Class class; - /** The dtable for this class. */ - dtable_t dtable; - /** The next uninstalled dtable in the list. */ - struct _InitializingDtable *next; -} InitializingDtable; - -/** Head of the list of temporary dtables. Protected by initialize_lock. */ -extern InitializingDtable *temporary_dtables; -extern mutex_t initialize_lock; - -/** - * Returns whether a class has an installed dtable. - */ -static inline int classHasInstalledDtable(struct objc_class *cls) -{ - return (cls->dtable != uninstalled_dtable); -} - -OBJC_PUBLIC -int objc_sync_enter(id object); -OBJC_PUBLIC -int objc_sync_exit(id object); -/** - * Returns the dtable for a given class. If we are currently in an +initialize - * method then this will block if called from a thread other than the one - * running the +initialize method. - */ -static inline dtable_t dtable_for_class(Class cls) -{ - if (classHasInstalledDtable(cls)) - { - return cls->dtable; - } - - dtable_t dtable = uninstalled_dtable; - - { - LOCK_FOR_SCOPE(&initialize_lock); - if (classHasInstalledDtable(cls)) - { - return cls->dtable; - } - /* This is a linear search, and so, in theory, could be very slow. It - * is O(n) where n is the number of +initialize methods on the stack. - * In practice, this is a very small number. Profiling with GNUstep - * showed that this peaks at 8. */ - InitializingDtable *buffer = temporary_dtables; - while (NULL != buffer) - { - if (buffer->class == cls) - { - dtable = buffer->dtable; - break; - } - buffer = buffer->next; - } - } - - if (dtable != uninstalled_dtable) - { - // Make sure that we block if +initialize is still running. We do this - // after we've released the initialize lock, so that the real dtable - // can be installed. This acquires / releases a recursive mutex, so if - // this mutex is already held by this thread then this will proceed - // immediately. If it's held by another thread (i.e. the one running - // +initialize) then we block here until it's run. We don't need to do - // this if the dtable is the uninstalled dtable, because that means - // +initialize has not yet been sent, so we can wait until something - // triggers it before needing any synchronisation. - objc_sync_enter((id)cls); - objc_sync_exit((id)cls); - } - return dtable; -} - -/** - * Returns whether a class has had a dtable created. The dtable may be - * installed, or stored in the look-aside buffer. - */ -static inline int classHasDtable(struct objc_class *cls) -{ - return (dtable_for_class(cls) != uninstalled_dtable); -} - -/** - * Updates the dtable for a class and its subclasses. Must be called after - * modifying a class's method list. - */ -void objc_update_dtable_for_class(Class); -/** - * Updates the dtable for a class and its subclasses. Must be called after - * changing and initializing a class's superclass. - */ -void objc_update_dtable_for_new_superclass(Class, Class); -/** - * Adds a single method list to a class. This is used when loading categories, - * and is faster than completely rebuilding the dtable. - */ -void add_method_list_to_class(Class cls, - struct objc_method_list *list); - -/** - * Destroys a dtable. - */ -void free_dtable(dtable_t dtable); - -/** - * Checks whether the class supports ARC. This can be used before the dtable - * is installed. - */ -void checkARCAccessorsSlow(Class cls); diff --git a/dwarf_eh.h b/dwarf_eh.h deleted file mode 100644 index e06a585..0000000 --- a/dwarf_eh.h +++ /dev/null @@ -1,327 +0,0 @@ -/** - * This file is Copyright PathScale 2010. Permission granted to distribute - * according to the terms of the MIT license (see COPYING.MIT) - */ -#include -#include - -// _GNU_SOURCE must be defined for unwind.h to expose some of the functions -// that we want. If it isn't, then we define it and undefine it to make sure -// that it doesn't impact the rest of the program. -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -# include "unwind.h" -# undef _GNU_SOURCE -#else -# include "unwind.h" -#endif - -/** - * Type used to store pointers to values computed by DWARF expressions. - */ -typedef unsigned char *dw_eh_ptr_t; -// Flag indicating a signed quantity -#define DW_EH_PE_signed 0x08 -/// DWARF data encoding types -enum dwarf_data_encoding -{ - // Unsigned, little-endian, base 128-encoded (variable length) - DW_EH_PE_uleb128 = 0x01, - // uint16 - DW_EH_PE_udata2 = 0x02, - // uint32 - DW_EH_PE_udata4 = 0x03, - // uint64 - DW_EH_PE_udata8 = 0x04, - // Signed versions of the above: - DW_EH_PE_sleb128 = DW_EH_PE_uleb128 | DW_EH_PE_signed, - DW_EH_PE_sdata2 = DW_EH_PE_udata2 | DW_EH_PE_signed, - DW_EH_PE_sdata4 = DW_EH_PE_udata4 | DW_EH_PE_signed, - DW_EH_PE_sdata8 = DW_EH_PE_udata8 | DW_EH_PE_signed -}; - -static inline enum dwarf_data_encoding get_encoding(unsigned char x) -{ - return (enum dwarf_data_encoding)(x & 0xf); -} - -enum dwarf_data_relative -{ - // Value is omitted - DW_EH_PE_omit = 0xff, - // Absolute pointer value - DW_EH_PE_absptr = 0x00, - // Value relative to program counter - DW_EH_PE_pcrel = 0x10, - // Value relative to the text segment - DW_EH_PE_textrel = 0x20, - // Value relative to the data segment - DW_EH_PE_datarel = 0x30, - // Value relative to the start of the function - DW_EH_PE_funcrel = 0x40, - // Aligned pointer (Not supported yet - are they actually used?) - DW_EH_PE_aligned = 0x50, - // Pointer points to address of real value - DW_EH_PE_indirect = 0x80 -}; -static inline enum dwarf_data_relative get_base(unsigned char x) -{ - return (enum dwarf_data_relative)(x & 0x70); -} -static int is_indirect(unsigned char x) -{ - return (x & DW_EH_PE_indirect); -} - -static inline int dwarf_size_of_fixed_size_field(unsigned char type) -{ - // Low three bits indicate size... - switch (type & 7) - { - case DW_EH_PE_udata2: return 2; - case DW_EH_PE_udata4: return 4; - case DW_EH_PE_udata8: return 8; - case DW_EH_PE_absptr: return sizeof(void*); - } - abort(); -} - -/** - * Read an unsigned, little-endian, base-128, DWARF value. Updates *data to - * point to the end of the value. - */ -static uint64_t read_leb128(unsigned char** data, int *b) -{ - uint64_t uleb = 0; - unsigned int bit = 0; - unsigned char digit = 0; - // We have to read at least one octet, and keep reading until we get to one - // with the high bit unset - do - { - // This check is a bit too strict - we should also check the highest - // bit of the digit. - assert(bit < sizeof(uint64_t) * 8); - // Get the base 128 digit - digit = (**data) & 0x7f; - // Add it to the current value - uleb += digit << bit; - // Increase the shift value - bit += 7; - // Proceed to the next octet - (*data)++; - // Terminate when we reach a value that does not have the high bit set - // (i.e. which was not modified when we mask it with 0x7f) - } while ((*(*data - 1)) != digit); - *b = bit; - - return uleb; -} - -static int64_t read_uleb128(unsigned char** data) -{ - int b; - return read_leb128(data, &b); -} - - -static int64_t read_sleb128(unsigned char** data) -{ - int bits; - // Read as if it's signed - uint64_t uleb = read_leb128(data, &bits); - // If the most significant bit read is 1, then we need to sign extend it - if (uleb >> (bits-1) == 1) - { - // Sign extend by setting all bits in front of it to 1 - uleb |= ((int64_t)-1) << bits; - } - return (int64_t)uleb; -} - -static uint64_t read_value(char encoding, unsigned char **data) -{ - enum dwarf_data_encoding type = get_encoding(encoding); - uint64_t v; - switch ((int)type) - { - // Read fixed-length types -#define READ(dwarf, type) \ - case dwarf:\ - v = (uint64_t)(*(type*)(*data));\ - *data += sizeof(type);\ - break; - READ(DW_EH_PE_udata2, uint16_t) - READ(DW_EH_PE_udata4, uint32_t) - READ(DW_EH_PE_udata8, uint64_t) - READ(DW_EH_PE_sdata2, int16_t) - READ(DW_EH_PE_sdata4, int32_t) - READ(DW_EH_PE_sdata8, int64_t) - case DW_EH_PE_absptr: - v = (uint64_t)(*(intptr_t*)(*data)); - *data += sizeof(intptr_t); - break; - //READ(DW_EH_PE_absptr, intptr_t) -#undef READ - case DW_EH_PE_sleb128: - v = read_sleb128(data); - break; - case DW_EH_PE_uleb128: - v = read_uleb128(data); - break; - default: abort(); - } - - return v; -} - -static uint64_t resolve_indirect_value(struct _Unwind_Context *c, unsigned char encoding, int64_t v, dw_eh_ptr_t start) -{ - switch (get_base(encoding)) - { - case DW_EH_PE_pcrel: - v += (uint64_t)(uintptr_t)start; - break; - case DW_EH_PE_textrel: - v += (uint64_t)(uintptr_t)_Unwind_GetTextRelBase(c); - break; - case DW_EH_PE_datarel: - v += (uint64_t)(uintptr_t)_Unwind_GetDataRelBase(c); - break; - case DW_EH_PE_funcrel: - v += (uint64_t)(uintptr_t)_Unwind_GetRegionStart(c); - default: - break; - } - // If this is an indirect value, then it is really the address of the real - // value - // TODO: Check whether this should really always be a pointer - it seems to - // be a GCC extensions, so not properly documented... - if (is_indirect(encoding)) - { - v = (uint64_t)(uintptr_t)*(void**)(uintptr_t)v; - } - return v; -} - - -static inline void read_value_with_encoding(struct _Unwind_Context *context, - dw_eh_ptr_t *data, - uint64_t *out) -{ - dw_eh_ptr_t start = *data; - unsigned char encoding = *((*data)++); - // If this value is omitted, skip it and don't touch the output value - if (encoding == DW_EH_PE_omit) { return; } - - *out = read_value(encoding, data); - *out = resolve_indirect_value(context, encoding, *out, start); -} - - -struct dwarf_eh_lsda -{ - dw_eh_ptr_t region_start; - dw_eh_ptr_t landing_pads; - dw_eh_ptr_t type_table; - unsigned char type_table_encoding; - dw_eh_ptr_t call_site_table; - dw_eh_ptr_t action_table; - unsigned char callsite_encoding; -}; - -static inline struct dwarf_eh_lsda parse_lsda(struct _Unwind_Context *context, unsigned char *data) -{ - struct dwarf_eh_lsda lsda; - - lsda.region_start = (dw_eh_ptr_t)(uintptr_t)_Unwind_GetRegionStart(context); - - // If the landing pads are relative to anything other than the start of - // this region, find out where. This is @LPStart in the spec, although the - // encoding that GCC uses does not quite match the spec. - uint64_t v = (uint64_t)(uintptr_t)lsda.region_start; - read_value_with_encoding(context, &data, &v); - lsda.landing_pads = (dw_eh_ptr_t)(uintptr_t)v; - - // If there is a type table, find out where it is. This is @TTBase in the - // spec. Note: we find whether there is a type table pointer by checking - // whether the leading byte is DW_EH_PE_omit (0xff), which is not what the - // spec says, but does seem to be how G++ indicates this. - lsda.type_table = 0; - lsda.type_table_encoding = *data++; - if (lsda.type_table_encoding != DW_EH_PE_omit) - { - v = read_uleb128(&data); - dw_eh_ptr_t type_table = data; - type_table += v; - lsda.type_table = type_table; - //lsda.type_table = (uintptr_t*)(data + v); - } - -#if defined(__arm__) && !defined(__ARM_DWARF_EH__) - lsda.type_table_encoding = (DW_EH_PE_pcrel | DW_EH_PE_indirect); -#endif - - lsda.callsite_encoding = (enum dwarf_data_encoding)(*(data++)); - - // Action table is immediately after the call site table - lsda.action_table = data; - uintptr_t callsite_size = (uintptr_t)read_uleb128(&data); - lsda.action_table = data + callsite_size; - // Call site table is immediately after the header - lsda.call_site_table = (dw_eh_ptr_t)data; - - - return lsda; -} - -struct dwarf_eh_action -{ - dw_eh_ptr_t landing_pad; - dw_eh_ptr_t action_record; -}; - -/** - * Look up the landing pad that corresponds to the current invoke. - */ -__attribute__((unused)) -static struct dwarf_eh_action - dwarf_eh_find_callsite(struct _Unwind_Context *context, struct dwarf_eh_lsda *lsda) -{ - struct dwarf_eh_action result = { 0, 0 }; - uint64_t ip = _Unwind_GetIP(context) - _Unwind_GetRegionStart(context); - unsigned char *callsite_table = (unsigned char*)lsda->call_site_table; - while (callsite_table <= lsda->action_table) - { - // Once again, the layout deviates from the spec. - uint64_t call_site_start, call_site_size, landing_pad, action; - call_site_start = read_value(lsda->callsite_encoding, &callsite_table); - call_site_size = read_value(lsda->callsite_encoding, &callsite_table); - - // Call site entries are started - if (call_site_start > ip) { break; } - - landing_pad = read_value(lsda->callsite_encoding, &callsite_table); - action = read_uleb128(&callsite_table); - - if (call_site_start <= ip && ip <= call_site_start + call_site_size) - { - if (action) - { - // Action records are 1-biased so both no-record and zeroth - // record can be stored. - result.action_record = lsda->action_table + action - 1; - } - // No landing pad means keep unwinding. - if (landing_pad) - { - // Landing pad is the offset from the value in the header - result.landing_pad = lsda->landing_pads + landing_pad; - } - break; - } - } - return result; -} - -#define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) ((((uint64_t)a) << 56) + (((uint64_t)b) << 48) + (((uint64_t)c) << 40) + (((uint64_t)d) << 32) + (((uint64_t)e) << 24) + (((uint64_t)f) << 16) + (((uint64_t)g) << 8) + (((uint64_t)h))) diff --git a/eh_personality.c b/eh_personality.c deleted file mode 100644 index d638d97..0000000 --- a/eh_personality.c +++ /dev/null @@ -1,772 +0,0 @@ -#include -#include -#include -#include "dwarf_eh.h" -#include "objc/runtime.h" -#include "objc/hooks.h" -#include "objc/objc-exception.h" -#include "class.h" -#include "objcxx_eh.h" - -#ifndef DEBUG_EXCEPTIONS -#define DEBUG_LOG(...) -#else -#define DEBUG_LOG(str, ...) fprintf(stderr, str, ## __VA_ARGS__) -#endif - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif -#if !__has_builtin(__builtin_unreachable) -#define __builtin_unreachable abort -#endif - -void test_cxx_eh_implementation(); -/** - * The Itanium C++ public structure for in-flight exception status. - */ -struct __cxa_eh_globals -{ - /** - * The head exception object. By convention, this is actually the end of - * the `__cxa_exception` structure and points to the address of the thrown - * object. This is either an `id*` or a pointer to a C++ type that we're - * not going to look at. - */ - struct __cxa_exception *caughtExceptions; - /** - * The number of in-flight exceptions thrown. - */ - unsigned int uncaughtExceptions; -}; - - -// Weak references to C++ runtime functions. We don't bother testing that -// these are 0 before calling them, because if they are not resolved then we -// should not be in a code path that involves a C++ exception. -__attribute__((weak)) void *__cxa_begin_catch(void *e); -__attribute__((weak)) void __cxa_end_catch(void); -__attribute__((weak)) void __cxa_rethrow(void); -__attribute__((weak)) struct __cxa_eh_globals *__cxa_get_globals(void); - - -/** - * Class of exceptions to distinguish between this and other exception types. - */ -static const uint64_t objc_exception_class = EXCEPTION_CLASS('G','N','U','C','O','B','J','C'); - -/** - * Structure used as a header on thrown exceptions. - */ -struct objc_exception -{ - /** The selector value to be returned when installing the catch handler. - * Used at the call site to determine which catch() block should execute. - * This is found in phase 1 of unwinding then installed in phase 2.*/ - int handlerSwitchValue; - /** The cached landing pad for the catch handler.*/ - void *landingPad; - /** - * Next pointer for chained exceptions. - */ - struct objc_exception *next; - /** - * The number of nested catches that may hold this exception. This is - * negative while an exception is being rethrown. - */ - int catch_count; - /** The language-agnostic part of the exception header. */ - struct _Unwind_Exception unwindHeader; - /** Thrown object. This is after the unwind header so that the C++ - * exception handler can catch this as a foreign exception. */ - id object; - /** C++ exception structure. Used for mixed exceptions. When we are in - * Objective-C++ code, we create this structure for passing to the C++ - * exception personality function. It will then handle installing - * exceptions for us. */ - struct _Unwind_Exception *cxx_exception; -}; - -struct objc_exception *objc_exception_from_header(struct _Unwind_Exception *ex) -{ - return (struct objc_exception*)((char*)ex - - offsetof(struct objc_exception, unwindHeader)); -} - -typedef enum -{ - handler_none, - handler_cleanup, - handler_catchall_id, - handler_catchall, - handler_class -} handler_type; - -enum exception_type -{ - NONE, - CXX, - OBJC, - FOREIGN, - BOXED_FOREIGN -}; -struct thread_data -{ - enum exception_type current_exception_type; - BOOL cxxCaughtException; - struct objc_exception *caughtExceptions; -}; - -static __thread struct thread_data thread_data; - -static struct thread_data *get_thread_data(void) -{ - return &thread_data; -} - -static struct thread_data *get_thread_data_fast(void) -{ - return &thread_data; -} - - -/** - * Saves the result of the landing pad that we have found. For ARM, this is - * stored in the generic unwind structure, while on other platforms it is - * stored in the Objective-C exception. - */ -static void saveLandingPad(struct _Unwind_Context *context, - struct _Unwind_Exception *ucb, - struct objc_exception *ex, - int selector, - dw_eh_ptr_t landingPad) -{ -#if defined(__arm__) && !defined(__ARM_DWARF_EH__) - // On ARM, we store the saved exception in the generic part of the structure - ucb->barrier_cache.sp = _Unwind_GetGR(context, 13); - ucb->barrier_cache.bitpattern[1] = (uint32_t)selector; - ucb->barrier_cache.bitpattern[3] = (uint32_t)landingPad; -#else - // Cache the results for the phase 2 unwind, if we found a handler - // and this is not a foreign exception. We can't cache foreign exceptions - // because we don't know their structure (although we could cache C++ - // exceptions...) - if (ex) - { - ex->handlerSwitchValue = selector; - ex->landingPad = landingPad; - } -#endif -} - -/** - * Loads the saved landing pad. Returns 1 on success, 0 on failure. - */ -static int loadLandingPad(struct _Unwind_Context *context, - struct _Unwind_Exception *ucb, - struct objc_exception *ex, - unsigned long *selector, - dw_eh_ptr_t *landingPad) -{ -#if defined(__arm__) && !defined(__ARM_DWARF_EH__) - *selector = ucb->barrier_cache.bitpattern[1]; - *landingPad = (dw_eh_ptr_t)ucb->barrier_cache.bitpattern[3]; - return 1; -#else - if (ex) - { - *selector = ex->handlerSwitchValue; - *landingPad = ex->landingPad; - return 0; - } - return 0; -#endif -} - -static inline _Unwind_Reason_Code continueUnwinding(struct _Unwind_Exception *ex, - struct _Unwind_Context *context) -{ -#if defined(__arm__) && !defined(__ARM_DWARF_EH__) - if (__gnu_unwind_frame(ex, context) != _URC_OK) { return _URC_FAILURE; } -#endif - return _URC_CONTINUE_UNWIND; -} - -static void cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *e) -{ - /* - if (header->exceptionDestructor) - header->exceptionDestructor (e + 1); - - free((struct objc_exception*) ((char*)e - offsetof(struct objc_exception, - unwindHeader))); - */ -} - -void objc_exception_rethrow(struct _Unwind_Exception *e); - -/** - * Throws an Objective-C exception. This function is, unfortunately, used for - * rethrowing caught exceptions too, even in @finally() blocks. Unfortunately, - * this means that we have some problems if the exception is boxed. - */ -void objc_exception_throw(id object) -{ - struct thread_data *td = get_thread_data(); - DEBUG_LOG("Throwing %p, in flight exception: %p\n", object, td->lastThrownObject); - DEBUG_LOG("Exception caught by C++: %d\n", td->cxxCaughtException); - // If C++ caught the exception, then we may need to make C++ rethrow it if - // we want to preserve exception state. Rethrows should be handled with - // objc_exception_rethrow, but clang appears to do the wrong thing for some - // cases. - if (td->cxxCaughtException) - { - struct __cxa_eh_globals *globals = __cxa_get_globals(); - if ((globals->caughtExceptions != NULL) && - (*(id*)globals->caughtExceptions == object)) - { - __cxa_rethrow(); - } - } - - SEL rethrow_sel = sel_registerName("rethrow"); - if ((nil != object) && - (class_respondsToSelector(classForObject(object), rethrow_sel))) - { - DEBUG_LOG("Rethrowing\n"); - IMP rethrow = objc_msg_lookup(object, rethrow_sel); - rethrow(object, rethrow_sel); - // Should not be reached! If it is, then the rethrow method actually - // didn't, so we throw it normally. - } - - DEBUG_LOG("Throwing %p\n", object); - - struct objc_exception *ex = calloc(1, sizeof(struct objc_exception)); - - ex->unwindHeader.exception_class = objc_exception_class; - ex->unwindHeader.exception_cleanup = cleanup; - - ex->object = object; - - td->cxxCaughtException = NO; - - _Unwind_Reason_Code err = _Unwind_RaiseException(&ex->unwindHeader); - free(ex); - if (_URC_END_OF_STACK == err && 0 != _objc_unexpected_exception) - { - _objc_unexpected_exception(object); - } - DEBUG_LOG("Throw returned %d\n",(int) err); - abort(); -} - -static Class get_type_table_entry(struct _Unwind_Context *context, - struct dwarf_eh_lsda *lsda, - int filter) -{ - dw_eh_ptr_t record = lsda->type_table - - dwarf_size_of_fixed_size_field(lsda->type_table_encoding)*filter; - dw_eh_ptr_t start = record; - int64_t offset = read_value(lsda->type_table_encoding, &record); - - if (0 == offset) { return Nil; } - - // ...so we need to resolve it - char *class_name = (char*)(intptr_t)resolve_indirect_value(context, - lsda->type_table_encoding, offset, start); - - if (0 == class_name) { return Nil; } - - DEBUG_LOG("Class name: %s\n", class_name); - - if (strcmp("@id", class_name) == 0) { return (Class)1; } - - return (Class)objc_getClass(class_name); -} - -static BOOL isKindOfClass(Class thrown, Class type) -{ - do - { - if (thrown == type) - { - return YES; - } - thrown = class_getSuperclass(thrown); - } while (Nil != thrown); - - return NO; -} - - -static handler_type check_action_record(struct _Unwind_Context *context, - BOOL foreignException, - struct dwarf_eh_lsda *lsda, - dw_eh_ptr_t action_record, - Class thrown_class, - unsigned long *selector) -{ - if (!action_record) { return handler_cleanup; } - while (action_record) - { - int filter = read_sleb128(&action_record); - dw_eh_ptr_t action_record_offset_base = action_record; - int displacement = read_sleb128(&action_record); - *selector = filter; - DEBUG_LOG("Filter: %d\n", filter); - if (filter > 0) - { - Class type = get_type_table_entry(context, lsda, filter); - DEBUG_LOG("%p type: %d\n", type, !foreignException); - // Catchall - if (Nil == type) - { - return handler_catchall; - } - // We treat id catches as catchalls when an object is thrown and as - // nothing when a foreign exception is thrown - else if ((Class)1 == type) - { - DEBUG_LOG("Found id catch\n"); - if (!foreignException) - { - return handler_catchall_id; - } - } - else if (!foreignException && isKindOfClass(thrown_class, type)) - { - DEBUG_LOG("found handler for %s\n", type->name); - return handler_class; - } - else if (thrown_class == type) - { - return handler_class; - } - } - else if (filter == 0) - { - DEBUG_LOG("0 filter\n"); - // Cleanup? I think the GNU ABI doesn't actually use this, but it - // would be a good way of indicating a non-id catchall... - return handler_cleanup; - } - else - { - DEBUG_LOG("Filter value: %d\n" - "Your compiler and I disagree on the correct layout of EH data.\n", - filter); - abort(); - } - *selector = 0; - action_record = displacement ? - action_record_offset_base + displacement : 0; - } - return handler_none; -} - -/** - * The Objective-C exception personality function implementation. This is - * shared by the GCC-compatible and the new implementation. - * - * The key difference is that the new implementation always returns the - * exception object and boxes it. - */ -static inline _Unwind_Reason_Code internal_objc_personality(int version, - _Unwind_Action actions, - uint64_t exceptionClass, - struct _Unwind_Exception *exceptionObject, - struct _Unwind_Context *context, - BOOL isNew) -{ - DEBUG_LOG("%s personality function called %p\n", isNew ? "New" : "Old", exceptionObject); - - // This personality function is for version 1 of the ABI. If you use it - // with a future version of the ABI, it won't know what to do, so it - // reports a fatal error and give up before it breaks anything. - if (1 != version) - { - return _URC_FATAL_PHASE1_ERROR; - } - struct objc_exception *ex = 0; -#ifdef DEBUG_EXCEPTIONS - char *cls = (char*)&exceptionClass; -#endif - DEBUG_LOG("Class: %c%c%c%c%c%c%c%c\n", cls[7], cls[6], cls[5], cls[4], cls[3], cls[2], cls[1], cls[0]); - - // Check if this is a foreign exception. If it is a C++ exception, then we - // have to box it. If it's something else, like a LanguageKit exception - // then we ignore it (for now) - BOOL foreignException = exceptionClass != objc_exception_class; - // Is this a C++ exception containing an Objective-C++ object? - BOOL objcxxException = NO; - // The object to return - void *object = NULL; - -#ifndef NO_OBJCXX - if (cxx_exception_class == 0) - { - test_cxx_eh_implementation(); - } - - if (exceptionClass == cxx_exception_class) - { - int objcxx; - id obj = objc_object_for_cxx_exception(exceptionObject, &objcxx); - objcxxException = objcxx; - if (objcxxException) - { - object = obj; - DEBUG_LOG("ObjC++ object exception %p\n", object); - // This is a foreign exception, buy for the purposes of exception - // matching, we pretend that it isn't. - foreignException = NO; - } - } -#endif - - Class thrown_class = Nil; - - if (objcxxException) - { - thrown_class = (object == 0) ? Nil : classForObject((id)object); - } - // If it's not a foreign exception, then we know the layout of the - // language-specific exception stuff. - else if (!foreignException) - { - ex = objc_exception_from_header(exceptionObject); - if (ex->object != nil) - { - thrown_class = classForObject(ex->object); - } - } - else if (_objc_class_for_boxing_foreign_exception) - { - thrown_class = _objc_class_for_boxing_foreign_exception(exceptionClass); - DEBUG_LOG("Foreign class: %p\n", thrown_class); - } - unsigned char *lsda_addr = (void*)_Unwind_GetLanguageSpecificData(context); - DEBUG_LOG("LSDA: %p\n", lsda_addr); - - // No LSDA implies no landing pads - try the next frame - if (0 == lsda_addr) - { - return continueUnwinding(exceptionObject, context); - } - - // These two variables define how the exception will be handled. - struct dwarf_eh_action action = {0}; - unsigned long selector = 0; - - if (actions & _UA_SEARCH_PHASE) - { - DEBUG_LOG("Search phase...\n"); - struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); - action = dwarf_eh_find_callsite(context, &lsda); - handler_type handler = check_action_record(context, foreignException, - &lsda, action.action_record, thrown_class, &selector); - DEBUG_LOG("handler: %d\n", handler); - // If there's no action record, we've only found a cleanup, so keep - // searching for something real - if (handler == handler_class || - ((handler == handler_catchall_id) && !foreignException) || - (handler == handler_catchall)) - { - saveLandingPad(context, exceptionObject, ex, selector, action.landing_pad); - DEBUG_LOG("Found handler! %d\n", handler); - return _URC_HANDLER_FOUND; - } - return continueUnwinding(exceptionObject, context); - } - DEBUG_LOG("Phase 2: Fight!\n"); - - // TODO: If this is a C++ exception, we can cache the lookup and cheat a - // bit - if (!(actions & _UA_HANDLER_FRAME)) - { - DEBUG_LOG("Not the handler frame, looking up the cleanup again\n"); - struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); - action = dwarf_eh_find_callsite(context, &lsda); - // If there's no cleanup here, continue unwinding. - if (0 == action.landing_pad) - { - return continueUnwinding(exceptionObject, context); - } - handler_type handler = check_action_record(context, foreignException, - &lsda, action.action_record, thrown_class, &selector); - DEBUG_LOG("handler! %d %d\n", (int)handler, (int)selector); - // On ARM, we occasionally get called to install a handler without - // phase 1 running (no idea why, I suspect a bug in the generic - // unwinder), so skip this check. -#if !(defined(__arm__) && !defined(__ARM_DWARF_EH__)) - // If this is not a cleanup, ignore it and keep unwinding. - if ((handler != handler_cleanup) && !objcxxException) - { - DEBUG_LOG("Ignoring handler! %d\n",handler); - return continueUnwinding(exceptionObject, context); - } -#endif - DEBUG_LOG("Installing cleanup...\n"); - // If there is a cleanup, we need to return the exception structure - // (not the object) to the calling frame. The exception object - object = exceptionObject; - } - else if (foreignException || objcxxException) - { - struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); - action = dwarf_eh_find_callsite(context, &lsda); - check_action_record(context, foreignException, &lsda, - action.action_record, thrown_class, &selector); - // If it's a foreign exception, then box it. If it's an Objective-C++ - // exception, then we need to delete the exception object. - if (foreignException) - { - DEBUG_LOG("Doing the foreign exception thing...\n"); - //[thrown_class exceptionWithForeignException: exceptionObject]; - SEL box_sel = sel_registerName("exceptionWithForeignException:"); - IMP boxfunction = objc_msg_lookup((id)thrown_class, box_sel); - if (!isNew) - { - object = boxfunction((id)thrown_class, box_sel, exceptionObject); - DEBUG_LOG("Boxed as %p\n", object); - } - } - else if (!isNew) // ObjCXX exception - { - _Unwind_DeleteException(exceptionObject); - } - // In the new EH ABI, we call objc_begin_catch() / and - // objc_end_catch(), which will wrap their __cxa* versions. - } - else - { - // Restore the saved info if we saved some last time. - loadLandingPad(context, exceptionObject, ex, &selector, &action.landing_pad); - object = ex->object; - if (!isNew) - { - free(ex); - } - } - - _Unwind_SetIP(context, (uintptr_t)action.landing_pad); - _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), - (uintptr_t)(isNew ? exceptionObject : object)); - _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), selector); - - DEBUG_LOG("Installing context, selector %d\n", (int)selector); - get_thread_data()->cxxCaughtException = NO; - return _URC_INSTALL_CONTEXT; -} - -OBJC_PUBLIC -BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) - return internal_objc_personality(version, actions, exceptionClass, - exceptionObject, context, NO); -} - -OBJC_PUBLIC -BEGIN_PERSONALITY_FUNCTION(__gnustep_objc_personality_v0) - return internal_objc_personality(version, actions, exceptionClass, - exceptionObject, context, YES); -} - -OBJC_PUBLIC -BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0) -#ifndef NO_OBJCXX - if (cxx_exception_class == 0) - { - test_cxx_eh_implementation(); - } - if (exceptionClass == objc_exception_class) - { - struct objc_exception *ex = objc_exception_from_header(exceptionObject); - if (0 == ex->cxx_exception) - { - ex->cxx_exception = objc_init_cxx_exception(ex->object); - } - // We now have two copies of the _Unwind_Exception object (which stores - // state for the unwinder) in flight. Make sure that they're in sync. - COPY_EXCEPTION(ex->cxx_exception, exceptionObject); - exceptionObject = ex->cxx_exception; - exceptionClass = cxx_exception_class; - int ret = CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); - COPY_EXCEPTION(exceptionObject, ex->cxx_exception); - if (ret == _URC_INSTALL_CONTEXT) - { - get_thread_data()->cxxCaughtException = YES; - } - return ret; - } -#endif - return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); -} - -OBJC_PUBLIC id objc_begin_catch(struct _Unwind_Exception *exceptionObject) -{ - struct thread_data *td = get_thread_data(); - DEBUG_LOG("Beginning catch %p\n", exceptionObject); - td->cxxCaughtException = NO; - if (exceptionObject->exception_class == objc_exception_class) - { - td->current_exception_type = OBJC; - struct objc_exception *ex = objc_exception_from_header(exceptionObject); - if (ex->catch_count == 0) - { - // If this is the first catch, add it to the list. - ex->catch_count = 1; - ex->next = td->caughtExceptions; - td->caughtExceptions = ex; - } - else if (ex->catch_count < 0) - { - // If this is being thrown, mark it as caught again and increment - // the refcount - ex->catch_count = -ex->catch_count + 1; - } - else - { - // Otherwise, just increment the catch count - ex->catch_count++; - } - DEBUG_LOG("objc catch\n"); - return ex->object; - } - // If we have a foreign exception while we have stacked exceptions, we have - // a problem. We can't chain them, so we follow the example of C++ and - // just abort. - if (td->caughtExceptions != 0) - { - // FIXME: Actually, we can handle a C++ exception if only ObjC - // exceptions are in-flight - abort(); - } -#ifndef NO_OBJCXX - // If this is a C++ exception, let the C++ runtime handle it. - if (exceptionObject->exception_class == cxx_exception_class) - { - DEBUG_LOG("c++ catch\n"); - td->current_exception_type = CXX; - return __cxa_begin_catch(exceptionObject); - } -#endif - DEBUG_LOG("foreign exception catch\n"); - // Box if we have a boxing function. - if (_objc_class_for_boxing_foreign_exception) - { - Class thrown_class = - _objc_class_for_boxing_foreign_exception(exceptionObject->exception_class); - SEL box_sel = sel_registerName("exceptionWithForeignException:"); - id(*boxfunction)(Class,SEL,struct _Unwind_Exception*) = - (id(*)(Class,SEL,struct _Unwind_Exception*))objc_msg_lookup((id)thrown_class, box_sel); - if (boxfunction != 0) - { - id boxed = boxfunction(thrown_class, box_sel, exceptionObject); - td->caughtExceptions = (struct objc_exception*)boxed; - td->current_exception_type = BOXED_FOREIGN; - return boxed; - } - } - td->current_exception_type = FOREIGN; - td->caughtExceptions = (struct objc_exception*)exceptionObject; - // If this is some other kind of exception, then assume that the value is - // at the end of the exception header. - return (id)((char*)exceptionObject + sizeof(struct _Unwind_Exception)); -} - -OBJC_PUBLIC void objc_end_catch(void) -{ - struct thread_data *td = get_thread_data_fast(); - // If this is a boxed foreign exception then the boxing class is - // responsible for cleaning it up - if (td->current_exception_type == BOXED_FOREIGN) - { - td->caughtExceptions = 0; - td->current_exception_type = NONE; - return; - } - DEBUG_LOG("Ending catch\n"); - // If this is a C++ exception, then just let the C++ runtime handle it. - if (td->current_exception_type == CXX) - { - __cxa_end_catch(); - td->current_exception_type = OBJC; - return; - } - if (td->current_exception_type == FOREIGN) - { - struct _Unwind_Exception *e = ((struct _Unwind_Exception*)td->caughtExceptions); - e->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT, e); - td->current_exception_type = NONE; - td->caughtExceptions = 0; - return; - } - // Otherwise we should do the cleanup thing. Nested catches are possible, - // so we only clean up the exception if this is the last reference. - assert(td->caughtExceptions != 0); - struct objc_exception *ex = td->caughtExceptions; - // If this is being rethrown decrement its (negated) catch count, but don't - // delete it even if its catch count would be 0. - if (ex->catch_count < 0) - { - ex->catch_count++; - return; - } - ex->catch_count--; - if (ex->catch_count == 0) - { - td->caughtExceptions = ex->next; - free(ex); - } -} - -OBJC_PUBLIC void objc_exception_rethrow(struct _Unwind_Exception *e) -{ - struct thread_data *td = get_thread_data_fast(); - // If this is an Objective-C exception, then - if (td->current_exception_type == OBJC) - { - struct objc_exception *ex = objc_exception_from_header(e); - assert(e->exception_class == objc_exception_class); - assert(ex == td->caughtExceptions); - assert(ex->catch_count > 0); - // Negate the catch count, so that we can detect that this is a - // rethrown exception in objc_end_catch - ex->catch_count = -ex->catch_count; - _Unwind_Reason_Code err = _Unwind_Resume_or_Rethrow(e); - free(ex); - if (_URC_END_OF_STACK == err && 0 != _objc_unexpected_exception) - { - _objc_unexpected_exception(ex->object); - } - abort(); - } -#ifndef NO_OBJCXX - else if (td->current_exception_type == CXX) - { - assert(e->exception_class == cxx_exception_class); - __cxa_rethrow(); - } -#endif - if (td->current_exception_type == BOXED_FOREIGN) - { - SEL rethrow_sel = sel_registerName("rethrow"); - id object = (id)td->caughtExceptions; - if ((nil != object) && - (class_respondsToSelector(classForObject(object), rethrow_sel))) - { - DEBUG_LOG("Rethrowing boxed exception\n"); - IMP rethrow = objc_msg_lookup(object, rethrow_sel); - rethrow(object, rethrow_sel); - } - } - assert(e == (struct _Unwind_Exception*)td->caughtExceptions); - _Unwind_Resume_or_Rethrow(e); - abort(); -} - -objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler) -{ - return __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST); -} diff --git a/eh_trampoline.cc b/eh_trampoline.cc deleted file mode 100644 index 77849e1..0000000 --- a/eh_trampoline.cc +++ /dev/null @@ -1,9 +0,0 @@ -void cxx_throw(); - -__attribute((visibility("hidden"))) -int eh_trampoline() -{ - struct X { ~X() {} } x; - cxx_throw(); - return 0; -} diff --git a/eh_win32_msvc.cc b/eh_win32_msvc.cc deleted file mode 100644 index 997dce5..0000000 --- a/eh_win32_msvc.cc +++ /dev/null @@ -1,287 +0,0 @@ -#include -#include -#include -#include - -#include "objc/runtime.h" -#include "objc/objc-exception.h" -#include "visibility.h" - -#include -#define RtlAddGrowableFunctionTable ClangIsConfusedByTypedefReturnTypes -#include - - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#if !__has_builtin(__builtin_unreachable) -#define __builtin_unreachable abort -#endif - -#define EH_EXCEPTION_NUMBER ('msc' | 0xE0000000) -#define EH_MAGIC_NUMBER1 0x19930520 -#define EXCEPTION_NONCONTINUABLE 0x1 - -struct _MSVC_TypeDescriptor -{ - const void* pVFTable; - void* spare; - char name[0]; -}; - -struct _MSVC_CatchableType -{ - unsigned int flags; - unsigned long type; - int mdisp; - int pdisp; - int vdisp; - int size; - unsigned long copyFunction; -}; - -struct _MSVC_CatchableTypeArray -{ - int count; - unsigned long types[0]; -}; - -struct _MSVC_ThrowInfo -{ - unsigned int attributes; - unsigned long pfnUnwind; - unsigned long pfnForwardCompat; - unsigned long pCatchableTypeArray; -}; - -static LPTOP_LEVEL_EXCEPTION_FILTER originalUnhandledExceptionFilter = nullptr; -void (*_objc_unexpected_exception)(id exception); -LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* exceptionInfo); - -#if defined(_WIN64) -#define IMAGE_RELATIVE(ptr, base) (static_cast((ptr ? ((uintptr_t)ptr - (uintptr_t)base) : (uintptr_t)nullptr))) -#else -#define IMAGE_RELATIVE(ptr, base) reinterpret_cast((ptr)) -#endif - -extern "C" void __stdcall _CxxThrowException(void*, _MSVC_ThrowInfo*); - -namespace -{ - -static std::string mangleObjcObject() -{ -#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8 - return ".PEAUobjc_object@@"; -#else - return ".PAUobjc_object@@"; -#endif -} - -static std::string mangleStructNamed(const char* className) -{ - // 32-bit: - // .PAUxxx@@ = ?? struct xxx * `RTTI Type Descriptor' - // 64-bit: - // .PEAUxxx@@ = ?? struct xxx * __ptr64 `RTTI Type Descriptor' - //return - auto r = -#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8 - std::string(".PEAU") + -#else - std::string(".PAU") + -#endif - className + "@@"; - return r; -} - -void fillCatchableType(_MSVC_CatchableType* exceptType) -{ - exceptType->flags = 1; - exceptType->mdisp = 0; - exceptType->pdisp = -1; - exceptType->vdisp = 0; - exceptType->size = sizeof(id); - exceptType->copyFunction = 0; -} - -} // - -struct X {}; -OBJC_PUBLIC extern "C" void objc_exception_rethrow(void* exc); - -OBJC_PUBLIC extern "C" void objc_exception_throw(id object) -{ - // Base used for image-relative addresses. - char x; - // This is the base vtable for all RTTI entries - static const void* typeid_vtable = *(void**)&typeid(void *); - - SEL rethrow_sel = sel_registerName("rethrow"); - if ((nil != object) && - (class_respondsToSelector(object_getClass(object), rethrow_sel))) - { - IMP rethrow = objc_msg_lookup(object, rethrow_sel); - rethrow(object, rethrow_sel); - // Should not be reached! If it is, then the rethrow method actually - // didn't, so we throw it normally. - } - - SEL processException_sel = sel_registerName("_processException"); - if ((nil != object) && - (class_respondsToSelector(object_getClass(object), processException_sel))) - { - IMP processException = objc_msg_lookup(object, processException_sel); - processException(object, processException_sel); - } - - // The 'id' base type will be taking up a spot in the list: - size_t typeCount = 1; - - // Get count of all types in exception - for (Class cls = object_getClass(object); cls != Nil; cls = class_getSuperclass(cls), ++typeCount) - ; - - // Unfortunately we can't put this in a real function since the alloca has to be in this stack frame: -#define CREATE_TYPE_DESCRIPTOR(desc, symName) \ - desc = reinterpret_cast<_MSVC_TypeDescriptor*>(alloca(sizeof(_MSVC_TypeDescriptor) + symName.size() + 1 /* null terminator */)); \ - desc->pVFTable = typeid_vtable; \ - desc->spare = nullptr; \ - strcpy_s(desc->name, symName.size() + 1, symName.c_str()); - - auto exceptTypes = - (_MSVC_CatchableTypeArray*)_alloca(sizeof(_MSVC_CatchableTypeArray) + sizeof(_MSVC_CatchableType*) * typeCount); - exceptTypes->count = typeCount; - - // Add exception type and all base types to throw information - size_t curTypeIndex = 0; - for (Class cls = object_getClass(object); cls != Nil; cls = class_getSuperclass(cls)) - { - auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType)); - fillCatchableType(exceptType); - - auto mangledName = mangleStructNamed(class_getName(cls)); - _MSVC_TypeDescriptor *ty; - CREATE_TYPE_DESCRIPTOR(ty, mangledName); - exceptType->type = IMAGE_RELATIVE(ty, &x); - exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType, &x); - } - - // Add id (struct objc_object*) - auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType)); - fillCatchableType(exceptType); - auto idName = mangleObjcObject(); - _MSVC_TypeDescriptor *ty; - CREATE_TYPE_DESCRIPTOR(ty, idName); - exceptType->type = IMAGE_RELATIVE(ty, &x); - exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType, &x); - - _MSVC_ThrowInfo ti = { - 0, // attributes - 0, // pfnUnwind - 0, // pfnForwardCompat - IMAGE_RELATIVE(exceptTypes, &x) // pCatchableTypeArray - }; - EXCEPTION_RECORD exception; - exception.ExceptionCode = EH_EXCEPTION_NUMBER; - exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE; - exception.ExceptionRecord = nullptr; - exception.ExceptionAddress = nullptr; - // The fourth parameter is the base address of the image (for us, this stack - // frame), but we only use image-relative 32-bit addresses on 64-bit - // platforms. On 32-bit platforms, we use 32-bit absolute addresses. - exception.NumberParameters = sizeof(void*) == 4 ? 3 : 4; - exception.ExceptionInformation[0] = EH_MAGIC_NUMBER1; - exception.ExceptionInformation[1] = reinterpret_cast(&object); - exception.ExceptionInformation[2] = reinterpret_cast(&ti); - exception.ExceptionInformation[3] = reinterpret_cast(&x); - -#ifdef _WIN64 - RtlRaiseException(&exception); -#else - RaiseException(exception.ExceptionCode, - exception.ExceptionFlags, - exception.NumberParameters, - exception.ExceptionInformation); -#endif - __builtin_unreachable(); -} - -OBJC_PUBLIC extern "C" void objc_exception_rethrow(void* exc) -{ - _CxxThrowException(nullptr, nullptr); - __builtin_unreachable(); -} - -// rebase_and_cast adds a constant offset to a U value, converting it into a T -template -static std::add_const_t> rebase_and_cast(intptr_t base, U value) { - // U value -> const T* (base+value) - return reinterpret_cast>>(base + (long)(value)); -} - -/** - * Unhandled exception filter that we install to get called when an exception is - * not otherwise handled in a process that is not being debugged. In here we - * check if the exception is an Objective C exception raised by - * objc_exception_throw() above, and if so call the _objc_unexpected_exception - * hook with the Objective-C exception object. - * - * https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setunhandledexceptionfilter - */ -LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* exceptionInfo) -{ - const EXCEPTION_RECORD* ex = exceptionInfo->ExceptionRecord; - - if (_objc_unexpected_exception != 0 - && ex->ExceptionCode == EH_EXCEPTION_NUMBER - && ex->ExceptionInformation[0] == EH_MAGIC_NUMBER1 - && ex->NumberParameters >= 3) - { - // On 64-bit platforms, thrown exception catch data are relative virtual addresses off the module base. - intptr_t imageBase = ex->NumberParameters >= 4 ? (intptr_t)(ex->ExceptionInformation[3]) : 0; - - auto throwInfo = reinterpret_cast<_MSVC_ThrowInfo*>(ex->ExceptionInformation[2]); - if (throwInfo && throwInfo->pCatchableTypeArray) { - auto catchableTypes = rebase_and_cast<_MSVC_CatchableTypeArray*>(imageBase, throwInfo->pCatchableTypeArray); - bool foundobjc_object = false; - for (int i = 0; i < catchableTypes->count; ++i) { - const _MSVC_CatchableType* catchableType = rebase_and_cast<_MSVC_CatchableType*>(imageBase, catchableTypes->types[i]); - const _MSVC_TypeDescriptor* typeDescriptor = rebase_and_cast<_MSVC_TypeDescriptor*>(imageBase, catchableType->type); - if (strcmp(typeDescriptor->name, mangleObjcObject().c_str()) == 0) { - foundobjc_object = true; - break; - } - } - - if (foundobjc_object) { - id exception = *reinterpret_cast(ex->ExceptionInformation[1]); - _objc_unexpected_exception(exception); - } - } - } - - // call original exception filter if any - if (originalUnhandledExceptionFilter) { - return originalUnhandledExceptionFilter(exceptionInfo); - } - - // EXCEPTION_CONTINUE_SEARCH instructs the exception handler to continue searching for appropriate exception handlers. - // Since this is the last one, it is not likely to find any more. - return EXCEPTION_CONTINUE_SEARCH; -} - -OBJC_PUBLIC extern "C" objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler) -{ - objc_uncaught_exception_handler previousHandler = __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST); - - // set unhandled exception filter to support hook - LPTOP_LEVEL_EXCEPTION_FILTER previousExceptionFilter = SetUnhandledExceptionFilter(&_objc_unhandled_exception_filter); - if (previousExceptionFilter != &_objc_unhandled_exception_filter) { - originalUnhandledExceptionFilter = previousExceptionFilter; - } - - return previousHandler; -} diff --git a/encoding2.c b/encoding2.c deleted file mode 100644 index 5fed3b2..0000000 --- a/encoding2.c +++ /dev/null @@ -1,639 +0,0 @@ -#include -#include -#include -#include - -#include "objc/runtime.h" -#include "objc/encoding.h" -#include "method.h" -#include "visibility.h" - -#ifdef max -# undef max -#endif - -size_t objc_alignof_type (const char *type); - -// It would be so nice if this works, but in fact it returns nonsense: -//#define alignof(x) __alignof__(x) -// -#define alignof(type) __builtin_offsetof(struct { const char c; type member; }, member) - -OBJC_PUBLIC -const char *objc_skip_type_qualifiers (const char *type) -{ - static const char *type_qualifiers = "rnNoORVA"; - while('\0' != *type && strchr(type_qualifiers, *type)) - { - type++; - } - return type; -} - -static const char *sizeof_type(const char *type, size_t *size); - -OBJC_PUBLIC -const char *objc_skip_typespec(const char *type) -{ - size_t ignored = 0; - return sizeof_type(type, &ignored); -} - -OBJC_PUBLIC -const char *objc_skip_argspec(const char *type) -{ - type = objc_skip_typespec(type); - while(isdigit(*type)) { type++; } - return type; -} - -PRIVATE size_t lengthOfTypeEncoding(const char *types) -{ - if ((NULL == types) || ('\0' == types[0])) { return 0; } - const char *end = objc_skip_typespec(types); - size_t length = end - types; - return length; -} - -static char* copyTypeEncoding(const char *types) -{ - size_t length = lengthOfTypeEncoding(types); - char *copy = malloc(length + 1); - memcpy(copy, types, length); - copy[length] = '\0'; - return copy; -} - -static const char * findParameterStart(const char *types, unsigned int index) -{ - // the upper bound of the loop is inclusive because the return type - // is the first element in the method signature - for (unsigned int i=0 ; i <= index ; i++) - { - types = objc_skip_argspec(types); - if ('\0' == *types) - { - return NULL; - } - } - return types; -} - - -typedef const char *(*type_parser)(const char*, void*); - -static int parse_array(const char **type, type_parser callback, void *context) -{ - // skip [ - (*type)++; - int element_count = (int)strtol(*type, (char**)type, 10); - *type = callback(*type, context); - // skip ] - (*type)++; - return element_count; -} - -static void parse_struct_or_union(const char **type, type_parser callback, void *context, char endchar) -{ - // Skip the ( and structure name - do - { - (*type)++; - // Opaque type has no =definition - if (endchar == **type) { (*type)++; return; } - } while('=' != **type); - // Skip = - (*type)++; - - while (**type != endchar) - { - // Structure elements sometimes have their names in front of each - // element, as in {NSPoint="x"f"y"f} - We need to skip the type name - // here. - // - // TODO: In a future version we should provide a callback that lets - // users of this code get the field name - if ('"'== **type) - { - - do - { - (*type)++; - } while ('"' != **type); - // Skip the closing " - (*type)++; - } - *type = callback(*type, context); - } - // skip } - (*type)++; -} - -static void parse_union(const char **type, type_parser callback, void *context) -{ - parse_struct_or_union(type, callback, context, ')'); -} - -static void parse_struct(const char **type, type_parser callback, void *context) -{ - parse_struct_or_union(type, callback, context, '}'); -} - -inline static void round_up(size_t *v, size_t b) -{ - if (0 == b) - { - return; - } - - if (*v % b) - { - *v += b - (*v % b); - } -} -inline static size_t max(size_t v, size_t v2) -{ - return v>v2 ? v : v2; -} - -static const char *skip_object_extended_qualifiers(const char *type) -{ - if (*(type+1) == '?') - { - type++; - if (*(type+1) == '<') - { - type += 2; - while (*type != '>') - { - type++; - } - } - } - else if (type[1] == '"') - { - type += 2; - while (*type != '"') - { - type++; - } - } - return type; -} - -static const char *sizeof_union_field(const char *type, size_t *size); - -static const char *sizeof_type(const char *type, size_t *size) -{ - type = objc_skip_type_qualifiers(type); - switch (*type) - { - // For all primitive types, we round up the current size to the - // required alignment of the type, then add the size -#define APPLY_TYPE(typeName, name, capitalizedName, encodingChar) \ - case encodingChar:\ - {\ - round_up(size, (alignof(typeName) * 8));\ - *size += (sizeof(typeName) * 8);\ - return type + 1;\ - } -#define SKIP_ID 1 -#define NON_INTEGER_TYPES 1 -#include "type_encoding_cases.h" - case '@': - { - round_up(size, (alignof(id) * 8)); - *size += (sizeof(id) * 8); - return skip_object_extended_qualifiers(type) + 1; - } - case '?': - case 'v': return type+1; - case 'j': - { - type++; - switch (*type) - { -#define APPLY_TYPE(typeName, name, capitalizedName, encodingChar) \ - case encodingChar:\ - {\ - round_up(size, (alignof(_Complex typeName) * 8));\ - *size += (sizeof(_Complex typeName) * 8);\ - return type + 1;\ - } -#include "type_encoding_cases.h" - } - } - case '{': - { - const char *t = type; - parse_struct(&t, (type_parser)sizeof_type, size); - size_t align = objc_alignof_type(type); - round_up(size, align * 8); - return t; - } - case '[': - { - const char *t = type; - size_t element_size = 0; - // FIXME: aligned size - int element_count = parse_array(&t, (type_parser)sizeof_type, &element_size); - (*size) += element_size * element_count; - return t; - } - case '(': - { - const char *t = type; - size_t union_size = 0; - parse_union(&t, (type_parser)sizeof_union_field, &union_size); - *size += union_size; - return t; - } - case 'b': - { - // Consume the b - type++; - // Ignore the offset - strtol(type, (char**)&type, 10); - // Consume the element type - type++; - // Read the number of bits - *size += strtol(type, (char**)&type, 10); - return type; - } - case '^': - { - // All pointers look the same to me. - *size += sizeof(void*) * 8; - size_t ignored = 0; - // Skip the definition of the pointeee type. - return sizeof_type(type+1, &ignored); - } - } - abort(); - return NULL; -} - -static const char *sizeof_union_field(const char *type, size_t *size) -{ - size_t field_size = 0; - const char *end = sizeof_type(type, &field_size); - *size = max(*size, field_size); - return end; -} - -static const char *alignof_type(const char *type, size_t *align) -{ - type = objc_skip_type_qualifiers(type); - switch (*type) - { - // For all primitive types, we return the maximum of the new alignment - // and the old one -#define APPLY_TYPE(typeName, name, capitalizedName, encodingChar) \ - case encodingChar:\ - {\ - *align = max((alignof(typeName) * 8), *align);\ - return type + 1;\ - } -#define NON_INTEGER_TYPES 1 -#define SKIP_ID 1 -#include "type_encoding_cases.h" - case '@': - { - *align = max((alignof(id) * 8), *align);\ - return skip_object_extended_qualifiers(type) + 1; - } - case '?': - case 'v': return type+1; - case 'j': - { - type++; - switch (*type) - { -#define APPLY_TYPE(typeName, name, capitalizedName, encodingChar) \ - case encodingChar:\ - {\ - *align = max((alignof(_Complex typeName) * 8), *align);\ - return type + 1;\ - } -#include "type_encoding_cases.h" - } - } - case '{': - { - const char *t = type; - parse_struct(&t, (type_parser)alignof_type, align); - return t; - } - case '(': - { - const char *t = type; - parse_union(&t, (type_parser)alignof_type, align); - return t; - } - case '[': - { - const char *t = type; - parse_array(&t, (type_parser)alignof_type, &align); - return t; - } - case 'b': - { - // Consume the b - type++; - // Ignore the offset - strtol(type, (char**)&type, 10); - // Alignment of a bitfield is the alignment of the type that - // contains it - type = alignof_type(type, align); - // Ignore the number of bits - strtol(type, (char**)&type, 10); - return type; - } - case '^': - { - *align = max((alignof(void*) * 8), *align); - // All pointers look the same to me. - size_t ignored = 0; - // Skip the definition of the pointeee type. - return alignof_type(type+1, &ignored); - } - } - abort(); - return NULL; -} - -OBJC_PUBLIC -size_t objc_sizeof_type(const char *type) -{ - size_t size = 0; - sizeof_type(type, &size); - return size / 8; -} - -OBJC_PUBLIC -size_t objc_alignof_type (const char *type) -{ - size_t align = 0; - alignof_type(type, &align); - return align / 8; -} - -OBJC_PUBLIC -size_t objc_aligned_size(const char *type) -{ - size_t size = objc_sizeof_type(type); - size_t align = objc_alignof_type(type); - return size + (size % align); -} - -OBJC_PUBLIC -size_t objc_promoted_size(const char *type) -{ - size_t size = objc_sizeof_type(type); - return size + (size % sizeof(void*)); -} - -OBJC_PUBLIC -void method_getReturnType(Method method, char *dst, size_t dst_len) -{ - if (NULL == method) { return; } - //TODO: Coped and pasted code. Factor it out. - const char *types = method_getTypeEncoding(method); - size_t length = lengthOfTypeEncoding(types); - if (length < dst_len) - { - memcpy(dst, types, length); - dst[length] = '\0'; - } - else - { - memcpy(dst, types, dst_len); - } -} - -OBJC_PUBLIC -const char *method_getTypeEncoding(Method method) -{ - if (NULL == method) { return NULL; } - return sel_getType_np(method->selector); -} - -OBJC_PUBLIC -void method_getArgumentType(Method method, - unsigned int index, - char *dst, - size_t dst_len) -{ - if (NULL == method) { return; } - const char *types = findParameterStart(method_getTypeEncoding(method), index); - if (NULL == types) - { - if (dst_len > 0) - { - *dst = '\0'; - } - return; - } - size_t length = lengthOfTypeEncoding(types); - if (length < dst_len) - { - memcpy(dst, types, length); - dst[length] = '\0'; - } - else - { - memcpy(dst, types, dst_len); - } -} - -OBJC_PUBLIC -unsigned method_getNumberOfArguments(Method method) -{ - if (NULL == method) { return 0; } - const char *types = method_getTypeEncoding(method); - unsigned int count = 0; - while('\0' != *types) - { - types = objc_skip_argspec(types); - count++; - } - return count - 1; -} - -OBJC_PUBLIC -unsigned method_get_number_of_arguments(struct objc_method *method) -{ - return method_getNumberOfArguments(method); -} - -OBJC_PUBLIC -char* method_copyArgumentType(Method method, unsigned int index) -{ - if (NULL == method) { return NULL; } - const char *types = findParameterStart(method_getTypeEncoding(method), index); - if (NULL == types) - { - return NULL; - } - return copyTypeEncoding(types); -} - -OBJC_PUBLIC -char* method_copyReturnType(Method method) -{ - if (NULL == method) { return NULL; } - return copyTypeEncoding(method_getTypeEncoding(method)); -} - -OBJC_PUBLIC -unsigned objc_get_type_qualifiers (const char *type) -{ - unsigned flags = 0; -#define MAP(chr, bit) case chr: flags |= bit; break; - do - { - switch (*(type++)) - { - default: return flags; - MAP('r', _F_CONST) - MAP('n', _F_IN) - MAP('o', _F_OUT) - MAP('N', _F_INOUT) - MAP('O', _F_BYCOPY) - MAP('V', _F_ONEWAY) - MAP('R', _F_BYREF) - } - } while (1); -} - -// Note: The implementations of these functions is horrible. -OBJC_PUBLIC -void objc_layout_structure (const char *type, - struct objc_struct_layout *layout) -{ - layout->original_type = type; - layout->type = 0; -} - -static const char *layout_structure_callback(const char *type, struct objc_struct_layout *layout) -{ - size_t align = 0; - size_t size = 0; - const char *end = sizeof_type(type, &size); - alignof_type(type, &align); - //printf("Callback called with %s\n", type); - if (layout->prev_type < type) - { - if (layout->record_align == 0) - { - layout->record_align = align; - layout->type = type; - } - } - else - { - size_t rsize = (size_t)layout->record_size; - round_up(&rsize, align); - layout->record_size = rsize + size; - } - return end; -} - -OBJC_PUBLIC -BOOL objc_layout_structure_next_member(struct objc_struct_layout *layout) -{ - const char *end = layout->type; - layout->record_size = 0; - layout->record_align = 0; - layout->prev_type = layout->type; - const char *type = layout->original_type; - parse_struct(&type, (type_parser)layout_structure_callback, layout); - //printf("Calculated: (%s) %s %d %d\n", layout->original_type, layout->type, layout->record_size, layout->record_align); - //printf("old start %s, new start %s\n", end, layout->type); - return layout->type != end; -} - -OBJC_PUBLIC -void objc_layout_structure_get_info (struct objc_struct_layout *layout, - unsigned int *offset, - unsigned int *align, - const char **type) -{ - //printf("%p\n", layout); - *type = layout->type; - size_t off = layout->record_size / 8; - *align= layout->record_align / 8; - round_up(&off, (size_t)*align); - *offset = (unsigned int)off; -} - -#ifdef ENCODING_TESTS - -#define TEST(type) do {\ - if (alignof(type) != objc_alignof_type(@encode(type)))\ - printf("Incorrect alignment for %s: %d != %d\n", @encode(type), objc_alignof_type(@encode(type)), alignof(type));\ - if (sizeof(type) != objc_sizeof_type(@encode(type)))\ - printf("Incorrect size for %s: %d != %d\n", @encode(type), objc_sizeof_type(@encode(type)), sizeof(type));\ - } while(0) - -struct foo -{ - int a[2]; - int b:5; - struct - { - double d; - const char *str; - float e; - }c; - long long **g; - union { const char c; long long b; } h; - long long f; - _Complex int z; - _Complex double y; - char v; -}; - -typedef struct -{ - float x,y; -} Point; - -typedef struct -{ - Point a, b; -} Rect; - - -int main(void) -{ - TEST(int); - TEST(const char); - TEST(unsigned long long); - TEST(_Complex int); - TEST(struct foo); - struct objc_struct_layout layout; - - objc_layout_structure(@encode(Rect), &layout); - while (objc_layout_structure_next_member (&layout)) - { - unsigned offset; - unsigned align; - const char *ftype; - struct objc_struct_layout layout2; - objc_layout_structure_get_info (&layout, &offset, &align, &ftype); - printf("%s: offset: %d, alignment: %d\n", ftype, offset, align); - objc_layout_structure(ftype, &layout2); - while (objc_layout_structure_next_member (&layout2)) - { - objc_layout_structure_get_info (&layout2, &offset, &align, &ftype); - printf("%s: offset: %d, alignment: %d\n", ftype, offset, align); - } - } - printf("%d\n", offsetof(Rect, a.x)); - printf("%d\n", offsetof(Rect, a.y)); - printf("%d\n", offsetof(Rect, b.x)); - printf("%d\n", offsetof(Rect, b.y)); - -} -#endif diff --git a/fast_paths.m b/fast_paths.m deleted file mode 100644 index 0eda113..0000000 --- a/fast_paths.m +++ /dev/null @@ -1,62 +0,0 @@ -#include "objc/runtime.h" -#include "class.h" - -typedef struct _NSZone NSZone; -@interface RootMethods -- (id)alloc; -- (id)allocWithZone: (NSZone*)aZone; -- (id)init; -@end -#include - -/** - * Equivalent to [cls alloc]. If there's a fast path opt-in, then this skips the message send. - */ -OBJC_PUBLIC -id -objc_alloc(Class cls) -{ - if (UNLIKELY(!objc_test_class_flag(cls->isa, objc_class_flag_initialized))) - { - objc_send_initialize(cls); - } - if (objc_test_class_flag(cls->isa, objc_class_flag_fast_alloc_init)) - { - return class_createInstance(cls, 0); - } - return [cls alloc]; -} - -/** - * Equivalent to [cls allocWithZone: null]. If there's a fast path opt-in, then this skips the message send. - */ -OBJC_PUBLIC -id -objc_allocWithZone(Class cls) -{ - if (UNLIKELY(!objc_test_class_flag(cls->isa, objc_class_flag_initialized))) - { - objc_send_initialize(cls); - } - if (objc_test_class_flag(cls->isa, objc_class_flag_fast_alloc_init)) - { - return class_createInstance(cls, 0); - } - return [cls allocWithZone: NULL]; -} - -/** - * Equivalent to [[cls alloc] init]. If there's a fast path opt-in, then this - * skips the message send. - */ -OBJC_PUBLIC -id -objc_alloc_init(Class cls) -{ - id instance = objc_alloc(cls); - if (objc_test_class_flag(cls, objc_class_flag_fast_alloc_init)) - { - return instance; - } - return [instance init]; -} diff --git a/gc_none.c b/gc_none.c deleted file mode 100644 index f263f37..0000000 --- a/gc_none.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "visibility.h" -#include "objc/runtime.h" -#include "gc_ops.h" -#include "class.h" -#include -#include -#include - -static id allocate_class(Class cls, size_t extraBytes) -{ - size_t size = cls->instance_size + extraBytes + sizeof(intptr_t); - intptr_t *addr = -#ifdef _WIN32 - // Malloc on Windows doesn't guarantee 32-byte alignment, but we - // require this for any class that may contain vectors - _aligned_malloc(size, 32); - memset(addr, 0, size); -#else - calloc(size, 1); -#endif - return (id)(addr + 1); -} - -static void free_object(id obj) -{ -#ifdef _WIN32 - _aligned_free((void*)(((intptr_t*)obj) - 1)); -#else - free((void*)(((intptr_t*)obj) - 1)); -#endif -} - -static void *alloc(size_t size) -{ - return calloc(size, 1); -} - -void objc_registerThreadWithCollector(void) {} -void objc_unregisterThreadWithCollector(void) {} -void objc_assertRegisteredThreadWithCollector() {} - -PRIVATE struct gc_ops gc_ops_none = -{ - .allocate_class = allocate_class, - .free_object = free_object, - .malloc = alloc, - .free = free -}; -PRIVATE struct gc_ops *gc = &gc_ops_none; - -void objc_set_collection_threshold(size_t threshold) {} -void objc_set_collection_ratio(size_t ratio) {} -void objc_collect(unsigned long options) {} -BOOL objc_collectingEnabled(void) { return NO; } -BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) -{ - return __sync_bool_compare_and_swap(objectLocation, predicate, replacement); -} -BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation) -{ - return __sync_bool_compare_and_swap(objectLocation, predicate, replacement); -} - -BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation) -{ - return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); -} -BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation) -{ - return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); -} -BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation) -{ - return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); -} -BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation) -{ - return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); -} - -id objc_assign_strongCast(id val, id *ptr) -{ - *ptr = val; - return val; -} - -id objc_assign_global(id val, id *ptr) -{ - *ptr = val; - return val; -} - -id objc_assign_ivar(id val, id dest, ptrdiff_t offset) -{ - *(id*)((char*)dest+offset) = val; - return val; -} - -void *objc_memmove_collectable(void *dst, const void *src, size_t size) -{ - return memmove(dst, src, size); -} -id objc_read_weak(id *location) -{ - return *location; -} -id objc_assign_weak(id value, id *location) -{ - *location = value; - return value; -} -id objc_allocate_object(Class cls, int extra) -{ - return class_createInstance(cls, extra); -} - -BOOL objc_collecting_enabled(void) { return NO; } -void objc_startCollectorThread(void) {} -void objc_clear_stack(unsigned long options) {} -BOOL objc_is_finalized(void *ptr) { return NO; } -void objc_finalizeOnMainThread(Class cls) {} diff --git a/gc_ops.h b/gc_ops.h deleted file mode 100644 index 153b4b5..0000000 --- a/gc_ops.h +++ /dev/null @@ -1,60 +0,0 @@ - -/** - * Garbage collection operations. - */ -struct gc_ops -{ - /** - * Initialises this collector. - */ - void (*init)(void); - /** - * Allocates enough space for a class, followed by some extra bytes. - */ - id (*allocate_class)(Class, size_t); - /** - * Frees an object. - */ - void (*free_object)(id); - /** - * Allocates some memory that can be used to store pointers. This must be - * used instead of malloc() for internal data structures that will store - * pointers passed in from outside. The function is expected to zero the - * memory that it returns. - */ - void* (*malloc)(size_t); - /** - * Frees some memory that was previously used to store pointers. - */ - void (*free)(void*); -}; - -/** - * The mode for garbage collection - */ -enum objc_gc_mode -{ - /** This module neither uses, nor supports, garbage collection. */ - GC_None = 0, - /** - * This module uses garbage collection, but also sends retain / release - * messages. It can be used with or without GC. - */ - GC_Optional = 1, - /** - * This module expects garbage collection and will break without it. - */ - GC_Required = 2, - /** - * This module was compiled with automatic reference counting. This - * guarantees the use of the non-fragile ABI and means that we could - * potentially support GC, although we don't currently. - */ - GC_ARC = 3 -}; - -/** - * The current set of garbage collector operations to use. - */ -extern struct gc_ops *gc; - diff --git a/hash_table.h b/hash_table.h deleted file mode 100644 index 10c4a2b..0000000 --- a/hash_table.h +++ /dev/null @@ -1,577 +0,0 @@ -/** - * hash_table.h provides a template for implementing hopscotch hash tables. - * - * Several macros must be defined before including this file: - * - * MAP_TABLE_NAME defines the name of the table. All of the operations and - * types related to this table will be prefixed with this value. - * - * MAP_TABLE_COMPARE_FUNCTION defines the function used for testing a key - * against a value in the table for equality. This must take two void* - * arguments. The first is the key and the second is the value. - * - * MAP_TABLE_HASH_KEY and MAP_TABLE_HASH_VALUE define a pair of functions that - * takes a key and a value pointer respectively as their argument and returns - * an int32_t representing the hash. - * - * Optionally, MAP_TABLE_STATIC_SIZE may be defined, to define a table type - * which has a static size. - */ -#include "lock.h" -#include -#include -#include - - -#ifdef ENABLE_GC -# include -# include -# define CALLOC(x,y) GC_MALLOC(x*y) -# define IF_NO_GC(x) -# define IF_GC(x) x -#else -# define CALLOC(x,y) calloc(x,y) -# define IF_NO_GC(x) x -# define IF_GC(x) -#endif - -#ifndef MAP_TABLE_NAME -# error You must define MAP_TABLE_NAME. -#endif -#ifndef MAP_TABLE_COMPARE_FUNCTION -# error You must define MAP_TABLE_COMPARE_FUNCTION. -#endif -#ifndef MAP_TABLE_HASH_KEY -# error You must define MAP_TABLE_HASH_KEY -#endif -#ifndef MAP_TABLE_HASH_VALUE -# error You must define MAP_TABLE_HASH_VALUE -#endif - -// Horrible multiple indirection to satisfy the weird precedence rules in cpp -#define REALLY_PREFIX_SUFFIX(x,y) x ## y -#define PREFIX_SUFFIX(x, y) REALLY_PREFIX_SUFFIX(x, y) - -/** - * PREFIX(x) macro adds the table name prefix to the argument. - */ -#define PREFIX(x) PREFIX_SUFFIX(MAP_TABLE_NAME, x) - - -/** - * Map tables are protected by a lock by default. Defining MAP_TABLE_NO_LOCK - * will prevent this and make you responsible for synchronization. - */ -#ifdef MAP_TABLE_NO_LOCK -# define MAP_LOCK() -# define MAP_UNLOCK() -#else -# define MAP_LOCK() (LOCK(&table->lock)) -# define MAP_UNLOCK() (UNLOCK(&table->lock)) -#endif -#ifndef MAP_TABLE_VALUE_TYPE -# define MAP_TABLE_VALUE_TYPE void* -static BOOL PREFIX(_is_null)(void *value) -{ - return value == NULL; -} -# define MAP_TABLE_TYPES_BITMAP 1 -# define MAP_TABLE_VALUE_NULL PREFIX(_is_null) -# define MAP_TABLE_VALUE_PLACEHOLDER NULL -#endif - -typedef struct PREFIX(_table_cell_struct) -{ - uint32_t secondMaps; - MAP_TABLE_VALUE_TYPE value; -} *PREFIX(_table_cell); - -#ifdef MAP_TABLE_STATIC_SIZE -typedef struct -{ - mutex_t lock; - unsigned int table_used; - IF_NO_GC(unsigned int enumerator_count;) - struct PREFIX(_table_cell_struct) table[MAP_TABLE_STATIC_SIZE]; -} PREFIX(_table); -static PREFIX(_table) MAP_TABLE_STATIC_NAME; -# ifndef MAP_TABLE_NO_LOCK -__attribute__((constructor)) void static PREFIX(_table_initializer)(void) -{ - INIT_LOCK(MAP_TABLE_STATIC_NAME.lock); -} -# endif -# define TABLE_SIZE(x) MAP_TABLE_STATIC_SIZE -#else -typedef struct PREFIX(_table_struct) -{ - mutex_t lock; - unsigned int table_size; - unsigned int table_used; - IF_NO_GC(unsigned int enumerator_count;) -# if defined(ENABLE_GC) && defined(MAP_TABLE_TYPES_BITMAP) - GC_descr descr; -# endif - struct PREFIX(_table_struct) *old; - struct PREFIX(_table_cell_struct) *table; -} PREFIX(_table); - -static struct PREFIX(_table_cell_struct) *PREFIX(alloc_cells)(PREFIX(_table) *table, int count) -{ -# if defined(ENABLE_GC) && defined(MAP_TABLE_TYPES_BITMAP) - return GC_CALLOC_EXPLICITLY_TYPED(count, - sizeof(struct PREFIX(_table_cell_struct)), table->descr); -# else - return CALLOC(count, sizeof(struct PREFIX(_table_cell_struct))); -# endif -} - -static PREFIX(_table) *PREFIX(_create)(uint32_t capacity) -{ - PREFIX(_table) *table = CALLOC(1, sizeof(PREFIX(_table))); -# ifndef MAP_TABLE_NO_LOCK - INIT_LOCK(table->lock); -# endif -# if defined(ENABLE_GC) && defined(MAP_TABLE_TYPES_BITMAP) - // The low word in the bitmap stores the offsets of the next entries - GC_word bitmap = (MAP_TABLE_TYPES_BITMAP << 1); - table->descr = GC_make_descriptor(&bitmap, - sizeof(struct PREFIX(_table_cell_struct)) / sizeof (void*)); -# endif - table->table = PREFIX(alloc_cells)(table, capacity); - table->table_size = capacity; - return table; -} - -static void PREFIX(_initialize)(PREFIX(_table) **table, uint32_t capacity) -{ -#ifdef ENABLE_GC - GC_add_roots(table, table+1); -#endif - *table = PREFIX(_create)(capacity); -} - -# define TABLE_SIZE(x) (x->table_size) -#endif - - -#ifdef MAP_TABLE_STATIC_SIZE -static int PREFIX(_table_resize)(PREFIX(_table) *table) -{ - return 0; -} -#else - -static int PREFIX(_insert)(PREFIX(_table) *table, MAP_TABLE_VALUE_TYPE value); - -static int PREFIX(_table_resize)(PREFIX(_table) *table) -{ - struct PREFIX(_table_cell_struct) *newArray = - PREFIX(alloc_cells)(table, table->table_size * 2); - if (NULL == newArray) { return 0; } - - // Allocate a new table structure and move the array into that. Now - // lookups will try using that one, if possible. - PREFIX(_table) *copy = CALLOC(1, sizeof(PREFIX(_table))); - memcpy(copy, table, sizeof(PREFIX(_table))); - table->old = copy; - - // Now we make the original table structure point to the new (empty) array. - table->table = newArray; - table->table_size *= 2; - // The table currently has no entries; the copy has them all. - table->table_used = 0; - - // Finally, copy everything into the new table - // Note: we should really do this in a background thread. At this stage, - // we can do the updates safely without worrying about read contention. - int copied = 0; - for (uint32_t i=0 ; itable_size ; i++) - { - MAP_TABLE_VALUE_TYPE value = copy->table[i].value; - if (!MAP_TABLE_VALUE_NULL(value)) - { - copied++; - PREFIX(_insert)(table, value); - } - } - __sync_synchronize(); - table->old = NULL; -# if !defined(ENABLE_GC) && defined(MAP_TABLE_SINGLE_THREAD) - free(copy->table); - free(copy); -# endif - return 1; -} -#endif - -struct PREFIX(_table_enumerator) -{ - PREFIX(_table) *table; - unsigned int seen; - unsigned int index; -}; - -static inline PREFIX(_table_cell) PREFIX(_table_lookup)(PREFIX(_table) *table, - uint32_t hash) -{ - hash = hash % TABLE_SIZE(table); - return &table->table[hash]; -} - -static int PREFIX(_table_move_gap)(PREFIX(_table) *table, uint32_t fromHash, - uint32_t toHash, PREFIX(_table_cell) emptyCell) -{ - for (uint32_t hash = fromHash - 32 ; hash < fromHash ; hash++) - { - // Get the cell n before the hash. - PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash); - // If this node is a primary entry move it down - if (MAP_TABLE_HASH_VALUE(cell->value) == hash) - { - emptyCell->value = cell->value; - cell->secondMaps |= (1 << ((fromHash - hash) - 1)); - cell->value = MAP_TABLE_VALUE_PLACEHOLDER; - if (hash - toHash < 32) - { - return 1; - } - return PREFIX(_table_move_gap)(table, hash, toHash, cell); - } - int hop = __builtin_ffs(cell->secondMaps); - if (hop > 0 && (hash + hop) < fromHash) - { - PREFIX(_table_cell) hopCell = PREFIX(_table_lookup)(table, hash+hop); - emptyCell->value = hopCell->value; - // Update the hop bit for the new offset - cell->secondMaps |= (1 << ((fromHash - hash) - 1)); - // Clear the hop bit in the original cell - cell->secondMaps &= ~(1 << (hop - 1)); - hopCell->value = MAP_TABLE_VALUE_PLACEHOLDER; - if (hash - toHash < 32) - { - return 1; - } - return PREFIX(_table_move_gap)(table, hash + hop, toHash, hopCell); - } - } - return 0; -} -static int PREFIX(_table_rebalance)(PREFIX(_table) *table, uint32_t hash) -{ - for (unsigned i=32 ; ivalue)) - { - // We've found a free space, try to move it up. - return PREFIX(_table_move_gap)(table, hash + i, hash, cell); - } - } - return 0; -} - -__attribute__((unused)) -static int PREFIX(_insert)(PREFIX(_table) *table, - MAP_TABLE_VALUE_TYPE value) -{ - MAP_LOCK(); - uint32_t hash = MAP_TABLE_HASH_VALUE(value); - PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash); - if (MAP_TABLE_VALUE_NULL(cell->value)) - { - cell->secondMaps = 0; - cell->value = value; - table->table_used++; - MAP_UNLOCK(); - return 1; - } - /* If this cell is full, try the next one. */ - for (unsigned int i=1 ; i<33 ; i++) - { - PREFIX(_table_cell) second = - PREFIX(_table_lookup)(table, hash+i); - if (MAP_TABLE_VALUE_NULL(second->value)) - { - cell->secondMaps |= (1 << (i-1)); - second->value = value; - table->table_used++; - MAP_UNLOCK(); - return 1; - } - } - /* If the table is full, or nearly full, then resize it. Note that we - * resize when the table is at 80% capacity because it's cheaper to copy - * everything than spend the next few updates shuffling everything around - * to reduce contention. A hopscotch hash table starts to degrade in - * performance at around 90% capacity, so stay below that. - */ - if (table->table_used > (0.8 * TABLE_SIZE(table))) - { - PREFIX(_table_resize)(table); - MAP_UNLOCK(); - return PREFIX(_insert)(table, value); - } - /* If this virtual cell is full, rebalance the hash from this point and - * try again. */ - if (PREFIX(_table_rebalance)(table, hash)) - { - MAP_UNLOCK(); - return PREFIX(_insert)(table, value); - } - /** If rebalancing failed, resize even if we are <80% full. This can - * happen if your hash function sucks. If you don't want this to happen, - * get a better hash function. */ - if (PREFIX(_table_resize)(table)) - { - MAP_UNLOCK(); - return PREFIX(_insert)(table, value); - } - fprintf(stderr, "Insert failed\n"); - MAP_UNLOCK(); - return 0; -} - -static void *PREFIX(_table_get_cell)(PREFIX(_table) *table, const void *key) -{ - uint32_t hash = MAP_TABLE_HASH_KEY(key); - PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash); - // Value does not exist. - if (!MAP_TABLE_VALUE_NULL(cell->value)) - { - if (MAP_TABLE_COMPARE_FUNCTION(key, cell->value)) - { - return cell; - } - uint32_t jump = cell->secondMaps; - // Look at each offset defined by the jump table to find the displaced location. - for (int hop = __builtin_ffs(jump) ; hop > 0 ; hop = __builtin_ffs(jump)) - { - PREFIX(_table_cell) hopCell = PREFIX(_table_lookup)(table, hash+hop); - if (MAP_TABLE_COMPARE_FUNCTION(key, hopCell->value)) - { - return hopCell; - } - // Clear the most significant bit and try again. - jump &= ~(1 << (hop-1)); - } - } -#ifndef MAP_TABLE_STATIC_SIZE - if (table->old) - { - return PREFIX(_table_get_cell)(table->old, key); - } -#endif - return NULL; -} - -__attribute__((unused)) -static void PREFIX(_table_move_second)(PREFIX(_table) *table, - PREFIX(_table_cell) emptyCell) -{ - uint32_t jump = emptyCell->secondMaps; - // Look at each offset defined by the jump table to find the displaced location. - int hop = __builtin_ffs(jump); - PREFIX(_table_cell) hopCell = - PREFIX(_table_lookup)(table, (emptyCell - table->table) + hop); - emptyCell->value = hopCell->value; - emptyCell->secondMaps &= ~(1 << (hop-1)); - if (0 == hopCell->secondMaps) - { - hopCell->value = MAP_TABLE_VALUE_PLACEHOLDER; - } - else - { - PREFIX(_table_move_second)(table, hopCell); - } -} -__attribute__((unused)) -static void PREFIX(_remove)(PREFIX(_table) *table, void *key) -{ - MAP_LOCK(); - PREFIX(_table_cell) cell = PREFIX(_table_get_cell)(table, key); - if (NULL == cell) { return; } - - uint32_t hash = MAP_TABLE_HASH_KEY(key); - PREFIX(_table_cell) baseCell = PREFIX(_table_lookup)(table, hash); - if (baseCell && baseCell != cell) - { - uint32_t displacement = (cell - baseCell + table->table_size) % table->table_size; - uint32_t jump = 1 << (displacement - 1); - if ((baseCell->secondMaps & jump)) - { - // If we are removing a cell stored adjacent to its base due to hash - // collision, we have to clear the base cell's neighbor bit. - // Otherwise, a later remove can move the new placeholder value to the head - // which will cause further chained lookups to fail. - baseCell->secondMaps &= ~jump; - } - } - - // If the cell contains a value, set it to the placeholder and shuffle up - // everything - if (0 == cell->secondMaps) - { - cell->value = MAP_TABLE_VALUE_PLACEHOLDER; - } - else - { - PREFIX(_table_move_second)(table, cell); - } - table->table_used--; - MAP_UNLOCK(); -} - -__attribute__((unused)) -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE -static MAP_TABLE_VALUE_TYPE* -#else -static MAP_TABLE_VALUE_TYPE -#endif - PREFIX(_table_get)(PREFIX(_table) *table, - const void *key) -{ - PREFIX(_table_cell) cell = PREFIX(_table_get_cell)(table, key); - if (NULL == cell) - { -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE - return NULL; -#else - return MAP_TABLE_VALUE_PLACEHOLDER; -#endif - } -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE - return &cell->value; -#else - return cell->value; -#endif -} -__attribute__((unused)) -static void PREFIX(_table_set)(PREFIX(_table) *table, const void *key, - MAP_TABLE_VALUE_TYPE value) -{ - PREFIX(_table_cell) cell = PREFIX(_table_get_cell)(table, key); - if (NULL == cell) - { - PREFIX(_insert)(table, value); - } - cell->value = value; -} - -__attribute__((unused)) -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE -static MAP_TABLE_VALUE_TYPE* -#else -static MAP_TABLE_VALUE_TYPE -#endif -PREFIX(_next)(PREFIX(_table) *table, - struct PREFIX(_table_enumerator) **state) -{ - if (NULL == *state) - { - *state = CALLOC(1, sizeof(struct PREFIX(_table_enumerator))); - // Make sure that we are not reallocating the table when we start - // enumerating - MAP_LOCK(); - (*state)->table = table; - (*state)->index = -1; - IF_NO_GC(__sync_fetch_and_add(&table->enumerator_count, 1);) - MAP_UNLOCK(); - } - if ((*state)->seen >= (*state)->table->table_used) - { -#ifndef ENABLE_GC - MAP_LOCK(); - __sync_fetch_and_sub(&table->enumerator_count, 1); - MAP_UNLOCK(); - free(*state); -#endif -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE - return NULL; -#else - return MAP_TABLE_VALUE_PLACEHOLDER; -#endif - } - while ((++((*state)->index)) < TABLE_SIZE((*state)->table)) - { - if (!MAP_TABLE_VALUE_NULL((*state)->table->table[(*state)->index].value)) - { - (*state)->seen++; -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE - return &(*state)->table->table[(*state)->index].value; -#else - return (*state)->table->table[(*state)->index].value; -#endif - } - } -#ifndef ENABLE_GC - // Should not be reached, but may be if the table is unsafely modified. - MAP_LOCK(); - table->enumerator_count--; - MAP_UNLOCK(); - free(*state); -#endif -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE - return NULL; -#else - return MAP_TABLE_VALUE_PLACEHOLDER; -#endif -} -/** - * Returns the current value for an enumerator. This is used when you remove - * objects during enumeration. It may cause others to be shuffled up the - * table. - */ -__attribute__((unused)) -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE -static MAP_TABLE_VALUE_TYPE* -#else -static MAP_TABLE_VALUE_TYPE -#endif -PREFIX(_current)(PREFIX(_table) *table, - struct PREFIX(_table_enumerator) **state) -{ -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE - return &(*state)->table->table[(*state)->index].value; -#else - return (*state)->table->table[(*state)->index].value; -#endif -} - -#undef TABLE_SIZE -#undef REALLY_PREFIX_SUFFIX -#undef PREFIX_SUFFIX -#undef PREFIX - -#undef MAP_TABLE_NAME -#undef MAP_TABLE_COMPARE_FUNCTION -#undef MAP_TABLE_HASH_KEY -#undef MAP_TABLE_HASH_VALUE - -#ifdef MAP_TABLE_STATIC_SIZE -# undef MAP_TABLE_STATIC_SIZE -#endif - -#undef MAP_TABLE_VALUE_TYPE - -#undef MAP_LOCK -#undef MAP_UNLOCK -#ifdef MAP_TABLE_NO_LOCK -# undef MAP_TABLE_NO_LOCK -#endif - -#ifdef MAP_TABLE_SINGLE_THREAD -# undef MAP_TABLE_SINGLE_THREAD -#endif - -#undef MAP_TABLE_VALUE_NULL -#undef MAP_TABLE_VALUE_PLACEHOLDER - -#ifdef MAP_TABLE_ACCESS_BY_REFERENCE -# undef MAP_TABLE_ACCESS_BY_REFERENCE -#endif - -#undef CALLOC -#undef IF_NO_GC -#undef IF_GC -#undef MAP_TABLE_TYPES_BITMAP diff --git a/hooks.c b/hooks.c deleted file mode 100644 index b7524d9..0000000 --- a/hooks.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "objc/runtime.h" -#define OBJC_HOOK OBJC_PUBLIC -#include "objc/hooks.h" diff --git a/ivar.c b/ivar.c deleted file mode 100644 index 5391a92..0000000 --- a/ivar.c +++ /dev/null @@ -1,190 +0,0 @@ -#include -#include -#include -#include -#include "objc/runtime.h" -#include "objc/objc-arc.h" -#include "class.h" -#include "visibility.h" -#include "gc_ops.h" -#include "legacy.h" - -ptrdiff_t objc_alignof_type(const char *); -ptrdiff_t objc_sizeof_type(const char *); - -PRIVATE void objc_compute_ivar_offsets(Class class) -{ - if (class->ivars == NULL) - { - Class super_class = class_getSuperclass(class); - if (super_class != Nil) - { - class->instance_size = super_class->instance_size; - } - return; - } - if (class->ivars->size != sizeof(struct objc_ivar)) - { - fprintf(stderr, "Downgrading ivar struct not yet implemented"); - abort(); - } - int i = 0; - /* If this class was compiled with support for late-bound ivars, the - * instance_size field will contain 0 - {the size of the instance variables - * declared for just this class}. The individual instance variable offset - * fields will then be the offsets from the start of the class, and so must - * have the size of the parent class prepended. */ - if (class->instance_size <= 0) - { - Class super = class_getSuperclass(class); - long ivar_start = 0; - if (Nil != super) - { - if (super->instance_size <= 0) - { - objc_compute_ivar_offsets(super); - } - ivar_start = super->instance_size; - } - class->instance_size = ivar_start; - /* For each instance variable, we add the offset if required (it will be zero - * if this class is compiled with a static ivar layout). We then set the - * value of a global variable to the offset value. - * - * Any class compiled with support for the non-fragile ABI, but not actually - * using it, will export the ivar offset field as a symbol. - * - * Note that using non-fragile ivars breaks @defs(). If you need equivalent - * functionality, provide an alternative @interface with all variables - * declared @public. - */ - if (class->ivars) - { - // If the first instance variable had any alignment padding, then we need - // to discard it. We will recompute padding ourself later. - long next_ivar = ivar_start; - long last_offset = LONG_MIN; - long last_size = 0; - long last_computed_offset = -1; - size_t refcount_size = sizeof(uintptr_t); - for (i = 0 ; i < class->ivars->count ; i++) - { - struct objc_ivar *ivar = ivar_at_index(class->ivars, i); - // Clang 7 and 8 have a bug where the size of _Bool is encoded - // as 0, not 1. Silently fix this up when we see it. - if (ivar->size == 0 && ivar->type[0] == 'B') - { - ivar->size = 1; - } - // We are going to be allocating an extra word for the reference count - // in front of the object. This doesn't matter for aligment most of - // the time, but if we have an instance variable that is a vector type - // then we will need to ensure that we are properly aligned again. - long ivar_size = ivar->size; - // Bitfields have the same offset - the base of the variable - // that contains them. If we are in a bitfield, then we need - // to make sure that we don't add any displacement from the - // previous value. - if (*ivar->offset < last_offset + last_size) - { - *ivar->offset = last_computed_offset + (*ivar->offset - last_offset); - ivar_size = 0; - continue; - } - last_offset = *ivar->offset; - *ivar->offset = next_ivar; - last_computed_offset = *ivar->offset; - next_ivar += ivar_size; - last_size = ivar->size; - size_t align = ivarGetAlign(ivar); - if ((*ivar->offset + refcount_size) % align != 0) - { - long padding = align - ((*ivar->offset + refcount_size) % align); - *ivar->offset += padding; - class->instance_size += padding; - next_ivar += padding; - } - assert((*ivar->offset + sizeof(uintptr_t)) % ivarGetAlign(ivar) == 0); - class->instance_size += ivar_size; - } -#ifdef OLDABI_COMPAT - // If we have a legacy ivar list, update the offset in it too - - // code from older compilers may access this directly! - struct objc_class_gsv1* legacy = objc_legacy_class_for_class(class); - if (legacy) - { - for (i = 0 ; i < class->ivars->count ; i++) - { - legacy->ivars->ivar_list[i].offset = *ivar_at_index(class->ivars, i)->offset; - } - } -#endif - } - } -} - - - -//////////////////////////////////////////////////////////////////////////////// -// Public API functions -//////////////////////////////////////////////////////////////////////////////// - -void object_setIvar(id object, Ivar ivar, id value) -{ - id *addr = (id*)((char*)object + ivar_getOffset(ivar)); - switch (ivarGetOwnership(ivar)) - { - case ownership_strong: - objc_storeStrong(addr, value); - break; - case ownership_weak: - objc_storeWeak(addr, value); - break; - case ownership_unsafe: - case ownership_invalid: - *addr = value; - break; - } -} - -Ivar object_setInstanceVariable(id obj, const char *name, void *value) -{ - Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); - if (ivar_getTypeEncoding(ivar)[0] == '@') - { - object_setIvar(obj, ivar, *(id*)value); - } - else - { - size_t size = objc_sizeof_type(ivar_getTypeEncoding(ivar)); - memcpy((char*)obj + ivar_getOffset(ivar), value, size); - } - return ivar; -} - -id object_getIvar(id object, Ivar ivar) -{ - id *addr = (id*)((char*)object + ivar_getOffset(ivar)); - switch (ivarGetOwnership(ivar)) - { - case ownership_strong: - return objc_retainAutoreleaseReturnValue(*addr); - case ownership_weak: - return objc_loadWeak(addr); - break; - case ownership_unsafe: - case ownership_invalid: - return *addr; - return nil; - } -} - -Ivar object_getInstanceVariable(id obj, const char *name, void **outValue) -{ - Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); - if (NULL != outValue) - { - *outValue = (((char*)obj) + ivar_getOffset(ivar)); - } - return ivar; -} diff --git a/ivar.h b/ivar.h deleted file mode 100644 index 67abd0c..0000000 --- a/ivar.h +++ /dev/null @@ -1,204 +0,0 @@ -#include - -/** - * Metadata structure for an instance variable. - * - */ -// begin: objc_ivar -struct objc_ivar -{ - /** - * Name of this instance variable. - */ - const char *name; - /** - * Type encoding for this instance variable. - */ - const char *type; - /** - * The offset from the start of the object. When using the non-fragile - * ABI, this is initialized by the compiler to the offset from the start of - * the ivars declared by this class. It is then set by the runtime to the - * offset from the object pointer. - */ - int *offset; - /** - * The size of this ivar. Note that the current ABI limits ivars to 4GB. - */ - uint32_t size; - /** - * Flags for this instance variable. - */ - uint32_t flags; -}; -// end: objc_ivar - -/** - * Instance variable ownership. - */ -// begin: objc_ivar_ownership -typedef enum { - /** - * Invalid. Indicates that this is not an instance variable with ownership - * semantics. - */ - ownership_invalid = 0, - /** - * Strong ownership. Assignments to this instance variable should retain - * the assigned value. - */ - ownership_strong = 1, - /** - * Weak ownership. This ivar is a zeroing weak reference to an object. - */ - ownership_weak = 2, - /** - * Object that has `__unsafe_unretained` semantics. - */ - ownership_unsafe = 3 -} objc_ivar_ownership; -// end: objc_ivar_ownership - -/** - * Shift for instance variable alignment. */ -static const int ivar_align_shift = 3; - -typedef enum { - /** - * Mask applied to the flags field to indicate ownership. - */ - ivar_ownership_mask = (1<<0) | (1<<1), - /** - * Flag indicating that the ivar contains an extended type encoding. - */ - ivar_extended_type_encoding = (1<<2), - /** - * Mask for describing the alignment. We need 6 bits to represent any - * power of two aligmnent from 0 to 63-bit alignment. There is probably no - * point supporting more than 32-bit aligment, because various bits of - * offset assume objects are less than 4GB, but there's definitely no point - * in supporting 64-bit alignment because we currently don't support any - * architectures where an address space could contain more than one 2^64 - * byte aligned value. - */ - ivar_align_mask = (((1<<6)-1) << ivar_align_shift) -} objc_ivar_flags; - - -static inline size_t ivarGetAlign(Ivar ivar) -{ - return 1<<((ivar->flags & ivar_align_mask) >> ivar_align_shift); -} - -static inline void ivarSetAlign(Ivar ivar, size_t align) -{ - if (align != 0) - { - if (sizeof(size_t) == 4) - { - align = 4 * 8 - __builtin_clz(align) - 1; - } - else if (sizeof(size_t) == 8) - { - align = 8 * 8 - __builtin_clzll(align) - 1; - } - _Static_assert((sizeof(size_t) == 4) || (sizeof(size_t) == 8), "Unexpected type for size_t"); - } - align <<= ivar_align_shift; - ivar->flags = (ivar->flags & ~ivar_align_mask) | align; -} - -static inline void ivarSetOwnership(Ivar ivar, objc_ivar_ownership o) -{ - ivar->flags = (ivar->flags & ~ivar_ownership_mask) | o; -} - - -/** - * Look up the ownership for a given instance variable. - */ -static inline objc_ivar_ownership ivarGetOwnership(Ivar ivar) -{ - return (objc_ivar_ownership)(ivar->flags & ivar_ownership_mask); -} - -/** - * Legacy ivar structure, inherited from the GCC ABI. - */ -struct objc_ivar_gcc -{ - /** - * Name of this instance variable. - */ - const char *name; - /** - * Type encoding for this instance variable. - */ - const char *type; - /** - * The offset from the start of the object. When using the non-fragile - * ABI, this is initialized by the compiler to the offset from the start of - * the ivars declared by this class. It is then set by the runtime to the - * offset from the object pointer. - */ - int offset; -}; - - -/** - * A list of instance variables declared on this class. Unlike the method - * list, this is a single array and size. Categories are not allowed to add - * instance variables, because that would require existing objects to be - * reallocated, which is only possible with accurate GC (i.e. not in C). - */ -// begin: objc_ivar_list -struct objc_ivar_list -{ - /** - * The number of instance variables in this list. - */ - int count; - /** - * The size of a `struct objc_ivar`. This allows the runtime to load - * versions of this that come from a newer compiler, if we ever need to do - * so. - */ - size_t size; - /** - * An array of instance variable metadata structures. Note that this array - * has count elements. - */ - struct objc_ivar ivar_list[]; -}; -// end: objc_ivar_list - -/** - * Returns a pointer to the ivar inside the `objc_ivar_list` structure. This - * structure is designed to allow the compiler to add other fields without - * breaking the ABI, so although the `ivar_list` field appears to be an array - * of `objc_ivar` structures, it may be an array of some future version of - * `objc_ivar` structs, which have fields appended that this version of the - * runtime does not know about. - */ -static inline struct objc_ivar *ivar_at_index(struct objc_ivar_list *l, int i) -{ - assert(l->size >= sizeof(struct objc_ivar)); - return (struct objc_ivar*)(((char*)l->ivar_list) + (i * l->size)); -} - -/** - * Legacy version of the ivar list - */ -struct objc_ivar_list_gcc -{ - /** - * The number of instance variables in this list. - */ - int count; - /** - * An array of instance variable metadata structures. Note that this array - * has count elements. - */ - struct objc_ivar_gcc ivar_list[]; -}; - diff --git a/legacy.c b/legacy.c deleted file mode 100644 index 51ae17b..0000000 --- a/legacy.c +++ /dev/null @@ -1,461 +0,0 @@ -#include -#include -#include -#include - -#include "objc/runtime.h" -#include "objc/encoding.h" -#include "legacy.h" -#include "properties.h" -#include "class.h" -#include "loader.h" - -PRIVATE size_t lengthOfTypeEncoding(const char *types); - -enum objc_class_flags_gsv1 -{ - /** This class structure represents a class. */ - objc_class_flag_class_gsv1 = (1<<0), - /** This class structure represents a metaclass. */ - objc_class_flag_meta_gsv1 = (1<<1), - /** - * The class uses the new, Objective-C 2, runtime ABI. This ABI defines an - * ABI version field inside the class, and so will be used for all - * subsequent versions that retain some degree of compatibility. - */ - objc_class_flag_new_abi_gsv1 = (1<<4) -}; - -static inline BOOL objc_test_class_flag_gsv1(struct objc_class_gsv1 *aClass, - enum objc_class_flags_gsv1 flag) -{ - return (aClass->info & (unsigned long)flag) == (unsigned long)flag; -} -/** - * Checks the version of a class. Return values are: - * 0. Legacy GCC ABI compatible class. - * 1. First release of GNUstep ABI. - * 2. Second release of the GNUstep ABI, adds strong / weak ivar bitmaps. - * 3. Third release of the GNUstep ABI. Many cleanups. - */ -static inline int objc_get_class_version_gsv1(struct objc_class_gsv1 *aClass) -{ - if (!objc_test_class_flag_gsv1(aClass, objc_class_flag_new_abi_gsv1)) - { - return 0; - } - return aClass->abi_version + 1; -} - -static objc_ivar_ownership ownershipForIvar(struct objc_class_gsv1 *cls, int idx) -{ - if (objc_get_class_version_gsv1(cls) < 2) - { - return ownership_unsafe; - } - if (objc_bitfield_test(cls->strong_pointers, idx)) - { - return ownership_strong; - } - if (objc_bitfield_test(cls->weak_pointers, idx)) - { - return ownership_weak; - } - return ownership_unsafe; -} - -static struct objc_ivar_list *upgradeIvarList(struct objc_class_gsv1 *cls) -{ - struct objc_ivar_list_gcc *l = cls->ivars; - if (l == NULL) - { - return NULL; - } - struct objc_ivar_list *n = calloc(1, sizeof(struct objc_ivar_list) + - l->count*sizeof(struct objc_ivar)); - n->size = sizeof(struct objc_ivar); - n->count = l->count; - for (int i=0 ; icount ; i++) - { - BOOL isBitfield = NO; - int bitfieldSize = 0; - int nextOffset; - // Bitfields have the same offset, but should have their size set to - // the size of the bitfield. We calculate the size of the bitfield by - // looking for the next ivar after the current one that has a different - // offset. - if (i+1 < l->count) - { - nextOffset = l->ivar_list[i+1].offset; - if (l->ivar_list[i].offset == l->ivar_list[i+1].offset) - { - isBitfield = YES; - for (int j=i+2 ; jcount ; j++) - { - if (l->ivar_list[i].offset != l->ivar_list[j].offset) - { - bitfieldSize = l->ivar_list[j].offset - l->ivar_list[i].offset; - break; - } - } - if (bitfieldSize == 0) - { - bitfieldSize = cls->instance_size - l->ivar_list[i].offset; - } - } - } - else - { - nextOffset = cls->instance_size; - } - if (nextOffset < 0) - { - nextOffset = -nextOffset; - } - const char *type = l->ivar_list[i].type; - int size = nextOffset - l->ivar_list[i].offset; - n->ivar_list[i].name = l->ivar_list[i].name; - n->ivar_list[i].type = type; - n->ivar_list[i].size = isBitfield ? bitfieldSize : size; - if (objc_test_class_flag_gsv1(cls, objc_class_flag_new_abi_gsv1)) - { - n->ivar_list[i].offset = cls->ivar_offsets[i]; - } - else - { - n->ivar_list[i].offset = &l->ivar_list[i].offset; - } - ivarSetAlign(&n->ivar_list[i], ((type == NULL) || type[0] == 0) ? __alignof__(void*) : objc_alignof_type(type)); - if (type[0] == '\0') - { - ivarSetAlign(&n->ivar_list[i], size); - } - ivarSetOwnership(&n->ivar_list[i], ownershipForIvar(cls, i)); - } - return n; -} - -static struct objc_method_list *upgradeMethodList(struct objc_method_list_gcc *old) -{ - if (old == NULL) - { - return NULL; - } - if (old->count == 0) - { - return NULL; - } - struct objc_method_list *l = calloc(sizeof(struct objc_method_list) + old->count * sizeof(struct objc_method), 1); - l->count = old->count; - if (old->next) - { - l->next = upgradeMethodList(old->next); - } - l->size = sizeof(struct objc_method); - for (int i=0 ; icount ; i++) - { - l->methods[i].imp = old->methods[i].imp; - l->methods[i].selector = old->methods[i].selector; - l->methods[i].types = old->methods[i].types; - } - return l; -} - -static inline BOOL checkAttribute(char field, int attr) -{ - return (field & attr) == attr; -} - -static void upgradeProperty(struct objc_property *n, struct objc_property_gsv1 *o) -{ - char *typeEncoding; - ptrdiff_t typeSize; - if (o->name[0] == '\0') - { - n->name = o->name + o->name[1]; - n->attributes = o->name + 2; - // If we have an attribute string, then it will contain a more accurate - // version of the types than we'll find in the getter (qualifiers such - // as _Atomic and volatile may be dropped) - assert(n->attributes[0] == 'T'); - const char *type_start = &n->attributes[1]; - const char *type_end = strchr(type_start, ','); - if (type_end == NULL) - { - type_end = type_start + strlen(type_start); - } - typeSize = type_end - type_start; - typeEncoding = malloc(typeSize + 1); - memcpy(typeEncoding, type_start, typeSize); - typeEncoding[typeSize] = 0; - } - else - { - typeSize = (ptrdiff_t)lengthOfTypeEncoding(o->getter_types); - typeEncoding = malloc(typeSize + 1); - memcpy(typeEncoding, o->getter_types, typeSize); - typeEncoding[typeSize] = 0; - } - n->type = typeEncoding; - - if (o->getter_name) - { - n->getter = sel_registerTypedName_np(o->getter_name, o->getter_types); - } - if (o->setter_name) - { - n->setter = sel_registerTypedName_np(o->setter_name, o->setter_types); - } - - if (o->name[0] == '\0') - { - return; - } - - n->name = o->name; - - const char *name = o->name; - size_t nameSize = (NULL == name) ? 0 : strlen(name); - // Encoding is T{type},V{name}, so 4 bytes for the "T,V" that we always - // need. We also need two bytes for the leading null and the length. - size_t encodingSize = typeSize + nameSize + 6; - char flags[20]; - size_t i = 0; - // Flags that are a comma then a character - if (checkAttribute(o->attributes, OBJC_PR_readonly)) - { - flags[i++] = ','; - flags[i++] = 'R'; - } - if (checkAttribute(o->attributes, OBJC_PR_retain)) - { - flags[i++] = ','; - flags[i++] = '&'; - } - if (checkAttribute(o->attributes, OBJC_PR_copy)) - { - flags[i++] = ','; - flags[i++] = 'C'; - } - if (checkAttribute(o->attributes2, OBJC_PR_weak)) - { - flags[i++] = ','; - flags[i++] = 'W'; - } - if (checkAttribute(o->attributes2, OBJC_PR_dynamic)) - { - flags[i++] = ','; - flags[i++] = 'D'; - } - if ((o->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) - { - flags[i++] = ','; - flags[i++] = 'N'; - } - encodingSize += i; - flags[i] = '\0'; - size_t setterLength = 0; - size_t getterLength = 0; - if ((o->attributes & OBJC_PR_getter) == OBJC_PR_getter) - { - getterLength = strlen(o->getter_name); - encodingSize += 2 + getterLength; - } - if ((o->attributes & OBJC_PR_setter) == OBJC_PR_setter) - { - setterLength = strlen(o->setter_name); - encodingSize += 2 + setterLength; - } - unsigned char *encoding = malloc(encodingSize); - // Set the leading 0 and the offset of the name - unsigned char *insert = encoding; - BOOL needsComma = NO; - *(insert++) = 0; - *(insert++) = 0; - // Set the type encoding - *(insert++) = 'T'; - memcpy(insert, typeEncoding, typeSize); - insert += typeSize; - needsComma = YES; - // Set the flags - memcpy(insert, flags, i); - insert += i; - if ((o->attributes & OBJC_PR_getter) == OBJC_PR_getter) - { - if (needsComma) - { - *(insert++) = ','; - } - i++; - needsComma = YES; - *(insert++) = 'G'; - memcpy(insert, o->getter_name, getterLength); - insert += getterLength; - } - if ((o->attributes & OBJC_PR_setter) == OBJC_PR_setter) - { - if (needsComma) - { - *(insert++) = ','; - } - i++; - needsComma = YES; - *(insert++) = 'S'; - memcpy(insert, o->setter_name, setterLength); - insert += setterLength; - } - if (needsComma) - { - *(insert++) = ','; - } - *(insert++) = 'V'; - memcpy(insert, name, nameSize); - insert += nameSize; - *(insert++) = '\0'; - - n->attributes = (const char*)encoding; -} - -static struct objc_property_list *upgradePropertyList(struct objc_property_list_gsv1 *l) -{ - if (l == NULL) - { - return NULL; - } - size_t data_size = l->count * sizeof(struct objc_property); - struct objc_property_list *n = calloc(1, sizeof(struct objc_property_list) + data_size); - n->count = l->count; - n->size = sizeof(struct objc_property); - for (int i=0 ; icount ; i++) - { - upgradeProperty(&n->properties[i], &l->properties[i]); - } - return n; -} - -static int legacy_key; - -PRIVATE struct objc_class_gsv1* objc_legacy_class_for_class(Class cls) -{ - return (struct objc_class_gsv1*)objc_getAssociatedObject((id)cls, &legacy_key); -} - -PRIVATE Class objc_upgrade_class(struct objc_class_gsv1 *oldClass) -{ - Class cls = calloc(sizeof(struct objc_class), 1); - cls->isa = oldClass->isa; - // super_class is left nil and we upgrade it later. - cls->name = oldClass->name; - cls->version = oldClass->version; - cls->info = objc_class_flag_meta; - cls->instance_size = oldClass->instance_size; - cls->ivars = upgradeIvarList(oldClass); - cls->methods = upgradeMethodList(oldClass->methods); - cls->protocols = oldClass->protocols; - cls->abi_version = oldClass->abi_version; - cls->properties = upgradePropertyList(oldClass->properties); - objc_register_selectors_from_class(cls); - if (!objc_test_class_flag_gsv1(oldClass, objc_class_flag_meta_gsv1)) - { - cls->info = 0; - cls->isa = objc_upgrade_class((struct objc_class_gsv1*)cls->isa); - objc_setAssociatedObject((id)cls, &legacy_key, (id)oldClass, OBJC_ASSOCIATION_ASSIGN); - } - else - { - cls->instance_size = sizeof(struct objc_class); - } - return cls; -} -PRIVATE struct objc_category *objc_upgrade_category(struct objc_category_gcc *old) -{ - struct objc_category *cat = calloc(1, sizeof(struct objc_category)); - memcpy(cat, old, sizeof(struct objc_category_gcc)); - cat->instance_methods = upgradeMethodList(old->instance_methods); - cat->class_methods = upgradeMethodList(old->class_methods); - if (cat->instance_methods != NULL) - { - objc_register_selectors_from_list(cat->instance_methods); - } - if (cat->class_methods != NULL) - { - objc_register_selectors_from_list(cat->class_methods); - } - for (int i=0 ; iprotocols->count ; i++) - { - objc_init_protocols(cat->protocols); - } - return cat; -} - -static struct objc_protocol_method_description_list* -upgrade_protocol_method_list_gcc(struct objc_protocol_method_description_list_gcc *l) -{ - if ((l == NULL) || (l->count == 0)) - { - return NULL; - } - struct objc_protocol_method_description_list *n = - malloc(sizeof(struct objc_protocol_method_description_list) + - l->count * sizeof(struct objc_protocol_method_description)); - n->count = l->count; - n->size = sizeof(struct objc_protocol_method_description); - for (int i=0 ; icount ; i++) - { - n->methods[i].selector = sel_registerTypedName_np(l->methods[i].name, l->methods[i].types); - n->methods[i].types = l->methods[i].types; - } - return n; -} - -PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc *p) -{ - // If the protocol has already been upgraded, the don't try to upgrade it twice. - if (p->isa == objc_getClass("ProtocolGCC")) - { - return objc_getProtocol(p->name); - } - p->isa = objc_getClass("ProtocolGCC"); - Protocol *proto = - (Protocol*)class_createInstance((Class)objc_getClass("Protocol"), - sizeof(struct objc_protocol) - sizeof(id)); - proto->name = p->name; - // Aliasing of this between the new and old structures means that when this - // returns these will all be updated. - proto->protocol_list = p->protocol_list; - proto->instance_methods = upgrade_protocol_method_list_gcc(p->instance_methods); - proto->class_methods = upgrade_protocol_method_list_gcc(p->class_methods); - assert(proto->isa); - return proto; -} - -PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gsv1 *p) -{ - // If the protocol has already been upgraded, the don't try to upgrade it twice. - if (p->isa == objc_getClass("ProtocolGSv1")) - { - return objc_getProtocol(p->name); - } - Protocol *n = - (Protocol*)class_createInstance((Class)objc_getClass("Protocol"), - sizeof(struct objc_protocol) - sizeof(id)); - n->instance_methods = upgrade_protocol_method_list_gcc(p->instance_methods); - // Aliasing of this between the new and old structures means that when this - // returns these will all be updated. - n->name = p->name; - n->protocol_list = p->protocol_list; - n->class_methods = upgrade_protocol_method_list_gcc(p->class_methods); - n->properties = upgradePropertyList(p->properties); - n->optional_properties = upgradePropertyList(p->optional_properties); - n->isa = objc_getClass("Protocol"); - // We do in-place upgrading of these, because they might be referenced - // directly - p->instance_methods = (struct objc_protocol_method_description_list_gcc*)n->instance_methods; - p->class_methods = (struct objc_protocol_method_description_list_gcc*)n->class_methods; - p->properties = (struct objc_property_list_gsv1*)n->properties; - p->optional_properties = (struct objc_property_list_gsv1*)n->optional_properties; - p->isa = objc_getClass("ProtocolGSv1"); - assert(p->isa); - return n; -} - diff --git a/legacy.h b/legacy.h deleted file mode 100644 index a132619..0000000 --- a/legacy.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "visibility.h" -#include "ivar.h" -#include "class.h" -#include "category.h" -#include "protocol.h" - -PRIVATE Class objc_upgrade_class(struct objc_class_gsv1 *oldClass); -PRIVATE struct objc_category *objc_upgrade_category(struct objc_category_gcc *); - -PRIVATE struct objc_class_gsv1* objc_legacy_class_for_class(Class); - -PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc*); -PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gsv1*); - diff --git a/legacy_malloc.c b/legacy_malloc.c deleted file mode 100644 index 3eedf27..0000000 --- a/legacy_malloc.c +++ /dev/null @@ -1,43 +0,0 @@ -#include - -void *valloc(size_t); - -// Stubs that just call the libc implementations when you call these. - -void *objc_malloc(size_t size) -{ - return malloc(size); -} - -void *objc_atomic_malloc(size_t size) -{ - return malloc(size); -} - -#ifdef __MINGW32__ -void *objc_valloc(size_t size) -{ - return malloc(size); -} -#else -void *objc_valloc(size_t size) -{ - return valloc(size); -} -#endif - -void *objc_realloc(void *mem, size_t size) -{ - return realloc(mem, size); -} - -void * objc_calloc(size_t nelem, size_t size) -{ - return calloc(nelem, size); -} - -void objc_free(void *mem) -{ - free(mem); -} - diff --git a/libobjc.pc.in b/libobjc.pc.in deleted file mode 100644 index b870e73..0000000 --- a/libobjc.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@PC_INSTALL_PREFIX@ -exec_prefix=${prefix} -libdir=${exec_prefix}/@LIB_INSTALL_PATH@ -includedir=${prefix}/@HEADER_INSTALL_PATH@ - -Name: libobjc -Description: GNUstep Objective-C runtime library -Version: @CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@ - -Cflags: -I${includedir} -Libs: -L${libdir} -lobjc -@PC_LIBS_PRIVATE@ diff --git a/loader.c b/loader.c deleted file mode 100644 index a49100a..0000000 --- a/loader.c +++ /dev/null @@ -1,400 +0,0 @@ -#include -#include -#include "objc/runtime.h" -#include "objc/objc-auto.h" -#include "objc/objc-arc.h" -#include "lock.h" -#include "loader.h" -#include "visibility.h" -#include "legacy.h" -#ifdef ENABLE_GC -#include -#endif -#include -#include - -/** - * Runtime lock. This is exposed in - */ -PRIVATE mutex_t runtime_mutex; -LEGACY void *__objc_runtime_mutex = &runtime_mutex; - -void init_alias_table(void); -void init_arc(void); -void init_class_tables(void); -void init_dispatch_tables(void); -void init_gc(void); -void init_protocol_table(void); -void init_selector_tables(void); -void init_trampolines(void); -void init_early_blocks(void); -void objc_send_load_message(Class class); - -void log_selector_memory_usage(void); - -static void log_memory_stats(void) -{ - log_selector_memory_usage(); -} - -/* Number of threads that are alive. */ -int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ - -// libdispatch hooks for registering threads -__attribute__((weak)) void (*dispatch_begin_thread_4GC)(void); -__attribute__((weak)) void (*dispatch_end_thread_4GC)(void); -__attribute__((weak)) void *(*_dispatch_begin_NSAutoReleasePool)(void); -__attribute__((weak)) void (*_dispatch_end_NSAutoReleasePool)(void *); - -__attribute__((used)) -static void link_protos(void) -{ - link_protocol_classes(); -} - -static void init_runtime(void) -{ - static BOOL first_run = YES; - if (first_run) - { -#if ENABLE_GC - init_gc(); -#endif - // Create the main runtime lock. This is not safe in theory, but in - // practice the first time that this function is called will be in the - // loader, from the main thread. Future loaders may run concurrently, - // but that is likely to break the semantics of a lot of languages, so - // we don't have to worry about it for a long time. - // - // The only case when this can potentially go badly wrong is when a - // pure-C main() function spawns two threads which then, concurrently, - // call dlopen() or equivalent, and the platform's implementation of - // this does not perform any synchronization. - INIT_LOCK(runtime_mutex); - // Create the various tables that the runtime needs. - init_selector_tables(); - init_dispatch_tables(); - init_protocol_table(); - init_class_tables(); - init_alias_table(); - init_early_blocks(); - init_arc(); - init_trampolines(); - first_run = NO; - if (getenv("LIBOBJC_MEMORY_PROFILE")) - { - atexit(log_memory_stats); - } - if (dispatch_begin_thread_4GC != 0) { - dispatch_begin_thread_4GC = objc_registerThreadWithCollector; - } - if (dispatch_end_thread_4GC != 0) { - dispatch_end_thread_4GC = objc_unregisterThreadWithCollector; - } - if (_dispatch_begin_NSAutoReleasePool != 0) { - _dispatch_begin_NSAutoReleasePool = objc_autoreleasePoolPush; - } - if (_dispatch_end_NSAutoReleasePool != 0) { - _dispatch_end_NSAutoReleasePool = objc_autoreleasePoolPop; - } - } -} - -/** - * Structure for a class alias. - */ -struct objc_alias -{ - /** - * The name by which this class is referenced. - */ - const char *alias_name; - /** - * A pointer to the indirection variable for this class. - */ - Class *alias; -}; - -/** - * Type of the NSConstantString structure. - */ -struct nsstr -{ - /** Class pointer. */ - id isa; - /** - * Flags. Low 2 bits store the encoding: - * 0: ASCII - * 1: UTF-8 - * 2: UTF-16 - * 3: UTF-32 - * - * Low 16 bits are reserved for the compiler, high 32 bits are reserved for - * the Foundation framework. - */ - uint32_t flags; - /** - * Number of UTF-16 code units in the string. - */ - uint32_t length; - /** - * Number of bytes in the string. - */ - uint32_t size; - /** - * Hash (Foundation framework defines the hash algorithm). - */ - uint32_t hash; - /** - * Character data. - */ - const char *data; -}; - -// begin: objc_init -struct objc_init -{ - uint64_t version; - SEL sel_begin; - SEL sel_end; - Class *cls_begin; - Class *cls_end; - Class *cls_ref_begin; - Class *cls_ref_end; - struct objc_category *cat_begin; - struct objc_category *cat_end; - struct objc_protocol *proto_begin; - struct objc_protocol *proto_end; - struct objc_protocol **proto_ref_begin; - struct objc_protocol **proto_ref_end; - struct objc_alias *alias_begin; - struct objc_alias *alias_end; - struct nsstr *strings_begin; - struct nsstr *strings_end; -}; -// end: objc_init - -#ifdef DEBUG_LOADING -#include -#endif - -static enum { - LegacyABI, - NewABI, - UnknownABI -} CurrentABI = UnknownABI; - -void registerProtocol(Protocol *proto); - -OBJC_PUBLIC void __objc_load(struct objc_init *init) -{ - init_runtime(); -#ifdef DEBUG_LOADING - Dl_info info; - if (dladdr(init, &info)) - { - fprintf(stderr, "Loading %p from object: %s (%p)\n", init, info.dli_fname, __builtin_return_address(0)); - } - else - { - fprintf(stderr, "Loading %p from unknown object\n", init); - } -#endif - LOCK_RUNTIME_FOR_SCOPE(); - BOOL isFirstLoad = NO; - switch (CurrentABI) - { - case LegacyABI: - fprintf(stderr, "Version 2 Objective-C ABI may not be mixed with earlier versions.\n"); - abort(); - case UnknownABI: - isFirstLoad = YES; - CurrentABI = NewABI; - break; - case NewABI: - break; - } - - // If we've already loaded this module, don't load it again. - if (init->version == ULONG_MAX) - { - return; - } - - assert(init->version == 0); - assert((((uintptr_t)init->sel_end-(uintptr_t)init->sel_begin) % sizeof(*init->sel_begin)) == 0); - assert((((uintptr_t)init->cls_end-(uintptr_t)init->cls_begin) % sizeof(*init->cls_begin)) == 0); - assert((((uintptr_t)init->cat_end-(uintptr_t)init->cat_begin) % sizeof(*init->cat_begin)) == 0); - for (SEL sel = init->sel_begin ; sel < init->sel_end ; sel++) - { - if (sel->name == 0) - { - continue; - } - objc_register_selector(sel); - } - for (struct objc_protocol *proto = init->proto_begin ; proto < init->proto_end ; - proto++) - { - if (proto->name == NULL) - { - continue; - } - registerProtocol((struct objc_protocol*)proto); - } - for (struct objc_protocol **proto = init->proto_ref_begin ; proto < init->proto_ref_end ; - proto++) - { - if (*proto == NULL) - { - continue; - } - struct objc_protocol *p = objc_getProtocol((*proto)->name); - assert(p); - *proto = p; - } - for (Class *cls = init->cls_begin ; cls < init->cls_end ; cls++) - { - if (*cls == NULL) - { - continue; - } - // As a special case, allow using legacy ABI code with a new runtime. - if (isFirstLoad && (strcmp((*cls)->name, "Protocol") == 0)) - { - CurrentABI = UnknownABI; - } -#ifdef DEBUG_LOADING - fprintf(stderr, "Loading class %s\n", (*cls)->name); -#endif - objc_load_class(*cls); - } -#if 0 - // We currently don't do anything with these pointers. They exist to - // provide a level of indirection that will permit us to completely change - // the `objc_class` struct without breaking the ABI (again) - for (Class *cls = init->cls_ref_begin ; cls < init->cls_ref_end ; cls++) - { - } -#endif - for (struct objc_category *cat = init->cat_begin ; cat < init->cat_end ; - cat++) - { - if ((cat == NULL) || (cat->class_name == NULL)) - { - continue; - } - objc_try_load_category(cat); -#ifdef DEBUG_LOADING - fprintf(stderr, "Loading category %s (%s)\n", cat->class_name, cat->name); -#endif - } - // Load categories and statics that were deferred. - objc_load_buffered_categories(); - // Fix up the class links for loaded classes. - objc_resolve_class_links(); - for (struct objc_category *cat = init->cat_begin ; cat < init->cat_end ; - cat++) - { - Class class = (Class)objc_getClass(cat->class_name); - if ((Nil != class) && - objc_test_class_flag(class, objc_class_flag_resolved)) - { - objc_send_load_message(class); - } - } - // Register aliases - for (struct objc_alias *alias = init->alias_begin ; alias < init->alias_end ; - alias++) - { - if (alias->alias_name) - { - class_registerAlias_np(*alias->alias, alias->alias_name); - } - } -#if 0 - // If future versions of the ABI need to do anything with constant strings, - // they may do so here. - for (struct nsstr *string = init->strings_begin ; string < init->strings_end ; - string++) - { - if (string->isa) - { - } - } -#endif - init->version = ULONG_MAX; -} - -#ifdef OLDABI_COMPAT -OBJC_PUBLIC void __objc_exec_class(struct objc_module_abi_8 *module) -{ - init_runtime(); - - switch (CurrentABI) - { - case UnknownABI: - CurrentABI = LegacyABI; - break; - case LegacyABI: - break; - case NewABI: - fprintf(stderr, "Version 2 Objective-C ABI may not be mixed with earlier versions.\n"); - abort(); - } - - // Check that this module uses an ABI version that we recognise. - // In future, we should pass the ABI version to the class / category load - // functions so that we can change various structures more easily. - assert(objc_check_abi_version(module)); - - - // The runtime mutex is held for the entire duration of a load. It does - // not need to be acquired or released in any of the called load functions. - LOCK_RUNTIME_FOR_SCOPE(); - - struct objc_symbol_table_abi_8 *symbols = module->symbol_table; - // Register all of the selectors used in this module. - if (symbols->selectors) - { - objc_register_selector_array(symbols->selectors, - symbols->selector_count); - } - - unsigned short defs = 0; - // Load the classes from this module - for (unsigned short i=0 ; iclass_count ; i++) - { - objc_load_class(objc_upgrade_class(symbols->definitions[defs++])); - } - unsigned int category_start = defs; - // Load the categories from this module - for (unsigned short i=0 ; icategory_count; i++) - { - objc_try_load_category(objc_upgrade_category(symbols->definitions[defs++])); - } - // Load the static instances - struct objc_static_instance_list **statics = (void*)symbols->definitions[defs]; - while (NULL != statics && NULL != *statics) - { - objc_init_statics(*(statics++)); - } - - // Load categories and statics that were deferred. - objc_load_buffered_categories(); - objc_init_buffered_statics(); - // Fix up the class links for loaded classes. - objc_resolve_class_links(); - for (unsigned short i=0 ; icategory_count; i++) - { - struct objc_category *cat = (struct objc_category*) - symbols->definitions[category_start++]; - Class class = (Class)objc_getClass(cat->class_name); - if ((Nil != class) && - objc_test_class_flag(class, objc_class_flag_resolved)) - { - objc_send_load_message(class); - } - } -} -#endif diff --git a/loader.h b/loader.h deleted file mode 100644 index 21ff1d6..0000000 --- a/loader.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef __OBJC_LOADER_H_INCLUDED -#define __OBJC_LOADER_H_INCLUDED -#include "category.h" -#include "method.h" -#include "module.h" -#include "class.h" -#include "protocol.h" - -/** - * Checks whether it is safe to load a module with the specified version and - * module size. This depends on whether another module with an incompatible - * ABI has already been loaded. - */ -BOOL objc_check_abi_version(struct objc_module_abi_8 *module); -/** - * Initializes a protocol list, uniquing the protocols in the list. - */ -void objc_init_protocols(struct objc_protocol_list *protocols); -/** - * Registers a set of selectors from a method list. - */ -void objc_register_selectors_from_list(struct objc_method_list *l); -/** - * Register all of the (unregistered) selectors that are used in a class. - */ -void objc_register_selectors_from_class(Class class); -/** - * Registers all of the selectors in an array. - */ -void objc_register_selector_array(SEL selectors, unsigned long count); -/** - * Loads a class into the runtime system. If possible, the class is resolved - * (inserted into the class tree) immediately. If its superclass is not yet - * resolved, it is enqueued for later resolution. - */ -void objc_load_class(struct objc_class *class); -/** - * Resolves classes that have not yet been resolved, if their superclasses have - * subsequently been loaded. - */ -void objc_resolve_class_links(void); -/** - * Attaches a category to its class, if the class is already loaded. Buffers - * it for future resolution if not. - */ -void objc_try_load_category(struct objc_category *cat); -/** - * Tries to load all of the categories that could not previously be loaded - * because their classes were not yet loaded. - */ -void objc_load_buffered_categories(void); -/** - * Updates the dispatch table for a class. - */ -void objc_update_dtable_for_class(Class cls); -/** - * Initialises a list of static object instances belonging to the same class if - * possible, or defers initialisation until the class has been loaded it not. - */ -void objc_init_statics(struct objc_static_instance_list *statics); -/** - * Tries again to initialise static instances which could not be initialised - * earlier. - */ -void objc_init_buffered_statics(void); - -#endif //__OBJC_LOADER_H_INCLUDED diff --git a/lock.h b/lock.h deleted file mode 100644 index e5dacd3..0000000 --- a/lock.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * libobjc requires recursive mutexes. These are delegated to the underlying - * threading implementation. This file contains a VERY thin wrapper over the - * Windows and POSIX mutex APIs. - */ - -#ifndef __LIBOBJC_LOCK_H_INCLUDED__ -#define __LIBOBJC_LOCK_H_INCLUDED__ -#ifdef _WIN32 -# include "safewindows.h" -typedef CRITICAL_SECTION mutex_t; -# define INIT_LOCK(x) InitializeCriticalSection(&(x)) -# define LOCK(x) EnterCriticalSection(x) -# define UNLOCK(x) LeaveCriticalSection(x) -# define DESTROY_LOCK(x) DeleteCriticalSection(x) -#else - -# include - -typedef pthread_mutex_t mutex_t; -// If this pthread implementation has a static initializer for recursive -// mutexes, use that, otherwise fall back to the portable version -# ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP -# define INIT_LOCK(x) x = (pthread_mutex_t)PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP -# elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER) -# define INIT_LOCK(x) x = (pthread_mutex_t)PTHREAD_RECURSIVE_MUTEX_INITIALIZER -# else -# define INIT_LOCK(x) init_recursive_mutex(&(x)) - -static inline void init_recursive_mutex(pthread_mutex_t *x) -{ - pthread_mutexattr_t recursiveAttributes; - pthread_mutexattr_init(&recursiveAttributes); - pthread_mutexattr_settype(&recursiveAttributes, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(x, &recursiveAttributes); - pthread_mutexattr_destroy(&recursiveAttributes); -} -# endif - -# define LOCK(x) pthread_mutex_lock(x) -# define UNLOCK(x) pthread_mutex_unlock(x) -# define DESTROY_LOCK(x) pthread_mutex_destroy(x) -#endif - -__attribute__((unused)) static void objc_release_lock(void *x) -{ - mutex_t *lock = *(mutex_t**)x; - UNLOCK(lock); -} -/** - * Concatenate strings during macro expansion. - */ -#define LOCK_HOLDERN_NAME_CAT(x, y) x ## y -/** - * Concatenate string with unique variable during macro expansion. - */ -#define LOCK_HOLDER_NAME_COUNTER(x, y) LOCK_HOLDERN_NAME_CAT(x, y) -/** - * Create a unique name for a lock holder variable - */ -#define LOCK_HOLDER_NAME(x) LOCK_HOLDER_NAME_COUNTER(x, __COUNTER__) - -/** - * Acquires the lock and automatically releases it at the end of the current - * scope. - */ -#define LOCK_FOR_SCOPE(lock) \ - __attribute__((cleanup(objc_release_lock)))\ - __attribute__((unused)) mutex_t *LOCK_HOLDER_NAME(lock_pointer) = lock;\ - LOCK(lock) - -/** - * The global runtime mutex. - */ -extern mutex_t runtime_mutex; - -#define LOCK_RUNTIME() LOCK(&runtime_mutex) -#define UNLOCK_RUNTIME() UNLOCK(&runtime_mutex) -#define LOCK_RUNTIME_FOR_SCOPE() LOCK_FOR_SCOPE(&runtime_mutex) - -#ifdef __cplusplus -/** - * C++ wrapper around our mutex, for use with std::lock_guard and friends. - */ -class RecursiveMutex -{ - /// The underlying mutex - mutex_t mutex; - - public: - /** - * Explicit initialisation of the underlying lock, so that this can be a - * global. - */ - void init() - { - INIT_LOCK(mutex); - } - - /// Acquire the lock. - void lock() - { - LOCK(&mutex); - } - - /// Release the lock. - void unlock() - { - UNLOCK(&mutex); - } -}; -#endif - -#endif // __LIBOBJC_LOCK_H_INCLUDED__ diff --git a/method.h b/method.h deleted file mode 100644 index 4e2997b..0000000 --- a/method.h +++ /dev/null @@ -1,103 +0,0 @@ -#include - -/** - * Metadata structure describing a method. - */ -// begin: objc_method -struct objc_method -{ - /** - * A pointer to the function implementing this method. - */ - IMP imp; - /** - * Selector used to send messages to this method. - */ - SEL selector; - /** - * The extended type encoding for this method. - */ - const char *types; -}; -// end: objc_method - -struct objc_method_gcc -{ - /** - * Selector used to send messages to this method. The type encoding of - * this method should match the types field. - */ - SEL selector; - /** - * The type encoding for this selector. Used only for introspection, and - * only required because of the stupid selector handling in the old GNU - * runtime. In future, this field may be reused for something else. - */ - const char *types; - /** - * A pointer to the function implementing this method. - */ - IMP imp; -}; - -/** - * Method list. Each class or category defines a new one of these and they are - * all chained together in a linked list, with new ones inserted at the head. - * When constructing the dispatch table, methods in the start of the list are - * used in preference to ones at the end. - */ -// begin: objc_method_list -struct objc_method_list -{ - /** - * The next group of methods in the list. - */ - struct objc_method_list *next; - /** - * The number of methods in this list. - */ - int count; - /** - * Sze of `struct objc_method`. This allows runtimes downgrading newer - * versions of this structure. - */ - size_t size; - /** - * An array of methods. Note that the actual size of this is count. - */ - struct objc_method methods[]; -}; -// end: objc_method_list - -/** - * Returns a pointer to the method inside the `objc_method` structure. This - * structure is designed to allow the compiler to add other fields without - * breaking the ABI, so although the `methods` field appears to be an array - * of `objc_method` structures, it may be an array of some future version of - * `objc_method` structs, which have fields appended that this version of the - * runtime does not know about. - */ -static inline struct objc_method *method_at_index(struct objc_method_list *l, int i) -{ - assert(l->size >= sizeof(struct objc_method)); - return (struct objc_method*)(((char*)l->methods) + (i * l->size)); -} - -/** - * Legacy version of the method list. - */ -struct objc_method_list_gcc -{ - /** - * The next group of methods in the list. - */ - struct objc_method_list_gcc *next; - /** - * The number of methods in this list. - */ - int count; - /** - * An array of methods. Note that the actual size of this is count. - */ - struct objc_method_gcc methods[]; -}; diff --git a/module.h b/module.h deleted file mode 100644 index 96e0a77..0000000 --- a/module.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Defines the module structures. - * - * When defining a new ABI, the - */ - -/** - * The symbol table for a module. This structure references all of the - * Objective-C symbols defined for a module, allowing the runtime to find and - * register them. - */ -struct objc_symbol_table_abi_8 -{ - /** - * The number of selectors referenced in this module. - */ - unsigned long selector_count; - /** - * An array of selectors used in this compilation unit. SEL is a pointer - * type and this points to the first element in an array of selectors. - */ - SEL selectors; - /** - * The number of classes defined in this module. - */ - unsigned short class_count; - /** - * The number of categories defined in this module. - */ - unsigned short category_count; - /** - * A null-terminated array of pointers to symbols defined in this module. - * This contains class_count pointers to class structures, category_count - * pointers to category structures, and then zero or more pointers to - * static object instances. - * - * Current compilers only use this for constant strings. The runtime - * permits other types. - */ - void *definitions[]; -}; - -/** - * The module structure is passed to the __objc_exec_class function by a - * constructor function when the module is loaded. - * - * When defining a new ABI version, the first two fields in this structure must - * be retained. - */ -struct objc_module_abi_8 -{ - /** - * The version of the ABI used by this module. This is checked against the - * list of ABIs that the runtime supports, and the list of incompatible - * ABIs. - */ - unsigned long version; - /** - * The size of the module. This is used for sanity checking, to ensure - * that the compiler and runtime's idea of the module size match. - */ - unsigned long size; - /** - * The full path name of the source for this module. Not currently used - * for anything, could be used for debugging in theory, but duplicates - * information available from DWARF data, so probably won't. - */ - const char *name; - /** - * A pointer to the symbol table for this compilation unit. - */ - struct objc_symbol_table_abi_8 *symbol_table; -}; - -struct objc_module_abi_10 -{ - /** - * Inherited fields from version 8 of the ABI. - */ - struct objc_module_abi_8 old; - /** - * GC mode. GC_Optional code can be mixed with anything, but GC_None code - * can't be mixed with GC_Required code. - */ - int gc_mode; -}; - -/** - * List of static instances of a named class provided in this module. - */ -struct objc_static_instance_list -{ - /** - * The name of the class. The isa pointer of all of the instances will be - * set to the class with this name. - */ - char *class_name; - /** - * NULL-terminated array of statically-allocated instances. - */ - id instances[]; -}; diff --git a/mutation.m b/mutation.m deleted file mode 100644 index 6a679b5..0000000 --- a/mutation.m +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include "objc/runtime.h" - -// This function is exported as a weak symbol to enable GNUstep or some other -// framework to replace it trivially -OBJC_PUBLIC -void __attribute__((weak)) objc_enumerationMutation(id obj) -{ - fprintf(stderr, "Mutation occurred during enumeration."); - abort(); -} - diff --git a/nsobject.h b/nsobject.h deleted file mode 100644 index 7703a22..0000000 --- a/nsobject.h +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Stub declaration of NSObject. Lots of things in the runtime require the - */ -@interface NSObject --retain; --copy; --(void)release; --autorelease; --(void)dealloc; -@end diff --git a/objc_msgSend.S b/objc_msgSend.S deleted file mode 100644 index 5427594..0000000 --- a/objc_msgSend.S +++ /dev/null @@ -1,20 +0,0 @@ -#include "common.S" -#include "asmconstants.h" -#if __x86_64 -#include "objc_msgSend.x86-64.S" -#elif __i386 -#include "objc_msgSend.x86-32.S" -#elif __arm__ -#include "objc_msgSend.arm.S" -#elif defined(__ARM_ARCH_ISA_A64) -#include "objc_msgSend.aarch64.S" -#elif defined(__riscv) && (__riscv_xlen == 64) && defined(__riscv_float_abi_double) -#include "objc_msgSend.riscv64.S" -#elif defined(__mips_n64) || defined(__mips_n32) -#include "objc_msgSend.mips.S" -#else -#warning objc_msgSend() not implemented for your architecture -#endif -#ifdef __ELF__ -.section .note.GNU-stack,"",%progbits -#endif diff --git a/objc_msgSend.aarch64.S b/objc_msgSend.aarch64.S deleted file mode 100644 index a41cba5..0000000 --- a/objc_msgSend.aarch64.S +++ /dev/null @@ -1,250 +0,0 @@ -#define ARGUMENT_SPILL_SIZE (8*10 + 8*16) - -/* Windows ARM64 Exception Handling - * - * Structured Exception Handling (SEH) on Windows ARM64 differs from the x64 - * implementation. Functions consist of a single prologue and zero or more - * epilogues. Instead of using offsets for the .seh* directives to manipulate the - * stack frame, each directive corresponds to a single instruction. - * - * This presents a challenge for our objc_msgSend function, which only modifies - * the stack when a slow lookup is needed (see label "5"). - * - * To address this, we move the directive marking the start of a function deep - * into the msgSend body to prevent marking every instruction as ".seh_nop." - * - * For Windows: - * - EH_START(x): Start of function (no effect on Windows) - * - EH_END(x): End of function (no effect on Windows) - * - EH_START_AT_OFFSET(x): Mark Start of function (Delayed) - * - EH_END_AT_OFFSET(x): Mark End of function (Delayed) - * - EH_END_PROLOGUE: End of function prologue - * - EH_START_EPILOGUE: Start of function epilogue - * - EH_END_EPILOGUE: End of function epilogue - * - EH_SAVE_FP_LR(x): Save Frame Pointer and Link Register - * - EH_STACK_ALLOC(x): Stack allocation (inside prologue) - * - EH_ADD_FP(x): Add to Frame Pointer - * - EH_NOP: Mark instruction with no unwinding relevance - * - * For non-64-bit Windows systems or other platforms, these macros have no effect and can be used without causing issues. - */ - -#ifdef _WIN32 -# define EH_START -# define EH_END - -# define EH_START_AT_OFFSET(x) .seh_proc x -# define EH_END_AT_OFFSET(x) .seh_endproc x - -# define EH_END_PROLOGUE .seh_endprologue -# define EH_START_EPILOGUE .seh_startepilogue -# define EH_END_EPILOGUE .seh_endepilogue - -# define EH_SAVE_FP_LR(x) .seh_save_fplr x -# define EH_STACK_ALLOC(x) .seh_stackalloc x -# define EH_ADD_FP(x) .seh_add_fp x - -# define EH_NOP .seh_nop -#else -// Marks the real start and end of the function -# define EH_START .cfi_startproc -# define EH_END .cfi_endproc - -// The following directives are either not -// needed or not available with CFI -# define EH_START_AT_OFFSET(x) -# define EH_END_AT_OFFSET(x) -# define EH_END_PROLOGUE -# define EH_START_EPILOGUE -# define EH_END_EPILOGUE -# define EH_SAVE_FP_LR(x) -# define EH_STACK_ALLOC(x) -# define EH_ADD_FP(x) -# define EH_NOP -#endif - -.macro MSGSEND fnname receiver, sel - EH_START - - cbz \receiver, 4f // Skip everything if the receiver is nil - // Jump to 6: if this is a small object - ubfx x9, \receiver, #0, #SMALLOBJ_BITS - cbnz x9, 6f - - ldr x9, [\receiver] // Load class to x9 if not a small int -1: - ldr x9, [x9, #DTABLE_OFFSET] // Dtable -> x9 - ldr w10, [\sel] // selector->index -> x10 - ldr w11, [x9, #SHIFT_OFFSET] // dtable->shift -> x11 - - cmp x11, #8 // If this is a small dtable, jump to the - // small dtable handlers - b.eq 2f - cbz x11, 3f - - ubfx x11, x10, #16, #8 // Put byte 3 of the sel id in x12 - add x11, x9, x11, lsl #3 // x11 = dtable address + dtable data offset - ldr x9, [x11, #DATA_OFFSET] // Load, adding in the data offset -2: // dtable16 - ubfx x11, x10, #8, #8 // Put byte 2 of the sel id in x12 - add x11, x9, x11, lsl #3 // x11 = dtable address + dtable data offset - ldr x9, [x11, #DATA_OFFSET] // Load, adding in the data offset -3: // dtable8 - ubfx x11, x10, #0, #8 // Put low byte of the sel id in x12 - add x11, x9, x11, lsl #3 // x11 = dtable address + dtable data offset - ldr x9, [x11, #DATA_OFFSET] // Load, adding in the data offset. - // Slot pointer is now in x9 - - cbz x9, 5f // If the slot is nil, go to the C path - - ldr x9, [x9, #SLOT_OFFSET] // Load the method from the slot - br x9 // Tail-call the method - -4: // Nil receiver - mov \receiver, #0 - mov v0.d[0], \receiver - mov v0.d[1], \receiver - br lr -5: // Slow lookup - EH_START_AT_OFFSET(\fnname) - - // Save anything that will be clobbered by - // the call. - // Note that we pre-index (see "!"), meaning - // that we adjust the sp before storing the pair - // of registers. - stp x0, x1, [sp, #-(ARGUMENT_SPILL_SIZE)]! - EH_STACK_ALLOC((ARGUMENT_SPILL_SIZE)) - - stp x2, x3, [sp, #16] - EH_NOP // The following instructions can be ignored by SEH - stp x4, x5, [sp, #32] - EH_NOP - stp x6, x7, [sp, #48] - EH_NOP - stp q0, q1, [sp, #64] - EH_NOP - stp q2, q3, [sp, #96] - EH_NOP - stp q4, q5, [sp, #128] - EH_NOP - stp q6, q7, [sp, #160] - EH_NOP - stp fp, lr, [sp, #192] // The order is arbitrary, except that - EH_SAVE_FP_LR(192) // fp and lr must be spilled together - - add fp, sp, 192 // Adjust frame pointer - EH_ADD_FP(192) - stp \receiver, x8, [sp, #-16]! // it's convenient if \receiver is spilled at sp - EH_STACK_ALLOC(16) // stp performed pre-indexing by sp-16 - - EH_END_PROLOGUE - - #ifndef _WIN32 - .cfi_def_cfa fp, 16 - .cfi_offset fp, -16 - .cfi_offset lr, -8 - #endif - // We now have all argument registers, the link - // register and the receiver spilled on the - // stack, with sp containing - // the address of the receiver - - mov x0, sp // &self, _cmd in arguments - mov x1, \sel - bl CDECL(slowMsgLookup) // This is the only place where the EH directives - // have to be accurate... - mov x9, x0 // IMP -> x9 - - EH_START_EPILOGUE - ldp x0, x1, [sp, #16] // Reload spilled argument registers - EH_NOP - ldp x2, x3, [sp, #32] - EH_NOP - ldp x4, x5, [sp, #48] - EH_NOP - ldp x6, x7, [sp, #64] - EH_NOP - ldp q0, q1, [sp, #80] - EH_NOP - ldp q2, q3, [sp, #112] - EH_NOP - ldp q4, q5, [sp, #144] - EH_NOP - ldp q6, q7, [sp, #176] - EH_NOP - ldp fp, lr, [sp, #208] - EH_SAVE_FP_LR(208) - - // Post-increment sp += ARGUMENT_SPILL_SIZE +16 - ldp \receiver, x8, [sp], #(ARGUMENT_SPILL_SIZE + 16) - EH_STACK_ALLOC((ARGUMENT_SPILL_SIZE + 16)) - - EH_END_EPILOGUE - EH_END_AT_OFFSET(\fnname) - - br x9 -6: - // Load 63:12 of SmallObjectClasses address - // We use the CDECL macro as Windows prefixes - // cdecl conforming symbols with "_". - adrp x10, CDECL(SmallObjectClasses) // The macro handles this transparently. - - // Add lower 12-bits of SmallObjectClasses address to x10 - add x10, x10, :lo12:CDECL(SmallObjectClasses) - ldr x9, [x10, x9, lsl #3] - - b 1b - EH_END -.endm - -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), %function) -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), %function) -.globl CDECL(objc_msgSend_stret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), %function) -CDECL(objc_msgSend): -CDECL(objc_msgSend_fpret): -CDECL(objc_msgSend_stret): - MSGSEND objc_msgSend, x0, x1 - -/* - In AAPCS, an SRet is passed in x8, not x0 like a normal pointer parameter. - On Windows, this is only the case for POD (plain old data) types. Non trivial - types with constructors and destructors are passed in x0 on sret. - - We thus need two objc_msgSend functions on Windows on ARM64 for Sret: - 1. objc_msgSend_stret for POD Sret - 2. objc_msgSend_stret2 for non-trivial Sret (like C++ class instances) - */ -#ifdef _WIN32 -.globl CDECL(objc_msgSend_stret2) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret2), %function) -CDECL(objc_msgSend_stret2): - MSGSEND objc_msgSend_stret2, x1, x2 - -.text -.def objc_msgSend; -.scl 2; -.type 32; -.endef -.def objc_msgSend_fpret; -.scl 2; -.type 32; -.endef -.def objc_msgSend_stret; -.scl 2; -.type 32; -.endef -.def objc_msgSend_stret2; -.scl 2; -.type 32; -.endef - -.section .drectve,"yn" -.ascii " /EXPORT:objc_msgSend" -.ascii " /EXPORT:objc_msgSend_fpret" -.ascii " /EXPORT:objc_msgSend_stret" -.ascii " /EXPORT:objc_msgSend_stret2" -#endif \ No newline at end of file diff --git a/objc_msgSend.arm.S b/objc_msgSend.arm.S deleted file mode 100644 index 6c455e3..0000000 --- a/objc_msgSend.arm.S +++ /dev/null @@ -1,146 +0,0 @@ -.syntax unified -.fpu neon -#if ((__ARM_ARCH >= 7) || defined (__ARM_ARCH_6T2__)) -#define RELOC_OFFSET 4 -// If we're using a CPU that supports Thumb-2, use it. This makes the -// objc_msgSend function 130 bytes instead of 176. The fast path drops from 108 -// bytes to 82, meaning that it will fit in 3 32-byte i-cache lines, rather -// than 4. For comparison, the i386 version is 119 for objc_msgSend and -// another 117 for objc_msgSend_fpret (the two are the same on ARM), with 70 -// bytes for the fast path.. -.thumb -.macro byte1 dst, src - uxtb \dst, \src -.endm -.macro byte2 dst, src - ubfx \dst, \src, #8, #8 -.endm -.macro byte3 dst, src - ubfx \dst, \src, #16, #8 -.endm -#else -#define RELOC_OFFSET 8 -.macro byte1 dst, src - and \dst, \src, #0xff -.endm -.macro byte2 dst, src - and \dst, \src, #0xff00 - lsr \dst, \dst, 8 -.endm -.macro byte3 dst, src - and \dst, \src, #0xff00 - lsr \dst, \dst, 16 -.endm -#endif - -// Macro for testing: logs a register value to standard error -.macro LOG reg - push {r0-r3, ip,lr} - mov r0, \reg - bl logInt(PLT) - pop {r0-r3, ip,lr} -.endm - -.macro MSGSEND receiver, sel - .fnstart - teq \receiver, 0 - beq 4f // Skip everything if the receiver is nil - push {r4-r6} // We're going to use these three as - .save {r4-r6} - // scratch registers, so save them now. - // These are callee-save, so the unwind library - // must be able to restore them, so we need CFI - // directives for them, but not for any other pushes - tst \receiver, SMALLOBJ_MASK // Sets Z if this is not a small int - - ldr r4, 7f -6: - add r4, pc - itte ne - ldrne r4, [r4] - ldrne r4, [r4] // Small Int class -> r4 if this is a small int - ldreq r4, [\receiver] // Load class to r4 if not a small int - - ldr r4, [r4, #DTABLE_OFFSET] // Dtable -> r4 - - ldr r5, [\sel] // selector->index -> r5 - - ldr r6, [r4, #SHIFT_OFFSET] // dtable->shift -> r6 - - teq r6, #8 // If this is a small dtable, jump to the small dtable handlers - beq 1f - teq r6, #0 - beq 2f - - byte3 r6, r5 // Put byte 3 of the sel id in r6 - add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset - ldr r4, [r6, #DATA_OFFSET] // Load, adding in the data offset -1: // dtable16 - byte2 r6, r5 // Put byte 2 of the sel id in r6 - add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset - ldr r4, [r6, #DATA_OFFSET] // Load, adding in the data offset -2: // dtable8 - byte1 r6, r5 // Low byte of sel id into r5 - add r6, r4, r6, lsl #2 // r6 = dtable address + dtable data offset - ldr ip, [r6, #DATA_OFFSET] // Load, adding in the data offset - - cmp ip, #0 // If the slot is nil - ittt ne - ldrne ip, [ip, #SLOT_OFFSET] // Load the method from the slot - popne {r4-r6} // Restore the saved callee-save registers - bxne ip - -5: // Slow lookup - push {r0-r4, lr} // Save anything that will be clobbered by the call - .save {r0-r4, lr} -#ifndef __SOFTFP__ - vpush {q0-q3} - .vsave {q0-q3} -#endif - - push {\receiver} // &self, _cmd in arguments - .save {\receiver} - - mov r0, sp - mov r1, \sel - - bl CDECL(slowMsgLookup)(PLT) // This is the only place where the CFI directives have to be accurate... - mov ip, r0 // IMP -> ip - - pop {r5} // restore (modified) self to r5 -#ifndef __SOFTFP__ - vpop {q0-q3} -#endif - pop {r0-r4, lr} // Load clobbered registers - mov \receiver, r5 - pop {r4-r6} // Restore the saved callee-save registers - bx ip -4: // Nil receiver - mov r0, 0 - mov r1, 0 -#ifndef __SOFTFP__ -# ifdef __ARM_NEON__ - vmov.i64 d0, #0 // Return 0 as a float / double -# else - fmdrr d0, r0, r1 -# endif -#endif - bx lr -7: - .long SmallObjectClasses(GOT_PREL)-((6b+RELOC_OFFSET)-7b) - .align 2 -.fnend -.endm - -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), %function) -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), %function) -CDECL(objc_msgSend): -CDECL(objc_msgSend_fpret): - MSGSEND r0, r1 -.globl CDECL(objc_msgSend_stret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), %function) -CDECL(objc_msgSend_stret): - MSGSEND r1, r2 - diff --git a/objc_msgSend.mips.S b/objc_msgSend.mips.S deleted file mode 100644 index 7c4fe35..0000000 --- a/objc_msgSend.mips.S +++ /dev/null @@ -1,207 +0,0 @@ -.set noreorder -# Some macros for n32 / n64 compatibility -#ifdef _ABI64 -#define LP ld -#define SP sd -#else -#warning N32 is untested, O32 is unsupported. -#define LP lw -#define SP sw -#endif - -.macro dump_and_crash reg -nop -move $a0, \reg -ld $25, %got_disp(logInt)($t8) -jalr $25 -nop -lw $zero, ($zero) -.endm - -// FIXME: CHERI needs (or, at least, strongly encourages) 32-byte aligned -// stacks. -#ifndef __mips_soft_float -#define SAVE_SIZE 136 -#else -#define SAVE_SIZE 72 -#endif - -.macro MSGSEND receiver, sel -0: - .cfi_startproc # Start emitting unwind data. We - # don't actually care about any of - # the stuff except the slow call, - # because that's the only one that - # can throw. - beq \receiver, $0, 4f # If the receiver is nil, return nil - nop - - lui $t8, %hi(%neg(%gp_rel(0b))) # Load the GOT address that we use for relocations into $t8 - daddu $t8, $t8, $t9 - daddiu $t8, $t8, %lo(%neg(%gp_rel(0b))) - - - andi $t0, \receiver, SMALLOBJ_MASK # Check if the receiver is a small object - bne $t0, $0, 6f # Get the small object class - nop - - LP $t1, (\sel) - - # By this point, we have a non-nil - # receiver that is a real pointer - LP $t0, (\receiver) # Load the class - -1: # class loaded, stored in $t0 - LP $t0, DTABLE_OFFSET($t0) # Load the dtable from the class - lw $t2, SHIFT_OFFSET($t0) # Load the shift (dtable size) - # $t0 = dtable, $t1 = sel index - daddi $t3, $t0, DATA_OFFSET # Compute the address of the start of the array - - - beq $0, $t2, 3f # If this is a small dtable, jump to the small dtable handlers - daddi $v0, $t2, -8 - - beq $0, $v0, 2f - lui $t2, 0x00ff # The mask for a big dtable won't fit in an and immediate - and $t2, $t2, $t1 # mask the selector -#ifdef _ABI64 - dsrl $t2, $t2, 13 # Right shift 16, but then left shift by pointer size -#else - srl $t2, $t2, 14 -#endif - dadd $t2, $t2, $t3 - LP $t3, ($t2) - daddi $t3, $t3, DATA_OFFSET # Compute the address of the start of the array -2: # dtable16: - andi $t2, $t1, 0xff00 # mask the selector -#ifdef _ABI64 - dsrl $t2, $t2, 5 # Right shift 8, but then left shift by pointer size -#else - srl $t2, $t2, 6 -#endif - dadd $t2, $t2, $t3 - LP $t3, ($t2) - daddi $t3, $t3, DATA_OFFSET # Compute the address of the start of the array -3: # dtable8: - andi $t2, $t1, 0xff # mask the selector -#ifdef _ABI64 - dsll $t2, $t2, 3 # Left shift by pointer size -#else - sll $t2, $t2, 2 -#endif - dadd $t2, $t2, $t3 - LP $t3, ($t2) - - - beq $0, $t3, 5f # Nil slot - invoke some kind of forwarding mechanism - nop - - LP $25, SLOT_OFFSET($t3) - jr $25 - nop -4: # returnNil: - # All of the return registers are - # callee-save, so we can - # return 0 in both in the same code: -#ifndef __mips_soft_float - dmtc1 $0, $f0 # Return 0 as a floating point value (only if we're not a soft-float target) - dmtc1 $0, $f2 -#endif - daddi $v0, $0, 0 # Return 0 as an integer - jr $ra - daddi $v1, $0, 0 - -5: # slowSend: - # Load the address of the slow lookup function now, so that we don't get - # pipeline stalls on the jump. This is more important on CHERI than proper - # MIPS implementations. - # Note: A better linker ought to be able to turn this into a single - # jump-immediate, so revisit this decision later... - LP $25, %got_disp(CDECL(slowMsgLookup))($t8) - - daddiu $sp, $sp, -SAVE_SIZE # We need to preserve all registers that may contain arguments: - - SP $a0, ($sp) - SP $a1, 8($sp) - SP $a2, 16($sp) - SP $a3, 24($sp) - SP $a4, 32($sp) - SP $a5, 40($sp) - SP $a6, 48($sp) - SP $a7, 56($sp) - SP $ra, 64($sp) -#ifndef __mips_soft_float - sdc1 $f12, 72($sp) - sdc1 $f13, 80($sp) - sdc1 $f14, 88($sp) - sdc1 $f15, 96($sp) - sdc1 $f16, 104($sp) - sdc1 $f17, 112($sp) - sdc1 $f18, 120($sp) - sdc1 $f19, 128($sp) -#endif - - # We're (potentially) modifying the self argument with the lookup. Use the - # address of the stack save slot for the address so that when we reload it - # we get the old or new version automatically. Note that we must reload it - # anyway, because argument registers are not guaranteed to be preserved - # across calls. -.ifc "\receiver", "$a0" - daddiu $a0, $sp, 0 # replace self with &self in $a0 -.else - daddiu $a0, $sp, 8 # replace sret pointer with &self in $a0 - daddiu $a1, $a2, 0 # replace self with _cmd in $a1 -.endif - - .cfi_def_cfa_offset SAVE_SIZE - .cfi_offset 31, (64 - SAVE_SIZE) - jalr $25 # Call the slow lookup function - nop - - move $25, $v0 # Move the return value to $25 for use with the call - - LP $a0, ($sp) # Restore all of the arguments. Note - LP $a1, 8($sp) # that the receiver may have been - LP $a2, 16($sp) # modified during the call - LP $a3, 24($sp) - LP $a4, 32($sp) - LP $a5, 40($sp) - LP $a6, 48($sp) - LP $a7, 56($sp) - LP $ra, 64($sp) -#ifndef __mips_soft_float - ldc1 $f12, 72($sp) - ldc1 $f13, 80($sp) - ldc1 $f14, 88($sp) - ldc1 $f15, 96($sp) - ldc1 $f16, 104($sp) - ldc1 $f17, 112($sp) - ldc1 $f18, 120($sp) - ldc1 $f19, 128($sp) -#endif - jr $25 - daddiu $sp, $sp, SAVE_SIZE -6: # smallObject: -#if _ABI64 - dsll $t0, $t0, 3 # Convert tag to pointer offset - LP $t2, %got_disp(CDECL(SmallObjectClasses))($t8) # Load small object classes array address - daddu $t0, $t0, $t2 # Add the base address to the offset - b 1b # Return to the normal path - LP $t0, ($t0) # Load the class (in delay slot) -#else - b 1b - LP $t0, %got_disp(CDECL(SmallIntClass))($t8) -#endif - .cfi_endproc -.endm -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), @function) -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), @function) -CDECL(objc_msgSend_fpret): -CDECL(objc_msgSend): - MSGSEND $a0, $a1 -.globl CDECL(objc_msgSend_stret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function) -CDECL(objc_msgSend_stret): - MSGSEND $a1, $a2 diff --git a/objc_msgSend.riscv64.S b/objc_msgSend.riscv64.S deleted file mode 100644 index c99a4dd..0000000 --- a/objc_msgSend.riscv64.S +++ /dev/null @@ -1,141 +0,0 @@ -#define ARGUMENT_SPILL_SIZE (10*8 + 8*8) - -.macro MSGSEND receiver, sel - .cfi_startproc - beqz \receiver, 3f // Skip everything if receiver is nil - - andi t0, \receiver, SMALLOBJ_MASK - bnez t0, 5f - - ld t0, 0(\receiver) // Load class into t0 -0: - ld t0, DTABLE_OFFSET(t0) // dtable -> t0 - ld t1, 0(\sel) // selector->index -> t1 - ld t2, SHIFT_OFFSET(t0) // dtable->shift -> t2 - - li t3, 8 - beq t2, t3, 1f - beqz t2, 2f - - srli t2, t1, 16-3 // Extract byte 3 of sel index and multiply by 2^3 - and t2, t2, 0x7F8 // Mask target byte - // Example: ((0xCAFEBA >> 13) & 0x7f8) == (0xCA << 3) - add t2, t0, t2 // t2 = dtable address + offset - ld t0, DATA_OFFSET(t2) // Load, adding in the data offset -1: - srli t2, t1, 8-3 // Extract byte 2 of sel index and multiply by 2^3 - and t2, t2, 0x7F8 // Mask target byte - add t2, t0, t2 // t2 = dtable address + offset - ld t0, DATA_OFFSET(t2) // Load, adding in the data offset -2: - slli t2, t1, 3 // Multiply by 2^3 - and t2, t2, 0x7F8 // Mask target byte - add t2, t0, t2 // t2 = dtable address + offset - ld t0, DATA_OFFSET(t2) // Load, adding in the data offset - // Slot pointer is now in t0 - - beqz t0, 4f // If the slot is nil, go to the C path - - ld t0, SLOT_OFFSET(t0) // Load the method from the slot - jalr zero, t0, 0 // Tail-call the method - -3: - li \receiver, 0 - li \sel, 0 - fmv.d.x fa0, zero - fmv.d.x fa1, zero - jalr zero, ra, 0 - -4: - add sp, sp, -(ARGUMENT_SPILL_SIZE) - - // Spill function arguments - sd a0, 0(sp) - sd a1, 8(sp) - sd a2, 16(sp) - sd a3, 24(sp) - sd a4, 32(sp) - sd a5, 40(sp) - sd a6, 48(sp) - sd a7, 56(sp) - - // Spill FP arguments - fsd fa0, 64(sp) - fsd fa1, 72(sp) - fsd fa2, 80(sp) - fsd fa3, 88(sp) - fsd fa4, 96(sp) - fsd fa5, 104(sp) - fsd fa6, 112(sp) - fsd fa7, 120(sp) - - sd fp, 128(sp) - sd ra, 136(sp) - - add fp, sp, 128 - add sp, sp, -16 - - sd \receiver, 0(sp) // it is convenient if \receiver is spilled at sp - - .cfi_def_cfa fp, 16 - .cfi_offset fp, -16 - .cfi_offset ra, -8 - - add a0, sp, zero // &self in first argument - call CDECL(slowMsgLookup) - - add t0, a0, zero // IMP -> t0 - - ld a0, 16(sp) - ld a1, 24(sp) - ld a2, 32(sp) - ld a3, 40(sp) - ld a4, 48(sp) - ld a5, 56(sp) - ld a6, 64(sp) - ld a7, 72(sp) - - fld fa0, 80(sp) - fld fa1, 88(sp) - fld fa2, 96(sp) - fld fa3, 104(sp) - fld fa4, 112(sp) - fld fa5, 120(sp) - fld fa6, 128(sp) - fld fa7, 136(sp) - - ld fp, 144(sp) - ld ra, 152(sp) - - ld \receiver, 0(sp) - - add sp, sp, ARGUMENT_SPILL_SIZE - add sp, sp, 16 - - jalr zero, t0, 0 // Tail-call the method - -5: - // Load address of SmallObjectClasses - auipc t1, %pcrel_hi(CDECL(SmallObjectClasses)) - addi t1, t1, %pcrel_lo(5b) - - // Calculate array offset (INDEX * 2^3) - slli t0, t0, 3 - add t0, t1, t0 - - ld t0, 0(t0) - - j 0b - .cfi_endproc -.endm - -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), %function) -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), %function) -.globl CDECL(objc_msgSend_stret) -CDECL(objc_msgSend): -CDECL(objc_msgSend_fpret): - MSGSEND a0, a1 -CDECL(objc_msgSend_stret): - MSGSEND a1, a2 // Pointer to stack frame in a0 diff --git a/objc_msgSend.x86-32.S b/objc_msgSend.x86-32.S deleted file mode 100644 index 99b4f4e..0000000 --- a/objc_msgSend.x86-32.S +++ /dev/null @@ -1,132 +0,0 @@ -.macro MSGSEND receiver, sel, fpret - .cfi_startproc - movl \receiver(%esp), %eax - test %eax, %eax # If the receiver is nil - jz 4f # return nil - test $SMALLOBJ_MASK, %eax # Check if the receiver is a small object - jnz 6f # Get the small object class - - mov (%eax), %eax # Load the class -1: # classLoaded - movl \sel(%esp), %ecx - mov DTABLE_OFFSET(%eax), %eax # Load the dtable from the class - - mov (%ecx), %ecx # Load the selector index - - # Register use at this point: - # %eax: dtable - # %ecx: Selector index - # %edx: selector index fragment - - mov SHIFT_OFFSET(%eax), %edx # Load the shift (dtable size) - cmpl $8, %edx # If this is a small dtable, jump to the small dtable handlers - je 2f - cmpl $0, %edx - je 3f - - mov %ecx, %edx - shrl $16, %edx - movl DATA_OFFSET(%eax, %edx, 4), %eax -2: # dtable16: - movzbl %ch, %edx - movl DATA_OFFSET(%eax, %edx, 4), %eax -3: # dtable8: - movzbl %cl, %edx - movl DATA_OFFSET(%eax, %edx, 4), %eax - - test %eax, %eax - jz 5f # Nil slot - invoke some kind of forwarding mechanism - mov SLOT_OFFSET(%eax), %ecx -#ifdef _MSC_VER - call *CDECL(__guard_check_icall_fptr) -#endif - jmp *%ecx -4: # returnNil: -.if \fpret - fldz -.else - xor %eax, %eax # return 0 (int) - xor %edx, %edx # Return 64-bit zero (%edx is - # caller-save, so it's safe to do this in the general case. -.endif - ret -5: # slowSend: - mov \sel(%esp), %ecx - lea \receiver(%esp), %eax - - push %ecx # Unused, stack alignment - push %ecx # _cmd - push %eax # &self - .cfi_def_cfa_offset 16 - call CDECL(slowMsgLookup)@PLT - add $12, %esp # restore the stack - - -#ifdef _MSC_VER - mov %eax, %ecx - call *CDECL(__guard_check_icall_fptr) - jmp *%ecx -#else - jmp *%eax -#endif -6: # smallObject: - push %ebx # Save old %ebx - calll 7f -7: - popl %ebx; -8: -#if __ELF__ - # ELF can support GOT-relative addressing; - # PE/COFF and Mach-O need a text relocation. - addl $_GLOBAL_OFFSET_TABLE_+(8b-7b), %ebx - leal SmallObjectClasses@GOTOFF(%ebx), %eax -#else - leal CDECL(SmallObjectClasses), %eax -#endif - mov (%eax), %eax - popl %ebx - jmp 1b - .cfi_endproc -.endm - -#ifdef _WIN32 -.text -.def @feat.00; -.scl 3; -.type 0; -.endef -.globl @feat.00 -@feat.00 = 1 -.def _objc_msgSend; -.scl 2; -.type 32; -.endef -.def _objc_msgSend_fpret; -.scl 2; -.type 32; -.endef -.def _objc_msgSend_stret; -.scl 2; -.type 32; -.endef -#endif - -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), @function) -CDECL(objc_msgSend_fpret): - MSGSEND 4, 8, 1 -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), @function) -CDECL(objc_msgSend): - MSGSEND 4, 8, 0 -.globl CDECL(objc_msgSend_stret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function) -CDECL(objc_msgSend_stret): - MSGSEND 8, 12, 0 - -#ifdef _WIN32 - .section .drectve,"yn" - EXPORT_SYMBOL(objc_msgSend) - EXPORT_SYMBOL(objc_msgSend_stret) - EXPORT_SYMBOL(objc_msgSend_fpret) -#endif diff --git a/objc_msgSend.x86-64.S b/objc_msgSend.x86-64.S deleted file mode 100644 index 2d15314..0000000 --- a/objc_msgSend.x86-64.S +++ /dev/null @@ -1,315 +0,0 @@ - -#ifdef _WIN64 -# define START_PROC(x) .seh_proc x -# define END_PROC(x) .seh_endproc -# define FRAME_OFFSET(x) .seh_stackalloc x -# define FIRST_ARGUMENT_STR "%rcx" -# define FIRST_ARGUMENT %rcx -# define SECOND_ARGUMENT %rdx -# define THIRD_ARGUMENT %r8 -#else -# define START_PROC(x) .cfi_startproc -# define END_PROC(x) .cfi_endproc -# define FRAME_OFFSET(x) .cfi_adjust_cfa_offset x -# define FIRST_ARGUMENT_STR "%rdi" -# define FIRST_ARGUMENT %rdi -# define SECOND_ARGUMENT %rsi -# define THIRD_ARGUMENT %rdx -#endif - -.macro MSGSEND fnname receiver, sel - START_PROC(\fnname) # Start emitting unwind data. We - # don't actually care about any of - # the stuff except the slow call, - # because that's the only one that - # can throw. - - test \receiver, \receiver # If the receiver is nil - jz 4f # return nil - movq $SMALLOBJ_MASK, %r10 # Load the small object mask - test \receiver, %r10 # Check if the receiver is a small object - jnz 6f # Get the small object class - - mov (\receiver), %r10 # Load the dtable from the class -1: # classLoaded - mov DTABLE_OFFSET(%r10), %r10 # Load the dtable from the class into r10 - mov %rax, -8(%rsp) # %rax contains information for variadic calls - mov %rbx, -16(%rsp) # On the fast path, spill into the red zone - mov (\sel), %eax # Load the selector index into %eax - mov SHIFT_OFFSET(%r10), %r11d # Load the shift (dtable size) into r11 - cmpl $8, %r11d # If this is a small dtable, jump to the small dtable handlers - je 2f - cmpl $0, %r11d - je 3f - - movl %eax, %r11d - shrl $16, %r11d - movq DATA_OFFSET(%r10, %r11, 8), %r10 -2: # dtable16: - movzbl %ah, %ebx - movq DATA_OFFSET(%r10, %rbx, 8), %r10 -3: # dtable8: - movzbl %al, %ebx - mov -8(%rsp), %rax - movq DATA_OFFSET(%r10, %rbx, 8), %r10 - mov -16(%rsp), %rbx - test %r10, %r10 - jz 5f # Nil slot - invoke some kind of forwarding mechanism - mov SLOT_OFFSET(%r10), %r10 - -7: -#ifdef WITH_TRACING - - push %r12 - push %r13 - push %r10 - - mov (\sel), %r11 # Load the selector index - lea tracing_dtable(%rip), %r10 - mov (%r10), %r10 - - mov SHIFT_OFFSET(%r10), %r13 # Load the shift (dtable size) - mov DATA_OFFSET(%r10), %r12 # load the address of the start of the array - pop %r10 - cmpl $8, %r13d # If this is a small dtable, jump to the small dtable handlers - je 10f - cmpl $0, %r13d - je 11f - - mov %r11, %r13 - and $0xff0000, %r13 - shrl $13, %r13d # Right shift 16, but then left shift by 3 *sizeof(void*) - add %r13, %r12 - mov (%r12), %r12 - mov DATA_OFFSET(%r12), %r12 -10: # dtable16: - mov %r11, %r13 - and $0xff00, %r13 - shrl $5, %r13d - add %r13, %r12 - mov (%r12), %r12 - mov DATA_OFFSET(%r12), %r12 -11: # dtable8: - mov %r11, %r13 - and $0xff, %r13 - shll $3, %r13d - add %r13, %r12 - mov (%r12), %r11 - pop %r13 - pop %r12 - test %r11, %r11 - jz 12f - - push %rax # We need to preserve all registers that may contain arguments: - push %rdi - push %rsi - push %rdx - push %rcx - push %r8 - push %r9 - push %r10 - push %r11 - mov \receiver, %rdi # Arg 0 is receiver - mov \sel, %rsi # Arg 1 is selector - mov %r10, %rdx # Arg 2 is IMP - mov $0, %rcx # Arg 3 is entry / exit (0/1) - mov $0, %r8 # Arg 4 is return value (0 on entry) - - call *%r11 # Call the tracing function - cmpq $0, %rax - jz 13f # If it returns 0, don't call the end-tracing function. - cmpq $1, %rax # If it returns 1, do call the tracing function - jne 14f # Any other value is an interposition - # function to call instead of the method - - call pushTraceReturnStack # rax now contains a thread-local buffer for storing returns - - pop %r11 # Restore all of the argument registers - pop %r10 # except rax, which we'll need before the call - pop %r9 - pop %r8 - pop %rcx - pop %rdx - pop %rsi - pop %rdi - - mov \receiver, (%rax) # Store the receiver in TLS - mov \sel, 8(%rax) # Store the selector in TLS - mov %r10, 16(%rax) # Store the method in TLS - mov %r11, 24(%rax) # Store the tracing function in TLS - mov 8(%rsp), %r11 # r11 now contains the return address - mov %r11, 32(%rax) # Store the method-return address in TLS - - pop %rax - pop %r11 # r11 now contains the return address, but we don't care - - call *%r10 # Call the IMP. The stack should now be in the same state - # that it was on entry into this function - - push %rax # Now we are free to clobber argument - push %rdx # registers, but we must preserve return registers... - - call popTraceReturnStack # rax now contains a thread-local buffer for storing returns - - push %rax # save the return value, because we'll need it after the tracing function call - mov (%rax), %rdi # Load the receiver into arg 0 - mov 8(%rax), %rsi # Load the selector into arg 1 - mov 16(%rax), %rdx # Load the IMP into arg 3 - mov $1, %rcx # Arg 4 is 1 (tracing on exit) - mov %rax, %r8 # Arg 5 is the return result - - mov 24(%rax), %r11 # Reload the address of the tracing function - - call *%r11 # Call the tracing function - pop %rax # Reload the real return address - mov 32(%rax), %r11 - pop %rdx # Reload saved values - pop %rax - jmp *%r11 # Simulate a return by jumping to the cached return address - -13: # Skip tracing on exit and just tail-call the method - pop %r11 - pop %r10 - pop %r9 - pop %r8 - pop %rcx - pop %rdx - pop %rsi - pop %rdi - pop %rax - jmp *%r10 - -14: - mov %rax, %r10 - pop %r9 - pop %r9 - pop %r9 - pop %r8 - pop %rcx - pop %rdx - pop %rsi - pop %rdi - pop %rax - -12: -#endif // WITH_TRACING -#ifdef _MSC_VER - mov %r10, %rax - jmp *__guard_dispatch_icall_fptr(%rip) -#else - jmp *%r10 -#endif -4: # returnNil: - # Both of the return registers are - # callee-save on x86-64, so we can - # return 0 in both in the same code: - xor %rax, %rax # Return 0 as an integer - pxor %xmm0, %xmm0 # Return 0 as a floating point value - ret -5: # slowSend: - push %rax # We need to preserve all registers that may contain arguments: - push %rbx - push %rcx - push %r8 - push %r9 - - sub $0x98, %rsp - movups %xmm0, 0x80(%rsp) - movups %xmm1, 0x70(%rsp) - movups %xmm2, 0x60(%rsp) - movups %xmm3, 0x50(%rsp) - movups %xmm4, 0x40(%rsp) - movups %xmm5, 0x30(%rsp) - movups %xmm6, 0x20(%rsp) - movups %xmm7, 0x10(%rsp) - -#rdi rsi rdx - # We're (potentially) modifying the self argument with the lookup, so we don't want to be -.ifc "\receiver", FIRST_ARGUMENT_STR - push FIRST_ARGUMENT - mov %rsp, FIRST_ARGUMENT - push SECOND_ARGUMENT # Save _cmd (not preserved across calls) - push THIRD_ARGUMENT -.else - push FIRST_ARGUMENT # Save the sret pointer - push SECOND_ARGUMENT # Save self where it can be modified - mov %rsp, FIRST_ARGUMENT - push THIRD_ARGUMENT - mov THIRD_ARGUMENT, SECOND_ARGUMENT # move _cmd to where the callee expects it to be -.endif - - FRAME_OFFSET(0xD8) - call CDECL(slowMsgLookup) # Call the slow lookup function - mov %rax, %r10 # Load the returned IMP - - pop THIRD_ARGUMENT - pop SECOND_ARGUMENT - pop FIRST_ARGUMENT - - movups 0x80(%rsp), %xmm0 - movups 0x70(%rsp), %xmm1 - movups 0x60(%rsp), %xmm2 - movups 0x50(%rsp), %xmm3 - movups 0x40(%rsp), %xmm4 - movups 0x30(%rsp), %xmm5 - movups 0x20(%rsp), %xmm6 - movups 0x10(%rsp), %xmm7 - add $0x98, %rsp - - pop %r9 - pop %r8 - pop %rcx - pop %rbx - pop %rax - jmp 7b -6: # smallObject: - and \receiver, %r10 # Find the small int type - lea CDECL(SmallObjectClasses)(%rip), %r11 - mov (%r11, %r10, 8), %r10 - jmp 1b - END_PROC(\fnname) -.endm -#ifdef _WIN64 -.text -.def objc_msgSend; -.scl 2; -.type 32; -.endef -.def objc_msgSend_fpret; -.scl 2; -.type 32; -.endef -.def objc_msgSend_stret; -.scl 2; -.type 32; -.endef -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), @function) -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), @function) -CDECL(objc_msgSend_fpret): -CDECL(objc_msgSend): - MSGSEND objc_msgSend, %rcx, %rdx -.globl CDECL(objc_msgSend_stret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function) -CDECL(objc_msgSend_stret): - MSGSEND objc_msgSend_stret, %rdx, %r8 -.section .drectve,"yn" -EXPORT_SYMBOL(objc_msgSend) - -EXPORT_SYMBOL(objc_msgSend_fpret) - -EXPORT_SYMBOL(objc_msgSend_stret) -#else -.globl CDECL(objc_msgSend) -TYPE_DIRECTIVE(CDECL(objc_msgSend), @function) -.globl CDECL(objc_msgSend_fpret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), @function) -CDECL(objc_msgSend_fpret): -CDECL(objc_msgSend): - MSGSEND objc_msgSend, %rdi, %rsi -.globl CDECL(objc_msgSend_stret) -TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), @function) -CDECL(objc_msgSend_stret): - MSGSEND objc_msgSend_stret, %rsi, %rdx -#endif diff --git a/objcxx_eh.cc b/objcxx_eh.cc deleted file mode 100644 index 8ef5baf..0000000 --- a/objcxx_eh.cc +++ /dev/null @@ -1,311 +0,0 @@ -#include -#include -#include -#include "dwarf_eh.h" -#include "objcxx_eh_private.h" -#include "objcxx_eh.h" -#include "objc/objc-arc.h" - -/** - * Helper function that has a custom personality function. - * This calls `cxx_throw` and has a destructor that must be run. We intercept - * the personality function calls and inspect the in-flight C++ exception. - */ -int eh_trampoline(); - -uint64_t cxx_exception_class; - -using namespace __cxxabiv1; - -namespace -{ -/** - * Helper needed by the unwind helper headers. - */ -inline _Unwind_Reason_Code continueUnwinding(struct _Unwind_Exception *ex, - struct _Unwind_Context *context) -{ -#if defined(__arm__) && !defined(__ARM_DWARF_EH__) - if (__gnu_unwind_frame(ex, context) != _URC_OK) { return _URC_FAILURE; } -#endif - return _URC_CONTINUE_UNWIND; -} - - -/** - * Flag indicating that we've already inspected a C++ exception and found all - * of the offsets. - */ -std::atomic done_setup; -/** - * The offset of the C++ type_info object in a thrown exception from the unwind - * header in a `__cxa_exception`. - */ -std::atomic type_info_offset; -/** - * The size of the `_Unwind_Exception` (including padding) in a - * `__cxa_exception`. - */ -std::atomic exception_struct_size; - - -/** - * Exception cleanup function for C++ exceptions that wrap Objective-C - * exceptions. - */ -void exception_cleanup(_Unwind_Reason_Code reason, - struct _Unwind_Exception *ex) -{ - // __cxa_exception takes a pointer to the end of the __cxa_exception - // structure, and so we find that by adding the size of the generic - // exception structure + padding to the pointer to the generic exception - // structure field of the enclosing structure. - auto *cxxEx = pointer_add<__cxa_exception>(ex, exception_struct_size); - __cxa_free_exception(cxxEx); -} - -} - -using namespace std; - - -static BOOL isKindOfClass(Class thrown, Class type) -{ - do - { - if (thrown == type) - { - return YES; - } - thrown = class_getSuperclass(thrown); - } while (Nil != thrown); - - return NO; -} - - - - -namespace gnustep -{ - namespace libobjc - { - __objc_type_info::__objc_type_info(const char *name) : type_info(name) {} - - bool __objc_type_info::__is_pointer_p() const { return true; } - - bool __objc_type_info::__is_function_p() const { return false; } - - bool __objc_type_info::__do_catch(const type_info *thrown_type, - void **thrown_object, - unsigned) const - { - assert(0); - return false; - }; - - bool __objc_type_info::__do_upcast( - const __class_type_info *target, - void **thrown_object) const - { - return false; - }; - - - /** - * The `id` type is mangled to `@id`, which is not a valid mangling - * of anything else. - */ - __objc_id_type_info::__objc_id_type_info() : __objc_type_info("@id") {}; - } - - static inline id dereference_thrown_object_pointer(void** obj) { - /* libc++-abi does not have __is_pointer_p and won't do the double dereference - * required to get the object pointer. We need to do it ourselves if we have - * caught an exception with libc++'s exception class. */ -#ifndef __MINGW32__ - if (cxx_exception_class == llvm_cxx_exception_class) { - return **(id**)obj; - } - return *(id*)obj; -#else -#ifdef _LIBCPP_VERSION - return **(id**)obj; -#else - return *(id*)obj; -#endif // _LIBCPP_VERSION -#endif // __MINGW32__ - } -}; - - -static bool AppleCompatibleMode = true; -extern "C" int objc_set_apple_compatible_objcxx_exceptions(int newValue) -{ - bool old = AppleCompatibleMode; - AppleCompatibleMode = newValue; - return old; -} - -gnustep::libobjc::__objc_class_type_info::~__objc_class_type_info() {} -gnustep::libobjc::__objc_id_type_info::~__objc_id_type_info() {} -bool gnustep::libobjc::__objc_class_type_info::__do_catch(const type_info *thrownType, - void **obj, - unsigned outer) const -{ - id thrown = nullptr; - bool found = false; - // Id throw matches any ObjC catch. This may be a silly idea! - if (dynamic_cast(thrownType) - || (AppleCompatibleMode && - dynamic_cast(thrownType))) - { - thrown = dereference_thrown_object_pointer(obj); - // nil only matches id catch handlers in Apple-compatible mode, or when thrown as an id - if (0 == thrown) - { - return false; - } - // Check whether the real thrown object matches the catch type. - found = isKindOfClass(object_getClass(thrown), - (Class)objc_getClass(name())); - } - else if (dynamic_cast(thrownType)) - { - thrown = dereference_thrown_object_pointer(obj); - found = isKindOfClass((Class)objc_getClass(thrownType->name()), - (Class)objc_getClass(name())); - } - if (found) - { - *obj = (void*)thrown; - } - - return found; -}; - -bool gnustep::libobjc::__objc_id_type_info::__do_catch(const type_info *thrownType, - void **obj, - unsigned outer) const -{ - // Id catch matches any ObjC throw - if (dynamic_cast(thrownType)) - { - *obj = dereference_thrown_object_pointer(obj); - DEBUG_LOG("gnustep::libobjc::__objc_id_type_info::__do_catch caught 0x%x\n", *obj); - return true; - } - if (dynamic_cast(thrownType)) - { - *obj = dereference_thrown_object_pointer(obj); - DEBUG_LOG("gnustep::libobjc::__objc_id_type_info::__do_catch caught 0x%x\n", *obj); - return true; - } - DEBUG_LOG("gnustep::libobjc::__objc_id_type_info::__do_catch returning false\n"); - return false; -}; - -/** - * Public interface to the Objective-C++ exception mechanism - */ -extern "C" -{ -/** - * The public symbol that the compiler uses to indicate the Objective-C id type. - */ -OBJC_PUBLIC gnustep::libobjc::__objc_id_type_info __objc_id_type_info; - -struct _Unwind_Exception *objc_init_cxx_exception(id obj) -{ - id *newEx = static_cast(__cxa_allocate_exception(sizeof(id))); - *newEx = obj; - _Unwind_Exception *ex = pointer_add<_Unwind_Exception>(newEx, -exception_struct_size); - *pointer_add(ex, type_info_offset) = &__objc_id_type_info; - ex->exception_class = cxx_exception_class; - ex->exception_cleanup = exception_cleanup; - __cxa_get_globals()->uncaughtExceptions++; - return ex; -} - -void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid) -{ - ptrdiff_t type_offset = type_info_offset; - if (type_offset == 0) - { - *isValid = 0; - return nullptr; - } - - const std::type_info *thrownType = - *pointer_add(thrown_exception, type_offset); - - if (!dynamic_cast(thrownType) && - !dynamic_cast(thrownType)) - { - *isValid = 0; - return 0; - } - *isValid = 1; - return *pointer_add(thrown_exception, exception_struct_size); -} - -} // extern "C" - - -MagicValueHolder::MagicValueHolder() { magic_value = magic; } - -/** - * Function that simply throws an instance of `MagicValueHolder`. - */ -PRIVATE void cxx_throw() -{ - MagicValueHolder x; - throw x; -} - -/** - * Personality function that wraps the C++ personality and inspects the C++ - * exception structure on the way past. This should be used only for the - * `eh_trampoline` function. - */ -extern "C" -PRIVATE -BEGIN_PERSONALITY_FUNCTION(test_eh_personality) - // Don't bother with a mutex here. It doesn't matter if two threads set - // these values at the same time. - if (!done_setup) - { - uint64_t cls = __builtin_bswap64(exceptionClass); - type_info_offset = find_backwards(exceptionObject, &typeid(MagicValueHolder)); - exception_struct_size = find_forwards(exceptionObject, MagicValueHolder::magic); - cxx_exception_class = exceptionClass; - done_setup = true; - } - return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); -} - -/** - * Probe the C++ exception handling implementation. This throws a C++ - * exception through a function that uses `test_eh_personality` as its - * personality function, allowing us to inspect a C++ exception that is in a - * known state. - */ -#ifndef __MINGW32__ -extern "C" void test_cxx_eh_implementation() -{ - if (done_setup) - { - return; - } - bool caught = false; - try - { - eh_trampoline(); - } - catch(MagicValueHolder) - { - caught = true; - } - assert(caught); -} -#endif diff --git a/objcxx_eh.h b/objcxx_eh.h deleted file mode 100644 index 5ac70a3..0000000 --- a/objcxx_eh.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif -/** - * Allocates a C++ exception. This function is part of the Itanium C++ ABI and - * is provided externally. - */ -/* - * Note: Recent versions of libsupc++ already provide a prototype for - * __cxa__allocate_exception(). Since the libsupc++ version is defined with - * _GLIBCXX_NOTHROW, clang gives a type mismatch error. - */ -#ifndef __cplusplus -#undef CXA_ALLOCATE_EXCEPTION_SPECIFIER -#define CXA_ALLOCATE_EXCEPTION_SPECIFIER -#endif -void *__cxa_allocate_exception(size_t thrown_size) CXA_ALLOCATE_EXCEPTION_SPECIFIER; - -/** - * Initialises an exception object returned by __cxa_allocate_exception() for - * storing an Objective-C object. The return value is the location of the - * _Unwind_Exception structure within this structure, and should be passed to - * the C++ personality function. - */ -struct _Unwind_Exception *objc_init_cxx_exception(id thrown_exception); -/** - * The GNU C++ exception personality function, provided by libsupc++ (GNU) or - * libcxxrt (PathScale). - */ -__attribute__((weak)) DECLARE_PERSONALITY_FUNCTION(__gxx_personality_v0); -/** - * Frees an exception object allocated by __cxa_allocate_exception(). Part of - * the Itanium C++ ABI. - */ -void __cxa_free_exception(void *thrown_exception); -/** - * Tests whether a C++ exception contains an Objective-C object, and returns if - * if it does. The second argument is a pointer to a boolean value indicating - * whether this is a valid object. - */ -void *objc_object_for_cxx_exception(void *thrown_exception, int *isValid); - -/** - * Prints the type info associated with an exception. Used only when - * debugging, not compiled in the normal build. - */ -void print_type_info(void *thrown_exception); - -/** - * The exception class that we've detected that C++ runtime library uses. - */ -extern uint64_t cxx_exception_class; - -/** - * The exception class that libsupc++ and libcxxrt use. - */ -const uint64_t gnu_cxx_exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0'); - -/** - * The exception class that libc++abi uses. - */ -const uint64_t llvm_cxx_exception_class = EXCEPTION_CLASS('C','L','N','G','C','+','+','\0'); - -#ifdef __cplusplus -} -#endif diff --git a/objcxx_eh_mingw.cc b/objcxx_eh_mingw.cc deleted file mode 100644 index a42c7ea..0000000 --- a/objcxx_eh_mingw.cc +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include -#include -#include -#include "dwarf_eh.h" -#include "objcxx_eh_private.h" -#include "objcxx_eh.h" -#include "objc/runtime.h" -#include "objc/objc-arc.h" -#include "objc/objc-exception.h" -#include "objc/hooks.h" - -namespace __cxxabiv1 -{ - struct __cxa_refcounted_exception - { - int referenceCount; - }; -} - -using namespace __cxxabiv1; - -extern "C" __cxa_refcounted_exception* __cxa_init_primary_exception(void *obj, std::type_info *tinfo, void (*dest) (void *)); - -static void eh_cleanup(void *exception) -{ - DEBUG_LOG("eh_cleanup: Releasing 0x%x\n", *(id*)exception); - objc_release(*(id*)exception); -} - -/** - * Flag indicating that we've already inspected a C++ exception and found all - * of the offsets. - */ -std::atomic done_setup; - -/** - * The size of the `_Unwind_Exception` (including padding) in a - * `__cxa_exception`. - */ -std::atomic exception_struct_size; - -extern "C" -OBJC_PUBLIC -void objc_exception_throw(id object) -{ - // Don't bother with a mutex here. It doesn't matter if two threads set - // these values at the same time. - if (!done_setup) - { - DEBUG_LOG("objc_exception_throw: Doing initial setup\n"); - MagicValueHolder *magicExc = (MagicValueHolder *)__cxa_allocate_exception(sizeof(MagicValueHolder)); - MagicValueHolder x; - *magicExc = x; - - __cxa_refcounted_exception *header = - __cxa_init_primary_exception(magicExc, & __objc_id_type_info, NULL); - exception_struct_size = find_forwards(header, MagicValueHolder::magic); - __cxa_free_exception(magicExc); - - DEBUG_LOG("objc_exception_throw: exception_struct_size: 0x%x\n", unsigned(exception_struct_size)); - - done_setup = true; - } - - id *exc = (id *)__cxa_allocate_exception(sizeof(id)); - *exc = object; - objc_retain(object); - DEBUG_LOG("objc_exception_throw: Throwing 0x%x\n", *exc); - - __cxa_eh_globals *globals = __cxa_get_globals (); - globals->uncaughtExceptions += 1; - __cxa_refcounted_exception *header = - __cxa_init_primary_exception(exc, & __objc_id_type_info, eh_cleanup); - header->referenceCount = 1; - - _Unwind_Exception *unwindHeader = pointer_add<_Unwind_Exception>(header, exception_struct_size - sizeof(_Unwind_Exception)); - _Unwind_Reason_Code err = _Unwind_RaiseException (unwindHeader); - - if (_URC_END_OF_STACK == err && 0 != _objc_unexpected_exception) - { - DEBUG_LOG("Invoking _objc_unexpected_exception\n"); - _objc_unexpected_exception(object); - } - DEBUG_LOG("Throw returned %d\n",(int) err); - abort(); -} - -OBJC_PUBLIC extern objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler) -{ - return __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST); -} - -extern "C" void* __cxa_begin_catch(void *object); - -extern "C" -OBJC_PUBLIC -void* objc_begin_catch(void* object) -{ - return __cxa_begin_catch(object); -} - -extern "C" void __cxa_end_catch(); - -extern "C" -OBJC_PUBLIC -void objc_end_catch() -{ - __cxa_end_catch(); -} - -extern "C" void __cxa_rethrow(); - -extern "C" -OBJC_PUBLIC -void objc_exception_rethrow() -{ - __cxa_rethrow(); -} - -extern "C" EXCEPTION_DISPOSITION __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, - void *this_frame, - PCONTEXT ms_orig_context, - PDISPATCHER_CONTEXT ms_disp); - -extern "C" -OBJC_PUBLIC -EXCEPTION_DISPOSITION __gnu_objc_personality_seh0(PEXCEPTION_RECORD ms_exc, - void *this_frame, - PCONTEXT ms_orig_context, - PDISPATCHER_CONTEXT ms_disp) -{ - return __gxx_personality_seh0(ms_exc, this_frame, ms_orig_context, ms_disp); -} diff --git a/objcxx_eh_private.h b/objcxx_eh_private.h deleted file mode 100644 index 1642e9a..0000000 --- a/objcxx_eh_private.h +++ /dev/null @@ -1,241 +0,0 @@ -typedef struct objc_object* id; - -#include "objc/runtime.h" -#include "visibility.h" - -#ifndef DEBUG_EXCEPTIONS -#define DEBUG_LOG(...) -#else -#define DEBUG_LOG(str, ...) fprintf(stderr, str, ## __VA_ARGS__) -#endif - - -/** - * Our own definitions of C++ ABI functions and types. These are provided - * because this file must not include cxxabi.h. We need to handle subtly - * different variations of the ABI and including one specific implementation - * would make that very difficult. - */ -namespace __cxxabiv1 -{ - /** - * Type info for classes. Forward declared because the GNU ABI provides a - * method on all type_info objects that the dynamic the dynamic cast header - * needs. - */ - struct __class_type_info; - /** - * The C++ in-flight exception object. We will derive the offset of fields - * in this, so we do not ever actually see a concrete definition of it. - */ - struct __cxa_exception; - /** - * The public ABI structure for current exception state. - */ - struct __cxa_eh_globals - { - /** - * The current exception that has been caught. - */ - __cxa_exception *caughtExceptions; - /** - * The number of uncaught exceptions still in flight. - */ - unsigned int uncaughtExceptions; - }; - /** - * Retrieve the above structure. - */ - extern "C" __cxa_eh_globals *__cxa_get_globals(); -} - -namespace std -{ - struct type_info; -} - -// Define some C++ ABI types here, rather than including them. This prevents -// conflicts with the libstdc++ headers, which expose only a subset of the -// type_info class (the part required for standards compliance, not the -// implementation details). - -typedef void (*unexpected_handler)(); -typedef void (*terminate_handler)(); - -namespace std -{ - /** - * std::type_info, containing the minimum requirements for the ABI. - * Public headers on some implementations also expose some implementation - * details. The layout of our subclasses must respect the layout of the - * C++ runtime library, but also needs to be portable across multiple - * implementations and so should not depend on internal symbols from those - * libraries. - */ - class type_info - { - public: - virtual ~type_info(); - bool operator==(const type_info &) const; - bool operator!=(const type_info &) const; - bool before(const type_info &) const; - type_info(); - private: - type_info(const type_info& rhs); - type_info& operator= (const type_info& rhs); - const char *__type_name; - protected: - type_info(const char *name): __type_name(name) { } - public: - const char* name() const { return __type_name; } - }; -} - -extern "C" void __cxa_throw(void*, std::type_info*, void(*)(void*)); -extern "C" void __cxa_rethrow(); - -/** - * Helper function to find a particular value scanning backwards in a - * structure. - */ -template -ptrdiff_t find_backwards(void *addr, T val) -{ - T *ptr = reinterpret_cast(addr); - for (ptrdiff_t disp = -1 ; (disp * sizeof(T) > -128) ; disp--) - { - if (ptr[disp] == val) - { - return disp * sizeof(T); - } - } - fprintf(stderr, "Unable to find field in C++ exception structure\n"); - abort(); -} - -/** - * Helper function to find a particular value scanning forwards in a - * structure. - */ -template -ptrdiff_t find_forwards(void *addr, T val) -{ - T *ptr = reinterpret_cast(addr); - for (ptrdiff_t disp = 0 ; (disp * sizeof(T) < 256) ; disp++) - { - if (ptr[disp] == val) - { - return disp * sizeof(T); - } - } - fprintf(stderr, "Unable to find field in C++ exception structure\n"); - abort(); -} - -template -T *pointer_add(void *ptr, ptrdiff_t offset) -{ - return reinterpret_cast(reinterpret_cast(ptr) + offset); -} - -namespace gnustep -{ - namespace libobjc - { - /** - * Superclass for the type info for Objective-C exceptions. - */ - struct OBJC_PUBLIC __objc_type_info : std::type_info - { - /** - * Constructor that sets the name. - */ - __objc_type_info(const char *name); - /** - * Helper function used by libsupc++ and libcxxrt to determine if - * this is a pointer type. If so, catches automatically - * dereference the pointer to the thrown pointer in - * `__cxa_begin_catch`. - */ - virtual bool __is_pointer_p() const; - /** - * Helper function used by libsupc++ and libcxxrt to determine if - * this is a function pointer type. Irrelevant for our purposes. - */ - virtual bool __is_function_p() const; - /** - * Catch handler. This is used in the C++ personality function. - * `thrown_type` is the type info of the thrown object, `this` is - * the type info at the catch site. `thrown_object` is a pointer - * to a pointer to the thrown object and may be adjusted by this - * function. - */ - virtual bool __do_catch(const type_info *thrown_type, - void **thrown_object, - unsigned) const; - /** - * Function used for `dynamic_cast` between two C++ class types in - * libsupc++ and libcxxrt. - * - * This should never be called on Objective-C types. - */ - virtual bool __do_upcast( - const __cxxabiv1::__class_type_info *target, - void **thrown_object) const; - }; - - /** - * Singleton type info for the `id` type. - */ - struct OBJC_PUBLIC __objc_id_type_info : __objc_type_info - { - __objc_id_type_info(); - virtual ~__objc_id_type_info(); - virtual bool __do_catch(const type_info *thrownType, - void **obj, - unsigned outer) const; - }; - - struct OBJC_PUBLIC __objc_class_type_info : __objc_type_info - { - virtual ~__objc_class_type_info(); - virtual bool __do_catch(const type_info *thrownType, - void **obj, - unsigned outer) const; - }; - } -} - -/** - * Public interface to the Objective-C++ exception mechanism - */ -extern "C" -{ -/** - * The public symbol that the compiler uses to indicate the Objective-C id type. - */ -extern OBJC_PUBLIC gnustep::libobjc::__objc_id_type_info __objc_id_type_info; -} // extern "C" - -/** - * C++ structure that is thrown through a frame with the `test_eh_personality` - * personality function. This contains a well-known value that we can search - * for after the unwind header. - */ -struct -PRIVATE -MagicValueHolder -{ - /** - * The constant that we will search for to identify the MagicValueHolder object. - */ - static constexpr uint32_t magic = 0x01020304; - /** - * The single field in this structure. - */ - uint32_t magic_value; - /** - * Constructor. Initialises the field with the magic constant. - */ - MagicValueHolder(); -}; diff --git a/pool.h b/pool.h deleted file mode 100644 index f0bd545..0000000 --- a/pool.h +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include "lock.h" - -#ifndef POOL_TYPE -#error POOL_TYPE must be defined -#endif -#ifndef POOL_TYPE -#error POOL_NAME must be defined -#endif - -// Horrible multiple indirection to satisfy the weird precedence rules in cpp -#define REALLY_PREFIX_SUFFIX(x,y) x ## y -#define PREFIX_SUFFIX(x, y) REALLY_PREFIX_SUFFIX(x, y) -#define NAME(x) PREFIX_SUFFIX(POOL_NAME, x) - -#define PAGE_SIZE 4096 - -// Malloc one page at a time. -#define POOL_SIZE ((PAGE_SIZE) / sizeof(POOL_TYPE)) -static POOL_TYPE* NAME(_pool); -static int NAME(_pool_next_index) = -1; - -#ifdef THREAD_SAFE_POOL -static mutex_t NAME(_lock); -#define LOCK_POOL() LOCK(&POOL_NAME##_lock) -#define UNLOCK_POOL() LOCK(&POOL_NAME##_lock) -#else -#define LOCK_POOL() -#define UNLOCK_POOL() -#endif - -static int pool_size = 0; -static int pool_allocs = 0; -static inline POOL_TYPE*NAME(_pool_alloc)(void) -{ - LOCK_POOL(); - pool_allocs++; - if (0 > NAME(_pool_next_index)) - { - NAME(_pool) = malloc(PAGE_SIZE); - NAME(_pool_next_index) = POOL_SIZE - 1; - pool_size += PAGE_SIZE; - } - POOL_TYPE* new = &NAME(_pool)[NAME(_pool_next_index)--]; - UNLOCK_POOL(); - return new; -} -#undef NAME -#undef POOL_SIZE -#undef PAGE_SIZE -#undef POOL_NAME -#undef POOL_TYPE -#undef LOCK_POOL -#undef UNLOCK_POOL -#ifdef THREAD_SAFE_POOL -#undef THREAD_SAFE_POOL -#endif diff --git a/pool.hh b/pool.hh deleted file mode 100644 index 4a2af59..0000000 --- a/pool.hh +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#ifdef _WIN32 -#include "safewindows.h" -#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP && _WIN32_WINNT >= 0x0A00 -// Prefer the *FromApp versions when we're being built in a Windows Store App context on -// Windows >= 10. *FromApp require the application to be manifested for "codeGeneration". -#define VirtualAlloc VirtualAllocFromApp -#define VirtualProtect VirtualProtectFromApp -#endif // App family partition - -inline void *allocate_pages(size_t size) -{ - return VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -} - -#else -#include -inline void *allocate_pages(size_t size) -{ - void *ret = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - return ret == MAP_FAILED ? nullptr : ret; - -} -#endif - -template -class PoolAllocate -{ - static constexpr size_t PageSize = 4096; - static constexpr size_t ChunkSize = sizeof(T) * PageSize; - static inline size_t index = PageSize; - static inline T *buffer = nullptr; - public: - static T *allocate() - { - if (index == PageSize) - { - index = 0; - buffer = static_cast(allocate_pages(ChunkSize)); - } - return &buffer[index++]; - } -}; - - diff --git a/prepare_android_env.sh b/prepare_android_env.sh deleted file mode 100644 index d4d3aae..0000000 --- a/prepare_android_env.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/24.0.8215888 -export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64 -export CCPREFIX=$TOOLCHAIN/bin/aarch64-linux-android24 -export CC="$CCPREFIX-clang" -export CXX="$CCPREFIX-clang++" -export OBJC="$CCPREFIX-clang" -export OBJCXX="$CCPREFIX-clang++" -export AS="$CCPREFIX-clang" -export LD="$TOOLCHAIN/bin/ld.lld" -export AR="$TOOLCHAIN/bin/llvm-ar" -export RANLIB="$TOOLCHAIN/bin/llvm-ranlib" -export STRIP="$TOOLCHAIN/bin/llvm-strip" -export NM="$TOOLCHAIN/bin/llvm-nm" -export OBJDUMP="$TOOLCHAIN/bin/llvm-objdump" -export LDFLAGS="-fuse-ld=lld" -export LIBS="-lc++_shared" - -cmake -B build \ - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ - -DANDROID_ABI=arm64-v8a \ - -DANDROID_NDK=$ANDROID_NDK_HOME \ - -DANDROID_STL=c++_shared \ - -DCMAKE_FIND_USE_CMAKE_PATH=false \ - -DCMAKE_C_COMPILER=$CC \ - -DCMAKE_CXX_COMPILER=$CXX \ - -DCMAKE_ASM_COMPILER=$AS \ - -DCMAKE_BUILD_TYPE=Debug \ - -DTESTS=ON \ - -DANDROID_PLATFORM=android-24 \ - -G Ninja diff --git a/properties.h b/properties.h deleted file mode 100644 index 02f2511..0000000 --- a/properties.h +++ /dev/null @@ -1,239 +0,0 @@ -#include "visibility.h" -#include - -enum PropertyAttributeKind -{ - /** - * Property has no attributes. - */ - OBJC_PR_noattr = 0x00, - /** - * The property is declared read-only. - */ - OBJC_PR_readonly = (1<<0), - /** - * The property has a getter. - */ - OBJC_PR_getter = (1<<1), - /** - * The property has assign semantics. - */ - OBJC_PR_assign = (1<<2), - /** - * The property is declared read-write. - */ - OBJC_PR_readwrite = (1<<3), - /** - * Property has retain semantics. - */ - OBJC_PR_retain = (1<<4), - /** - * Property has copy semantics. - */ - OBJC_PR_copy = (1<<5), - /** - * Property is marked as non-atomic. - */ - OBJC_PR_nonatomic = (1<<6), - /** - * Property has setter. - */ - OBJC_PR_setter = (1<<7) -}; - -/** - * Flags in the second attributes field in declared properties. - * Note: This field replaces the old 'is synthesized' field and so these values - * are shifted left one from their values in clang. - */ -enum PropertyAttributeKind2 -{ - /** - * No extended attributes. - */ - OBJC_PR_noextattr = 0, - /** - * The property is synthesized. This has no meaning in properties on - * protocols. - */ - OBJC_PR_synthesized = (1<<0), - /** - * The property is dynamic (i.e. the implementation is inherited or - * provided at run time). - */ - OBJC_PR_dynamic = (1<<1), - /** - * This property belongs to a protocol. - */ - OBJC_PR_protocol = OBJC_PR_synthesized | OBJC_PR_dynamic, - /** - * The property is atomic. - */ - OBJC_PR_atomic = (1<<2), - /** - * The property value is a zeroing weak reference. - */ - OBJC_PR_weak = (1<<3), - /** - * The property value is strong (retained). Currently, this is equivalent - * to the strong attribute. - */ - OBJC_PR_strong = (1<<4), - /** - * The property value is just copied. - */ - OBJC_PR_unsafe_unretained = (1<<5), -}; - -/** - * Structure used for property enumeration. Note that property enumeration is - * currently quite broken on OS X, so achieving full compatibility there is - * impossible. Instead, we strive to achieve compatibility with the - * documentation. - */ -// begin: objc_property -struct objc_property -{ - /** - * Name of this property. - */ - const char *name; - /** - * The type encoding of the property. - */ - const char *attributes; - /** - * The type encoding of the property. - */ - const char *type; - /** - * The selector for the getter for this property. - */ - SEL getter; - /** - * The selector for the setter for this property. - */ - SEL setter; -}; -// end: objc_property - -/** - * GNUstep v1 ABI version of `struct objc_property` - */ -struct objc_property_gsv1 -{ - /** - * Name of this property. - */ - const char *name; - /** - * Attributes for this property. Made by ORing together - * PropertyAttributeKinds. - */ - char attributes; - /** - * Flag set if the property is synthesized. - */ - char attributes2; - /** - * Padding field. These were implicit in the structure field alignment - * (four more on 64-bit platforms), but we'll make them explicit now for - * future use. - */ - char unused1; - /** - * More padding. - */ - char unused2; - /** - * Name of the getter for this property. - */ - const char *getter_name; - /** - * Type encoding for the get method for this property. - */ - const char *getter_types; - /** - * Name of the set method for this property. - */ - const char *setter_name; - /** - * Type encoding of the setter for this property. - */ - const char *setter_types; -}; - -/** - * List of property introspection data. - */ -struct objc_property_list_gsv1 -{ - /** - * Number of properties in this array. - */ - int count; - /* - * The next property in a linked list. - */ - struct objc_property_list *next; - /** - * List of properties. - */ - struct objc_property_gsv1 properties[]; -}; - -/** - * List of property introspection data. - */ -// begin: objc_property_list -struct objc_property_list -{ - /** - * Number of properties in this array. - */ - int count; - /** - * Size of `struct objc_property`. This allows the runtime to - * transparently support newer ABIs with more fields in the property - * metadata. - */ - int size; - /* - * The next property in a linked list. - */ - struct objc_property_list *next; - /** - * List of properties. - */ - struct objc_property properties[]; -}; -// end: objc_property_list - -/** - * Returns a pointer to the property inside the `objc_property` structure. - * This structure is designed to allow the compiler to add other fields without - * breaking the ABI, so although the `properties` field appears to be an array - * of `objc_property` structures, it may be an array of some future version of - * `objc_property` structs, which have fields appended that this version of the - * runtime does not know about. - */ -static inline struct objc_property *property_at_index(struct objc_property_list *l, int i) -{ - assert(l->size >= sizeof(struct objc_property)); - return (struct objc_property*)(((char*)l->properties) + (i * l->size)); -} - -/** - * Constructs a property description from a list of attributes, returning the - * instance variable name via the third parameter. - */ -PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes, - unsigned int attributeCount, - const char *name); - -/** - * Constructs and installs a property attribute string from the property - * attributes and, optionally, an ivar string. - */ -PRIVATE const char *constructPropertyAttributes(objc_property_t property, - const char *iVarName); diff --git a/properties.m b/properties.m deleted file mode 100644 index 7c3dcc3..0000000 --- a/properties.m +++ /dev/null @@ -1,613 +0,0 @@ -#include "objc/runtime.h" -#include "objc/objc-arc.h" -#include -#include -#include -#include -#include "class.h" -#include "properties.h" -#include "spinlock.h" -#include "visibility.h" -#include "nsobject.h" -#include "gc_ops.h" -#include "lock.h" - -PRIVATE int spinlocks[spinlock_count]; - -/** - * Public function for getting a property. - */ -OBJC_PUBLIC -id objc_getProperty(id obj, SEL _cmd, ptrdiff_t offset, BOOL isAtomic) -{ - if (nil == obj) { return nil; } - char *addr = (char*)obj; - addr += offset; - id ret; - if (isAtomic) - { - volatile int *lock = lock_for_pointer(addr); - lock_spinlock(lock); - ret = *(id*)addr; - ret = objc_retain(ret); - unlock_spinlock(lock); - ret = objc_autoreleaseReturnValue(ret); - } - else - { - ret = *(id*)addr; - ret = objc_retainAutoreleaseReturnValue(ret); - } - return ret; -} - -OBJC_PUBLIC -void objc_setProperty(id obj, SEL _cmd, ptrdiff_t offset, id arg, BOOL isAtomic, BOOL isCopy) -{ - if (nil == obj) { return; } - char *addr = (char*)obj; - addr += offset; - - if (isCopy) - { - arg = [arg copy]; - } - else - { - arg = objc_retain(arg); - } - id old; - if (isAtomic) - { - volatile int *lock = lock_for_pointer(addr); - lock_spinlock(lock); - old = *(id*)addr; - *(id*)addr = arg; - unlock_spinlock(lock); - } - else - { - old = *(id*)addr; - *(id*)addr = arg; - } - objc_release(old); -} - -OBJC_PUBLIC -void objc_setProperty_atomic(id obj, SEL _cmd, id arg, ptrdiff_t offset) -{ - char *addr = (char*)obj; - addr += offset; - arg = objc_retain(arg); - volatile int *lock = lock_for_pointer(addr); - lock_spinlock(lock); - id old = *(id*)addr; - *(id*)addr = arg; - unlock_spinlock(lock); - objc_release(old); -} - -OBJC_PUBLIC -void objc_setProperty_atomic_copy(id obj, SEL _cmd, id arg, ptrdiff_t offset) -{ - char *addr = (char*)obj; - addr += offset; - - arg = [arg copy]; - volatile int *lock = lock_for_pointer(addr); - lock_spinlock(lock); - id old = *(id*)addr; - *(id*)addr = arg; - unlock_spinlock(lock); - objc_release(old); -} - -OBJC_PUBLIC -void objc_setProperty_nonatomic(id obj, SEL _cmd, id arg, ptrdiff_t offset) -{ - char *addr = (char*)obj; - addr += offset; - arg = objc_retain(arg); - id old = *(id*)addr; - *(id*)addr = arg; - objc_release(old); -} - -OBJC_PUBLIC -void objc_setProperty_nonatomic_copy(id obj, SEL _cmd, id arg, ptrdiff_t offset) -{ - char *addr = (char*)obj; - addr += offset; - id old = *(id*)addr; - *(id*)addr = [arg copy]; - objc_release(old); -} - -OBJC_PUBLIC -void objc_copyCppObjectAtomic(void *dest, const void *src, - void (*copyHelper) (void *dest, const void *source)) -{ - volatile int *lock = lock_for_pointer(src < dest ? src : dest); - volatile int *lock2 = lock_for_pointer(src < dest ? dest : src); - lock_spinlock(lock); - lock_spinlock(lock2); - copyHelper(dest, src); - unlock_spinlock(lock); - unlock_spinlock(lock2); -} - -OBJC_PUBLIC -void objc_getCppObjectAtomic(void *dest, const void *src, - void (*copyHelper) (void *dest, const void *source)) -{ - volatile int *lock = lock_for_pointer(src); - lock_spinlock(lock); - copyHelper(dest, src); - unlock_spinlock(lock); -} - -OBJC_PUBLIC -void objc_setCppObjectAtomic(void *dest, const void *src, - void (*copyHelper) (void *dest, const void *source)) -{ - volatile int *lock = lock_for_pointer(dest); - lock_spinlock(lock); - copyHelper(dest, src); - unlock_spinlock(lock); -} - -/** - * Structure copy function. This is provided for compatibility with the Apple - * APIs (it's an ABI function, so it's semi-public), but it's a bad design so - * it's not used. The problem is that it does not identify which of the - * pointers corresponds to the object, which causes some excessive locking to - * be needed. - */ -OBJC_PUBLIC -void objc_copyPropertyStruct(void *dest, - void *src, - ptrdiff_t size, - BOOL atomic, - BOOL strong) -{ - if (atomic) - { - volatile int *lock = lock_for_pointer(src < dest ? src : dest); - volatile int *lock2 = lock_for_pointer(src < dest ? dest : src); - lock_spinlock(lock); - lock_spinlock(lock2); - memcpy(dest, src, size); - unlock_spinlock(lock); - unlock_spinlock(lock2); - } - else - { - memcpy(dest, src, size); - } -} - -/** - * Get property structure function. Copies a structure from an ivar to another - * variable. Locks on the address of src. - */ -OBJC_PUBLIC -void objc_getPropertyStruct(void *dest, - void *src, - ptrdiff_t size, - BOOL atomic, - BOOL strong) -{ - if (atomic) - { - volatile int *lock = lock_for_pointer(src); - lock_spinlock(lock); - memcpy(dest, src, size); - unlock_spinlock(lock); - } - else - { - memcpy(dest, src, size); - } -} - -/** - * Set property structure function. Copes a structure to an ivar. Locks on - * dest. - */ -OBJC_PUBLIC -void objc_setPropertyStruct(void *dest, - void *src, - ptrdiff_t size, - BOOL atomic, - BOOL strong) -{ - if (atomic) - { - volatile int *lock = lock_for_pointer(dest); - lock_spinlock(lock); - memcpy(dest, src, size); - unlock_spinlock(lock); - } - else - { - memcpy(dest, src, size); - } -} - - -OBJC_PUBLIC -objc_property_t class_getProperty(Class cls, const char *name) -{ - if (Nil == cls) - { - return NULL; - } - struct objc_property_list *properties = cls->properties; - while (NULL != properties) - { - for (int i=0 ; icount ; i++) - { - objc_property_t p = property_at_index(properties, i); - if (strcmp(property_getName(p), name) == 0) - { - return p; - } - } - properties = properties->next; - } - return NULL; -} - -OBJC_PUBLIC -objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount) -{ - if (Nil == cls) - { - if (NULL != outCount) { *outCount = 0; } - return NULL; - } - struct objc_property_list *properties = cls->properties; - if (!properties) - { - if (NULL != outCount) { *outCount = 0; } - return NULL; - } - unsigned int count = 0; - for (struct objc_property_list *l=properties ; NULL!=l ; l=l->next) - { - count += l->count; - } - if (NULL != outCount) - { - *outCount = count; - } - if (0 == count) - { - return NULL; - } - objc_property_t *list = calloc(sizeof(objc_property_t), count); - unsigned int out = 0; - for (struct objc_property_list *l=properties ; NULL!=l ; l=l->next) - { - for (int i=0 ; icount ; i++) - { - list[out++] = property_at_index(l, i); - } - } - return list; -} -static const char* property_getIVar(objc_property_t property) -{ - const char *iVar = property_getAttributes(property); - if (iVar != 0) - { - while ((*iVar != 0) && (*iVar != 'V')) - { - iVar++; - } - if (*iVar == 'V') - { - return iVar+1; - } - } - return 0; -} - -OBJC_PUBLIC -const char *property_getName(objc_property_t property) -{ - if (NULL == property) { return NULL; } - - const char *name = property->name; - if (NULL == name) { return NULL; } - if (name[0] == 0) - { - name += name[1]; - } - return name; -} - -/* - * The compiler stores the type encoding of the getter. We replace this with - * the type encoding of the property itself. We use a 0 byte at the start to - * indicate that the swap has taken place. - */ -static const char *property_getTypeEncoding(objc_property_t property) -{ - if (NULL == property) { return NULL; } - return property->type; -} - -OBJC_PUBLIC -const char *property_getAttributes(objc_property_t property) -{ - if (NULL == property) { return NULL; } - return property->attributes; -} - - -OBJC_PUBLIC -objc_property_attribute_t *property_copyAttributeList(objc_property_t property, - unsigned int *outCount) -{ - if (NULL == property) - { - if (NULL != outCount) - { - *outCount = 0; - } - return NULL; - } - objc_property_attribute_t attrs[12]; - int count = 0; - - const char *types = property_getTypeEncoding(property); - if (NULL != types) - { - attrs[count].name = "T"; - attrs[count].value = types; - count++; - } - // If the compiler provides a type encoding string, then it's more - // informative than the bitfields and should be treated as canonical. If - // the compiler didn't provide a type encoding string, then this will - // create a best-effort one. - const char *attributes = property_getAttributes(property); - for (int i=strlen(types)+1 ; attributes[i] != 0 ; i++) - { - assert(count<12); - if (attributes[i] == ',') - { - // Comma is never the last character in the string, so this should - // never push us past the end. - i++; - } - attrs[count].value = ""; - switch (attributes[i]) - { - case 'R': - attrs[count].name = "R"; - break; - case 'C': - attrs[count].name = "C"; - break; - case '&': - attrs[count].name = "&"; - break; - case 'D': - attrs[count].name = "D"; - break; - case 'W': - attrs[count].name = "W"; - break; - case 'N': - attrs[count].name = "N"; - break; - case 'G': - attrs[count].name = "G"; - attrs[count].value = sel_getName(property->getter); - i += strlen(attrs[count].value); - break; - case 'S': - attrs[count].name = "S"; - attrs[count].value = sel_getName(property->setter); - i += strlen(attrs[count].value); - break; - case 'V': - attrs[count].name = "V"; - attrs[count].value = attributes+i+1; - i += strlen(attributes+i)-1; - break; - default: - continue; - } - count++; - } - objc_property_attribute_t *propAttrs = calloc(sizeof(objc_property_attribute_t), count); - memcpy(propAttrs, attrs, count * sizeof(objc_property_attribute_t)); - if (NULL != outCount) - { - *outCount = count; - } - return propAttrs; -} - -static const objc_property_attribute_t *findAttribute(char attr, - const objc_property_attribute_t *attributes, - unsigned int attributeCount) -{ - // This linear scan is N^2 in the worst case, but that's still probably - // cheaper than sorting the array because N<12 - for (int i=0 ; iname[0]; - if (attr->value) - { - size_t len = strlen(attr->value); - memcpy(buffer, attr->value, len); - buffer += len; - } - *(buffer++) = ','; - } - return buffer; -} - -static const char *encodingFromAttrs(const objc_property_attribute_t *attributes, - unsigned int attributeCount) -{ - // Length of the attributes string (initially the number of keys and commas and trailing null) - size_t attributesSize = 2 * attributeCount; - for (int i=0 ; ivalue); - } - p.getter = NULL; - attr = findAttribute('G', attributes, attributeCount); - if (attr) - { - // TODO: We should be able to construct the full type encoding if we - // also have a type, but for now use an untyped selector. - p.getter = sel_registerName(attr->value); - } - p.setter = NULL; - attr = findAttribute('S', attributes, attributeCount); - if (attr) - { - // TODO: We should be able to construct the full type encoding if we - // also have a type, but for now use an untyped selector. - p.setter = sel_registerName(attr->value); - } - return p; -} - - -OBJC_PUBLIC -BOOL class_addProperty(Class cls, - const char *name, - const objc_property_attribute_t *attributes, - unsigned int attributeCount) -{ - if ((Nil == cls) || (NULL == name) || (class_getProperty(cls, name) != 0)) { return NO; } - - struct objc_property p = propertyFromAttrs(attributes, attributeCount, name); - - struct objc_property_list *l = calloc(1, sizeof(struct objc_property_list) - + sizeof(struct objc_property)); - l->count = 1; - l->size = sizeof(struct objc_property); - memcpy(&l->properties, &p, sizeof(struct objc_property)); - LOCK_RUNTIME_FOR_SCOPE(); - l->next = cls->properties; - cls->properties = l; - return YES; -} - -OBJC_PUBLIC -void class_replaceProperty(Class cls, - const char *name, - const objc_property_attribute_t *attributes, - unsigned int attributeCount) -{ - if ((Nil == cls) || (NULL == name)) { return; } - objc_property_t old = class_getProperty(cls, name); - if (NULL == old) - { - class_addProperty(cls, name, attributes, attributeCount); - return; - } - struct objc_property p = propertyFromAttrs(attributes, attributeCount, name); - LOCK_RUNTIME_FOR_SCOPE(); - memcpy(old, &p, sizeof(struct objc_property)); -} -OBJC_PUBLIC -char *property_copyAttributeValue(objc_property_t property, - const char *attributeName) -{ - if ((NULL == property) || (NULL == attributeName)) { return NULL; } - const char *attributes = property_getAttributes(property); - switch (attributeName[0]) - { - case 'T': - { - const char *types = property_getTypeEncoding(property); - return (NULL == types) ? NULL : strdup(types); - } - case 'D': - case 'R': - case 'W': - case 'C': - case '&': - case 'N': - { - return strchr(attributes, attributeName[0]) ? strdup("") : 0; - } - case 'V': - { - return strdup(property_getIVar(property)); - } - case 'S': - { - return strdup(sel_getName(property->setter)); - } - case 'G': - { - return strdup(sel_getName(property->getter)); - } - } - return 0; -} diff --git a/protocol.c b/protocol.c deleted file mode 100644 index e0144e2..0000000 --- a/protocol.c +++ /dev/null @@ -1,712 +0,0 @@ -#include "objc/runtime.h" -#include "protocol.h" -#include "properties.h" -#include "class.h" -#include "lock.h" -#include "legacy.h" -#include -#include - -#define BUFFER_TYPE struct objc_protocol_list * -#include "buffer.h" - -// Get the functions for string hashing -#include "string_hash.h" - -static int protocol_compare(const char *name, - const struct objc_protocol *protocol) -{ - return string_compare(name, protocol->name); -} -static int protocol_hash(const struct objc_protocol *protocol) -{ - return string_hash(protocol->name); -} -#define MAP_TABLE_NAME protocol -#define MAP_TABLE_COMPARE_FUNCTION protocol_compare -#define MAP_TABLE_HASH_KEY string_hash -#define MAP_TABLE_HASH_VALUE protocol_hash -#include "hash_table.h" - -static protocol_table *known_protocol_table; -mutex_t protocol_table_lock; - -PRIVATE void init_protocol_table(void) -{ - protocol_initialize(&known_protocol_table, 128); - INIT_LOCK(protocol_table_lock); -} - -static void protocol_table_insert(const struct objc_protocol *protocol) -{ - protocol_insert(known_protocol_table, (void*)protocol); -} - -struct objc_protocol *protocol_for_name(const char *name) -{ - return protocol_table_get(known_protocol_table, name); -} - -static id incompleteProtocolClass(void) -{ - static id IncompleteProtocolClass = 0; - if (IncompleteProtocolClass == nil) - { - IncompleteProtocolClass = objc_getClass("__IncompleteProtocol"); - } - return IncompleteProtocolClass; -} - -/** - * Class used for legacy GCC protocols (`ProtocolGCC`). - */ -static id protocol_class_gcc; -/** - * Class used for legacy GNUstep V1 ABI protocols (`ProtocolGSv1`). - */ -static id protocol_class_gsv1; -/** - * Class used for protocols (`Protocol`). - */ -static id protocol_class_gsv2; - -static BOOL init_protocol_classes(void) -{ - if (nil == protocol_class_gcc) - { - protocol_class_gcc = objc_getClass("ProtocolGCC"); - } - if (nil == protocol_class_gsv1) - { - protocol_class_gsv1 = objc_getClass("ProtocolGSv1"); - } - if (nil == protocol_class_gsv2) - { - protocol_class_gsv2 = objc_getClass("Protocol"); - } - if ((nil == protocol_class_gcc) || - (nil == protocol_class_gsv1) || - (nil == protocol_class_gsv2)) - { - return NO; - } - return YES; -} - -static BOOL protocol_hasClassProperties(struct objc_protocol *p) -{ - if (!init_protocol_classes()) - { - return NO; - } - return p->isa == protocol_class_gsv2; -} - -static BOOL protocol_hasOptionalMethodsAndProperties(struct objc_protocol *p) -{ - if (!init_protocol_classes()) - { - return NO; - } - if (p->isa == protocol_class_gcc) - { - return NO; - } - return YES; -} - -static int isEmptyProtocol(struct objc_protocol *aProto) -{ - int isEmpty = - ((aProto->instance_methods == NULL) || - (aProto->instance_methods->count == 0)) && - ((aProto->class_methods == NULL) || - (aProto->class_methods->count == 0)) && - ((aProto->protocol_list == NULL) || - (aProto->protocol_list->count == 0)); - if (protocol_hasOptionalMethodsAndProperties(aProto)) - { - isEmpty &= (aProto->optional_instance_methods == NULL) || - (aProto->optional_instance_methods->count == 0); - isEmpty &= (aProto->optional_class_methods == NULL) || - (aProto->optional_class_methods->count == 0); - isEmpty &= (aProto->properties == 0) || (aProto->properties->count == 0); - isEmpty &= (aProto->optional_properties == 0) || (aProto->optional_properties->count == 0); - } - return isEmpty; -} - -// FIXME: Make p1 adopt all of the stuff in p2 -static void makeProtocolEqualToProtocol(struct objc_protocol *p1, - struct objc_protocol *p2) -{ -#define COPY(x) p1->x = p2->x - COPY(instance_methods); - COPY(class_methods); - COPY(protocol_list); - if (protocol_hasOptionalMethodsAndProperties(p1) && - protocol_hasOptionalMethodsAndProperties(p2)) - { - COPY(optional_instance_methods); - COPY(optional_class_methods); - COPY(properties); - COPY(optional_properties); - } -#undef COPY -} - -static struct objc_protocol *unique_protocol(struct objc_protocol *aProto) -{ - struct objc_protocol *oldProtocol = - protocol_for_name(aProto->name); - if (NULL == oldProtocol) - { - // This is the first time we've seen this protocol, so add it to the - // hash table and ignore it. - protocol_table_insert(aProto); - return aProto; - } - if (isEmptyProtocol(oldProtocol)) - { - if (isEmptyProtocol(aProto)) - { - return aProto; - // Add protocol to a list somehow. - } - else - { - // This protocol is not empty, so we use its definitions - makeProtocolEqualToProtocol(oldProtocol, aProto); - return aProto; - } - } - else - { - if (isEmptyProtocol(aProto)) - { - makeProtocolEqualToProtocol(aProto, oldProtocol); - return oldProtocol; - } - else - { - return oldProtocol; - //FIXME: We should really perform a check here to make sure the - //protocols are actually the same. - } - } -} - -static BOOL init_protocols(struct objc_protocol_list *protocols) -{ - if (!init_protocol_classes()) - { - return NO; - } - - for (unsigned i=0 ; icount ; i++) - { - struct objc_protocol *aProto = protocols->list[i]; - // Don't initialise a protocol twice - if ((aProto->isa == protocol_class_gcc) || - (aProto->isa == protocol_class_gsv1) || - (aProto->isa == protocol_class_gsv2)) - { - continue; - } - - // Protocols in the protocol list have their class pointers set to the - // version of the protocol class that they expect. - enum protocol_version version = - (enum protocol_version)(uintptr_t)aProto->isa; - switch (version) - { - default: - fprintf(stderr, "Unknown protocol version"); - abort(); -#ifdef OLDABI_COMPAT - case protocol_version_gcc: - protocols->list[i] = objc_upgrade_protocol_gcc((struct objc_protocol_gcc *)aProto); - assert(aProto->isa == protocol_class_gcc); - assert(protocols->list[i]->isa == protocol_class_gsv2); - aProto = protocols->list[i]; - break; - case protocol_version_gsv1: - protocols->list[i] = objc_upgrade_protocol_gsv1((struct objc_protocol_gsv1 *)aProto); - assert(aProto->isa == protocol_class_gsv1); - assert(protocols->list[i]->isa == protocol_class_gsv2); - aProto = protocols->list[i]; - break; -#endif - case protocol_version_gsv2: - aProto->isa = protocol_class_gsv2; - break; - } - // Initialize all of the protocols that this protocol refers to - if (NULL != aProto->protocol_list) - { - init_protocols(aProto->protocol_list); - } - // Replace this protocol with a unique version of it. - protocols->list[i] = unique_protocol(aProto); - } - return YES; -} - -PRIVATE void objc_init_protocols(struct objc_protocol_list *protocols) -{ - LOCK_FOR_SCOPE(&protocol_table_lock); - if (!init_protocols(protocols)) - { - set_buffered_object_at_index(protocols, buffered_objects++); - return; - } - if (buffered_objects == 0) { return; } - - // If we can load one protocol, then we can load all of them. - for (unsigned i=0 ; iname, p2->name) == 0) { return YES; } - - for (struct objc_protocol_list *list = p1->protocol_list ; - list != NULL ; list = list->next) - { - for (int i=0 ; icount ; i++) - { - if (strcmp(list->list[i]->name, p2->name) == 0) - { - return YES; - } - if (protocol_conformsToProtocol((Protocol*)list->list[i], p2)) - { - return YES; - } - } - } - return NO; -} - -BOOL class_conformsToProtocol(Class cls, Protocol *protocol) -{ - if (Nil == cls || NULL == protocol) { return NO; } - for ( ; Nil != cls ; cls = class_getSuperclass(cls)) - { - for (struct objc_protocol_list *protocols = cls->protocols; - protocols != NULL ; protocols = protocols->next) - { - for (int i=0 ; icount ; i++) - { - Protocol *p1 = (Protocol*)protocols->list[i]; - if (protocol_conformsToProtocol(p1, protocol)) - { - return YES; - } - } - } - } - return NO; -} - -static struct objc_protocol_method_description_list * -get_method_list(Protocol *p, - BOOL isRequiredMethod, - BOOL isInstanceMethod) -{ - struct objc_protocol_method_description_list *list; - if (isRequiredMethod) - { - if (isInstanceMethod) - { - list = p->instance_methods; - } - else - { - list = p->class_methods; - } - } - else - { - if (!protocol_hasOptionalMethodsAndProperties(p)) { return NULL; } - - if (isInstanceMethod) - { - list = p->optional_instance_methods; - } - else - { - list = p->optional_class_methods; - } - } - return list; -} - -struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, - BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count) -{ - if ((NULL == p) || (NULL == count)){ return NULL; } - struct objc_protocol_method_description_list *list = - get_method_list(p, isRequiredMethod, isInstanceMethod); - *count = 0; - if (NULL == list || list->count == 0) { return NULL; } - - *count = list->count; - struct objc_method_description *out = - calloc(sizeof(struct objc_method_description), list->count); - for (int i=0 ; i < (list->count) ; i++) - { - out[i].name = protocol_method_at_index(list, i)->selector; - out[i].types = sel_getType_np(protocol_method_at_index(list, i)->selector); - } - return out; -} - -Protocol*__unsafe_unretained* protocol_copyProtocolList(Protocol *p, unsigned int *count) -{ - if (NULL == p) { return NULL; } - *count = 0; - if (p->protocol_list == NULL || p->protocol_list->count ==0) - { - return NULL; - } - - *count = p->protocol_list->count; - Protocol **out = calloc(sizeof(Protocol*), p->protocol_list->count); - for (int i=0 ; iprotocol_list->count ; i++) - { - out[i] = (Protocol*)p->protocol_list->list[i]; - } - return out; -} - -objc_property_t *protocol_copyPropertyList2(Protocol *p, unsigned int *outCount, - BOOL isRequiredProperty, BOOL isInstanceProperty) -{ - struct objc_property_list *properties = - isInstanceProperty ? - (isRequiredProperty ? p->properties : p->optional_properties) : - (isRequiredProperty ? p->class_properties : p->optional_class_properties); - if (NULL == p) { return NULL; } - // If it's an old protocol, it won't have any of the other options. - if (!isRequiredProperty && !isInstanceProperty && - !protocol_hasOptionalMethodsAndProperties(p)) - { - return NULL; - } - if (properties == NULL) - { - return NULL; - } - unsigned int count = 0; - for (struct objc_property_list *l=properties ; l!=NULL ; l=l->next) - { - count += l->count; - } - if (0 == count) - { - return NULL; - } - objc_property_t *list = calloc(sizeof(objc_property_t), count); - unsigned int out = 0; - for (struct objc_property_list *l=properties ; l!=NULL ; l=l->next) - { - for (int i=0 ; icount ; i++) - { - list[out++] = property_at_index(l, i); - } - } - *outCount = count; - return list; -} - -objc_property_t *protocol_copyPropertyList(Protocol *p, - unsigned int *outCount) -{ - return protocol_copyPropertyList2(p, outCount, YES, YES); -} - -objc_property_t protocol_getProperty(Protocol *p, - const char *name, - BOOL isRequiredProperty, - BOOL isInstanceProperty) -{ - if (NULL == p) { return NULL; } - if (!protocol_hasOptionalMethodsAndProperties(p)) - { - return NULL; - } - if (!isInstanceProperty && !protocol_hasClassProperties(p)) - { - return NULL; - } - struct objc_property_list *properties = - isInstanceProperty ? - (isRequiredProperty ? p->properties : p->optional_properties) : - (isRequiredProperty ? p->class_properties : p->optional_class_properties); - while (NULL != properties) - { - for (int i=0 ; icount ; i++) - { - objc_property_t prop = property_at_index(properties, i); - if (strcmp(property_getName(prop), name) == 0) - { - return prop; - } - } - properties = properties->next; - } - return NULL; -} - -static struct objc_protocol_method_description * -get_method_description(Protocol *p, - SEL aSel, - BOOL isRequiredMethod, - BOOL isInstanceMethod) -{ - struct objc_protocol_method_description_list *list = - get_method_list(p, isRequiredMethod, isInstanceMethod); - if (NULL == list) - { - return NULL; - } - for (int i=0 ; icount ; i++) - { - SEL s = protocol_method_at_index(list, i)->selector; - if (sel_isEqual(s, aSel)) - { - return protocol_method_at_index(list, i); - } - } - return NULL; -} - -struct objc_method_description -protocol_getMethodDescription(Protocol *p, - SEL aSel, - BOOL isRequiredMethod, - BOOL isInstanceMethod) -{ - struct objc_method_description d = {0,0}; - struct objc_protocol_method_description *m = - get_method_description(p, aSel, isRequiredMethod, isInstanceMethod); - if (m != NULL) - { - SEL s = m->selector; - d.name = s; - d.types = sel_getType_np(s); - } - return d; -} - -const char *_protocol_getMethodTypeEncoding(Protocol *p, - SEL aSel, - BOOL isRequiredMethod, - BOOL isInstanceMethod) -{ - struct objc_protocol_method_description *m = - get_method_description(p, aSel, isRequiredMethod, isInstanceMethod); - if (m != NULL) - { - return m->types; - } - return NULL; -} - - -const char *protocol_getName(Protocol *p) -{ - if (NULL != p) - { - return p->name; - } - return NULL; -} - -BOOL protocol_isEqual(Protocol *p, Protocol *other) -{ - if (NULL == p || NULL == other) - { - return NO; - } - if (p == other || - p->name == other->name || - 0 == strcmp(p->name, other->name)) - { - return YES; - } - return NO; -} - -Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount) -{ - LOCK_FOR_SCOPE(&protocol_table_lock); - unsigned int total = known_protocol_table->table_used; - Protocol **p = calloc(sizeof(Protocol*), known_protocol_table->table_used); - - struct protocol_table_enumerator *e = NULL; - Protocol *next; - - unsigned int count = 0; - while ((count < total) && (next = protocol_next(known_protocol_table, &e))) - { - p[count++] = next; - } - if (NULL != outCount) - { - *outCount = total; - } - return p; -} - - -Protocol *objc_allocateProtocol(const char *name) -{ - if (objc_getProtocol(name) != NULL) { return NULL; } - // Create this as an object and add extra space at the end for the properties. - Protocol *p = (Protocol*)class_createInstance((Class)incompleteProtocolClass(), - sizeof(struct objc_protocol) - sizeof(id)); - p->name = strdup(name); - return p; -} -void objc_registerProtocol(Protocol *proto) -{ - if (NULL == proto) { return; } - LOCK_FOR_SCOPE(&protocol_table_lock); - if (objc_getProtocol(proto->name) != NULL) { return; } - if (incompleteProtocolClass() != proto->isa) { return; } - init_protocol_classes(); - proto->isa = protocol_class_gsv2; - protocol_table_insert(proto); -} -PRIVATE void registerProtocol(Protocol *proto) -{ - init_protocol_classes(); - LOCK_FOR_SCOPE(&protocol_table_lock); - proto->isa = protocol_class_gsv2; - if (protocol_for_name(proto->name) == NULL) - { - protocol_table_insert(proto); - } -} -void protocol_addMethodDescription(Protocol *aProtocol, - SEL name, - const char *types, - BOOL isRequiredMethod, - BOOL isInstanceMethod) -{ - if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } - if (incompleteProtocolClass() != aProtocol->isa) { return; } - struct objc_protocol_method_description_list **listPtr; - if (isInstanceMethod) - { - if (isRequiredMethod) - { - listPtr = &aProtocol->instance_methods; - } - else - { - listPtr = &aProtocol->optional_instance_methods; - } - } - else - { - if (isRequiredMethod) - { - listPtr = &aProtocol->class_methods; - } - else - { - listPtr = &aProtocol->optional_class_methods; - } - } - if (NULL == *listPtr) - { - // FIXME: Factor this out, we do the same thing in multiple places. - *listPtr = calloc(1, sizeof(struct objc_protocol_method_description_list) + - sizeof(struct objc_protocol_method_description)); - (*listPtr)->count = 1; - (*listPtr)->size = sizeof(struct objc_protocol_method_description); - } - else - { - (*listPtr)->count++; - *listPtr = realloc(*listPtr, sizeof(struct objc_protocol_method_description_list) + - sizeof(struct objc_protocol_method_description) * (*listPtr)->count); - } - struct objc_protocol_method_description_list *list = *listPtr; - int index = list->count-1; - protocol_method_at_index(list, index)->selector = sel_registerTypedName_np(sel_getName(name), types); - protocol_method_at_index(list, index)->types = types; -} -void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) -{ - if ((NULL == aProtocol) || (NULL == addition)) { return; } - if (incompleteProtocolClass() != aProtocol->isa) { return; } - if (NULL == aProtocol->protocol_list) - { - aProtocol->protocol_list = calloc(1, sizeof(struct objc_property_list) + sizeof(Protocol*)); - aProtocol->protocol_list->count = 1; - } - else - { - aProtocol->protocol_list->count++; - aProtocol->protocol_list = realloc(aProtocol->protocol_list, sizeof(struct objc_property_list) + - aProtocol->protocol_list->count * sizeof(Protocol*)); - } - aProtocol->protocol_list->list[aProtocol->protocol_list->count-1] = (Protocol*)addition; -} -void protocol_addProperty(Protocol *aProtocol, - const char *name, - const objc_property_attribute_t *attributes, - unsigned int attributeCount, - BOOL isRequiredProperty, - BOOL isInstanceProperty) -{ - if ((NULL == aProtocol) || (NULL == name)) { return; } - if (incompleteProtocolClass() != aProtocol->isa) { return; } - if (!isInstanceProperty) { return; } - struct objc_property_list **listPtr = - isInstanceProperty ? - (isRequiredProperty ? &aProtocol->properties : &aProtocol->optional_properties) : - (isRequiredProperty ? &aProtocol->class_properties : &aProtocol->optional_class_properties); - if (NULL == *listPtr) - { - *listPtr = calloc(1, sizeof(struct objc_property_list) + sizeof(struct objc_property)); - (*listPtr)->size = sizeof(struct objc_property); - (*listPtr)->count = 1; - } - else - { - (*listPtr)->count++; - *listPtr = realloc(*listPtr, sizeof(struct objc_property_list) + - sizeof(struct objc_property) * (*listPtr)->count); - } - struct objc_property_list *list = *listPtr; - int index = list->count-1; - struct objc_property p = propertyFromAttrs(attributes, attributeCount, name); - assert(list->size == sizeof(p)); - memcpy(&(list->properties[index]), &p, sizeof(p)); -} - diff --git a/protocol.h b/protocol.h deleted file mode 100644 index 15a0838..0000000 --- a/protocol.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef PROTOCOL_H_INCLUDED -#define PROTOCOL_H_INCLUDED - -#include "selector.h" -#include -#include - -struct objc_protocol_method_description_list_gcc -{ - /** - * Number of method descriptions in this list. - */ - int count; - /** - * Methods in this list. Note: these selectors are NOT resolved. The name - * field points to the name, not to the index of the uniqued version of the - * name. You must not use them for dispatch. - */ - struct objc_selector methods[]; -}; - -/** - * Protocol versions. The compiler generates these in the `isa` field for - * protocols and they are replaced with class pointers by the runtime. - */ -enum protocol_version -{ - /** - * Legacy (GCC-compatible) protocol version. - */ - protocol_version_gcc = 2, - /** - * GNUstep V1 ABI protocol. - */ - protocol_version_gsv1 = 3, - /** - * GNUstep V2 ABI protocol. - */ - protocol_version_gsv2 = 4 -}; - - -/** - * A description of a method in a protocol. - */ -struct objc_protocol_method_description -{ - /** - * The selector for this method, includes traditional type encoding. - */ - SEL selector; - /** - * The extended type encoding. - */ - const char *types; -}; - -struct objc_protocol_method_description_list -{ - /** - * Number of method descriptions in this list. - */ - int count; - /** - * Size of `struct objc_method_description` - */ - int size; - /** - * Methods in this list. `count` elements long. - */ - struct objc_protocol_method_description methods[]; -}; - -/** - * Returns a pointer to the method inside the method description list - * structure. This structure is designed to allow the compiler to add other - * fields without breaking the ABI, so although the `methods` field appears to - * be an array of `objc_protocol_method_description` structures, it may be an - * array of some future version of these structs, which have fields appended - * that this version of the runtime does not know about. - */ -static inline struct objc_protocol_method_description * -protocol_method_at_index(struct objc_protocol_method_description_list *l, int i) -{ - assert(l->size >= sizeof(struct objc_protocol_method_description)); - return (struct objc_protocol_method_description*)(((char*)l->methods) + (i * l->size)); -} - -struct objc_protocol -{ - /** - * Class pointer. - */ - id isa; - /** - * Protocol name. - */ - char *name; - /** - * Protocols that this protocol conforms to. - */ - struct objc_protocol_list *protocol_list; - /** - * Required instance methods that classes conforming to this protocol must - * implement. - */ - struct objc_protocol_method_description_list *instance_methods; - /** - * Required class methods that classes conforming to this protocol must - * implement. - */ - struct objc_protocol_method_description_list *class_methods; - /** - * Instance methods that are declared as optional for this protocol. - */ - struct objc_protocol_method_description_list *optional_instance_methods; - /** - * Class methods that are declared as optional for this protocol. - */ - struct objc_protocol_method_description_list *optional_class_methods; - /** - * Properties that are required by this protocol. - */ - struct objc_property_list *properties; - /** - * Optional properties. - */ - struct objc_property_list *optional_properties; - /** - * Class properties that are required by this protocol. - */ - struct objc_property_list *class_properties; - /** - * Optional class properties. - */ - struct objc_property_list *optional_class_properties; -}; - -struct objc_protocol_gcc -{ - /** Class pointer. */ - id isa; - /** - * The name of this protocol. Two protocols are regarded as identical if - * they have the same name. - */ - char *name; - /** - * The list of protocols that this protocol conforms to. - */ - struct objc_protocol_list *protocol_list; - /** - * List of instance methods required by this protocol. - */ - struct objc_protocol_method_description_list_gcc *instance_methods; - /** - * List of class methods required by this protocol. - */ - struct objc_protocol_method_description_list_gcc *class_methods; -}; - -struct objc_protocol_gsv1 -{ - /** - * The first five ivars are shared with `objc_protocol_gcc`. - */ - id isa; - char *name; - struct objc_protocol_list *protocol_list; - struct objc_protocol_method_description_list_gcc *instance_methods; - struct objc_protocol_method_description_list_gcc *class_methods; - /** - * Instance methods that are declared as optional for this protocol. - */ - struct objc_protocol_method_description_list_gcc *optional_instance_methods; - /** - * Class methods that are declared as optional for this protocol. - */ - struct objc_protocol_method_description_list_gcc *optional_class_methods; - /** - * Properties that are required by this protocol. - */ - struct objc_property_list_gsv1 *properties; - /** - * Optional properties. - */ - struct objc_property_list_gsv1 *optional_properties; -}; - -#ifdef __OBJC__ -@interface Object { id isa; } @end -/** - * Definition of the Protocol type. Protocols are objects, but are rarely used - * as such. - */ -@interface Protocol : Object -@end - -@interface ProtocolGCC : Protocol -@end - -@interface ProtocolGSv1 : Protocol -@end - -#endif - -/** - * List of protocols. Attached to a class or a category by the compiler and to - * a class by the runtime. - */ -// begin: objc_protocol_list -struct objc_protocol_list -{ - /** - * Additional protocol lists. Loading a category that declares protocols - * will cause a new list to be prepended using this pointer to the protocol - * list for the class. Unlike methods, protocols can not be overridden, - * although it is possible for a protocol to appear twice. - */ - struct objc_protocol_list *next; - /** - * The number of protocols in this list. - */ - size_t count; - /** - * An array of protocols. Contains `count` elements. - * - * On load, this contains direct references to other protocols and should - * be updated to point to the canonical (possibly upgraded) version. - */ - struct objc_protocol *list[]; -}; -// end: objc_protocol_list - - -/** - * Function that ensures that protocol classes are linked. Calling this - * guarantees that the Protocol classes are linked into a statically linked - * runtime. - */ -void link_protocol_classes(void); - -#endif // PROTOCOL_H_INCLUDED diff --git a/runtime.c b/runtime.c deleted file mode 100644 index be03c3f..0000000 --- a/runtime.c +++ /dev/null @@ -1,849 +0,0 @@ -#include "objc/runtime.h" -#include "selector.h" -#include "class.h" -#include "protocol.h" -#include "ivar.h" -#include "method.h" -#include "lock.h" -#include "dtable.h" -#include "gc_ops.h" - -/* Make glibc export strdup() */ - -#if defined __GLIBC__ - #define __USE_BSD 1 -#endif - -#include -#include -#include -#include - -#define CHECK_ARG(arg) if (0 == arg) { return 0; } - -static inline void safe_remove_from_subclass_list(Class cls); -PRIVATE void objc_resolve_class(Class); -void objc_send_initialize(id object); - -/** - * Calls C++ destructors in the correct order. - */ -PRIVATE void call_cxx_destruct(id obj) -{ - static SEL cxx_destruct; - if (NULL == cxx_destruct) - { - cxx_destruct = sel_registerName(".cxx_destruct"); - } - // Don't call object_getClass(), because we want to get hidden classes too - Class cls = classForObject(obj); - - while (cls) - { - // If we're deallocating a class with a hidden class, then the - // `.cxx_destruct` method may deallocate the class. - Class currentClass = cls; - cls = cls->super_class; - if (currentClass->cxx_destruct) - { - currentClass->cxx_destruct(obj, cxx_destruct); - } - } -} - -static void call_cxx_construct_for_class(Class cls, id obj) -{ - static SEL cxx_construct; - if (NULL == cxx_construct) - { - cxx_construct = sel_registerName(".cxx_construct"); - } - - if (cls->super_class) - { - call_cxx_construct_for_class(cls->super_class, obj); - } - if (cls->cxx_construct) - { - cls->cxx_construct(obj, cxx_construct); - } -} - -PRIVATE void call_cxx_construct(id obj) -{ - call_cxx_construct_for_class(classForObject(obj), obj); -} - -/** - * Looks up the instance method in a specific class, without recursing into - * superclasses. - */ -static Method class_getInstanceMethodNonrecursive(Class aClass, SEL aSelector) -{ - for (struct objc_method_list *methods = aClass->methods; - methods != NULL ; methods = methods->next) - { - for (int i=0 ; icount ; i++) - { - Method method = method_at_index(methods, i); - if (sel_isEqual(method->selector, aSelector)) - { - return method; - } - } - } - return NULL; -} - -BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, - const char *types) -{ - CHECK_ARG(cls); - CHECK_ARG(name); - CHECK_ARG(types); - // You can't add ivars to initialized classes. Note: We can't use the - // resolved flag here because class_getInstanceVariable() sets it. - if (objc_test_class_flag(cls, objc_class_flag_initialized)) - { - return NO; - } - - if (class_getInstanceVariable(cls, name) != NULL) - { - return NO; - } - - struct objc_ivar_list *ivarlist = cls->ivars; - - if (NULL == ivarlist) - { - cls->ivars = malloc(sizeof(struct objc_ivar_list) + sizeof(struct objc_ivar)); - cls->ivars->size = sizeof(struct objc_ivar); - cls->ivars->count = 1; - } - else - { - ivarlist->count++; - // objc_ivar_list contains one ivar. Others follow it. - cls->ivars = realloc(ivarlist, sizeof(struct objc_ivar_list) + - (ivarlist->count) * sizeof(struct objc_ivar)); - } - Ivar ivar = ivar_at_index(cls->ivars, cls->ivars->count - 1); - ivar->name = strdup(name); - ivar->type = strdup(types); - ivarSetAlign(ivar, alignment); - // Round up the offset of the ivar so it is correctly aligned. - long offset = cls->instance_size; - if (alignment != 0) - { - offset >>= alignment; - - if (offset << alignment != cls->instance_size) - { - offset++; - } - offset <<= alignment; - } - - ivar->offset = (int*)(uintptr_t)offset; - // Increase the instance size to make space for this. - cls->instance_size = offset + size; - return YES; -} - -BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) -{ - CHECK_ARG(cls); - CHECK_ARG(name); - CHECK_ARG(imp); - CHECK_ARG(types); - const char *methodName = sel_getName(name); - struct objc_method_list *methods; - for (methods=cls->methods; methods!=NULL ; methods=methods->next) - { - for (int i=0 ; icount ; i++) - { - Method method = method_at_index(methods, i); - if (strcmp(sel_getName(method->selector), methodName) == 0) - { - return NO; - } - } - } - - methods = malloc(sizeof(struct objc_method_list) + sizeof(struct objc_method)); - methods->next = cls->methods; - methods->size = sizeof(struct objc_method); - cls->methods = methods; - - methods->count = 1; - struct objc_method *m0 = method_at_index(methods, 0); - m0->selector = sel_registerTypedName_np(methodName, types); - m0->types = strdup(types); - m0->imp = imp; - - if (classHasDtable(cls)) - { - add_method_list_to_class(cls, methods); - } - - return YES; -} - -BOOL class_addProtocol(Class cls, Protocol *protocol) -{ - CHECK_ARG(cls); - CHECK_ARG(protocol); - if (class_conformsToProtocol(cls, protocol)) { return NO; } - struct objc_protocol_list *protocols = - malloc(sizeof(struct objc_protocol_list) + sizeof(Protocol*)); - if (protocols == NULL) { return NO; } - protocols->next = cls->protocols; - protocols->count = 1; - protocols->list[0] = protocol; - cls->protocols = protocols; - - return YES; -} - -Ivar * class_copyIvarList(Class cls, unsigned int *outCount) -{ - if (outCount != NULL) - { - *outCount = 0x0; - } - - CHECK_ARG(cls); - struct objc_ivar_list *ivarlist = NULL; - unsigned int count = 0; - unsigned int index; - Ivar *list; - - if (Nil != cls) - { - ivarlist = cls->ivars; - } - if (ivarlist != NULL) - { - count = ivarlist->count; - } - if (outCount != NULL) - { - *outCount = count; - } - if (count == 0) - { - return NULL; - } - - list = malloc((count + 1) * sizeof(struct objc_ivar *)); - list[count] = NULL; - count = 0; - for (index = 0; index < ivarlist->count; index++) - { - list[count++] = ivar_at_index(ivarlist, index); - } - - return list; -} - -Method * class_copyMethodList(Class cls, unsigned int *outCount) -{ - if (outCount != NULL) - { - *outCount = 0x0; - } - - CHECK_ARG(cls); - unsigned int count = 0; - Method *list; - struct objc_method_list *methods; - - if (cls != NULL) - { - for (methods = cls->methods; methods != NULL; methods = methods->next) - { - count += methods->count; - } - } - - if (outCount != NULL) - { - *outCount = count; - } - - if (count == 0) - { - return NULL; - } - - list = malloc((count + 1) * sizeof(struct objc_method *)); - list[count] = NULL; - count = 0; - for (methods = cls->methods; methods != NULL; methods = methods->next) - { - unsigned int index; - for (index = 0; index < methods->count; index++) - { - list[count++] = method_at_index(methods, index); - } - } - - return list; -} - -Protocol*__unsafe_unretained* class_copyProtocolList(Class cls, unsigned int *outCount) -{ - if (outCount != NULL) - { - *outCount = 0x0; - } - - CHECK_ARG(cls); - struct objc_protocol_list *protocolList = NULL; - struct objc_protocol_list *list; - unsigned int count = 0; - Protocol **protocols; - - if (Nil != cls) - { - protocolList = cls->protocols; - } - for (list = protocolList; list != NULL; list = list->next) - { - count += list->count; - } - if (outCount != NULL) - { - *outCount = count; - } - if (count == 0) - { - return NULL; - } - - protocols = malloc((count + 1) * sizeof(Protocol *)); - protocols[count] = NULL; - count = 0; - for (list = protocolList; list != NULL; list = list->next) - { - memcpy(&protocols[count], list->list, list->count * sizeof(Protocol *)); - count += list->count; - } - return protocols; -} - -id class_createInstance(Class cls, size_t extraBytes) -{ - CHECK_ARG(cls); - if (sizeof(id) == 4) - { - if (cls == SmallObjectClasses[0]) - { - return (id)1; - } - } - else - { - for (int i=0 ; i<4 ; i++) - { - if (cls == SmallObjectClasses[i]) - { - return (id)(uintptr_t)((i<<1)+1); - } - } - } - - if (Nil == cls) { return nil; } - // Don't try to allocate an object of size 0, because there's no space for - // its isa pointer! - if (cls->instance_size < sizeof(Class)) { return nil; } - id obj = gc->allocate_class(cls, extraBytes); - obj->isa = cls; - checkARCAccessorsSlow(cls); - call_cxx_construct(obj); - return obj; -} - -id object_copy(id obj, size_t size) -{ - Class cls = object_getClass(obj); - id cpy = class_createInstance(cls, size - class_getInstanceSize(cls)); - memcpy(((char*)cpy + sizeof(id)), ((char*)obj + sizeof(id)), size - sizeof(id)); - return cpy; -} - -id object_dispose(id obj) -{ - call_cxx_destruct(obj); - gc->free_object(obj); - return nil; -} - -Method class_getInstanceMethod(Class aClass, SEL aSelector) -{ - CHECK_ARG(aClass); - CHECK_ARG(aSelector); - // If the class has a dtable installed, then we can use the fast path - if (classHasInstalledDtable(aClass)) - { - // Do a dtable lookup to find out which class the method comes from. - struct objc_slot2 *slot = objc_get_slot2(aClass, aSelector, NULL); - if (NULL == slot) - { - slot = objc_get_slot2(aClass, sel_registerName(sel_getName(aSelector)), NULL); - if (NULL == slot) - { - return NULL; - } - } - // Slots are the same as methods. - return (struct objc_method*)slot; - } - Method m = class_getInstanceMethodNonrecursive(aClass, aSelector); - if (NULL != m) - { - return m; - } - return class_getInstanceMethod(class_getSuperclass(aClass), aSelector); -} - -Method class_getClassMethod(Class aClass, SEL aSelector) -{ - return class_getInstanceMethod(object_getClass((id)aClass), aSelector); -} - -Ivar class_getClassVariable(Class cls, const char* name) -{ - // Note: We don't have compiler support for cvars in ObjC - return class_getInstanceVariable(object_getClass((id)cls), name); -} - -size_t class_getInstanceSize(Class cls) -{ - if (Nil == cls) { return 0; } - return cls->instance_size; -} - -Ivar class_getInstanceVariable(Class cls, const char *name) -{ - if (name != NULL) - { - while (cls != Nil) - { - struct objc_ivar_list *ivarlist = cls->ivars; - - if (ivarlist != NULL) - { - for (int i = 0; i < ivarlist->count; i++) - { - Ivar ivar = ivar_at_index(ivarlist, i); - if (strcmp(ivar->name, name) == 0) - { - return ivar; - } - } - } - cls = class_getSuperclass(cls); - } - } - return NULL; -} - -// The format of the char* is undocumented. This function is only ever used in -// conjunction with class_setIvarLayout(). -const char *class_getIvarLayout(Class cls) -{ - CHECK_ARG(cls); - return (char*)cls->ivars; -} - - -const char * class_getName(Class cls) -{ - if (Nil == cls) { return "nil"; } - return cls->name; -} - -int class_getVersion(Class theClass) -{ - CHECK_ARG(theClass); - return theClass->version; -} - -const char *class_getWeakIvarLayout(Class cls) -{ - assert(0 && "Weak ivars not supported"); - return NULL; -} - -BOOL class_isMetaClass(Class cls) -{ - CHECK_ARG(cls); - return objc_test_class_flag(cls, objc_class_flag_meta); -} - -IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) -{ - if (Nil == cls) { return (IMP)0; } - SEL sel = sel_registerTypedName_np(sel_getName(name), types); - Method method = class_getInstanceMethodNonrecursive(cls, sel); - if (method == NULL) - { - class_addMethod(cls, sel, imp, types); - return NULL; - } - IMP old = (IMP)method->imp; - method->imp = imp; - return old; -} - - -void class_setIvarLayout(Class cls, const char *layout) -{ - if ((Nil == cls) || (NULL == layout)) { return; } - struct objc_ivar_list *list = (struct objc_ivar_list*)layout; - size_t listsize = sizeof(struct objc_ivar_list) + - sizeof(struct objc_ivar) * (list->count); - cls->ivars = malloc(listsize); - memcpy(cls->ivars, list, listsize); -} - -__attribute__((deprecated)) -Class class_setSuperclass(Class cls, Class newSuper) -{ - CHECK_ARG(cls); - CHECK_ARG(newSuper); - Class oldSuper; - if (Nil == cls) { return Nil; } - - { - LOCK_RUNTIME_FOR_SCOPE(); - - oldSuper = cls->super_class; - - if (oldSuper == newSuper) { return newSuper; } - - safe_remove_from_subclass_list(cls); - objc_resolve_class(newSuper); - - cls->super_class = newSuper; - - // The super class's subclass list is used in certain method resolution scenarios. - cls->sibling_class = cls->super_class->subclass_list; - cls->super_class->subclass_list = cls; - - if (UNLIKELY(class_isMetaClass(cls))) - { - // newSuper is presumably a metaclass. Its isa will therefore be the appropriate root metaclass. - cls->isa = newSuper->isa; - } - else - { - Class meta = cls->isa, newSuperMeta = newSuper->isa; - // Update the metaclass's superclass. - safe_remove_from_subclass_list(meta); - objc_resolve_class(newSuperMeta); - - meta->super_class = newSuperMeta; - meta->isa = newSuperMeta->isa; - - // The super class's subclass list is used in certain method resolution scenarios. - meta->sibling_class = newSuperMeta->subclass_list; - newSuperMeta->subclass_list = meta; - } - - LOCK_FOR_SCOPE(&initialize_lock); - if (!objc_test_class_flag(cls, objc_class_flag_initialized)) - { - // Uninitialized classes don't have dtables to update - // and don't need their superclasses initialized. - return oldSuper; - } - } - - objc_send_initialize((id)newSuper); // also initializes the metaclass - objc_update_dtable_for_new_superclass(cls->isa, newSuper->isa); - objc_update_dtable_for_new_superclass(cls, newSuper); - - return oldSuper; -} - -void class_setVersion(Class theClass, int version) -{ - if (Nil == theClass) { return; } - theClass->version = version; -} - -void class_setWeakIvarLayout(Class cls, const char *layout) -{ - assert(0 && "Not implemented"); -} - -const char * ivar_getName(Ivar ivar) -{ - CHECK_ARG(ivar); - return ivar->name; -} - -ptrdiff_t ivar_getOffset(Ivar ivar) -{ - CHECK_ARG(ivar); - return *ivar->offset; -} - -const char * ivar_getTypeEncoding(Ivar ivar) -{ - CHECK_ARG(ivar); - return ivar->type; -} - - -void method_exchangeImplementations(Method m1, Method m2) -{ - if (NULL == m1 || NULL == m2) { return; } - IMP tmp = (IMP)m1->imp; - m1->imp = m2->imp; - m2->imp = tmp; -} - -IMP method_getImplementation(Method method) -{ - if (NULL == method) { return (IMP)NULL; } - return (IMP)method->imp; -} - -SEL method_getName(Method method) -{ - if (NULL == method) { return (SEL)NULL; } - return (SEL)method->selector; -} - - -IMP method_setImplementation(Method method, IMP imp) -{ - if (NULL == method) { return (IMP)NULL; } - IMP old = (IMP)method->imp; - method->imp = imp; - return old; -} - -id objc_getRequiredClass(const char *name) -{ - CHECK_ARG(name); - id cls = objc_getClass(name); - if (nil == cls) - { - abort(); - } - return cls; -} - -PRIVATE void freeMethodLists(Class aClass) -{ - struct objc_method_list *methods = aClass->methods; - while(methods != NULL) - { - for (int i=0 ; icount ; i++) - { - free((void*)method_at_index(methods, i)->types); - } - struct objc_method_list *current = methods; - methods = methods->next; - free(current); - } -} - -PRIVATE void freeIvarLists(Class aClass) -{ - struct objc_ivar_list *ivarlist = aClass->ivars; - if (NULL == ivarlist) { return; } - - if (ivarlist->count > 0) - { - // For dynamically created classes, ivar offset variables are allocated - // as a contiguous range starting with the first one. - free(ivar_at_index(ivarlist, 0)->offset); - } - - for (int i=0 ; icount ; i++) - { - Ivar ivar = ivar_at_index(ivarlist, i); - free((void*)ivar->type); - free((void*)ivar->name); - } - free(ivarlist); -} - -/* - * Removes a class from the subclass list found on its super class. - * Must be called with the objc runtime mutex locked. - */ -static inline void safe_remove_from_subclass_list(Class cls) -{ - // If this class hasn't been added to the class hierarchy, then this is easy - if (!objc_test_class_flag(cls, objc_class_flag_resolved)) { return; } - Class sub = cls->super_class->subclass_list; - if (sub == cls) - { - cls->super_class->subclass_list = cls->sibling_class; - } - else - { - while (sub != NULL) - { - if (sub->sibling_class == cls) - { - sub->sibling_class = cls->sibling_class; - break; - } - sub = sub->sibling_class; - } - } -} - -void objc_disposeClassPair(Class cls) -{ - if (0 == cls) { return; } - Class meta = ((id)cls)->isa; - // Remove from the runtime system so nothing tries updating the dtable - // while we are freeing the class. - { - LOCK_RUNTIME_FOR_SCOPE(); - safe_remove_from_subclass_list(meta); - safe_remove_from_subclass_list(cls); - class_table_remove(cls); - } - - // Free the method and ivar lists. - freeMethodLists(cls); - freeMethodLists(meta); - freeIvarLists(cls); - if (cls->dtable != uninstalled_dtable) - { - free_dtable(cls->dtable); - } - if (meta->dtable != uninstalled_dtable) - { - free_dtable(meta->dtable); - } - - // Free the class and metaclass - gc->free(meta); - gc->free(cls); -} - -Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) -{ - // Check the class doesn't already exist. - if (nil != objc_lookUpClass(name)) { return Nil; } - - Class newClass = gc->malloc(sizeof(struct objc_class) + extraBytes); - - if (Nil == newClass) { return Nil; } - - // Create the metaclass - Class metaClass = gc->malloc(sizeof(struct objc_class)); - - if (Nil == superclass) - { - /* - * Metaclasses of root classes are precious little flowers and work a - * little differently: - */ - metaClass->isa = metaClass; - metaClass->super_class = newClass; - } - else - { - // Initialize the metaclass - // Set the meta-metaclass pointer to the name. The runtime will fix this - // in objc_resolve_class(). - // If the superclass is not yet resolved, then we need to look it up - // via the class table. - metaClass->isa = superclass->isa; - metaClass->super_class = superclass->isa; - } - metaClass->name = strdup(name); - metaClass->info = objc_class_flag_meta | objc_class_flag_user_created; - metaClass->dtable = uninstalled_dtable; - metaClass->instance_size = sizeof(struct objc_class); - - // Set up the new class - newClass->isa = metaClass; - newClass->super_class = superclass; - - newClass->name = strdup(name); - newClass->info = objc_class_flag_user_created; - newClass->dtable = uninstalled_dtable; - - newClass->abi_version = 2; - metaClass->abi_version = 2; - - if (Nil == superclass) - { - newClass->instance_size = sizeof(struct objc_class*); - } - else - { - newClass->instance_size = superclass->instance_size; - } - - return newClass; -} - - -void *object_getIndexedIvars(id obj) -{ - CHECK_ARG(obj); - size_t size = classForObject(obj)->instance_size; - if ((0 == size) && class_isMetaClass(classForObject(obj))) - { - size = sizeof(struct objc_class); - } - return ((char*)obj) + size; -} - -Class object_getClass(id obj) -{ - CHECK_ARG(obj); - Class isa = classForObject(obj); - while ((Nil != isa) && objc_test_class_flag(isa, objc_class_flag_hidden_class)) - { - isa = isa->super_class; - } - return isa; -} - -Class object_setClass(id obj, Class cls) -{ - CHECK_ARG(obj); - // If this is a small object, then don't set its class. - if (isSmallObject(obj)) { return classForObject(obj); } - Class oldClass = obj->isa; - obj->isa = cls; - return oldClass; -} - -const char *object_getClassName(id obj) -{ - CHECK_ARG(obj); - return class_getName(object_getClass(obj)); -} - -void objc_registerClassPair(Class cls) -{ - if (cls->ivars != NULL) - { - int *ptrs = calloc(cls->ivars->count, sizeof(int)); - for (int i=0 ; iivars->count ; i++) - { - ptrs[i] = (int)(intptr_t)ivar_at_index(cls->ivars, i)->offset; - ivar_at_index(cls->ivars, i)->offset = &ptrs[i]; - } - } - LOCK_RUNTIME_FOR_SCOPE(); - class_table_insert(cls); - objc_resolve_class(cls); -} - diff --git a/safewindows.h b/safewindows.h deleted file mode 100644 index 8a0850d..0000000 --- a/safewindows.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __LIBOBJC_SAFEWINDOWS_H_INCLUDED__ -#define __LIBOBJC_SAFEWINDOWS_H_INCLUDED__ - -#pragma push_macro("BOOL") - -#ifdef BOOL -#undef BOOL -#endif -#define BOOL _WINBOOL - -#include - -// Windows.h defines interface -> struct -#ifdef interface -#undef interface -#endif - -#pragma pop_macro("BOOL") - -#endif // __LIBOBJC_SAFEWINDOWS_H_INCLUDED__ diff --git a/sarray2.c b/sarray2.c deleted file mode 100644 index 5f50f6d..0000000 --- a/sarray2.c +++ /dev/null @@ -1,254 +0,0 @@ -#include -#include -#include -#include - -#include "sarray2.h" -#include "visibility.h" - -const static SparseArray EmptyArray = { 0, 0, .data[0 ... 255] = 0 }; -const static SparseArray EmptyArray8 = { 8, 0, .data[0 ... 255] = (void*)&EmptyArray}; -const static SparseArray EmptyArray16 = { 16, 0, .data[0 ... 255] = (void*)&EmptyArray8}; -const static SparseArray EmptyArray24 = { 24, 0, .data[0 ... 255] = (void*)&EmptyArray16}; - -#define MAX_INDEX(sarray) (0xff) - -// Tweak this value to trade speed for memory usage. Bigger values use more -// memory, but give faster lookups. -#define base_shift 8 -#define base_mask ((1<shift != 0) - { - void *data = EmptyChildForShift(sarray->shift); - for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) - { - sarray->data[i] = data; - } - } -} - -PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth) -{ - SparseArray * sarray = calloc(1, sizeof(SparseArray)); - sarray->refCount = 1; - sarray->shift = depth-base_shift; - init_pointers(sarray); - return sarray; -} - -PRIVATE SparseArray *SparseArrayNew() -{ - return SparseArrayNewWithDepth(32); -} -PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth) -{ - if (new_depth == sarray->shift) - { - return sarray; - } - assert(new_depth > sarray->shift); - // Expanding a child sarray has undefined results. - assert(sarray->refCount == 1); - SparseArray *new = calloc(1, sizeof(SparseArray)); - new->refCount = 1; - new->shift = sarray->shift + 8; - new->data[0] = sarray; - void *data = EmptyChildForShift(new->shift); - for(unsigned i=1 ; i<=MAX_INDEX(sarray) ; i++) - { - new->data[i] = data; - } - // Now, any lookup in sarray for any value less than its capacity will have - // all non-zero values shifted away, resulting in 0. All lookups will - // therefore go to the new sarray. - return new; -} - -static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) -{ - uint32_t j = MASK_INDEX((*index)); - uint32_t max = MAX_INDEX(sarray); - if (sarray->shift == 0) - { - while (j<=max) - { - if (sarray->data[j] != SARRAY_EMPTY) - { - return sarray->data[j]; - } - (*index)++; - j++; - } - } - else while (jshift) >> base_shift); - while (jdata[j]; - // Skip over known-empty children - if ((&EmptyArray == child) || - (&EmptyArray8 == child) || - (&EmptyArray16 == child) || - (&EmptyArray24 == child)) - { - //Add 2^n to index so j is still correct - (*index) += 1<shift; - //Zero off the next component of the index so we don't miss any. - *index &= zeromask; - } - else - { - // The recursive call will set index to the correct value for - // the next index, but won't update j - void * ret = SparseArrayFind(child, index); - if (ret != SARRAY_EMPTY) - { - return ret; - } - } - //Go to the next child - j++; - } - } - return SARRAY_EMPTY; -} - -PRIVATE void *SparseArrayNext(SparseArray * sarray, uint32_t * idx) -{ - (*idx)++; - return SparseArrayFind(sarray, idx); -} - -PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value) -{ - if (sarray->shift > 0) - { - uint32_t i = MASK_INDEX(index); - SparseArray *child = sarray->data[i]; - if ((&EmptyArray == child) || - (&EmptyArray8 == child) || - (&EmptyArray16 == child) || - (&EmptyArray24 == child)) - { - // Insert missing nodes - SparseArray * newsarray = calloc(1, sizeof(SparseArray)); - newsarray->refCount = 1; - if (base_shift >= sarray->shift) - { - newsarray->shift = 0; - } - else - { - newsarray->shift = sarray->shift - base_shift; - } - init_pointers(newsarray); - sarray->data[i] = newsarray; - child = newsarray; - } - else if (child->refCount > 1) - { - // Copy the copy-on-write part of the tree - sarray->data[i] = SparseArrayCopy(child); - SparseArrayDestroy(child); - child = sarray->data[i]; - } - SparseArrayInsert(child, index, value); - } - else - { - sarray->data[MASK_INDEX(index)] = value; - } -} - -PRIVATE SparseArray *SparseArrayCopy(SparseArray * sarray) -{ - SparseArray *copy = calloc(sizeof(SparseArray), 1); - memcpy(copy, sarray, sizeof(SparseArray)); - copy->refCount = 1; - // If the sarray has children, increase their refcounts and link them - if (sarray->shift > 0) - { - for (unsigned int i = 0 ; i<=MAX_INDEX(sarray); i++) - { - SparseArray *child = copy->data[i]; - if (!(child == &EmptyArray || - child == &EmptyArray8 || - child == &EmptyArray16 || - child == &EmptyArray24)) - { - __sync_fetch_and_add(&child->refCount, 1); - } - // Non-lazy copy. Uncomment if debugging - // copy->data[i] = SparseArrayCopy(copy->data[i]); - } - } - return copy; -} - -PRIVATE void SparseArrayDestroy(SparseArray * sarray) -{ - // Don't really delete this sarray if its ref count is > 0 - if (sarray == &EmptyArray || - sarray == &EmptyArray8 || - sarray == &EmptyArray16 || - sarray == &EmptyArray24 || - (__sync_sub_and_fetch(&sarray->refCount, 1) > 0)) - { - return; - } - - if(sarray->shift > 0) - { - for(uint32_t i=0 ; idata[i]); - } - } - free(sarray); -} - -#if 0 -// Unused function, but helpful when debugging. -PRIVATE int SparseArraySize(SparseArray *sarray) -{ - int size = 0; - if (sarray->shift == 0) - { - return 256*sizeof(void*) + sizeof(SparseArray); - } - size += 256*sizeof(void*) + sizeof(SparseArray); - for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) - { - SparseArray *child = sarray->data[i]; - if (child == &EmptyArray || - child == &EmptyArray8 || - child == &EmptyArray16) - { - continue; - } - size += SparseArraySize(child); - } - return size; -} -#endif diff --git a/sarray2.h b/sarray2.h deleted file mode 100644 index b287438..0000000 --- a/sarray2.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Sparse Array - * - * Author: David Chisnall - * - * License: See COPYING.MIT - * - */ - -#ifndef _SARRAY_H_INCLUDED_ -#define _SARRAY_H_INCLUDED_ -#include -#include -#include "visibility.h" - - -/** - * The size of the data array. The sparse array is a tree with this many - * children at each node depth. - */ -static const uint32_t data_size = 256; -/** - * The mask used to access the elements in the data array in a sparse array - * node. - */ -static const uint32_t data_mask = data_size - 1; -/** - * Sparse arrays, used to implement dispatch tables. Current implementation is - * quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers. - * - * Note that deletion from the array is not supported. This allows accesses to - * be done without locking; the worst that can happen is that the caller gets - * an old value (and if this is important to you then you should be doing your - * own locking). For this reason, you should be very careful when deleting a - * sparse array that there are no references to it held by other threads. - */ -typedef struct -{ - /** - * Number of bits that the masked value should be right shifted by to get - * the index in the subarray. If this value is greater than zero, then the - * value in the array is another SparseArray*. - */ - uint32_t shift; - /** - * The reference count for this. Used for copy-on-write. When making a - * copy of a sparse array, we only copy the root node, and increment the - * reference count of the remaining nodes. When modifying any leaf node, - * we copy if its reference count is greater than one. - */ - uint32_t refCount; - /** - * The data stored in this sparse array node. - */ - void *data[data_size]; -} SparseArray; - -/** - * Turn an index in the array into an index in the current depth. - */ -#define MASK_INDEX(index) \ - ((index >> sarray->shift) & 0xff) - -#define SARRAY_EMPTY ((void*)0) -/** - * Look up the specified value in the sparse array. This is used in message - * dispatch and so has been put in the header to allow compilers to inline it, - * even though this breaks the abstraction. - */ -static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index) -{ - // This unrolled version of the commented-out segment below only works with - // sarrays that use one-byte leafs. It's really ugly, but seems to be faster. - // With this version, we get the same performance as the old GNU code, but - // with about half the memory usage. - uint32_t i = index; - switch (sarray->shift) - { - default: UNREACHABLE("broken sarray"); - case 0: - return sarray->data[i & 0xff]; - case 8: - return - ((SparseArray*)sarray->data[(i & 0xff00)>>8])->data[(i & 0xff)]; - case 16: - return - ((SparseArray*)((SparseArray*) - sarray->data[(i & 0xff0000)>>16])-> - data[(i & 0xff00)>>8])->data[(i & 0xff)]; - case 24: - return - ((SparseArray*)((SparseArray*)((SparseArray*) - sarray->data[(i & 0xff000000)>>24])-> - data[(i & 0xff0000)>>16])-> - data[(i & 0xff00)>>8])->data[(i & 0xff)]; - } - /* - while(sarray->shift > 0) - { - uint32_t i = MASK_INDEX(index); - sarray = (SparseArray*) sarray->data[i]; - } - uint32_t i = index & sarray->mask; - return sarray->data[i]; - */ -} -/** - * Create a new sparse array. - */ -SparseArray *SparseArrayNew(); -/** - * Creates a new sparse array with the specified capacity. The depth indicates - * the number of bits to use for the key. Must be a value between 8 and 32 and - * should ideally be a multiple of base_shift. - */ -SparseArray *SparseArrayNewWithDepth(uint32_t depth); -/** - * Returns a new sparse array created by adding this one as the first child - * node in an expanded one. - */ -SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth); -/** - * Insert a value at the specified index. - */ -void SparseArrayInsert(SparseArray * sarray, uint32_t index, void * value); -/** - * Destroy the sparse array. Note that calling this while other threads are - * performing lookups is guaranteed to break. - */ -void SparseArrayDestroy(SparseArray * sarray); -/** - * Iterate through the array. Returns the next non-NULL value after index and - * sets index to the following value. For example, an array containing values - * at 0 and 10 will, if called with index set to 0 first return the value at 0 - * and set index to 1. A subsequent call with index set to 1 will return the - * value at 10 and set index to 11. - */ -void * SparseArrayNext(SparseArray * sarray, uint32_t * index); - -/** - * Creates a copy of the sparse array. - */ -SparseArray *SparseArrayCopy(SparseArray * sarray); - -/** - * Returns the total memory usage of a sparse array. - */ -int SparseArraySize(SparseArray *sarray); - -#endif //_SARRAY_H_INCLUDED_ diff --git a/selector.h b/selector.h deleted file mode 100644 index 1030749..0000000 --- a/selector.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef OBJC_SELECTOR_H_INCLUDED -#define OBJC_SELECTOR_H_INCLUDED - -/** - * Structure used to store selectors in the list. - */ -// begin: objc_selector -struct objc_selector -{ - union - { - /** - * The name of this selector. Used for unregistered selectors. - */ - const char *name; - /** - * The index of this selector in the selector table. When a selector - * is registered with the runtime, its name is replaced by an index - * uniquely identifying this selector. The index is used for dispatch. - */ - uintptr_t index; - }; - /** - * The Objective-C type encoding of the message identified by this selector. - */ - const char * types; -}; -// end: objc_selector - - -/** - * Returns the untyped variant of a selector. - */ -__attribute__((unused)) -static uint32_t get_untyped_idx(SEL aSel) -{ - SEL untyped = sel_registerTypedName_np(sel_getName(aSel), 0); - return untyped->index; -} - -__attribute__((unused)) -static SEL sel_getUntyped(SEL aSel) -{ - return sel_registerTypedName_np(sel_getName(aSel), 0); -} - -#ifdef __cplusplus -extern "C" -{ -#endif -/** - * Registers the selector. This selector may be returned later, so it must not - * be freed. - */ -SEL objc_register_selector(SEL aSel); - -#ifdef __cplusplus -} -#endif - -/** - * SELECTOR() macro to work around the fact that GCC hard-codes the type of - * selectors. This is functionally equivalent to @selector(), but it ensures - * that the selector has the type that the runtime uses for selectors. - */ -#ifdef __clang__ -#define SELECTOR(x) @selector(x) -#else -#define SELECTOR(x) (SEL)@selector(x) -#endif - -#endif // OBJC_SELECTOR_H_INCLUDED diff --git a/selector_table.cc b/selector_table.cc deleted file mode 100644 index 3723ad5..0000000 --- a/selector_table.cc +++ /dev/null @@ -1,741 +0,0 @@ -/** - * Handle selector uniquing. - * - * When building, you may define TYPE_DEPENDENT_DISPATCH to enable message - * sends to depend on their types. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "class.h" -#include "lock.h" -#include "method.h" -#include "objc/runtime.h" -#include "pool.hh" -#include "selector.h" -#include "string_hash.h" -#include "visibility.h" - -#ifdef TYPE_DEPENDENT_DISPATCH -# define TDD(x) x -#else -# define TDD(x) -#endif - -namespace { - -/** - * Type representing a selector that has not been registered with the runtime. - * - * This is used only for looking up entries in the selector table, it is never - * stored. - */ -struct UnregisteredSelector -{ - /// The selector name. - const char *name; - - /// The type encoding of the selector. - const char *types; -}; - -/** - * Class for holding the name and list of types for a selector. With - * type-dependent dispatch, we store all of the types that we've seen for each - * selector name alongside the untyped variant of the selector. When a - * selector is registered with the runtime, its name is replaced with the UID - * (dtable index) used for dispatch and we use the first element of the types - * list to store the name. - * - * In the common case, this will have 1-2 entries. - */ -struct TypeList : public std::forward_list -{ - /// The superclass type. - using Super = std::forward_list; - /// Inherit constructors - using Super::forward_list; - - /// Get the name of the selector represented by this list - const char *name() - { - return front(); - } - - /** - * Begin iterator. This skips the name and returns an iterator to the - * first type. - */ - auto begin() - { - return ++(std::forward_list::begin()); - } - - /** - * Add a type. The order of types is not defined and so, for simplicity, - * we store new ones immediately after the name element. - */ - void add_types(const char *types) - { - // Types cannot be added to an empty type list, a name is the first element. - assert(!empty()); - insert_after(Super::begin(), types); - } -}; - -/** - * Mapping from selector numbers to selector names, followed by types. - * - * Note: This must be a pointer so that we do not hit issues with - */ -std::vector *selector_list; - -/** - * Lock protecting the selector table. - */ -RecursiveMutex selector_table_lock; - -/// Type to use as a lock guard -using LockGuard = std::lock_guard; - -inline TypeList *selLookup_locked(uint32_t idx) -{ - if (idx >= selector_list->size()) - { - return nullptr; - } - return &(*selector_list)[idx]; -} - -inline TypeList *selLookup(uint32_t idx) -{ - LockGuard g{selector_table_lock}; - return selLookup_locked(idx); -} - -BOOL isSelRegistered(SEL sel) -{ - if (sel->index < selector_list->size()) - { - return YES; - } - return NO; -} - -/// Gets the name of a registered selector. -const char *sel_getNameRegistered(SEL sel) -{ - const char *name = sel->name; - return selLookup_locked(sel->index)->name(); -} - -/** - * Gets the name of a selector that might not have been registered. This - * should be used only on legacy-ABI compatibility code paths. - */ -const char *sel_getNameNonUnique(SEL sel) -{ - const char *name = sel->name; - if (isSelRegistered(sel)) - { - auto* list = selLookup_locked(sel->index); - name = (list == nullptr) ? nullptr : list->name(); - } - if (nullptr == name) - { - name = ""; - } - return name; -} - -/** - * Skip anything in a type encoding that is irrelevant to the comparison - * between selectors, including type qualifiers and argframe info. - */ -static const char *skip_irrelevant_type_info(const char *t) -{ - switch (*t) - { - default: return t; - case 'r': case 'n': case 'N': case 'o': case 'O': case 'R': - case 'V': case 'A': case '!': case '0'...'9': - return skip_irrelevant_type_info(t+1); - } -} - -static BOOL selector_types_equal(const char *t1, const char *t2) -{ - if (t1 == nullptr || t2 == nullptr) { return t1 == t2; } - - while (('\0' != *t1) && ('\0' != *t2)) - { - t1 = skip_irrelevant_type_info(t1); - t2 = skip_irrelevant_type_info(t2); - // This is a really ugly hack. For some stupid reason, the people - // designing Objective-C type encodings decided to allow * as a - // shorthand for char*, because strings are 'special'. Unfortunately, - // FSF GCC generates "*" for @encode(BOOL*), while Clang and Apple GCC - // generate "^c" or "^C" (depending on whether BOOL is declared - // unsigned). - // - // The correct fix is to remove * completely from type encodings, but - // unfortunately my time machine is broken so I can't travel to 1986 - // and apply a cluebat to those responsible. - if ((*t1 == '*') && (*t2 != '*')) - { - if (*t2 == '^' && (((*(t2+1) == 'C') || (*(t2+1) == 'c')))) - { - t2++; - } - else - { - return NO; - } - } - else if ((*t2 == '*') && (*t1 != '*')) - { - if (*t1 == '^' && (((*(t1+1) == 'C') || (*(t1+1) == 'c')))) - { - t1++; - } - else - { - return NO; - } - } - else if (*t1 != *t2) - { - return NO; - } - - if ('\0' != *t1) { t1++; } - if ('\0' != *t2) { t2++; } - } - return YES; -} - -#ifdef TYPE_DEPENDENT_DISPATCH - -static BOOL selector_types_equivalent(const char *t1, const char *t2) -{ - // We always treat untyped selectors as having the same type as typed - // selectors, for dispatch purposes. - if (t1 == nullptr || t2 == nullptr) { return YES; } - - return selector_types_equal(t1, t2); -} -#endif - -/** - * Compare selectors based on whether they are treated as equivalent for the - * purpose of dispatch. - */ -struct SelectorEqual -{ - /// Opt into heterogeneous lookup - using is_transparent = void; - - /// Compare two registered selectors - bool operator()(const SEL a, const SEL b) const - { -#ifdef TYPE_DEPENDENT_DISPATCH - return string_compare(sel_getNameRegistered(a), sel_getNameRegistered(b)) && - selector_types_equal(sel_getType_np(a), sel_getType_np(b)); -#else - return string_compare(sel_getNameRegistered(a), sel_getNameRegistered(b)); -#endif - } - - /// Compare an unregistered and registered selector - bool operator()(const UnregisteredSelector &a, const SEL b) const - { -#ifdef TYPE_DEPENDENT_DISPATCH - return string_compare(a.name, sel_getNameRegistered(b)) && - selector_types_equal(a.types, sel_getType_np(b)); -#else - return string_compare(a.name, sel_getNameRegistered(b)); -#endif - } - - /// Compare a registered and unregistered selector - bool operator()(const SEL b, const UnregisteredSelector &a) const - { - return (*this)(a, b); - } -}; - -/** - * Compare whether two selectors are identical. - */ -static int selector_identical(const UnregisteredSelector &key, - const SEL value) -{ - return SelectorEqual{}(key, value); -} - - -/** - * Hash a selector. - */ -struct SelectorHash -{ - size_t hash(const char *name, const char *types) const - { - size_t hash = 5381; - const char *str = name; - size_t c; - while((c = (size_t)*str++)) - { - hash = hash * 33 + c; - } -#ifdef TYPE_DEPENDENT_DISPATCH - // We can't use all of the values in the type encoding for the hash, - // because our equality test is a bit more complex than simple string - // encoding (for example, * and ^C have to be considered equivalent, since - // they are both used as encodings for C strings in different situations) - if ((str = types)) - { - while((c = (size_t)*str++)) - { - switch (c) - { - case '@': case 'i': case 'I': case 'l': case 'L': - case 'q': case 'Q': case 's': case 'S': - hash = hash * 33 + c; - } - } - } -#endif - return hash; - } - - size_t operator()(objc_selector *sel) const - { - return hash(sel_getNameNonUnique(sel), sel_getType_np(sel)); - } - - size_t operator()(const UnregisteredSelector &sel) const - { - return hash(sel.name, sel.types); - } -}; - -using SelectorAllocator = PoolAllocate; -using SelectorTable = tsl::robin_set; - -/** - * Table of registered selector. Maps from selector to selector. - */ -static SelectorTable *selector_table; - -static int selector_name_copies; -} - -extern "C" PRIVATE void log_selector_memory_usage(void) -{ -#if 0 - fprintf(stderr, "%lu bytes in selector name list.\n", (unsigned long)(table_size * sizeof(void*))); - fprintf(stderr, "%d bytes in selector names.\n", selector_name_copies); - fprintf(stderr, "%d bytes (%d entries) in selector hash table.\n", (int)(sel_table->table_size * - sizeof(struct selector_table_cell_struct)), sel_table->table_size); - fprintf(stderr, "%d selectors registered.\n", selector_count); - fprintf(stderr, "%d hash table cells per selector (%.2f%% full)\n", sel_table->table_size / selector_count, ((float)selector_count) / sel_table->table_size * 100); -#endif -} - - - - -/** - * Resizes the dtables to ensure that they can store as many selectors as - * exist. - */ -extern "C" void objc_resize_dtables(uint32_t); - -/** - * Create data structures to store selectors. - */ -extern "C" PRIVATE void init_selector_tables() -{ - selector_list = new std::vector(1<<16); - selector_table = new SelectorTable(1024); - selector_table_lock.init(); -} - -static SEL selector_lookup(const char *name, const char *types) -{ - UnregisteredSelector sel = {name, types}; - LockGuard g{selector_table_lock}; - auto result = selector_table->find(sel); - return (result == selector_table->end()) ? nullptr : *result; -} - -static inline void add_selector_to_table(SEL aSel) -{ - // Store the name at the head of the list. - if (selector_list->capacity() == selector_list->size()) - { - selector_list->reserve(selector_list->capacity() * 2); - } - selector_list->push_back({aSel->name}); - // Set the selector's name to the uid. - aSel->index = selector_list->size() - 1; - // Store the selector in the set. - selector_table->insert(aSel); -} - -/** - * Really registers a selector. Must be called with the selector table locked. - */ -static inline void register_selector_locked(SEL aSel) -{ - if (aSel->name == nullptr) - { - return; - } - if (nullptr == aSel->types) - { - add_selector_to_table(aSel); - objc_resize_dtables(selector_list->size()); - return; - } - SEL untyped = selector_lookup(aSel->name, 0); - // If this has a type encoding, store the untyped version too. - if (untyped == nullptr) - { - untyped = SelectorAllocator::allocate(); - untyped->name = aSel->name; - untyped->types = 0; - add_selector_to_table(untyped); - } - else - { - // Make sure we only store one copy of the name - aSel->name = sel_getNameNonUnique(untyped); - } - add_selector_to_table(aSel); - - // Add this set of types to the list. - if (aSel->types) - { - (*selector_list)[aSel->index].add_types(aSel->types); - TDD((*selector_list)[untyped->index].add_types(aSel->types)); - } - objc_resize_dtables(selector_list->size()); -} -/** - * Registers a selector. This assumes that the argument is never deallocated. - */ -extern "C" PRIVATE SEL objc_register_selector(SEL aSel) -{ - if (isSelRegistered(aSel)) - { - return aSel; - } - UnregisteredSelector unregistered{aSel->name, aSel->types}; - // Check that this isn't already registered, before we try - SEL registered = selector_lookup(aSel->name, aSel->types); - SelectorEqual eq; - if (nullptr != registered && eq(unregistered, registered)) - { - aSel->name = registered->name; - return registered; - } - assert(!(aSel->types && (strstr(aSel->types, "@\"") != nullptr))); - LockGuard g{selector_table_lock}; - register_selector_locked(aSel); - return aSel; -} - -/** - * Registers a selector by copying the argument. - */ -SEL objc_register_selector_copy(UnregisteredSelector &aSel, BOOL copyArgs) -{ - // If an identical selector is already registered, return it. - SEL copy = selector_lookup(aSel.name, aSel.types); - if ((nullptr != copy) && selector_identical(aSel, copy)) - { - return copy; - } - LockGuard g{selector_table_lock}; - copy = selector_lookup(aSel.name, aSel.types); - if (nullptr != copy && selector_identical(aSel, copy)) - { - return copy; - } - assert(!(aSel.types && (strstr(aSel.types, "@\"") != nullptr))); - // Create a copy of this selector. - copy = SelectorAllocator::allocate(); - copy->name = aSel.name; - copy->types = (nullptr == aSel.types) ? nullptr : aSel.types; - if (copyArgs) - { - SEL untyped = selector_lookup(aSel.name, 0); - if (untyped != nullptr) - { - copy->name = sel_getName(untyped); - } - else - { - copy->name = strdup(aSel.name); - if (copy->name == nullptr) - { - fprintf(stderr, "Failed to allocate memory for selector %s\n", aSel.name); - abort(); - } - assert(copy->name); - selector_name_copies += strlen(copy->name); - } - if (copy->types != nullptr) - { - copy->types = strdup(copy->types); - if (copy->name == nullptr) - { - fprintf(stderr, "Failed to allocate memory for selector %s\n", aSel.name); - abort(); - } - selector_name_copies += strlen(copy->types); - } - } - // Try to register the copy as the authoritative version - register_selector_locked(copy); - return copy; -} - -/** - * Public API functions. - */ -extern "C" -{ - -const char *sel_getName(SEL sel) -{ - if (nullptr == sel) { return ""; } - auto list = selLookup(sel->index); - return (list == nullptr) ? "" : list->front(); -} - -SEL sel_getUid(const char *selName) -{ - return sel_registerName(selName); -} - -BOOL sel_isEqual(SEL sel1, SEL sel2) -{ - if ((0 == sel1) || (0 == sel2)) - { - return sel1 == sel2; - } - if (sel1->name == sel2->name) - { - return YES; - } - // Otherwise, do a slow compare - return string_compare(sel_getNameNonUnique(sel1), sel_getNameNonUnique(sel2)) TDD(&& - (sel1->types == nullptr || sel2->types == nullptr || - selector_types_equivalent(sel_getType_np(sel1), sel_getType_np(sel2)))); -} - -SEL sel_registerName(const char *selName) -{ - if (nullptr == selName) { return nullptr; } - UnregisteredSelector sel = {selName, nullptr}; - return objc_register_selector_copy(sel, YES); -} - -SEL sel_registerTypedName_np(const char *selName, const char *types) -{ - if (nullptr == selName) { return nullptr; } - UnregisteredSelector sel = {selName, types}; - return objc_register_selector_copy(sel, YES); -} - -const char *sel_getType_np(SEL aSel) -{ - if (nullptr == aSel) { return nullptr; } - return aSel->types; -} - - -unsigned sel_copyTypes_np(const char *selName, const char **types, unsigned count) -{ - if (nullptr == selName) { return 0; } - SEL untyped = selector_lookup(selName, 0); - if (untyped == nullptr) { return 0; } - - auto *l = selLookup(untyped->index); - if (l == nullptr) - { - return 0; - } - if (count == 0) - { - for (auto type : *l) - { - count++; - } - return count; - } - - unsigned found = 0; - for (auto type : *l) - { - if (found < count) - { - types[found] = type; - } - found++; - } - return found; -} - -unsigned sel_copyTypedSelectors_np(const char *selName, SEL *const sels, unsigned count) -{ - if (nullptr == selName) { return 0; } - SEL untyped = selector_lookup(selName, 0); - if (untyped == nullptr) { return 0; } - - auto *l = selLookup(untyped->index); - if (l == nullptr) - { - return 0; - } - - if (count == 0) - { - for (auto type : *l) - { - count++; - } - return count; - } - - unsigned found = 0; - for (auto type : *l) - { - if (found > count) - { - break; - } - sels[found++] = selector_lookup(selName, type); - } - return found; -} - -extern "C" PRIVATE void objc_register_selectors_from_list(struct objc_method_list *l) -{ - for (int i=0 ; icount ; i++) - { - Method m = method_at_index(l, i); - UnregisteredSelector sel{(const char*)m->selector, m->types}; - m->selector = objc_register_selector_copy(sel, NO); - } -} -/** - * Register all of the (unregistered) selectors that are used in a class. - */ -extern "C" PRIVATE void objc_register_selectors_from_class(Class aClass) -{ - for (struct objc_method_list *l=aClass->methods ; nullptr!=l ; l=l->next) - { - objc_register_selectors_from_list(l); - } -} -extern "C" PRIVATE void objc_register_selector_array(SEL selectors, unsigned long count) -{ - // GCC is broken and always sets the count to 0, so we ignore count until - // we can throw stupid and buggy compilers in the bin. - for (unsigned long i=0 ; (nullptr != selectors[i].name) ; i++) - { - objc_register_selector(&selectors[i]); - } -} - - -/** - * Legacy GNU runtime compatibility. - * - * All of the functions in this section are deprecated and should not be used - * in new code. - */ -#ifndef NO_LEGACY -SEL sel_get_typed_uid (const char *name, const char *types) -{ - if (nullptr == name) { return nullptr; } - SEL sel = selector_lookup(name, types); - if (nullptr == sel) { return sel_registerTypedName_np(name, types); } - - struct sel_type_list *l = selLookup(sel->index); - // Skip the head, which just contains the name, not the types. - l = l->next; - if (nullptr != l) - { - sel = selector_lookup(name, l->value); - } - return sel; -} - -SEL sel_get_any_typed_uid (const char *name) -{ - if (nullptr == name) { return nullptr; } - SEL sel = selector_lookup(name, 0); - if (nullptr == sel) { return sel_registerName(name); } - - struct sel_type_list *l = selLookup(sel->index); - // Skip the head, which just contains the name, not the types. - l = l->next; - if (nullptr != l) - { - sel = selector_lookup(name, l->value); - } - return sel; -} - -SEL sel_get_any_uid (const char *name) -{ - return selector_lookup(name, 0); -} - -SEL sel_get_uid(const char *name) -{ - return selector_lookup(name, 0); -} - -const char *sel_get_name(SEL selector) -{ - return sel_getNameNonUnique(selector); -} - -BOOL sel_is_mapped(SEL selector) -{ - return isSelRegistered(selector); -} - -const char *sel_get_type(SEL selector) -{ - return sel_getType_np(selector); -} - -SEL sel_register_name(const char *name) -{ - return sel_registerName(name); -} - -SEL sel_register_typed_name(const char *name, const char *type) -{ - return sel_registerTypedName_np(name, type); -} - -BOOL sel_eq(SEL s1, SEL s2) -{ - return sel_isEqual(s1, s2); -} - -#endif // NO_LEGACY -} diff --git a/sendmsg2.c b/sendmsg2.c deleted file mode 100644 index 4ffdebd..0000000 --- a/sendmsg2.c +++ /dev/null @@ -1,493 +0,0 @@ -#include "objc/runtime.h" -#include "lock.h" -#include "dtable.h" -#include "selector.h" -#include "loader.h" -#include "objc/hooks.h" -#include -#include - -void objc_send_initialize(id object); - -static long long nil_method(id self, SEL _cmd) { return 0; } -static long double nil_method_D(id self, SEL _cmd) { return 0; } -static double nil_method_d(id self, SEL _cmd) { return 0; } -static float nil_method_f(id self, SEL _cmd) { return 0; } - -static struct objc_slot nil_slot_v1 = { Nil, Nil, 0, 1, (IMP)nil_method }; -static struct objc_slot nil_slot_D_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_D }; -static struct objc_slot nil_slot_d_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_d }; -static struct objc_slot nil_slot_f_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_f }; - -static struct objc_method nil_slot = { (IMP)nil_method, NULL, NULL }; -static struct objc_method nil_slot_D = { (IMP)nil_method_D, NULL, NULL }; -static struct objc_method nil_slot_d = { (IMP)nil_method_d, NULL, NULL }; -static struct objc_method nil_slot_f = { (IMP)nil_method_f, NULL, NULL }; - -static struct objc_slot2* objc_slot_lookup(id *receiver, SEL selector); - -// Default implementations of the two new hooks. Return NULL. -static id objc_proxy_lookup_null(id receiver, SEL op) { return nil; } -static struct objc_slot *objc_msg_forward3_null(id receiver, SEL op) { return &nil_slot_v1; } - -id (*objc_proxy_lookup)(id receiver, SEL op) = objc_proxy_lookup_null; -struct objc_slot *(*__objc_msg_forward3)(id receiver, SEL op) = objc_msg_forward3_null; - -static IMP forward2(id self, SEL _cmd) -{ - return __objc_msg_forward3(self, _cmd)->method; -} -IMP (*__objc_msg_forward2)(id, SEL) = forward2; - -__thread struct objc_method uncacheable_slot = { (IMP)nil_method, NULL, NULL }; -__thread struct objc_slot uncacheable_slot_v1 = { Nil, Nil, 0, 0, (IMP)nil_method }; - -#ifndef NO_SELECTOR_MISMATCH_WARNINGS -static IMP objc_selector_type_mismatch(Class cls, SEL - selector, struct objc_slot2 *result) -{ - fprintf(stderr, "Calling [%s %c%s] with incorrect signature. " - "Method has %s (%s), selector has %s\n", - cls->name, - class_isMetaClass(cls) ? '+' : '-', - sel_getName(selector), - sel_getType_np(((struct objc_method*)result)->selector), - ((struct objc_method*)result)->types, - sel_getType_np(selector)); - return result->method; -} -#else -static IMP objc_selector_type_mismatch(Class cls, SEL - selector, struct objc_slot2 *result) -{ - return result->method; -} -#endif - -IMP (*_objc_selector_type_mismatch2)(Class cls, SEL - selector, struct objc_slot2 *result) = objc_selector_type_mismatch; -struct objc_slot *(*_objc_selector_type_mismatch)(Class cls, SEL - selector, struct objc_slot *result); - -static IMP call_mismatch_hook(Class cls, SEL sel, struct objc_slot2 *slot) -{ - if (_objc_selector_type_mismatch && - (!_objc_selector_type_mismatch2 || - (_objc_selector_type_mismatch2 == objc_selector_type_mismatch))) - { - struct objc_slot fwdslot; - fwdslot.types = ((struct objc_method*)slot)->types; - fwdslot.selector = sel; - fwdslot.method = slot->method; - struct objc_slot *slot_v1 = _objc_selector_type_mismatch(cls, sel, &uncacheable_slot_v1); - return slot_v1->method; - } - return _objc_selector_type_mismatch2(cls, sel, slot); -} - -static -// Uncomment for debugging -//__attribute__((noinline)) -__attribute__((always_inline)) -struct objc_slot2 *objc_msg_lookup_internal(id *receiver, SEL selector, uint64_t *version) -{ - if (version) - { -#ifdef NO_SAFE_CACHING - // Always write 0 to version, marking the slot as uncacheable. - *version = 0; -#else - *version = objc_method_cache_version; -#endif - } - Class class = classForObject((*receiver)); -retry:; - struct objc_slot2 * result = objc_dtable_lookup(class->dtable, selector->index); - if (UNLIKELY(0 == result)) - { - dtable_t dtable = dtable_for_class(class); - /* Install the dtable if it hasn't already been initialized. */ - if (dtable == uninstalled_dtable) - { - objc_send_initialize(*receiver); - dtable = dtable_for_class(class); - result = objc_dtable_lookup(dtable, selector->index); - } - else - { - // Check again incase another thread updated the dtable while we - // weren't looking - result = objc_dtable_lookup(dtable, selector->index); - } - if (0 == result) - { - if ((result = objc_dtable_lookup(dtable, get_untyped_idx(selector)))) - { -#ifndef NO_SAFE_CACHING - if (version) - { - *version = 0; - } -#endif - uncacheable_slot.imp = call_mismatch_hook(class, selector, result); - result = (struct objc_slot2*)&uncacheable_slot; - } - id newReceiver = objc_proxy_lookup(*receiver, selector); - // If some other library wants us to play forwarding games, try - // again with the new object. - if (nil != newReceiver) - { - *receiver = newReceiver; - return objc_slot_lookup(receiver, selector); - } - if (0 == result) - { -#ifndef NO_SAFE_CACHING - if (version) - { - *version = 0; - } -#endif - uncacheable_slot.imp = __objc_msg_forward2(*receiver, selector); - result = (struct objc_slot2*)&uncacheable_slot; - } - } - } - return result; -} - -PRIVATE IMP slowMsgLookup(id *receiver, SEL cmd) -{ - // By the time we've got here, the assembly version of this function has - // already done the nil checks. - return objc_msg_lookup_internal(receiver, cmd, NULL)->method; -} - -PRIVATE void logInt(void *a) -{ - fprintf(stderr, "Value: %p\n", a); -} - -/** - * New Objective-C lookup function. This permits the lookup to modify the - * receiver and also supports multi-dimensional dispatch based on the sender. - */ -struct objc_slot *objc_msg_lookup_sender(id *receiver, SEL selector, id sender) -{ - // Returning a nil slot allows the caller to cache the lookup for nil too, - // although this is not particularly useful because the nil method can be - // inlined trivially. - if (UNLIKELY(*receiver == nil)) - { - // Return the correct kind of zero, depending on the type encoding. - if (selector->types) - { - const char *t = selector->types; - // Skip type qualifiers - while ('r' == *t || 'n' == *t || 'N' == *t || 'o' == *t || - 'O' == *t || 'R' == *t || 'V' == *t || 'A' == *t) - { - t++; - } - switch (selector->types[0]) - { - case 'D': return &nil_slot_D_v1; - case 'd': return &nil_slot_d_v1; - case 'f': return &nil_slot_f_v1; - } - } - return &nil_slot_v1; - } - - struct objc_slot2 *slot = objc_msg_lookup_internal(receiver, selector, NULL); - uncacheable_slot_v1.owner = Nil; - uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)slot)->selector); - uncacheable_slot_v1.selector = selector; - uncacheable_slot_v1.method = slot->method; - return &uncacheable_slot_v1; -} - - -static struct objc_slot2* objc_slot_lookup(id *receiver, SEL selector) -{ - // Returning a nil slot allows the caller to cache the lookup for nil too, - // although this is not particularly useful because the nil method can be - // inlined trivially. - if (UNLIKELY(*receiver == nil)) - { - // Return the correct kind of zero, depending on the type encoding. - if (selector->types) - { - const char *t = selector->types; - // Skip type qualifiers - while ('r' == *t || 'n' == *t || 'N' == *t || 'o' == *t || - 'O' == *t || 'R' == *t || 'V' == *t || 'A' == *t) - { - t++; - } - switch (selector->types[0]) - { - case 'D': return (struct objc_slot2*)&nil_slot_D; - case 'd': return (struct objc_slot2*)&nil_slot_d; - case 'f': return (struct objc_slot2*)&nil_slot_f; - } - } - return (struct objc_slot2*)&nil_slot; - } - - return objc_msg_lookup_internal(receiver, selector, NULL); -} - -struct objc_slot2 *objc_slot_lookup_version(id *receiver, SEL selector, uint64_t *version) -{ - // Returning a nil slot allows the caller to cache the lookup for nil too, - // although this is not particularly useful because the nil method can be - // inlined trivially. - if (UNLIKELY(*receiver == nil)) - { -#ifndef NO_SAFE_CACHING - if (version) - { - *version = 0; - } -#endif - // Return the correct kind of zero, depending on the type encoding. - if (selector->types) - { - const char *t = selector->types; - // Skip type qualifiers - while ('r' == *t || 'n' == *t || 'N' == *t || 'o' == *t || - 'O' == *t || 'R' == *t || 'V' == *t || 'A' == *t) - { - t++; - } - switch (selector->types[0]) - { - case 'D': return (struct objc_slot2*)&nil_slot_D; - case 'd': return (struct objc_slot2*)&nil_slot_d; - case 'f': return (struct objc_slot2*)&nil_slot_f; - } - } - return (struct objc_slot2*)&nil_slot; - } - - return objc_msg_lookup_internal(receiver, selector, version); -} - -IMP objc_msg_lookup2(id *receiver, SEL selector) -{ - return objc_slot_lookup(receiver, selector)->method; -} - - -struct objc_slot2 *objc_slot_lookup_super2(struct objc_super *super, SEL selector) -{ - id receiver = super->receiver; - if (receiver) - { - Class class = super->class; - struct objc_slot2 * result = objc_dtable_lookup(dtable_for_class(class), - selector->index); - if (0 == result) - { - Class class = classForObject(receiver); - // Dtable should always be installed in the superclass in - // Objective-C, but may not be for other languages (Python). - if (dtable_for_class(class) == uninstalled_dtable) - { - if (class_isMetaClass(class)) - { - objc_send_initialize(receiver); - } - else - { - objc_send_initialize((id)class); - } - objc_send_initialize((id)class); - return objc_slot_lookup_super2(super, selector); - } - uncacheable_slot.imp = __objc_msg_forward2(receiver, selector); - return (struct objc_slot2*)&uncacheable_slot; - } - return result; - } - return (struct objc_slot2*)&nil_slot; -} - -OBJC_PUBLIC -struct objc_slot *objc_slot_lookup_super(struct objc_super *super, SEL selector) -{ - id receiver = super->receiver; - if (receiver) - { - Class class = super->class; - struct objc_slot2 * result = objc_dtable_lookup(dtable_for_class(class), - selector->index); - if (0 == result) - { - Class class = classForObject(receiver); - // Dtable should always be installed in the superclass in - // Objective-C, but may not be for other languages (Python). - if (dtable_for_class(class) == uninstalled_dtable) - { - if (class_isMetaClass(class)) - { - objc_send_initialize(receiver); - } - else - { - objc_send_initialize((id)class); - } - objc_send_initialize((id)class); - return objc_slot_lookup_super(super, selector); - } - uncacheable_slot_v1.owner = Nil; - uncacheable_slot_v1.types = sel_getType_np(selector); - uncacheable_slot_v1.selector = selector; - uncacheable_slot_v1.method = __objc_msg_forward2(receiver, selector); - return &uncacheable_slot_v1; - } - uncacheable_slot_v1.owner = Nil; - uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)result)->selector); - uncacheable_slot_v1.selector = selector; - uncacheable_slot_v1.method = result->method; - return &uncacheable_slot_v1; - } - return &nil_slot_v1; -} - -/** - * looks up a slot without invoking any forwarding mechanisms - */ -struct objc_slot2 *objc_get_slot2(Class cls, SEL selector, uint64_t *version) -{ -#ifndef NO_SAFE_CACHING - if (version) - { - *version = objc_method_cache_version; - } -#endif - struct objc_slot2 * result = objc_dtable_lookup(cls->dtable, selector->index); - if (0 == result) - { - void *dtable = dtable_for_class(cls); - /* Install the dtable if it hasn't already been initialized. */ - if (dtable == uninstalled_dtable) - { - dtable = dtable_for_class(cls); - result = objc_dtable_lookup(dtable, selector->index); - } - else - { - // Check again incase another thread updated the dtable while we - // weren't looking - result = objc_dtable_lookup(dtable, selector->index); - } - if (NULL == result) - { - if ((result = objc_dtable_lookup(dtable, get_untyped_idx(selector)))) - { -#ifndef NO_SAFE_CACHING - if (version) - { - *version = 0; - } -#endif - uncacheable_slot.imp = call_mismatch_hook(cls, selector, result); - result = (struct objc_slot2*)&uncacheable_slot; - } - } - } - return result; -} - -struct objc_slot *objc_get_slot(Class cls, SEL selector) -{ - struct objc_slot2 *result = objc_get_slot2(cls, selector, NULL); - if (result == NULL) - { - return NULL; - } - uncacheable_slot_v1.owner = Nil; - // Don't leak extended type encodings! - uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)result)->selector); - uncacheable_slot_v1.selector = selector; - uncacheable_slot_v1.method = result->method; - return &uncacheable_slot_v1; -} - -//////////////////////////////////////////////////////////////////////////////// -// Public API -//////////////////////////////////////////////////////////////////////////////// - -BOOL class_respondsToSelector(Class cls, SEL selector) -{ - if (0 == selector || 0 == cls) { return NO; } - - return NULL != objc_get_slot2(cls, selector, NULL); -} - -IMP class_getMethodImplementation(Class cls, SEL name) -{ - if ((Nil == cls) || (NULL == name)) { return (IMP)0; } - struct objc_slot2 * slot = objc_get_slot2(cls, name, NULL); - return NULL != slot ? slot->method : __objc_msg_forward2(nil, name); -} - -IMP class_getMethodImplementation_stret(Class cls, SEL name) -{ - return class_getMethodImplementation(cls, name); -} - - -//////////////////////////////////////////////////////////////////////////////// -// Legacy compatibility -//////////////////////////////////////////////////////////////////////////////// - -#ifndef NO_LEGACY -/** - * Legacy message lookup function. - */ -BOOL __objc_responds_to(id object, SEL sel) -{ - return class_respondsToSelector(classForObject(object), sel); -} - -IMP get_imp(Class cls, SEL selector) -{ - return class_getMethodImplementation(cls, selector); -} - -/** - * Message send function that only ever worked on a small subset of compiler / - * architecture combinations. - */ -void *objc_msg_sendv(void) -{ - fprintf(stderr, "objc_msg_sendv() never worked correctly. Don't use it.\n"); - abort(); -} -#endif -/** - * Legacy message lookup function. Does not support fast proxies or safe IMP - * caching. - */ -IMP objc_msg_lookup(id receiver, SEL selector) -{ - if (nil == receiver) { return (IMP)nil_method; } - - id self = receiver; - struct objc_slot2 * slot = objc_msg_lookup_internal(&self, selector, NULL); - // If the receiver is changed by the lookup mechanism then we have to fall - // back to old-style forwarding. - if (self != receiver) - { - return __objc_msg_forward2(receiver, selector); - } - return slot->method; -} - -IMP objc_msg_lookup_super(struct objc_super *super, SEL selector) -{ - return objc_slot_lookup_super2(super, selector)->method; -} diff --git a/spinlock.h b/spinlock.h deleted file mode 100644 index 2efec47..0000000 --- a/spinlock.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifdef _WIN32 -#include "safewindows.h" -static unsigned sleep(unsigned seconds) -{ - Sleep(seconds*1000); - return 0; -} -#else -#include -#endif - -/** - * Number of spinlocks. This allocates one page on 32-bit platforms. - */ -#define spinlock_count (1<<10) -static const int spinlock_mask = spinlock_count - 1; -/** - * Integers used as spinlocks for atomic property access. - */ -extern int spinlocks[spinlock_count]; -/** - * Get a spin lock from a pointer. We want to prevent lock contention between - * properties in the same object - if someone is stupid enough to be using - * atomic property access, they are probably stupid enough to do it for - * multiple properties in the same object. We also want to try to avoid - * contention between the same property in different objects, so we can't just - * use the ivar offset. - */ -static inline volatile int *lock_for_pointer(const void *ptr) -{ - intptr_t hash = (intptr_t)ptr; - // Most properties will be pointers, so disregard the lowest few bits - hash >>= sizeof(void*) == 4 ? 2 : 8; - intptr_t low = hash & spinlock_mask; - hash >>= 16; - hash |= low; - return spinlocks + (hash & spinlock_mask); -} - -/** - * Unlocks the spinlock. This is not an atomic operation. We are only ever - * modifying the lowest bit of the spinlock word, so it doesn't matter if this - * is two writes because there is no contention among the high bit. There is - * no possibility of contention among calls to this, because it may only be - * called by the thread owning the spin lock. - */ -inline static void unlock_spinlock(volatile int *spinlock) -{ - __sync_synchronize(); - *spinlock = 0; -} -/** - * Attempts to lock a spinlock. This is heavily optimised for the uncontended - * case, because property access should (generally) not be contended. In the - * uncontended case, this is a single atomic compare and swap instruction and a - * branch. Atomic CAS is relatively expensive (can be a pipeline flush, and - * may require locking a cache line in a cache-coherent SMP system, but it's a - * lot cheaper than a system call). - * - * If the lock is contended, then we just sleep and then try again after the - * other threads have run. Note that there is no upper bound on the potential - * running time of this function, which is one of the great many reasons that - * using atomic accessors is a terrible idea, but in the common case it should - * be very fast. - */ -inline static void lock_spinlock(volatile int *spinlock) -{ - int count = 0; - // Set the spin lock value to 1 if it is 0. - while(!__sync_bool_compare_and_swap(spinlock, 0, 1)) - { - count++; - if (0 == count % 10) - { - // If it is already 1, let another thread play with the CPU for a - // bit then try again. - sleep(0); - } - } -} - diff --git a/statics_loader.c b/statics_loader.c deleted file mode 100644 index 7dc440c..0000000 --- a/statics_loader.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include "objc/runtime.h" -#include "module.h" -#include "constant_string.h" -#include "visibility.h" - -#define BUFFER_TYPE struct objc_static_instance_list * -#include "buffer.h" - -static BOOL try_init_statics(struct objc_static_instance_list *statics) -{ - const char *class_name = statics->class_name; - - // This is a horrible hack. - // - // Very bad things happen when you have more than one constant string class - // used in a program. Unfortunately, GCC defaults to using - // NXConstantString, and if you forget to specify - // -fconstant-string-class=NSConstantString for some compilation units then - // you will end up with some NSConstantString instances and some - // NXConstantString instances. This is a mess. We hack around this by - // silently assuming that the user meant NSConstantString when they said - // NXConstantString if NSConstantString is set as the constant string class - // in string_class.h or by an external -D flag. - if (strcmp(class_name, "NXConstantString") == 0) - { - class_name = CONSTANT_STRING_CLASS; - } - - Class class = (Class)objc_getClass(class_name); - - if (Nil == class) - { - return NO; - } - for (id *instance=statics->instances ; nil!=*instance ; instance++) - { - (*instance)->isa = class; - } - return YES; -} -PRIVATE void objc_init_statics(struct objc_static_instance_list *statics) -{ - if (!try_init_statics(statics)) - { - set_buffered_object_at_index(statics, buffered_objects++); - } -} - -PRIVATE void objc_init_buffered_statics(void) -{ - BOOL shouldReshuffle = NO; - - for (unsigned i=0 ; i -#include - -/** - * Efficient string hash function. - */ -__attribute__((unused)) -static uint32_t string_hash(const char *str) -{ - uint32_t hash = 0; - int32_t c; - while ((c = *str++)) - { - hash = c + (hash << 6) + (hash << 16) - hash; - } - return hash; -} - -/** - * Test two strings for equality. - */ -__attribute__((unused)) -static int string_compare(const char *str1, const char *str2) -{ - if (str1 == str2) - { - return 1; - } - if (str1 == NULL || str2 == NULL) - { - return 0; - } - return strcmp(str1, str2) == 0; -} diff --git a/type_encoding_cases.h b/type_encoding_cases.h deleted file mode 100644 index 419dc86..0000000 --- a/type_encoding_cases.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * type_encoding_cases.h - expects the APPLY_TYPE macro to be defined. This - * macro is invoked once for every type and its Objective-C name. Use this - * file when implementing things like the -unsignedIntValue family of methods. - * For this case, the macro will be invoked with unsigned int as the type and - * unsignedInt as the name. - */ -#ifndef APPLY_TYPE -#error Define APPLY_TYPE(type, name, capitalizedName, encodingChar) before including this file -#endif -APPLY_TYPE(long double, long double, LongDouble, 'D') -APPLY_TYPE(double, double, Double, 'd') -APPLY_TYPE(float, float, Float, 'f') -APPLY_TYPE(signed char, char, Char, 'c') -APPLY_TYPE(int, int, Int, 'i') -APPLY_TYPE(short, short, Short, 's') -APPLY_TYPE(long, long, Long, 'l') -APPLY_TYPE(long long, longLong, LongLong, 'q') -//APPLY_TYPE(__int128, int128, Int128, 't') -APPLY_TYPE(unsigned char, unsignedChar, UnsignedChar, 'C') -APPLY_TYPE(unsigned short, unsignedShort, UnsignedShort, 'S') -APPLY_TYPE(unsigned int, unsignedInt, UnsignedInt, 'I') -APPLY_TYPE(unsigned long, unsignedLong, UnsignedLong, 'L') -APPLY_TYPE(unsigned long long, unsignedLongLong, UnsignedLongLong, 'Q') -//APPLY_TYPE(unsigned __int128, unsignedInt128, UnsignedInt128, 'T') -#ifdef NON_INTEGER_TYPES -#undef NON_INTEGER_TYPES -APPLY_TYPE(_Bool, bool, Bool, 'B') -#ifndef SKIP_ID -APPLY_TYPE(id, object, Object, '@') -#endif -APPLY_TYPE(Class, class, Class, '#') -APPLY_TYPE(SEL, selector, Selector, ':') -APPLY_TYPE(char*, cString, CString, '*') -#endif -#undef APPLY_TYPE diff --git a/unwind-arm.h b/unwind-arm.h deleted file mode 100644 index 724624e..0000000 --- a/unwind-arm.h +++ /dev/null @@ -1,217 +0,0 @@ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * ARM-specific unwind definitions. These are taken from the ARM EHABI - * specification. - */ - typedef enum -{ - _URC_NO_REASON = 0, - _URC_OK = 0, /* operation completed successfully */ - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8, - _URC_FAILURE = 9, /* unspecified failure of some kind */ - _URC_FATAL_PHASE1_ERROR = _URC_FAILURE -} _Unwind_Reason_Code; - -typedef uint32_t _Unwind_State; -#ifdef __clang__ -static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0; -static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1; -static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2; -static const _Unwind_State _US_FORCE_UNWIND = 8; -#else // GCC fails at knowing what a constant expression is -# define _US_VIRTUAL_UNWIND_FRAME 0 -# define _US_UNWIND_FRAME_STARTING 1 -# define _US_UNWIND_FRAME_RESUME 2 -# define _US_FORCE_UNWIND 8 -#endif - -typedef int _Unwind_Action; - -typedef struct _Unwind_Context _Unwind_Context; - -typedef uint32_t _Unwind_EHT_Header; - -struct _Unwind_Exception -{ - uint64_t exception_class; - void (*exception_cleanup)(_Unwind_Reason_Code, struct _Unwind_Exception *); - /* Unwinder cache, private fields for the unwinder's use */ - struct - { - uint32_t reserved1; - uint32_t reserved2; - uint32_t reserved3; - uint32_t reserved4; - uint32_t reserved5; - /* init reserved1 to 0, then don't touch */ - } unwinder_cache; - /* Propagation barrier cache (valid after phase 1): */ - struct - { - uint32_t sp; - uint32_t bitpattern[5]; - } barrier_cache; - /* Cleanup cache (preserved over cleanup): */ - struct - { - uint32_t bitpattern[4]; - } cleanup_cache; - /* Pr cache (for pr's benefit): */ - struct - { - /** function start address */ - uint32_t fnstart; - /** pointer to EHT entry header word */ - _Unwind_EHT_Header *ehtp; - /** additional data */ - uint32_t additional; - uint32_t reserved1; - } pr_cache; - /** Force alignment of next item to 8-byte boundary */ - long long int :0; -}; - -/* Unwinding functions */ -_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *ucbp); -void _Unwind_Resume(struct _Unwind_Exception *ucbp); -_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *); -void _Unwind_Complete(struct _Unwind_Exception *ucbp); -void _Unwind_DeleteException(struct _Unwind_Exception *ucbp); -void *_Unwind_GetLanguageSpecificData(struct _Unwind_Context*); - -typedef enum -{ - _UVRSR_OK = 0, - _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2 -} _Unwind_VRS_Result; -typedef enum -{ - _UVRSC_CORE = 0, - _UVRSC_VFP = 1, - _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4 -} _Unwind_VRS_RegClass; -typedef enum -{ - _UVRSD_UINT32 = 0, - _UVRSD_VFPX = 1, - _UVRSD_UINT64 = 3, - _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5 -} _Unwind_VRS_DataRepresentation; - -_Unwind_VRS_Result _Unwind_VRS_Get(_Unwind_Context *context, - _Unwind_VRS_RegClass regclass, - uint32_t regno, - _Unwind_VRS_DataRepresentation representation, - void *valuep); -_Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context *context, - _Unwind_VRS_RegClass regclass, - uint32_t regno, - _Unwind_VRS_DataRepresentation representation, - void *valuep); - -/* Return the base-address for data references. */ -extern unsigned long _Unwind_GetDataRelBase(struct _Unwind_Context *); - -/* Return the base-address for text references. */ -extern unsigned long _Unwind_GetTextRelBase(struct _Unwind_Context *); -extern unsigned long _Unwind_GetRegionStart(struct _Unwind_Context *); - -/** - * The next set of functions are compatibility extensions, implementing Itanium - * ABI functions on top of ARM ones. - */ - -#define _UA_SEARCH_PHASE 1 -#define _UA_CLEANUP_PHASE 2 -#define _UA_HANDLER_FRAME 4 -#define _UA_FORCE_UNWIND 8 - -static inline unsigned long _Unwind_GetGR(struct _Unwind_Context *context, int reg) -{ - unsigned long val; - _Unwind_VRS_Get(context, _UVRSC_CORE, reg, _UVRSD_UINT32, &val); - return val; -} -static inline void _Unwind_SetGR(struct _Unwind_Context *context, int reg, unsigned long val) -{ - _Unwind_VRS_Set(context, _UVRSC_CORE, reg, _UVRSD_UINT32, &val); -} -static inline unsigned long _Unwind_GetIP(_Unwind_Context *context) -{ - // Low bit store the thumb state - discard it - return _Unwind_GetGR(context, 15) & ~1; -} -static inline void _Unwind_SetIP(_Unwind_Context *context, unsigned long val) -{ - // The lowest bit of the instruction pointer indicates whether we're in - // thumb or ARM mode. This is assumed to be fixed throughout a function, - // so must be propagated when setting the program counter. - unsigned long thumbState = _Unwind_GetGR(context, 15) & 1; - _Unwind_SetGR(context, 15, (val | thumbState)); -} - -/** GNU API function that unwinds the frame */ -_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception*, struct _Unwind_Context*); - - -#define DECLARE_PERSONALITY_FUNCTION(name) \ -_Unwind_Reason_Code name(_Unwind_State state,\ - struct _Unwind_Exception *exceptionObject,\ - struct _Unwind_Context *context); - -#define BEGIN_PERSONALITY_FUNCTION(name) \ -_Unwind_Reason_Code name(_Unwind_State state,\ - struct _Unwind_Exception *exceptionObject,\ - struct _Unwind_Context *context)\ -{\ - int version = 1;\ - uint64_t exceptionClass = exceptionObject->exception_class;\ - int actions;\ - switch (state & ~_US_FORCE_UNWIND)\ - {\ - default: return _URC_FAILURE;\ - case _US_VIRTUAL_UNWIND_FRAME:\ - {\ - actions = _UA_SEARCH_PHASE;\ - break;\ - }\ - case _US_UNWIND_FRAME_STARTING:\ - {\ - actions = _UA_CLEANUP_PHASE;\ - if (exceptionObject->barrier_cache.sp == _Unwind_GetGR(context, 13))\ - {\ - actions |= _UA_HANDLER_FRAME;\ - }\ - break;\ - }\ - case _US_UNWIND_FRAME_RESUME:\ - {\ - return continueUnwinding(exceptionObject, context);\ - break;\ - }\ - }\ - _Unwind_SetGR (context, 12, (unsigned long)exceptionObject); - -#define CALL_PERSONALITY_FUNCTION(name) name(state,exceptionObject,context) - -#define COPY_EXCEPTION(dst, src) \ - (dst)->unwinder_cache = (src)->unwinder_cache; \ - (dst)->barrier_cache = (src)->barrier_cache; \ - (dst)->cleanup_cache = (src)->cleanup_cache; \ - (dst)->pr_cache = (src)->pr_cache; - -#ifdef __cplusplus -} -#endif diff --git a/unwind-itanium.h b/unwind-itanium.h deleted file mode 100644 index b169421..0000000 --- a/unwind-itanium.h +++ /dev/null @@ -1,188 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2003 Hewlett-Packard Co - Contributed by David Mosberger-Tang - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _UNWIND_H -#define _UNWIND_H - -/* For uint64_t */ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Minimal interface as per C++ ABI draft standard: - - http://www.codesourcery.com/cxx-abi/abi-eh.html */ - -typedef enum - { - _URC_NO_REASON = 0, - _URC_OK = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8 - } -_Unwind_Reason_Code; - -typedef int _Unwind_Action; - -#define _UA_SEARCH_PHASE 1 -#define _UA_CLEANUP_PHASE 2 -#define _UA_HANDLER_FRAME 4 -#define _UA_FORCE_UNWIND 8 - -struct _Unwind_Context; /* opaque data-structure */ -struct _Unwind_Exception; /* forward-declaration */ - -typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, - struct _Unwind_Exception *); - -typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) (int, _Unwind_Action, - uint64_t, - struct _Unwind_Exception *, - struct _Unwind_Context *, - void *); - -/* The C++ ABI requires exception_class, private_1, and private_2 to - be of type uint64 and the entire structure to be - double-word-aligned. Please note that exception_class stays 64-bit - even on 32-bit machines for gcc compatibility. */ -struct _Unwind_Exception - { - uint64_t exception_class; - _Unwind_Exception_Cleanup_Fn exception_cleanup; -#ifdef __SEH__ - uintptr_t private_[6]; -#else - uintptr_t private_1; - uintptr_t private_2; -#endif - } __attribute__((__aligned__)); - -extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *); -extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *, - _Unwind_Stop_Fn, void *); -extern void _Unwind_Resume (struct _Unwind_Exception *); -extern void _Unwind_DeleteException (struct _Unwind_Exception *); -extern uintptr_t _Unwind_GetGR (struct _Unwind_Context *, int); -extern void _Unwind_SetGR (struct _Unwind_Context *, int, uintptr_t); -extern uintptr_t _Unwind_GetIP (struct _Unwind_Context *); -extern uintptr_t _Unwind_GetIPInfo (struct _Unwind_Context *, int *); -extern void _Unwind_SetIP (struct _Unwind_Context *, uintptr_t); -extern uintptr_t _Unwind_GetLanguageSpecificData (struct _Unwind_Context*); -extern uintptr_t _Unwind_GetRegionStart (struct _Unwind_Context *); - -#ifdef _GNU_SOURCE - -/* Callback for _Unwind_Backtrace(). The backtrace stops immediately - if the callback returns any value other than _URC_NO_REASON. */ -typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (struct _Unwind_Context *, - void *); - -/* See http://gcc.gnu.org/ml/gcc-patches/2001-09/msg00082.html for why - _UA_END_OF_STACK exists. */ -# define _UA_END_OF_STACK 16 - -/* If the unwind was initiated due to a forced unwind, resume that - operation, else re-raise the exception. This is used by - __cxa_rethrow(). */ -extern _Unwind_Reason_Code - _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *); - -/* See http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00154.html for why - _Unwind_GetBSP() exists. */ -extern uintptr_t _Unwind_GetBSP (struct _Unwind_Context *); - -/* Return the "canonical frame address" for the given context. - This is used by NPTL... */ -extern uintptr_t _Unwind_GetCFA (struct _Unwind_Context *); - -/* Return the base-address for data references. */ -extern uintptr_t _Unwind_GetDataRelBase (struct _Unwind_Context *); - -/* Return the base-address for text references. */ -extern uintptr_t _Unwind_GetTextRelBase (struct _Unwind_Context *); - -/* Call _Unwind_Trace_Fn once for each stack-frame, without doing any - cleanup. The first frame for which the callback is invoked is the - one for the caller of _Unwind_Backtrace(). _Unwind_Backtrace() - returns _URC_END_OF_STACK when the backtrace stopped due to - reaching the end of the call-chain or _URC_FATAL_PHASE1_ERROR if it - stops for any other reason. */ -extern _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void *); - -/* Find the start-address of the procedure containing the specified IP - or NULL if it cannot be found (e.g., because the function has no - unwind info). Note: there is not necessarily a one-to-one - correspondence between source-level functions and procedures: some - functions don't have unwind-info and others are split into multiple - procedures. */ -extern void *_Unwind_FindEnclosingFunction (void *); - -/* See also Linux Standard Base Spec: - http://www.linuxbase.org/spec/refspecs/LSB_1.3.0/gLSB/gLSB/libgcc-s.html */ - -#endif /* _GNU_SOURCE */ - -#define DECLARE_PERSONALITY_FUNCTION(name) \ -_Unwind_Reason_Code name(int version,\ - _Unwind_Action actions,\ - uint64_t exceptionClass,\ - struct _Unwind_Exception *exceptionObject,\ - struct _Unwind_Context *context); -#define BEGIN_PERSONALITY_FUNCTION(name) \ -_Unwind_Reason_Code name(int version,\ - _Unwind_Action actions,\ - uint64_t exceptionClass,\ - struct _Unwind_Exception *exceptionObject,\ - struct _Unwind_Context *context)\ -{ - -#define CALL_PERSONALITY_FUNCTION(name) name(version, actions, exceptionClass, exceptionObject, context) - -#ifdef __SEH__ -#define COPY_EXCEPTION(dst, src) \ - do { \ - memcpy((dst)->private_, (src)->private_, sizeof((src)->private_)); \ - } while(0) -#else -#define COPY_EXCEPTION(dst, src) \ - do { \ - (dst)->private_1 = (src)->private_1; \ - (dst)->private_2 = (src)->private_2; \ - } while(0) -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _UNWIND_H */ \ No newline at end of file diff --git a/unwind.h b/unwind.h deleted file mode 100644 index 76b6780..0000000 --- a/unwind.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef __arm__ -#include "unwind-arm.h" -#else -#include "unwind-itanium.h" -#endif diff --git a/visibility.h b/visibility.h deleted file mode 100644 index 07637fd..0000000 --- a/visibility.h +++ /dev/null @@ -1,24 +0,0 @@ -#include "objc/objc-visibility.h" - -#if defined _WIN32 || defined __CYGWIN__ -# define PRIVATE -#else -# define PRIVATE __attribute__ ((visibility("hidden"))) -#endif -#ifdef NO_LEGACY -# define LEGACY PRIVATE -#else -# define LEGACY OBJC_PUBLIC -#endif - -#if defined(DEBUG) || (!defined(__clang__)) -# include -# define UNREACHABLE(x) assert(0 && x) -# define ASSERT(x) assert(x) -#else -# define UNREACHABLE(x) __builtin_unreachable() -# define ASSERT(x) do { if (!(x)) __builtin_unreachable(); } while(0) -#endif - -#define LIKELY(x) __builtin_expect(x, 1) -#define UNLIKELY(x) __builtin_expect(x, 0)