remove cmake
parent
9206f48bce
commit
e6dee6bd79
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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 "$@"
|
|
||||||
@ -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
|
|
||||||
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
*~
|
|
||||||
.*.sw?
|
|
||||||
[b|B]uild
|
|
||||||
Debug
|
|
||||||
Release
|
|
||||||
@ -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
|
|
||||||
@ -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 <gnustep-dev@gnu.org>. A 1.1 release,
|
|
||||||
fixing any bugs that are encountered in wider deployment, is planned to
|
|
||||||
coincide with the next GNUstep release.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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 <gnustep-dev@gnu.org>.
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -1 +0,0 @@
|
|||||||
#include <objc/blocks_private.h>
|
|
||||||
@ -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()
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
@ -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($<$<CONFIG:Release>:NO_SELECTOR_MISMATCH_WARNINGS>)
|
|
||||||
add_compile_definitions($<$<BOOL:${TYPE_DEPENDENT_DISPATCH}>:TYPE_DEPENDENT_DISPATCH>)
|
|
||||||
add_compile_definitions($<$<BOOL:${ENABLE_TRACING}>:WITH_TRACING=1>)
|
|
||||||
add_compile_definitions($<$<BOOL:${DEBUG_ARC_COMPAT}>: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($<$<STREQUAL:${CMAKE_SYSTEM_PROCESSOR},i686>:-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 "$<$<OR:$<COMPILE_LANGUAGE:OBJC>,$<COMPILE_LANGUAGE:OBJCXX>>:-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$<COMPILE_LANGUAGE:C>:-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
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
||||||
$<INSTALL_INTERFACE:include>)
|
|
||||||
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 <gnustep-dev@gnu.org>")
|
|
||||||
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 <stdlib.h>
|
|
||||||
extern \"C\" {
|
|
||||||
__attribute__((weak))
|
|
||||||
void *__cxa_allocate_exception(size_t thrown_size) noexcept;
|
|
||||||
}
|
|
||||||
#include <exception>
|
|
||||||
int main() { return 0; }" CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES)
|
|
||||||
|
|
||||||
add_compile_definitions($<IF:$<BOOL:${CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES}>,CXA_ALLOCATE_EXCEPTION_SPECIFIER=noexcept,CXA_ALLOCATE_EXCEPTION_SPECIFIER>)
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
@PACKAGE_INIT@
|
|
||||||
|
|
||||||
include ( "${CMAKE_CURRENT_LIST_DIR}/libobjcTargets.cmake" )
|
|
||||||
@ -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 <assert.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
#include "objc/runtime.h"
|
|
||||||
#include "protocol.h"
|
|
||||||
#include "class.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
@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];
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
```
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -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];
|
|
||||||
}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
#include "../objc/runtime.h"
|
|
||||||
#include "../objc/blocks_runtime.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
#include "../unwind.h"
|
|
||||||
#include "Test.h"
|
|
||||||
#include "../objc/hooks.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -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 $<TARGET_OBJECTS:test_runtime>)
|
|
||||||
addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.2 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}")
|
|
||||||
target_sources("${TEST}_optimised" PRIVATE $<TARGET_OBJECTS:test_runtime>)
|
|
||||||
|
|
||||||
# -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 $<TARGET_OBJECTS:test_runtime_legacy>)
|
|
||||||
addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}")
|
|
||||||
target_sources("${TEST}_legacy_optimised" PRIVATE $<TARGET_OBJECTS:test_runtime_legacy>)
|
|
||||||
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 ()
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
#import "Test.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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 <stdio.h>
|
|
||||||
|
|
||||||
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
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
include $(GNUSTEP_MAKEFILES)/common.make
|
|
||||||
|
|
||||||
TOOL_NAME = RuntimeTest
|
|
||||||
|
|
||||||
RuntimeTest_OBJC_FILES=\
|
|
||||||
RuntimeTest.m
|
|
||||||
|
|
||||||
include $(GNUSTEP_MAKEFILES)/tool.make
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#import <stdio.h>
|
|
||||||
#import "../objc/runtime.h"
|
|
||||||
#include "Test.h"
|
|
||||||
|
|
||||||
#import <sys/types.h>
|
|
||||||
#import <sys/stat.h>
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "../selector.h"
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
#import "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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];
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#import "Test.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
@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 <X> {
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#import "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#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<count ; i++)
|
|
||||||
{
|
|
||||||
switch (l[i].name[0])
|
|
||||||
{
|
|
||||||
case 'T': assert(0==strcmp(l[i].value, "@")); break;
|
|
||||||
case 'C': assert(0==strcmp(l[i].value, "")); break;
|
|
||||||
case 'N': assert(0==strcmp(l[i].value, "")); break;
|
|
||||||
case 'G': assert(0==strcmp(l[i].value, "bar")); break;
|
|
||||||
case 'S': assert(0==strcmp(l[i].value, "setBar:")); break;
|
|
||||||
case 'B': assert(0==strcmp(l[i].value, "foo")); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(0 == property_copyAttributeList(0, &count));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,740 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdatomic.h>
|
|
||||||
|
|
||||||
#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 <ProtocolTest>
|
|
||||||
{
|
|
||||||
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; index<size; index++) {
|
|
||||||
int found = 0;
|
|
||||||
for (unsigned int attrsIndex=0; attrsIndex<attrsCount; attrsIndex++) {
|
|
||||||
if (strcmp(attrsList[attrsIndex].name, list[index].name) == 0) {
|
|
||||||
OPT_ASSERT(strcmp(attrsList[attrsIndex].value, list[index].value) == 0);
|
|
||||||
found = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OPT_ASSERT(found);
|
|
||||||
}
|
|
||||||
free(attrsList);
|
|
||||||
attrsList = property_copyAttributeList(p, NULL);
|
|
||||||
OPT_ASSERT(0 != attrsList);
|
|
||||||
objc_property_attribute_t *ra;
|
|
||||||
for (attrsCount = 0, ra = attrsList; (ra->name != NULL) && (attrsCount < size); attrsCount++, ra++) {}
|
|
||||||
OPT_ASSERT(attrsCount == size);
|
|
||||||
free(attrsList);
|
|
||||||
for (unsigned int index=0; index<size; index++) {
|
|
||||||
const char* value = property_copyAttributeValue(p, list[index].name);
|
|
||||||
OPT_ASSERT(0 != value);
|
|
||||||
OPT_ASSERT(strcmp(value, list[index].value) == 0);
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void testPropertyForProperty(objc_property_t p,
|
|
||||||
const char *name,
|
|
||||||
const char *types,
|
|
||||||
objc_property_attribute_t* list,
|
|
||||||
unsigned int size)
|
|
||||||
{
|
|
||||||
testPropertyForProperty_alt(p, name, types, list, size, YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testPropertyForClass(Class testClass,
|
|
||||||
const char *name,
|
|
||||||
const char *types,
|
|
||||||
objc_property_attribute_t* list,
|
|
||||||
unsigned int size)
|
|
||||||
{
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, name), name, types, list, size);
|
|
||||||
|
|
||||||
static int addPropertyForClassIndex = 0;
|
|
||||||
char addPropertyName[32];
|
|
||||||
sprintf(addPropertyName, "addPropertyForClass%d", ++addPropertyForClassIndex);
|
|
||||||
assert(class_addProperty(testClass, addPropertyName, list, size));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, addPropertyName), addPropertyName, types, list, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL testPropertyForProtocol_alt(Protocol *testProto,
|
|
||||||
const char *name,
|
|
||||||
const char *types,
|
|
||||||
objc_property_attribute_t* list,
|
|
||||||
unsigned int size, BOOL abort)
|
|
||||||
{
|
|
||||||
if (!testPropertyForProperty_alt(protocol_getProperty(testProto, name, YES, YES), name, types, list, size, abort))
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int addPropertyForProtocolIndex = 0;
|
|
||||||
char addPropertyName[32];
|
|
||||||
sprintf(addPropertyName, "addPropertyForProtocol%d", ++addPropertyForProtocolIndex);
|
|
||||||
protocol_addProperty(testProto, addPropertyName, list, size, YES, YES);
|
|
||||||
OPT_ASSERT(0 == protocol_getProperty(testProto, addPropertyName, YES, YES));
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testPropertyForProtocol(Protocol *testProto,
|
|
||||||
const char *name,
|
|
||||||
const char *types,
|
|
||||||
objc_property_attribute_t* list,
|
|
||||||
unsigned int size)
|
|
||||||
{
|
|
||||||
testPropertyForProtocol_alt(testProto, name, types, list, size, YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL testProperty_alt(const char *name, const char *types, objc_property_attribute_t* list, unsigned int size, BOOL abort)
|
|
||||||
{
|
|
||||||
return testPropertyForProperty_alt(class_getProperty(objc_getClass("PropertyTest"), name), name, types, list, size, abort)
|
|
||||||
&& testPropertyForProperty_alt(class_getProperty(objc_getClass("PropertyProtocolTest"), name), name, types, list, size, abort);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testProperty(const char *name, const char *types, objc_property_attribute_t* list, unsigned int size)
|
|
||||||
{
|
|
||||||
testProperty_alt(name, types, list, size, YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testAddPropertyForClass(Class testClass)
|
|
||||||
{
|
|
||||||
objc_property_attribute_t emptyType = { "T", "i" };
|
|
||||||
assert(!class_addProperty(testClass, NULL, &emptyType, 1));
|
|
||||||
class_replaceProperty(testClass, NULL, &emptyType, 1);
|
|
||||||
|
|
||||||
assert(class_addProperty(testClass, "addProperty1", ATTRS(ATTR("T", "@"))));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "addProperty1"),
|
|
||||||
"addProperty1", "T@", ATTRS(ATTR("T", "@")));
|
|
||||||
|
|
||||||
assert(class_addProperty(testClass, "addProperty2", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""))));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "addProperty2"),
|
|
||||||
"addProperty2", "T@,D", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", "")));
|
|
||||||
|
|
||||||
assert(class_addProperty(testClass, "addProperty3", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar"))));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "addProperty3"),
|
|
||||||
"addProperty3", "T@,D,VbackingIvar", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar")));
|
|
||||||
|
|
||||||
assert(class_addProperty(testClass, "addProperty4", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("C", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar"))));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "addProperty4"),
|
|
||||||
"addProperty4", "T@,R,&,C," WEAK_STR "D,VbackingIvar", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("C", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar")));
|
|
||||||
|
|
||||||
assert(class_addProperty(testClass, "addProperty5", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("C", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("V", "backingIvar"))));
|
|
||||||
// The only concession to MacOS X is that we reorder the attributes string
|
|
||||||
if (!testPropertyForProperty_alt(class_getProperty(testClass, "addProperty5"),
|
|
||||||
"addProperty5", "T@,D," WEAK_STR "C,&,R,VbackingIvar", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("C", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("V", "backingIvar")), NO))
|
|
||||||
{
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "addProperty5"),
|
|
||||||
"addProperty5", "T@,R,&,C," WEAK_STR "D,VbackingIvar", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("C", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar")));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(class_addProperty(testClass, "replaceProperty", ATTRS(ATTR("T", "@"))));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "replaceProperty"),
|
|
||||||
"replaceProperty", "T@", ATTRS(ATTR("T", "@")));
|
|
||||||
|
|
||||||
assert(!class_addProperty(testClass, "replaceProperty", ATTRS(ATTR("T", "i"))));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "replaceProperty"),
|
|
||||||
"replaceProperty", "T@", ATTRS(ATTR("T", "@")));
|
|
||||||
|
|
||||||
class_replaceProperty(testClass, "replaceProperty", ATTRS(ATTR("T", "i")));
|
|
||||||
testPropertyForProperty(class_getProperty(testClass, "replaceProperty"),
|
|
||||||
"replaceProperty", "Ti", ATTRS(ATTR("T", "i")));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testAddProperty()
|
|
||||||
{
|
|
||||||
testAddPropertyForClass(objc_getClass("PropertyTest"));
|
|
||||||
testAddPropertyForClass(objc_getClass("PropertyProtocolTest"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void testAddPropertyForProtocol(Protocol *testProto)
|
|
||||||
{
|
|
||||||
objc_property_attribute_t emptyType = { "T", "i" };
|
|
||||||
protocol_addProperty(testProto, NULL, &emptyType, 1, YES, YES);
|
|
||||||
|
|
||||||
protocol_addProperty(testProto, "addProperty1", ATTRS(ATTR("T", "@")), YES, YES);
|
|
||||||
protocol_addProperty(testProto, "addProperty2", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", "")), YES, YES);
|
|
||||||
protocol_addProperty(testProto, "addProperty3", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar")), YES, YES);
|
|
||||||
|
|
||||||
objc_registerProtocol(testProto);
|
|
||||||
|
|
||||||
testPropertyForProperty(protocol_getProperty(testProto, "addProperty1", YES, YES),
|
|
||||||
"addProperty1", "T@", ATTRS(ATTR("T", "@")));
|
|
||||||
testPropertyForProperty(protocol_getProperty(testProto, "addProperty2", YES, YES),
|
|
||||||
"addProperty2", "T@,D", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", "")));
|
|
||||||
testPropertyForProperty(protocol_getProperty(testProto, "addProperty3", YES, YES),
|
|
||||||
"addProperty3", "T@,D,VbackingIvar", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("V", "backingIvar")));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int intDefault2Getter(id self, SEL _cmd) {
|
|
||||||
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "intDefault");
|
|
||||||
return (int)object_getIvar(self, ivar);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void intDefault2Setter(id self, SEL _cmd, int value) {
|
|
||||||
Ivar ivar = class_getInstanceVariable(objc_getClass("PropertyTest"), "intDefault");
|
|
||||||
object_setIvar(self, ivar, (__bridge id)(void*)(intptr_t)value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct YorkshireTeaStruct structDefault2Getter(id self, SEL _cmd) {
|
|
||||||
struct YorkshireTeaStruct *s;
|
|
||||||
object_getInstanceVariable(self, "structDefault", (void**)&s);
|
|
||||||
return *s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void structDefault2Setter(id self, SEL _cmd, struct YorkshireTeaStruct value) {
|
|
||||||
object_setInstanceVariable(self, "structDefault", &value);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
testProperty("atomicBoolDefault", "TAB,VatomicBoolDefault", ATTRS(ATTR("T", "AB"), ATTR("V", "atomicBoolDefault")));
|
|
||||||
testProperty("charDefault", "Tc,VcharDefault", ATTRS(ATTR("T", "c"), ATTR("V", "charDefault")));
|
|
||||||
testProperty("doubleDefault", "Td,VdoubleDefault", ATTRS(ATTR("T", "d"), ATTR("V", "doubleDefault")));
|
|
||||||
testProperty("enumDefault", "Ti,VenumDefault", ATTRS(ATTR("T", "i"), ATTR("V", "enumDefault")));
|
|
||||||
testProperty("floatDefault", "Tf,VfloatDefault", ATTRS(ATTR("T", "f"), ATTR("V", "floatDefault")));
|
|
||||||
testProperty("intDefault", "Ti,VintDefault", ATTRS(ATTR("T", "i"), ATTR("V", "intDefault")));
|
|
||||||
if (sizeof(long) == 4)
|
|
||||||
{
|
|
||||||
testProperty("longDefault", "Tl,VlongDefault", ATTRS(ATTR("T", "l"), ATTR("V", "longDefault")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
testProperty("longDefault", "Tq,VlongDefault", ATTRS(ATTR("T", "q"), ATTR("V", "longDefault")));
|
|
||||||
}
|
|
||||||
testProperty("shortDefault", "Ts,VshortDefault", ATTRS(ATTR("T", "s"), ATTR("V", "shortDefault")));
|
|
||||||
testProperty("signedDefault", "Ti,VsignedDefault", ATTRS(ATTR("T", "i"), ATTR("V", "signedDefault")));
|
|
||||||
testProperty("structDefault", "T{YorkshireTeaStruct=ic},VstructDefault", ATTRS(ATTR("T", "{YorkshireTeaStruct=ic}"),
|
|
||||||
ATTR("V", "structDefault")));
|
|
||||||
testProperty("typedefDefault", "T{YorkshireTeaStruct=ic},VtypedefDefault", ATTRS(ATTR("T", "{YorkshireTeaStruct=ic}"),
|
|
||||||
ATTR("V", "typedefDefault")));
|
|
||||||
testProperty("unionDefault", "T(MoneyUnion=fd),VunionDefault", ATTRS(ATTR("T", "(MoneyUnion=fd)"),
|
|
||||||
ATTR("V", "unionDefault")));
|
|
||||||
testProperty("unsignedDefault", "TI,VunsignedDefault", ATTRS(ATTR("T", "I"), ATTR("V", "unsignedDefault")));
|
|
||||||
testProperty("functionPointerDefault", "T^?,VfunctionPointerDefault", ATTRS(ATTR("T", "^?"), ATTR("V", "functionPointerDefault")));
|
|
||||||
testProperty("intPointer", "T^i,VintPointer", ATTRS(ATTR("T", "^i"), ATTR("V", "intPointer")));
|
|
||||||
testProperty("voidPointerDefault", "T^v,VvoidPointerDefault", ATTRS(ATTR("T", "^v"), ATTR("V", "voidPointerDefault")));
|
|
||||||
testProperty("intSetterGetter", "Ti,GintGetFoo,SintSetFoo:,VintSetterGetter", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("G", "intGetFoo"),
|
|
||||||
ATTR("S", "intSetFoo:"),
|
|
||||||
ATTR("V", "intSetterGetter")));
|
|
||||||
testProperty("intReadonly", "Ti,R,VintReadonly", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("V", "intReadonly")));
|
|
||||||
testProperty("intReadonlyGetter", "Ti,R,GisIntReadOnlyGetter,VintReadonlyGetter", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("G", "isIntReadOnlyGetter"),
|
|
||||||
ATTR("V", "intReadonlyGetter")));
|
|
||||||
testProperty("intReadwrite", "Ti,VintReadwrite", ATTRS(ATTR("T", "i"), ATTR("V", "intReadwrite")));
|
|
||||||
testProperty("intAssign", "Ti,VintAssign", ATTRS(ATTR("T", "i"), ATTR("V", "intAssign")));
|
|
||||||
testProperty("idDefault", "T@,VidDefault", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("V", "idDefault")));
|
|
||||||
testProperty("idRetain", "T@,&,VidRetain", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("V", "idRetain")));
|
|
||||||
testProperty("idCopy", "T@,C,VidCopy", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("C", ""),
|
|
||||||
ATTR("V", "idCopy")));
|
|
||||||
testProperty("idWeak", "T@,W,VidWeak", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("W", ""),
|
|
||||||
ATTR("V", "idWeak")));
|
|
||||||
testProperty("idStrong", "T@,&,VidStrong", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("V", "idStrong")));
|
|
||||||
testProperty("intNonatomic", "Ti,N,VintNonatomic", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("V", "intNonatomic")));
|
|
||||||
testProperty("idReadonlyCopyNonatomic", "T@,R,C,N,VidReadonlyCopyNonatomic", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("C", ""),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("V", "idReadonlyCopyNonatomic")));
|
|
||||||
testProperty("idReadonlyRetainNonatomic", "T@,R,&,N,VidReadonlyRetainNonatomic", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("V", "idReadonlyRetainNonatomic")));
|
|
||||||
/**
|
|
||||||
* The weak attribute was not present for earlier versions of clang, so we test
|
|
||||||
* for all variants that the compiler may produce.
|
|
||||||
*/
|
|
||||||
if (!testProperty_alt("idReadonlyWeakNonatomic", "T@,R," WEAK_STR "N,VidReadonlyWeakNonatomic", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("V", "idReadonlyWeakNonatomic")), NO))
|
|
||||||
{
|
|
||||||
testProperty("idReadonlyWeakNonatomic", "T@,R," WEAK_STR "N,VidReadonlyWeakNonatomic", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("V", "idReadonlyWeakNonatomic")));
|
|
||||||
}
|
|
||||||
testProperty("idOther", "T@,&,V_idOther", ATTRS(ATTR("T", "@"), ATTR("&", ""), ATTR("V", "_idOther")));
|
|
||||||
testProperty("idDynamic", "T@,&,D", ATTRS(ATTR("T", "@"), ATTR("&", ""), ATTR("D", "")));
|
|
||||||
testProperty("idDynamicGetterSetter", "T@,&,D,N,GdynamicGetterSetter,SsetDynamicGetterSetter:", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("D", ""),
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("G", "dynamicGetterSetter"),
|
|
||||||
ATTR("S", "setDynamicGetterSetter:")));
|
|
||||||
|
|
||||||
Protocol *testProto = objc_getProtocol("ProtocolTest");
|
|
||||||
testPropertyForProtocol(testProto, "atomicBoolDefault", "TAB", ATTRS(ATTR("T", "AB")));
|
|
||||||
testPropertyForProtocol(testProto, "charDefault", "Tc", ATTRS(ATTR("T", "c")));
|
|
||||||
testPropertyForProtocol(testProto, "doubleDefault", "Td", ATTRS(ATTR("T", "d")));
|
|
||||||
testPropertyForProtocol(testProto, "enumDefault", "Ti", ATTRS(ATTR("T", "i")));
|
|
||||||
testPropertyForProtocol(testProto, "floatDefault", "Tf", ATTRS(ATTR("T", "f")));
|
|
||||||
testPropertyForProtocol(testProto, "intDefault", "Ti", ATTRS(ATTR("T", "i")));
|
|
||||||
if (sizeof(long) == 4)
|
|
||||||
{
|
|
||||||
testPropertyForProtocol(testProto, "longDefault", "Tl", ATTRS(ATTR("T", "l")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
testPropertyForProtocol(testProto, "longDefault", "Tq", ATTRS(ATTR("T", "q")));
|
|
||||||
}
|
|
||||||
testPropertyForProtocol(testProto, "shortDefault", "Ts", ATTRS(ATTR("T", "s")));
|
|
||||||
testPropertyForProtocol(testProto, "signedDefault", "Ti", ATTRS(ATTR("T", "i")));
|
|
||||||
testPropertyForProtocol(testProto, "structDefault", "T{YorkshireTeaStruct=ic}", ATTRS(ATTR("T", "{YorkshireTeaStruct=ic}")));
|
|
||||||
testPropertyForProtocol(testProto, "typedefDefault", "T{YorkshireTeaStruct=ic}", ATTRS(ATTR("T", "{YorkshireTeaStruct=ic}")));
|
|
||||||
testPropertyForProtocol(testProto, "unionDefault", "T(MoneyUnion=fd)", ATTRS(ATTR("T", "(MoneyUnion=fd)")));
|
|
||||||
testPropertyForProtocol(testProto, "unsignedDefault", "TI", ATTRS(ATTR("T", "I")));
|
|
||||||
testPropertyForProtocol(testProto, "functionPointerDefault", "T^?", ATTRS(ATTR("T", "^?")));
|
|
||||||
testPropertyForProtocol(testProto, "intPointer", "T^i", ATTRS(ATTR("T", "^i")));
|
|
||||||
testPropertyForProtocol(testProto, "voidPointerDefault", "T^v", ATTRS(ATTR("T", "^v")));
|
|
||||||
testPropertyForProtocol(testProto, "intSetterGetter", "Ti,GintGetFoo,SintSetFoo:", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("G", "intGetFoo"),
|
|
||||||
ATTR("S", "intSetFoo:")));
|
|
||||||
testPropertyForProtocol(testProto, "intReadonly", "Ti,R", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("R", "")));
|
|
||||||
testPropertyForProtocol(testProto, "intReadonlyGetter", "Ti,R,GisIntReadOnlyGetter", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("G", "isIntReadOnlyGetter")));
|
|
||||||
testPropertyForProtocol(testProto, "intReadwrite", "Ti", ATTRS(ATTR("T", "i")));
|
|
||||||
testPropertyForProtocol(testProto, "intAssign", "Ti", ATTRS(ATTR("T", "i")));
|
|
||||||
testPropertyForProtocol(testProto, "idDefault", "T@", ATTRS(ATTR("T", "@")));
|
|
||||||
testPropertyForProtocol(testProto, "idRetain", "T@,&", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idCopy", "T@,C", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("C", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idWeak", "T@,W", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("W", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idStrong", "T@,&", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", "")));
|
|
||||||
testPropertyForProtocol(testProto, "intNonatomic", "Ti,N", ATTRS(ATTR("T", "i"),
|
|
||||||
ATTR("N", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idReadonlyCopyNonatomic", "T@,R,C,N", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("C", ""),
|
|
||||||
ATTR("N", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idReadonlyRetainNonatomic", "T@,R,&,N", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("N", "")));
|
|
||||||
/*
|
|
||||||
* Again, different clang versions emit slightly different property declarations.
|
|
||||||
*/
|
|
||||||
if (!testPropertyForProtocol_alt(testProto, "idReadonlyWeakNonatomic", "T@,R," WEAK_STR "N", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
WEAK_ATTR
|
|
||||||
ATTR("N", "")), NO))
|
|
||||||
{
|
|
||||||
|
|
||||||
testPropertyForProtocol(testProto, "idReadonlyWeakNonatomic", "T@,R,N", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("R", ""),
|
|
||||||
ATTR("N", "")));
|
|
||||||
}
|
|
||||||
testPropertyForProtocol(testProto, "idOther", "T@,&", ATTRS(ATTR("T", "@"), ATTR("&", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idDynamic", "T@,&", ATTRS(ATTR("T", "@"), ATTR("&", "")));
|
|
||||||
testPropertyForProtocol(testProto, "idDynamicGetterSetter", "T@,&,N,GdynamicGetterSetter,SsetDynamicGetterSetter:", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("N", ""),
|
|
||||||
ATTR("G", "dynamicGetterSetter"),
|
|
||||||
ATTR("S", "setDynamicGetterSetter:")));
|
|
||||||
|
|
||||||
testAddProperty();
|
|
||||||
|
|
||||||
Protocol *testAddProtocol = objc_allocateProtocol("TestAddProtocol");
|
|
||||||
assert(0 != testAddProtocol);
|
|
||||||
testAddPropertyForProtocol(testAddProtocol);
|
|
||||||
|
|
||||||
Class testClass = objc_getClass("PropertyTest");
|
|
||||||
objc_property_attribute_t intDefault2Attrs[] = { ATTR("T", "i"), ATTR("V", "intDefault") };
|
|
||||||
assert(class_addProperty(testClass, "intDefault2", intDefault2Attrs, 2));
|
|
||||||
assert(class_addMethod(testClass, @selector(intDefault2), (IMP)intDefault2Getter, "i@:"));
|
|
||||||
assert(class_addMethod(testClass, @selector(setIntDefault2:), (IMP)intDefault2Setter, "v@:i"));
|
|
||||||
testPropertyForClass(testClass, "intDefault2", "Ti,VintDefault", ATTRS(ATTR("T", "i"), ATTR("V", "intDefault")));
|
|
||||||
|
|
||||||
objc_property_attribute_t structDefault2Attrs[] = { ATTR("T", "{YorkshireTeaStruct=ic}"),
|
|
||||||
ATTR("V", "structDefault") };
|
|
||||||
assert(class_addProperty(testClass, "structDefault2", structDefault2Attrs, 2));
|
|
||||||
assert(class_addMethod(testClass, @selector(structDefault2), (IMP)structDefault2Getter, "{YorkshireTeaStruct=ic}@:"));
|
|
||||||
assert(class_addMethod(testClass, @selector(setStructDefault2:), (IMP)structDefault2Setter, "v@:{YorkshireTeaStruct=ic}"));
|
|
||||||
testPropertyForClass(testClass, "structDefault2", "T{YorkshireTeaStruct=ic},VstructDefault", ATTRS(ATTR("T", "{YorkshireTeaStruct=ic}"),
|
|
||||||
ATTR("V", "structDefault")));
|
|
||||||
|
|
||||||
PropertyTest* t = class_createInstance(testClass, 0);
|
|
||||||
assert(t != nil);
|
|
||||||
object_setClass(t, testClass);
|
|
||||||
t.intDefault = 2;
|
|
||||||
assert(t.intDefault == 2);
|
|
||||||
[t setIntDefault2:3];
|
|
||||||
assert((int)[t intDefault2] == 3);
|
|
||||||
assert(t.intDefault == 3);
|
|
||||||
|
|
||||||
struct YorkshireTeaStruct struct1 = { 2, 'A' };
|
|
||||||
t.structDefault = struct1;
|
|
||||||
struct YorkshireTeaStruct readStruct = t.structDefault;
|
|
||||||
assert(memcmp(&struct1, &readStruct, sizeof(struct1)) == 0);
|
|
||||||
struct YorkshireTeaStruct struct2 = { 3, 'B' };
|
|
||||||
[t setStructDefault2:struct2];
|
|
||||||
struct YorkshireTeaStruct readStruct2 = [t structDefault2];
|
|
||||||
assert(memcmp(&struct2, &readStruct2, sizeof(struct2)) == 0);
|
|
||||||
readStruct = t.structDefault;
|
|
||||||
assert(memcmp(&struct2, &readStruct, sizeof(struct2)) == 0);
|
|
||||||
|
|
||||||
objc_property_attribute_t idRetainAttrs[] = { ATTR("T", "@"),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("V", "_idOther") };
|
|
||||||
class_replaceProperty(testClass, "idRetain", idRetainAttrs, 3);
|
|
||||||
testPropertyForClass(testClass, "idRetain", "T@,&,V_idOther", ATTRS(ATTR("T", "@"),
|
|
||||||
ATTR("&", ""),
|
|
||||||
ATTR("V", "_idOther")));
|
|
||||||
id testValue = [Test new];
|
|
||||||
t.idRetain = testValue;
|
|
||||||
assert(t->idRetain == 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;
|
|
||||||
}
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
#import "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
@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<count ; i++)
|
|
||||||
{
|
|
||||||
Protocol *f = list[i];
|
|
||||||
for (int j=0 ; j<4 ; j++)
|
|
||||||
{
|
|
||||||
if (strcmp(expectedNames[j], protocol_getName(f)) == 0)
|
|
||||||
{
|
|
||||||
assert(protocol_isEqual(f, expected[j]));
|
|
||||||
found[j] = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int j=0 ; j<4 ; j++)
|
|
||||||
{
|
|
||||||
assert(found[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
@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@?<v@?i>16
|
|
||||||
assert(strstr(encoding, "@\"NSString\"") == encoding);
|
|
||||||
assert(strstr(encoding, "@?<v@?i>") != NULL);
|
|
||||||
#else
|
|
||||||
assert(strstr(encoding, "@?") != NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@ -1,343 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include "../safewindows.h"
|
|
||||||
# define sleep(x) Sleep(1000 * x)
|
|
||||||
#else
|
|
||||||
# include <unistd.h>
|
|
||||||
#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 <NSCoding>
|
|
||||||
{
|
|
||||||
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("<null selector>", 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;
|
|
||||||
}
|
|
||||||
@ -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 = "<group>"; };
|
|
||||||
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
|
||||||
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 = "<group>";
|
|
||||||
};
|
|
||||||
08FB7795FE84155DC02AAC07 /* Source */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
08FB7796FE84155DC02AAC07 /* RuntimeTest.m */,
|
|
||||||
);
|
|
||||||
name = Source;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
08FB779EFE84155DC02AAC07 /* Foundation.framework */,
|
|
||||||
);
|
|
||||||
name = "External Frameworks and Libraries";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1AB674ADFE9D54B511CA2CBB /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8DD76FA10486AA7600D96B5E /* RuntimeTest */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* 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 */;
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include "objc/hooks.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
@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];
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
#import "../objc/runtime.h"
|
|
||||||
#import "../objc/objc-arc.h"
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#undef NDEBUG
|
|
||||||
#endif
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
#import "../objc/runtime.h"
|
|
||||||
#import "../objc/objc-arc.h"
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#undef NDEBUG
|
|
||||||
#endif
|
|
||||||
#include <assert.h>
|
|
||||||
#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
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include "../objc/hooks.h"
|
|
||||||
#include "../objc/objc-exception.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <Windows.h>
|
|
||||||
#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
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
|
|
||||||
|
|
||||||
__attribute__((weak_import))
|
|
||||||
@interface WeakClass
|
|
||||||
- (id)class;
|
|
||||||
@end
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
assert([WeakClass class] == nil);
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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; i<SIZE; i++)
|
|
||||||
{
|
|
||||||
values[i] = [Test new];
|
|
||||||
refs[i] = values[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
for (int i=0; i<SIZE; i++)
|
|
||||||
{
|
|
||||||
assert(refs[i] != nil);
|
|
||||||
assert(refs[i] == values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release the value, one by one
|
|
||||||
for (int indexToRelease=0; indexToRelease<SIZE; indexToRelease++)
|
|
||||||
{
|
|
||||||
values[indexToRelease] = nil;
|
|
||||||
// Check all refs
|
|
||||||
for (int i=0; i<SIZE; i++)
|
|
||||||
{
|
|
||||||
if (i <= indexToRelease)
|
|
||||||
{
|
|
||||||
assert(refs[i] == nil);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(refs[i] != nil);
|
|
||||||
assert(refs[i] == values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#include "stdio.h"
|
|
||||||
#include "Test.h"
|
|
||||||
|
|
||||||
@interface T2 : Test @end
|
|
||||||
@implementation T2 @end
|
|
||||||
|
|
||||||
@compatibility_alias Foo T2;
|
|
||||||
|
|
||||||
Class alias_getClass(const char*);
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
assert([T2 class] == [Foo class]);
|
|
||||||
assert(objc_getClass("T2") == objc_getClass("Foo"));
|
|
||||||
assert(objc_getClass("T2") == alias_getClass("Foo"));
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
#include "stdio.h"
|
|
||||||
#include "Test.h"
|
|
||||||
|
|
||||||
// This is a large vector type, which the compiler will lower to some sequence
|
|
||||||
// of vector ops on the target, or scalar ops if there is no vector FPU.
|
|
||||||
typedef double __attribute__((vector_size(32))) v4d;
|
|
||||||
|
|
||||||
@interface X : Test
|
|
||||||
{
|
|
||||||
id f;
|
|
||||||
id g;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
@implementation X @end
|
|
||||||
|
|
||||||
@interface Vector : X
|
|
||||||
{
|
|
||||||
v4d x;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
@implementation Vector
|
|
||||||
+ (Vector*)alloc
|
|
||||||
{
|
|
||||||
Vector *v = class_createInstance(self, 0);
|
|
||||||
// The initialisation might be done with memset, but will probably be a
|
|
||||||
// vector load / store and so will likely fail if x is incorrectly aligned.
|
|
||||||
v->x = (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));
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
@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
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
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 ; i<table->table_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
#include "Test.h"
|
|
||||||
|
|
||||||
#import <stdatomic.h>
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
#include <locale.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#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<loops ; i++)
|
|
||||||
{
|
|
||||||
[TestCls nothing];
|
|
||||||
}
|
|
||||||
assert(count == calls);
|
|
||||||
assert(tracecount == calls);
|
|
||||||
assert(interposecount == 0);
|
|
||||||
objc_registerTracingHook(@selector(nothing), hook0);
|
|
||||||
interposecount = 0;
|
|
||||||
count = 0;
|
|
||||||
tracecount = 0;
|
|
||||||
for (int i=0 ; i<loops ; i++)
|
|
||||||
{
|
|
||||||
[TestCls nothing];
|
|
||||||
}
|
|
||||||
assert(count == calls);
|
|
||||||
assert(tracecount == calls * 2);
|
|
||||||
assert(interposecount == 0);
|
|
||||||
objc_registerTracingHook(@selector(nothing), hook1);
|
|
||||||
interposecount = 0;
|
|
||||||
count = 0;
|
|
||||||
tracecount = 0;
|
|
||||||
for (int i=0 ; i<loops ; i++)
|
|
||||||
{
|
|
||||||
[TestCls nothing];
|
|
||||||
}
|
|
||||||
assert(count == calls);
|
|
||||||
assert(tracecount == calls);
|
|
||||||
assert(interposecount == calls);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,265 +0,0 @@
|
|||||||
// Needed with glibc to expose vasprintf
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#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<iterations ; i++)
|
|
||||||
{
|
|
||||||
[TestCls nothing];
|
|
||||||
}
|
|
||||||
c2 = clock();
|
|
||||||
times[0] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
|
|
||||||
fprintf(stderr, "Traditional message send took %f seconds. \n", times[0]);
|
|
||||||
c1 = clock();
|
|
||||||
for (int i=0 ; i<iterations ; i++)
|
|
||||||
{
|
|
||||||
objc_msgSend(TestCls, @selector(nothing));
|
|
||||||
}
|
|
||||||
c2 = clock();
|
|
||||||
times[1] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
|
|
||||||
fprintf(stderr, "objc_msgSend() message send took %f seconds. \n", times[1]);
|
|
||||||
IMP nothing = objc_msg_lookup(TestCls, @selector(nothing));
|
|
||||||
c1 = clock();
|
|
||||||
for (int i=0 ; i<iterations ; i++)
|
|
||||||
{
|
|
||||||
nothing(TestCls, @selector(nothing));
|
|
||||||
}
|
|
||||||
c2 = clock();
|
|
||||||
times[2] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
|
|
||||||
fprintf(stderr, "Direct IMP call took %f seconds. \n", times[2]);
|
|
||||||
printf("%f\t%f\t%f\n", times[0], times[1], times[2]);
|
|
||||||
#endif // BENCHMARK
|
|
||||||
return 0;
|
|
||||||
#endif // __GNUSTEP_MSGSEND__
|
|
||||||
return 77; // Skip test
|
|
||||||
}
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@ -1,328 +0,0 @@
|
|||||||
#include "../objc/runtime.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#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));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,130 +0,0 @@
|
|||||||
#include "visibility.h"
|
|
||||||
#include "objc/runtime.h"
|
|
||||||
#include "module.h"
|
|
||||||
#include "gc_ops.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 ; i<known_abi_count ; i++)
|
|
||||||
{
|
|
||||||
if (known_abis[i].version == version)
|
|
||||||
{
|
|
||||||
v = &known_abis[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FAIL_IF(NULL == v, "Unknown ABI version");
|
|
||||||
FAIL_IF((v->module_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;
|
|
||||||
}
|
|
||||||
@ -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 <niels.grewe@halbordnung.de>
|
|
||||||
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);
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
/** A hash table for mapping compatibility aliases to classes.
|
|
||||||
Copyright (c) 2011 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
Written by: Niels Grewe <niels.grewe@halbordnung.de>
|
|
||||||
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 <stdlib.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -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<<SMALLOBJ_BITS) - 1)
|
|
||||||
|
|
||||||
// Page size configuration
|
|
||||||
#if defined(__powerpc64__)
|
|
||||||
# define PAGE_SHIFT 16
|
|
||||||
#else
|
|
||||||
# define PAGE_SHIFT 12
|
|
||||||
#endif
|
|
||||||
#define PAGE_SIZE (1<<PAGE_SHIFT)
|
|
||||||
@ -1,469 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#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 ; i<REFERENCE_LIST_SIZE ; i++)
|
|
||||||
{
|
|
||||||
if (list->list[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 ; i<REFERENCE_LIST_SIZE ; i++)
|
|
||||||
{
|
|
||||||
struct reference *r = &list->list[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);
|
|
||||||
}
|
|
||||||
@ -1,338 +0,0 @@
|
|||||||
// On some platforms, we need _GNU_SOURCE to expose asprintf()
|
|
||||||
#ifndef _GNU_SOURCE
|
|
||||||
#define _GNU_SOURCE 1
|
|
||||||
#endif
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#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 <nbutil.h>
|
|
||||||
#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 ; i<HEADERS_PER_PAGE ; i++)
|
|
||||||
{
|
|
||||||
metadata->buffers->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;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue