Skip to content

Commit

Permalink
Merge branch 'master' into builtin-classes
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchisnall authored Jun 8, 2024
2 parents cba7887 + 2855d17 commit fdc7b8b
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .cirrus.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
libcxxrt_freebsd_task:
matrix:
- freebsd_instance:
image_family: freebsd-13-2
image_family: freebsd-13-3
- freebsd_instance:
image_family: freebsd-15-0-snap
- freebsd_instance:
Expand Down
18 changes: 16 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
# 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
Expand All @@ -41,7 +42,7 @@ jobs:
# 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 }}
name: ${{ matrix.os }} ${{ matrix.build-type }} LLVM-${{ matrix.llvm-version }} ${{ matrix.cxxlib }} BlocksRuntime-${{ matrix.blocks-runtime }}
steps:
- uses: actions/checkout@v3
- name: Install dependencies
Expand All @@ -53,11 +54,24 @@ jobs:
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 -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}}"
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
Expand Down
52 changes: 42 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ endif ()

INCLUDE (CheckCXXSourceCompiles)
INCLUDE (FetchContent)
INCLUDE (CheckSymbolExists)

set(libobjc_VERSION 4.6)

Expand Down Expand Up @@ -57,7 +58,7 @@ set(libobjc_OBJCXX_SRCS
set(libobjc_OBJC_SRCS
NSBlocks.m
associate.m
blocks_runtime.m
blocks_runtime_np.m
properties.m)
set(libobjc_C_SRCS
alias_table.c
Expand All @@ -83,8 +84,6 @@ set(libobjc_HDRS
objc/Availability.h
objc/Object.h
objc/Protocol.h
objc/blocks_private.h
objc/blocks_runtime.h
objc/capabilities.h
objc/developer.h
objc/encoding.h
Expand All @@ -101,10 +100,7 @@ set(libobjc_HDRS
objc/runtime-deprecated.h
objc/runtime.h
objc/slot.h)
set(libBlocksRuntime_COMPATIBILITY_HDRS
Block.h
Block_private.h
)

set(libobjc_CXX_SRCS
selector_table.cc
)
Expand Down Expand Up @@ -143,7 +139,7 @@ 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
Expand Down Expand Up @@ -230,6 +226,31 @@ else ()
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>")

Expand Down Expand Up @@ -268,6 +289,10 @@ 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
Expand Down Expand Up @@ -340,8 +365,12 @@ install(TARGETS ${INSTALL_TARGETS}

install(FILES ${libobjc_HDRS}
DESTINATION "${HEADER_INSTALL_PATH}/${INCLUDE_DIRECTORY}")
install(FILES ${libBlocksRuntime_COMPATIBILITY_HDRS}
DESTINATION "${HEADER_INSTALL_PATH}")

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")
Expand Down Expand Up @@ -386,6 +415,9 @@ 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}")
Expand Down
14 changes: 14 additions & 0 deletions INSTALL
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ this configuration, we provide a separate libobjcxx.so, which avoids the need
for the Objective-C runtime to depend on the STL implementation just to be able
to interoperate with C++ exceptions.

Blocks Runtime Integration
--------------------------

libobjc2 ships with a runtime for the blocks C extension (i.e. closures/lambdas) and
will install compatibility headers for the libBlocksRuntime library that ships with
LLVM's compiler-rt or Swift's libdispatch. Alternatively, libobjc2 can be built without
the embedded blocks runtime and utilise the one from libdispatch instead. This can be
enabled by adding `-DEMBEDDED_BLOCKS_RUNTIME=OFF` to the `cmake` command. It's required
that your version of libBlocksRuntime provides the `Blocks_private.h` header.
(enabled with `-DINSTALL_PRIVATE_HEADERS=ON` when building libdispatch from source)

Regardless of the chosen blocks runtime implementation, blocks will be fully integrated
into the Objective-C runtime.

Installation Location
---------------------

Expand Down
29 changes: 26 additions & 3 deletions NSBlocks.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@
#include "dtable.h"
#include <assert.h>

OBJC_PUBLIC struct objc_class _NSConcreteGlobalBlock;
OBJC_PUBLIC struct objc_class _NSConcreteStackBlock;
OBJC_PUBLIC struct objc_class _NSConcreteMallocBlock;
#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;
Expand All @@ -31,6 +41,7 @@ static void createNSBlockSubclass(Class superclass, Class newClass,
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);
Expand All @@ -49,8 +60,20 @@ BOOL objc_create_block_classes_as_subclasses_of(Class super)
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;
}
71 changes: 56 additions & 15 deletions arc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@
#include <tsl/robin_map.h>
#import "lock.h"
#import "objc/runtime.h"
#ifdef EMBEDDED_BLOCKS_RUNTIME
#import "objc/blocks_private.h"
#import "objc/blocks_runtime.h"
#else
#include <Block.h>
#include <Block_private.h>
#endif
#import "nsobject.h"
#import "class.h"
#import "selector.h"
#import "visibility.h"
#import "objc/hooks.h"
#import "objc/objc-arc.h"
#import "objc/blocks_runtime.h"
#include "objc/message.h"

/**
Expand Down Expand Up @@ -75,12 +80,16 @@ static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFun
arc_tls_key_t ARCThreadKey;
#endif

#ifndef HAVE_BLOCK_USE_RR2
extern "C"
{
extern struct objc_class _NSConcreteMallocBlock;
extern struct objc_class _NSConcreteStackBlock;
extern struct objc_class _NSConcreteGlobalBlock;
extern struct objc_class _NSConcreteAutoBlock;
extern struct objc_class _NSConcreteFinalizingBlock;
}
#endif

@interface NSAutoreleasePool
+ (Class)class;
Expand Down Expand Up @@ -315,8 +324,7 @@ static inline id retain(id obj, BOOL isWeak)
{
if (isPersistentObject(obj)) { return obj; }
Class cls = obj->isa;
if ((Class)&_NSConcreteMallocBlock == cls ||
(Class)&_NSConcreteStackBlock == cls)
if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block)))
{
return Block_copy(obj);
}
Expand Down Expand Up @@ -376,15 +384,15 @@ static inline void release(id obj)
{
if (isPersistentObject(obj)) { return; }
Class cls = obj->isa;
if (cls == static_cast<Class>(&_NSConcreteMallocBlock))
if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block)))
{
if (cls == static_cast<void*>(&_NSConcreteStackBlock))
{
return;
}
_Block_release(obj);
return;
}
if (cls == static_cast<Class>(&_NSConcreteStackBlock))
{
return;
}
if (objc_test_class_flag(cls, objc_class_flag_fast_arc))
{
objc_release_fast_np(obj);
Expand Down Expand Up @@ -702,12 +710,24 @@ void deallocate(T* p, std::size_t)

}

#ifdef HAVE_BLOCK_USE_RR2
static const struct Block_callbacks_RR blocks_runtime_callbacks = {
sizeof(Block_callbacks_RR),
(void (*)(const void*))objc_retain,
(void (*)(const void*))objc_release,
(void (*)(const void*))objc_delete_weak_refs
};
#endif

PRIVATE extern "C" void init_arc(void)
{
INIT_LOCK(weakRefLock);
#ifdef arc_tls_store
ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools);
#endif
#ifdef HAVE_BLOCK_USE_RR2
_Block_use_RR2(&blocks_runtime_callbacks);
#endif
}

/**
Expand Down Expand Up @@ -843,9 +863,18 @@ static BOOL setObjectHasWeakRefs(id obj)
*addr = obj;
return obj;
}
// If the object is being deallocated return nil.
if (object_getRetainCount_np(obj) == 0)
Class cls = classForObject(obj);
if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block)))
{
// Check whether the block is being deallocated and return nil if so
if (_Block_isDeallocating(obj)) {
*addr = nil;
return nil;
}
}
else if (object_getRetainCount_np(obj) == 0)
{
// If the object is being deallocated return nil.
*addr = nil;
return nil;
}
Expand Down Expand Up @@ -916,19 +945,31 @@ static BOOL setObjectHasWeakRefs(id obj)
return nil;
}
Class cls = classForObject(obj);
if (static_cast<Class>(&_NSConcreteMallocBlock) == cls)
if (objc_test_class_flag(cls, objc_class_flag_permanent_instances))
{
obj = static_cast<id>(block_load_weak(obj));
return obj;
}
else if (objc_test_class_flag(cls, objc_class_flag_permanent_instances))
else if (UNLIKELY(objc_test_class_flag(cls, objc_class_flag_is_block)))
{
return obj;
obj = static_cast<id>(block_load_weak(obj));
if (obj == nil)
{
return nil;
}
// This is a defeasible retain operation that protects against another thread concurrently
// starting to deallocate the block.
if (_Block_tryRetain(obj))
{
return obj;
}
return nil;

}
else if (!objc_test_class_flag(cls, objc_class_flag_fast_arc))
{
obj = _objc_weak_load(obj);
}
// block_load_weak() or _objc_weak_load() can return nil
// _objc_weak_load() can return nil
if (obj == nil) { return nil; }
return retain(obj, YES);
}
Expand Down
Loading

0 comments on commit fdc7b8b

Please sign in to comment.