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)