remove cmake

main
sandyx86 1 year ago
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

5
.gitignore vendored

@ -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_runtime.h>

@ -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;
}

1057
arc.mm

File diff suppressed because it is too large Load Diff

@ -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…
Cancel
Save