From f222bb92afc4958ac1569cd3991d4f62e4d6f52a Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 20 Oct 2026 10:12:45 +0000 Subject: [PATCH] Initial commit --- CMakeLists.txt | 29 ++++++++++++++ example/CMakeLists.txt | 24 ++++++++++++ example/MObject.c | 86 ++++++++++++++++++++++++++++++++++++++++++ example/MObject.h | 5 +++ example/main.c | 55 +++++++++++++++++++++++++++ flake.lock | 61 ++++++++++++++++++++++++++++++ flake.nix | 35 +++++++++++++++++ mini-obj-c.c | 37 ++++++++++++++++++ mini-obj-c.h | 27 +++++++++++++ 9 files changed, 359 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 example/CMakeLists.txt create mode 100644 example/MObject.c create mode 100644 example/MObject.h create mode 100644 example/main.c create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 mini-obj-c.c create mode 100644 mini-obj-c.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c7ca24a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 4.1) + +project( + mini-obj-c + LANGUAGES + C +) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_library( + ${PROJECT_NAME} + mini-obj-c.c +) +target_include_directories( + ${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) +set_target_properties( + ${PROJECT_NAME} + PROPERTIES + C_STANDARD 23 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF +) + +add_subdirectory(example) + diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..7432c65 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,24 @@ +add_executable( + example + main.c + MObject.c +) + +set_target_properties( + example + PROPERTIES + C_STANDARD 23 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF +) +target_link_libraries( + example + PRIVATE + ${PROJECT_NAME} +) +target_include_directories( + example + PRIVATE + ${CMAKE_SOURCE_DIR} +) + diff --git a/example/MObject.c b/example/MObject.c new file mode 100644 index 0000000..b2c5587 --- /dev/null +++ b/example/MObject.c @@ -0,0 +1,86 @@ +#include "MObject.h" + +#include +#include +#include + +static struct Object* alloc(struct Object* self); +static struct Object* retain(struct Object* self); +static struct Object* release(struct Object* self); + +struct Method MObject_meta_methods[] = { + { .sel = "alloc", .impl = alloc }, +}; +struct Class MObjectMetaClass = { + .class = &MObjectMetaClass, + .name = "MObject(meta)", + .super = nullptr, + .methods = MObject_meta_methods, + .methods_count = sizeof(MObject_meta_methods) / sizeof(struct Method), +}; + +struct Method MObject_methods[] = { + { .sel = "retain", .impl = retain }, + { .sel = "release", .impl = release }, +}; +struct Class MObject = { + .class = &MObjectMetaClass, + .name = "MObject", + .super = nullptr, + .methods = MObject_methods, + .methods_count = sizeof(MObject_methods) / sizeof(struct Method), +}; + +struct Header +{ + size_t retain_count; +}; + +static struct Object* alloc(struct Object* self) +{ + if (!self) { + return nullptr; + } + + struct Class* class = (struct Class*)self; + + auto size = sizeof(struct Header) + sizeof(struct Object); + void* mem = malloc(size); + memset(mem, 0, size); + + struct Header* header = (struct Header*)mem; + header->retain_count = 1; + + struct Object* obj = (struct Object*)((uintptr_t)mem + sizeof(struct Header)); + obj->class = class; + + return obj; +} + +static struct Object* retain(struct Object* self) +{ + if (!self) { + return nullptr; + } + + struct Header* header = (struct Header*)((uintptr_t)self - sizeof(struct Header)); + header->retain_count += 1; + + return self; +} + +static struct Object* release(struct Object* self) +{ + if (!self) { + return nullptr; + } + + struct Header* header = (struct Header*)((uintptr_t)self - sizeof(struct Header)); + header->retain_count -= 1; + if (header->retain_count <= 0) { + free(header); + } + + return nullptr; +} + diff --git a/example/MObject.h b/example/MObject.h new file mode 100644 index 0000000..a0dcd2c --- /dev/null +++ b/example/MObject.h @@ -0,0 +1,5 @@ +#include + +extern struct Class MObjectMetaClass; +extern struct Class MObject; + diff --git a/example/main.c b/example/main.c new file mode 100644 index 0000000..df00a49 --- /dev/null +++ b/example/main.c @@ -0,0 +1,55 @@ +#include + +#include + +#include "MObject.h" + +static struct Object* static_method(struct Object* self); +static struct Object* instance_method(struct Object* self); + +int main() +{ + struct Method Test_meta_methods[] = { + { .sel = "staticMethod", .impl = static_method }, + }; + struct Class TestMetaClass = { + .class = &TestMetaClass, + .name = "Test(meta)", + .super = &MObjectMetaClass, + .methods = Test_meta_methods, + .methods_count = sizeof(Test_meta_methods) / sizeof(struct Method), + }; + + struct Method Test_methods[] = { + { .sel = "instanceMethod", .impl = instance_method }, + }; + struct Class Test = { + .class = &TestMetaClass, + .name = "Test", + .super = &MObject, + .methods = Test_methods, + .methods_count = sizeof(Test_methods) / sizeof(struct Method), + }; + + msg_send((struct Object*)&Test, "staticMethod"); + + struct Object* obj = msg_send((struct Object*)&Test, "alloc"); + msg_send(obj, "instanceMethod"); + msg_send(obj, "release"); +} + +static struct Object* static_method(struct Object* self) +{ + struct Class* class = (struct Class*)self; + printf("Hello from class '%s'\n", class->name); + + return self; +} + +static struct Object* instance_method(struct Object* self) +{ + printf("Hello from instance of '%s'\n", self->class->name); + + return self; +} + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ca8de7e --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1791914995, + "narHash": "sha256-Zf/xfQEAasnwnmEfHqW/4hjDE/s44yn2hD4RuVIZRjA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b7a79d3fea1e214f6d668d521cecec0c01e26544", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1792116957, + "narHash": "sha256-MTJCMrhDFb+GRbLxBPTp8kB4dDlO+egmQ5qsJgYuaWw=", + "rev": "f20cd90e28fe0403873f473b29e42662cd86e8c4", + "type": "tarball", + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1093421.f20cd90e28fe/nixexprs.tar.zst" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "systems": { + "locked": { + "lastModified": 1680978846, + "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=", + "owner": "nix-systems", + "repo": "x86_64-linux", + "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "x86_64-linux", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..bb5c0e2 --- /dev/null +++ b/flake.nix @@ -0,0 +1,35 @@ +{ + description = "Mini Obj-C"; + + inputs = { + nixpkgs.url = "nixpkgs"; + systems.url = "github:nix-systems/x86_64-linux"; + flake-utils = { + url = "github:numtide/flake-utils"; + inputs.systems.follows = "systems"; + }; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + pname = "mini-obj-c"; + version = "0.0.1"; + src = ./.; + buildInputs = with pkgs; [ ]; + nativeBuildInputs = with pkgs; [ + ninja + cmake + clang-tools + ]; + in { + devShells.default = pkgs.mkShell { + inherit buildInputs nativeBuildInputs; + }; + + packages.default = pkgs.stdenv.mkDerivation { + inherit buildInputs nativeBuildInputs pname version src; + }; + }); +} diff --git a/mini-obj-c.c b/mini-obj-c.c new file mode 100644 index 0000000..fc68081 --- /dev/null +++ b/mini-obj-c.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#include "mini-obj-c.h" + +#define WARN(f, ...) fprintf(stderr, f "\n", ##__VA_ARGS__) + +MethodImplementation method_lookup(struct Class* class, const char* sel) +{ + for (; class; class = class->super) { + for (size_t i = 0; i < class->methods_count; ++i) { + auto entry = class->methods[i]; + if (strcmp(entry.sel, sel) == 0) { + return entry.impl; + } + } + } + return nullptr; +} + +struct Object* msg_send(struct Object* self, const char* sel) +{ + if (!self || !self->class) { + WARN("Invalid object"); + return nullptr; + } + + auto impl = method_lookup(self->class, sel); + if (impl) { + return impl(self); + } else { + WARN("Method '%s' not found for class '%s'", sel, self->class->name); + return nullptr; + } +} + diff --git a/mini-obj-c.h b/mini-obj-c.h new file mode 100644 index 0000000..54cb265 --- /dev/null +++ b/mini-obj-c.h @@ -0,0 +1,27 @@ +#include + +struct Object +{ + struct Class* class; +}; + +struct Class +{ + struct Class* class; + + const char* name; + struct Class* super; + struct Method* methods; + size_t methods_count; +}; + +typedef struct Object* (*MethodImplementation)(struct Object* self); + +struct Method +{ + const char* sel; + MethodImplementation impl; +}; + +struct Object* msg_send(struct Object* self, const char* sel); +