Skip to content

djthorpe/objc

Repository files navigation

objc runtime

This is a minimal Objective C runtime written in C, designed to be portable across different platforms, including ARM and x86 architectures, mostly targeting embedded systems. It uses the "gcc" ABI for the moment, as that is the most portable across different platforms.

Requirements

You will minimally need the following tools to build the runtime:

  • Build system make and cmake - for the build system
  • Compiler clang or gcc - for compiling the runtime (clang is not supported on Apple Silicon). You can use the environment variable CC to specify the compiler, e.g. CC=clang or CC=gcc.
  • Library Dependencies openssl for Linux and Darwin - for the hash functions, which are used in the runtime and NXFoundation framework.
  • Documentation docker is needed for generating the documentation from the source code.
  • Cross-Compilation For cross-compilation for embedded systems based on some ARM variant, get the ARM LLVM toolchain: https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases. Install this to the /opt directory. You can use the environment variable TOOLCHAIN_PATH to specify the path to the toolchain, e.g. TOOLCHAIN_PATH=/opt/LLVM-ET-Arm-19.1.5-Darwin-universal.

Building the libraries

Three static libraries are currently built:

  • objc-gcc - the Objective C runtime library using the ancient GCC ABI
  • NXFoundation - a minimal set of classes, to support memory management and basic types such as string, array, dictionary, date, time and number.
  • runtime-sys - a minimal set of system functions, needed to bind the runtime to the underlying system, on a per-platform basis. Includes cryptographic hash functions (MD5, SHA-256).

Download the source code from GitHub:

git clone git@github.com:djthorpe/objc.git
cd objc

The TOOLCHAIN_PATH environment variable should point to the directory where the toolchain is installed. For Macintosh, you can use Homebrew to install the GCC toolchain, which is required for compiling the runtime on MacOS:

# Compile with GCC 15 for MacOS
brew install gcc@15
TOOLCHAIN_PATH=/opt/homebrew RELEASE=1 CC=gcc-15 make

Once you've made the libraries, use the make tests target to run the unit tests. There is information about test coverage in the tests directory.

You can target different architectures by setting the TARGET environment variable. For a RP2040-based board, you can use the clang compiler with the ARM toolchain. The TARGET environment variable should be set to the target architecture, such as armv6m-none-eabi for the RP2040 Pico board:

# Compile for the RP2040 Pico board
CC=clang TARGET=armv6m-none-eabi TOOLCHAIN_PATH=/opt/LLVM-ET-Arm-19.1.5-Darwin-universal RELEASE=1 make 

See the list of supported targets in the cmake directory. You can exclude the environment variable RELEASE=1 to build debugging versions of the libraries.

Installing the libraries

TODO: the libraries should be installed under a prefix path:

PREFIX=/opt/objc make install
  • /opt/objc/lib/armv6m-none-eabi/libobjc-gcc.a
  • /opt/objc/lib/armv6m-none-eabi/libruntime-sys.a
  • /opt/objc/lib/armv6m-none-eabi/libFoundation.a
  • /opt/objc/include/objc
  • /opt/objc/include/sys
  • /opt/objc/include/NXFoundation
  • /opt/objc/doc/...

These can subsequently be used to make your executables!

Documentation

To build the API documentation, you will need to have docker installed. The documentation is built using doxygen through docker so you don't need to have it installed directly.

make docs
open docs/index.html 

The documentation is also published here.

Current status

  • Registering classes
  • Simple message calling
  • NXConstantString
  • Resolving super classes and meta classes for message lookup
  • Calling methods in super classes - implement [super init] for example
  • Calling methods in categories
  • Memory management - alloc, dealloc, memory arenas - require malloc in an NXZone
  • Printf support - vsprintf - printf-like function which can be used by NXLog, NXString and sys_panicf
  • Memory management - retain, release - reference counting for objects through NXZone
  • Date and Time - NXDate - mutable date and time
  • NXNumber with booleans
  • NXNull - singleton for null objects that can be inserted into collections
  • Protocols and conformsTo:
  • Number - NXNumber with NXNumberInt16 and NXNumberUnsignedInt16
  • Number - NXNumber with NXNumberInt32 and NXNumberUnsignedInt32
  • Number - NXNumber with NXNumberInt64 and NXNumberUnsignedInt64
  • Fix linking categories in static libraries (see test NXFoundation_05)
  • NXString - mutable strings - append, appendFormat
  • NXArray - ordered collections of objects, with methods for adding, removing, and accessing objects
  • NXData - mutable binary data with Base64/hex encoding and append operations
  • Hash functions - MD5 and SHA-256 hash computation through sys_hash_* API
  • Pico toolchain - integrate with Pico SDK
  • printf - %@ format specifier for logging objects and [Object description]
  • printf - %T format specifier for time intervals
  • NXArray - string methods - join
  • Robust sys_hash functions which hash some void* data against an arbitrary key, and a hashing function that can be used
  • NXMap - unordered collections with string-keys (what about NXDictionary?)
  • NXMap - arbitary key sizes
  • @synchronized support - use fixed-size table to store locks for objects, no allocations
  • clang compatibility
  • Number - NXNumberByte and NXNumberInt8
  • respondsToSelector: (see test runtime_14)
  • Make all NX classes thread-safe, so that they can be used in multi-threaded applications
  • NXScanner, ReaderProtocol - scanning, parsing and tokenizing
  • NXURL class - URL/filepath parsing and manipulation
  • Classes have a unique number so we can do NXCoder to serialize and deserialize objects from binary data
  • NXData - fix hexString encoding and decoding (see test NXFoundation_22)
  • NXArray sortWithFunction: sortedArrayWithFunction: reverse: and reversedArray:
  • NXArray filterWithFunction: filteredArrayWithFunction:
  • Pico - timer alarm pool should be on both cores, not just core 0, and then use the right pool for the core that the timer is running on
  • Pico - when building RELEASE=1 builds it includes stdout and printf, which is not needed
  • NXRange and NXString - substringWithRange and substringWithRange:options:
  • NXString - rangeOfSubstring and rangeOfSubstring:options:
  • NXArray - subarrayWithRange: and subarrayWithRange:options:
  • NXString - array methods - componentsSeparatedByString and componentsSeparatedByByte
  • NXApplication and NXRunLoop
  • printf - %f and %lf format specifier for floats and doubles
  • Number - NXNumber with NXNumberFloat and NXNumberDouble
  • More efficient method implementation lookup
  • NXCoder - JSON / Binary marshalling and unmarshalling
  • make install will compile the libraries and install them to a prefix path
  • Calling +[initialise] for categories
  • Exception handling?

References

Here are some references that may be useful for understanding the Objective C runtime and its implementation:

About

Objective C experiments on embedded systems

Topics

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •