Odin icon indicating copy to clipboard operation
Odin copied to clipboard

Compile Error when using `objc_send` on an Objective-C subclass created at runtime.

Open jceipek opened this issue 2 years ago • 0 comments

Context

        Odin: dev-2022-10:29f2ecd2
        OS:   macOS Monterey 12.5 (build: 21G115, kernel: 21.6.0)
        CPU:  Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
        RAM:  32768 MiB

Minimal example (subclassing/subclassing.odin):

// Compile and run with: odin run .
package main

@require foreign import "system:Cocoa.framework"

import "core:fmt"
import "core:intrinsics"
import "core:runtime"

foreign import "system:Foundation.framework"

Class   :: ^intrinsics.objc_class
id      :: ^intrinsics.objc_object
SEL     :: ^intrinsics.objc_selector
BOOL :: bool
IMP :: proc "c" (object: id, sel: SEL, #c_vararg args: ..any) -> id

foreign Foundation {
    objc_allocateClassPair :: proc "c" (superclass : Class, name : cstring, /* size_t */ extraBytes : uint) -> Class ---
    objc_registerClassPair :: proc "c" (cls : Class) ---
    object_getClass :: proc "c" (obj : id) -> Class ---
    class_addMethod :: proc "c" (cls: Class, name: SEL, imp: IMP, types: cstring) -> BOOL ---
}

on_test :: proc "c" (self : id, cmd: SEL) {
    context = runtime.default_context();
    fmt.println("Test Called")
}

main :: proc () {
    nsViewClass := intrinsics.objc_find_class("NSView")
    newClass := objc_allocateClassPair(nsViewClass, "CustomView", 0)

    testSel := intrinsics.objc_find_selector("test")
    class_addMethod(newClass, testSel, auto_cast on_test, "v@:v");

    objc_registerClassPair(newClass)

    allocedClass := intrinsics.objc_send(id, newClass, "alloc")
    initedClass := intrinsics.objc_send(id, allocedClass, "init")
    isFlipped := intrinsics.objc_send(bool, initedClass, "isFlipped")
    fmt.printf("Class: %v IsFlipped: %v", initedClass, isFlipped)
    intrinsics.objc_send(nil, initedClass, "test")
}

Expected Behavior

I expect the program to compile and run without errors. The output should be:

Class: 7fdb5a907320 IsFlipped: 0
Test Called

Current Behavior

subclassing/subclassing.odin(39:46) 'objc_send' expected a type or value derived from intrinsics.objc_object, got 'newClass' of type ^objc_class
subclassing/subclassing.odin(40:45) 'objc_send' expected a type or value derived from intrinsics.objc_object, got 'allocedClass' of type invalid type
subclassing/subclassing.odin(41:45) 'objc_send' expected a type or value derived from intrinsics.objc_object, got 'initedClass' of type invalid type
subclassing/subclassing.odin(43:31) 'objc_send' expected a type or value derived from intrinsics.objc_object, got 'initedClass' of type invalid type

Failure Information (for bugs)

This C program has the expected behavior:

// Compile and run with: clang subclassing.c -framework Cocoa; ./a.out
#include <objc/runtime.h>
#include <objc/message.h>
#include <stdio.h>

#define cls objc_getClass
#define sel sel_getUid

typedef id (*object_message_send)(id, SEL, ...);
typedef id (*class_message_send)(Class, SEL, ...);

#define msg ((object_message_send)objc_msgSend)
#define cls_msg ((class_message_send)objc_msgSend)

void on_test(id self, SEL cmd) {
    printf("Test Called\n");
}

int main(void) {
    Class nsViewClass = objc_lookUpClass("NSView");
    Class newClass = objc_allocateClassPair(nsViewClass, "CustomView", 0);
    
    // Class metaclass = object_getClass(newClass); // The headers say to use class_addMethod on this, but that doesn't seem to work.

    SEL test_sel = sel("test");
    class_addMethod(newClass, test_sel, (IMP) on_test, "v@:v");

    objc_registerClassPair(newClass);

    id allocedClass = cls_msg(newClass, sel("alloc"));
    id initedClass = msg(allocedClass, sel("init"));
    bool isFlipped = msg(initedClass, sel("isFlipped"));
    printf("Class: %lx IsFlipped: %d\n", (uintptr_t)initedClass, isFlipped);

    msg(initedClass, test_sel);
    
    return 0;
}

Steps to Reproduce

Compile and run the provided example program with odin run ..

jceipek avatar Oct 18 '22 05:10 jceipek