Squash commits for public release
This commit is contained in:
29
libs/libobjc/BUILD.gn
Normal file
29
libs/libobjc/BUILD.gn
Normal file
@@ -0,0 +1,29 @@
|
||||
import("//build/libs/TEMPLATE.gni")
|
||||
|
||||
xOS_static_library("libobjc") {
|
||||
sources = [
|
||||
"src/NSObject.mm",
|
||||
"src/class.mm",
|
||||
"src/init.mm",
|
||||
"src/memory.mm",
|
||||
"src/msgsend_$target_arch.S",
|
||||
"src/selector.mm",
|
||||
]
|
||||
|
||||
include_dirs = [
|
||||
"include/",
|
||||
"//libs/libc/include/",
|
||||
"//libs/",
|
||||
]
|
||||
|
||||
deplibs = [ "libc" ]
|
||||
|
||||
configs = [ "//build/libs:libobjcc_flags" ]
|
||||
|
||||
# Currently, using Clang+LTO and arm32 as a traget, a weird issue
|
||||
# happens, when linker can't resolve objc_load_function.
|
||||
# Such functions contain `und` instruction and leads to a crash.
|
||||
if (host == "llvm" && target_arch != "arm32") {
|
||||
cflags = [ "-flto" ]
|
||||
}
|
||||
}
|
||||
19
libs/libobjc/include/libfoundation/NSObject.h
Normal file
19
libs/libobjc/include/libfoundation/NSObject.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _LIBOBJC_NSOBJECT_H
|
||||
#define _LIBOBJC_NSOBJECT_H
|
||||
|
||||
#include <libobjc/runtime.h>
|
||||
|
||||
@interface NSObject {
|
||||
Class isa;
|
||||
}
|
||||
|
||||
+ (id)init;
|
||||
+ (id)new;
|
||||
+ (id)alloc;
|
||||
|
||||
- (id)init;
|
||||
- (Class)class;
|
||||
|
||||
@end
|
||||
|
||||
#endif // _LIBOBJC_NSOBJECT_H
|
||||
17
libs/libobjc/include/libobjc/class.h
Normal file
17
libs/libobjc/include/libobjc/class.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _LIBOBJC_CLASS_H
|
||||
#define _LIBOBJC_CLASS_H
|
||||
|
||||
#include <libobjc/module.h>
|
||||
|
||||
#define DISPATCH_TABLE_NOT_INITIALIZED (void*)0x0
|
||||
|
||||
bool class_resolve_links(Class cls);
|
||||
void class_resolve_all_unresolved();
|
||||
|
||||
void class_table_init();
|
||||
void class_add_from_module(struct objc_symtab* symtab);
|
||||
IMP class_get_implementation(Class cls, SEL sel);
|
||||
|
||||
Class objc_getClass(const char* name);
|
||||
|
||||
#endif // _LIBOBJC_CLASS_H
|
||||
18
libs/libobjc/include/libobjc/helpers.h
Normal file
18
libs/libobjc/include/libobjc/helpers.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _LIBOBJC_HELPERS_H
|
||||
#define _LIBOBJC_HELPERS_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define OBJC_EXPORT extern "C"
|
||||
|
||||
#define OBJC_DEBUG
|
||||
|
||||
#ifdef OBJC_DEBUG
|
||||
#define OBJC_DEBUGPRINT(...) \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout)
|
||||
#else
|
||||
#define OBJC_DEBUGPRINT(...) (sizeof(int))
|
||||
#endif
|
||||
|
||||
#endif // _LIBOBJC_HELPERS_H
|
||||
6
libs/libobjc/include/libobjc/isa.h
Normal file
6
libs/libobjc/include/libobjc/isa.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _LIBOBJC_ISA_H
|
||||
#define _LIBOBJC_ISA_H
|
||||
|
||||
#define ISA_MASK 0xfffffffc
|
||||
|
||||
#endif // _LIBOBJC_ISA_H
|
||||
14
libs/libobjc/include/libobjc/memory.h
Normal file
14
libs/libobjc/include/libobjc/memory.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _LIBOBJC_MEMORY_H
|
||||
#define _LIBOBJC_MEMORY_H
|
||||
|
||||
#include <libobjc/runtime.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define objc_malloc(...) malloc(__VA_ARGS__)
|
||||
#define objc_realloc(...) realloc(__VA_ARGS__)
|
||||
#define objc_calloc(...) calloc(__VA_ARGS__)
|
||||
#define objc_free(...) free(__VA_ARGS__)
|
||||
|
||||
id alloc_instance(Class cls);
|
||||
|
||||
#endif // _LIBOBJC_MEMORY_H
|
||||
6
libs/libobjc/include/libobjc/module.h
Normal file
6
libs/libobjc/include/libobjc/module.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _LIBOBJC_MODULE_H
|
||||
#define _LIBOBJC_MODULE_H
|
||||
|
||||
#include <libobjc/v1/decls.h>
|
||||
|
||||
#endif // _LIBOBJC_MODULE_H
|
||||
10
libs/libobjc/include/libobjc/objc.h
Normal file
10
libs/libobjc/include/libobjc/objc.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _LIBOBJC_OBJC_H
|
||||
#define _LIBOBJC_OBJC_H
|
||||
|
||||
#include <libobjc/helpers.h>
|
||||
#include <libobjc/runtime.h>
|
||||
|
||||
OBJC_EXPORT id _Nullable objc_alloc(Class _Nullable cls);
|
||||
OBJC_EXPORT id _Nullable objc_alloc_init(Class _Nullable cls);
|
||||
|
||||
#endif // _LIBOBJC_OBJC_H
|
||||
21
libs/libobjc/include/libobjc/runtime.h
Normal file
21
libs/libobjc/include/libobjc/runtime.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _LIBOBJC_RUNTIME_H
|
||||
#define _LIBOBJC_RUNTIME_H
|
||||
|
||||
#include <libobjc/helpers.h>
|
||||
#include <libobjc/isa.h>
|
||||
#include <libobjc/selector.h>
|
||||
#include <libobjc/v1/decls.h>
|
||||
|
||||
#define ROOT_CLASS "NSObject"
|
||||
|
||||
OBJC_EXPORT void objc_msgSend(id reciever, SEL sel, ...);
|
||||
|
||||
static inline Class object_getClass(id object)
|
||||
{
|
||||
if (!object) {
|
||||
return Nil;
|
||||
}
|
||||
return object->get_isa();
|
||||
}
|
||||
|
||||
#endif // _LIBOBJC_RUNTIME_H
|
||||
23
libs/libobjc/include/libobjc/selector.h
Normal file
23
libs/libobjc/include/libobjc/selector.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _LIBOBJC_SELECTOR_H
|
||||
#define _LIBOBJC_SELECTOR_H
|
||||
|
||||
#include <libobjc/v1/decls.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static inline bool sel_equal(SEL s1, SEL s2)
|
||||
{
|
||||
if (s1 == NULL || s2 == NULL) {
|
||||
return s1 == s2;
|
||||
}
|
||||
return s1->id == s2->id;
|
||||
}
|
||||
|
||||
void selector_table_init();
|
||||
void selector_add_from_module(struct objc_selector*);
|
||||
void selector_add_from_method_list(struct objc_method_list*);
|
||||
void selector_add_from_class(Class);
|
||||
bool selector_is_valid(SEL sel);
|
||||
SEL sel_registerName(const char* name);
|
||||
SEL sel_registerTypedName(const char* name, const char* types);
|
||||
|
||||
#endif // _LIBOBJC_SELECTOR_H
|
||||
139
libs/libobjc/include/libobjc/v1/decls.h
Normal file
139
libs/libobjc/include/libobjc/v1/decls.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#ifndef _LIBOBJC_V1_DECLS_H
|
||||
#define _LIBOBJC_V1_DECLS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct objc_class;
|
||||
struct objc_object;
|
||||
struct objc_selector;
|
||||
struct objc_method;
|
||||
|
||||
typedef struct objc_class* Class;
|
||||
typedef struct objc_object* id;
|
||||
typedef struct objc_selector* SEL;
|
||||
typedef struct objc_method* Method;
|
||||
typedef void (*IMP)(id, SEL, ...);
|
||||
|
||||
#define nil_method (IMP) NULL
|
||||
#define nil (id) NULL
|
||||
#define Nil (Class) NULL
|
||||
|
||||
struct objc_symtab {
|
||||
unsigned long sel_ref_cnt;
|
||||
struct objc_selector* refs;
|
||||
unsigned short cls_def_cnt;
|
||||
unsigned short cat_def_cnt;
|
||||
void* defs[1]; // Variable len
|
||||
};
|
||||
|
||||
struct objc_module {
|
||||
unsigned long version;
|
||||
unsigned long size;
|
||||
const char* name;
|
||||
struct objc_symtab* symtab;
|
||||
};
|
||||
|
||||
struct objc_object final {
|
||||
Class isa;
|
||||
|
||||
inline void set_isa(Class nisa) { isa = nisa; }
|
||||
inline Class get_isa() { return isa; }
|
||||
};
|
||||
|
||||
struct objc_ivar {
|
||||
const char* ivar_name;
|
||||
const char* ivar_type;
|
||||
int ivar_offset;
|
||||
};
|
||||
|
||||
struct objc_ivar_list {
|
||||
int ivar_count;
|
||||
struct objc_ivar ivar_list[1]; // Variable len
|
||||
};
|
||||
|
||||
struct objc_method {
|
||||
SEL method_name;
|
||||
const char* method_types;
|
||||
IMP method_imp;
|
||||
};
|
||||
|
||||
struct objc_method_description {
|
||||
SEL name;
|
||||
char* types;
|
||||
};
|
||||
|
||||
struct objc_method_list {
|
||||
struct objc_method_list* method_next;
|
||||
int method_count;
|
||||
struct objc_method method_list[1]; // Variable len
|
||||
};
|
||||
|
||||
struct objc_method_description_list {
|
||||
int count;
|
||||
struct objc_method_description list[1]; // Variable len
|
||||
};
|
||||
|
||||
struct objc_protocol {
|
||||
struct objc_class* class_pointer;
|
||||
char* protocol_name;
|
||||
struct objc_protocol_list* protocol_list;
|
||||
struct objc_method_description_list* instance_methods;
|
||||
struct objc_method_description_list* class_methods;
|
||||
};
|
||||
|
||||
struct objc_protocol_list {
|
||||
struct objc_protocol_list* next;
|
||||
uint32_t count;
|
||||
struct objc_protocol* list[1]; // Variable len
|
||||
};
|
||||
|
||||
#define CLS_CLASS 0x1
|
||||
#define CLS_META 0x2
|
||||
#define CLS_INITIALIZED 0x4
|
||||
#define CLS_RESOLVED 0x8 // This means that it has had correct super and sublinks assigned
|
||||
#define CLS_INCONSTRUCTION 0x10
|
||||
#define CLS_NUMBER_OFFSET 16 // Long is 32bit long on our target, we use 16bit for number
|
||||
|
||||
struct objc_class final {
|
||||
Class isa;
|
||||
Class superclass;
|
||||
const char* name;
|
||||
long version;
|
||||
unsigned long info;
|
||||
long instance_size;
|
||||
struct objc_ivar_list* ivars;
|
||||
struct objc_method_list* methods;
|
||||
void* disp_table;
|
||||
struct objc_class* subclass_list; // TODO: Currently unresolved
|
||||
struct objc_class* sibling_class; // TODO: Currently unresolved
|
||||
struct objc_protocol_list* protocols;
|
||||
void* gc_object_type;
|
||||
|
||||
inline void set_isa(Class nisa) { isa = nisa; }
|
||||
inline Class get_isa() { return isa; }
|
||||
|
||||
inline void set_info(unsigned long mask) { info = mask; }
|
||||
inline void add_info(unsigned long mask) { info |= mask; }
|
||||
inline void rem_info(unsigned long mask) { info &= (~mask); }
|
||||
inline bool has_info(unsigned long mask) { return ((info & mask) == mask); }
|
||||
inline unsigned long get_info() { return info; }
|
||||
|
||||
inline bool is_class() { return has_info(CLS_CLASS); }
|
||||
inline bool is_meta() { return has_info(CLS_META); }
|
||||
inline bool is_resolved() { return has_info(CLS_RESOLVED); }
|
||||
inline bool is_initialized() { return has_info(CLS_INITIALIZED); }
|
||||
inline bool is_root() { return (bool)superclass; }
|
||||
|
||||
inline void set_number(int num) { info = (info & ((1 << CLS_NUMBER_OFFSET) - 1)) | (num << CLS_NUMBER_OFFSET); }
|
||||
inline int number() { return get_info() >> CLS_NUMBER_OFFSET; }
|
||||
|
||||
inline long size() { return instance_size; }
|
||||
inline long aligned_size() { return instance_size; } // FIXME
|
||||
};
|
||||
|
||||
struct objc_selector {
|
||||
void* id;
|
||||
const char* types;
|
||||
};
|
||||
|
||||
#endif // _LIBOBJC_V1_DECLS_H
|
||||
61
libs/libobjc/src/NSObject.mm
Normal file
61
libs/libobjc/src/NSObject.mm
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <libfoundation/NSObject.h>
|
||||
#include <libobjc/class.h>
|
||||
#include <libobjc/memory.h>
|
||||
#include <libobjc/objc.h>
|
||||
#include <libobjc/runtime.h>
|
||||
|
||||
OBJC_EXPORT IMP objc_msg_lookup(id receiver, SEL sel)
|
||||
{
|
||||
IMP impl = class_get_implementation(receiver->get_isa(), sel);
|
||||
return impl;
|
||||
}
|
||||
|
||||
static inline id call_alloc(Class cls, bool checkNil, bool allocWithZone = false)
|
||||
{
|
||||
if (allocWithZone) {
|
||||
return ((id(*)(id, SEL, void*))objc_msgSend)(cls, @selector(allocWithZone:), NULL);
|
||||
}
|
||||
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
|
||||
}
|
||||
|
||||
// Called with [Class alloc]
|
||||
OBJC_EXPORT id objc_alloc(Class cls)
|
||||
{
|
||||
return call_alloc(cls, true, false);
|
||||
}
|
||||
|
||||
// Called with [[Class alloc] init]
|
||||
OBJC_EXPORT id objc_alloc_init(Class cls)
|
||||
{
|
||||
return [call_alloc(cls, true, false) init];
|
||||
}
|
||||
|
||||
@implementation NSObject
|
||||
|
||||
+ (id)init
|
||||
{
|
||||
return (id)self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
// Init root classes
|
||||
return (id)self;
|
||||
}
|
||||
|
||||
+ (id)alloc
|
||||
{
|
||||
return alloc_instance(self);
|
||||
}
|
||||
|
||||
+ (id)new
|
||||
{
|
||||
return [call_alloc(self, false) init];
|
||||
}
|
||||
|
||||
- (Class)class
|
||||
{
|
||||
return object_getClass(self);
|
||||
}
|
||||
|
||||
@end
|
||||
295
libs/libobjc/src/class.mm
Normal file
295
libs/libobjc/src/class.mm
Normal file
@@ -0,0 +1,295 @@
|
||||
#include <assert.h>
|
||||
#include <libobjc/class.h>
|
||||
#include <libobjc/memory.h>
|
||||
#include <libobjc/module.h>
|
||||
#include <libobjc/objc.h>
|
||||
#include <libobjc/runtime.h>
|
||||
#include <libobjc/selector.h>
|
||||
#include <string.h>
|
||||
|
||||
struct class_node {
|
||||
const char* name;
|
||||
int length;
|
||||
Class cls;
|
||||
};
|
||||
|
||||
// FIXME: Allocate it dynamically.
|
||||
static class_node class_tabel_storage[512];
|
||||
static int class_table_next_free = 0;
|
||||
|
||||
static Class unresolved_classes[128];
|
||||
static int unresolved_classes_next = 0;
|
||||
|
||||
void class_table_init()
|
||||
{
|
||||
}
|
||||
|
||||
Class class_table_find(const char* name)
|
||||
{
|
||||
int len = strlen(name);
|
||||
for (int i = 0; i < class_table_next_free; i++) {
|
||||
if (len == class_tabel_storage[i].length) {
|
||||
if (strcmp(name, class_tabel_storage[i].name) == 0) {
|
||||
return class_tabel_storage[i].cls;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Nil;
|
||||
}
|
||||
|
||||
static void class_table_add(const char* name, Class cls)
|
||||
{
|
||||
int place = class_table_next_free++;
|
||||
class_tabel_storage[place].name = name;
|
||||
class_tabel_storage[place].length = strlen(name);
|
||||
class_tabel_storage[place].cls = cls;
|
||||
}
|
||||
|
||||
bool class_add(Class cls)
|
||||
{
|
||||
Class fnd = class_table_find(cls->name);
|
||||
if (fnd) {
|
||||
return false;
|
||||
} else {
|
||||
static int next_class_num = 1;
|
||||
cls->set_number(next_class_num);
|
||||
cls->get_isa()->set_number(next_class_num);
|
||||
class_table_add(cls->name, cls);
|
||||
next_class_num++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void class_disp_table_preinit(Class cls)
|
||||
{
|
||||
cls->disp_table = DISPATCH_TABLE_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
static Method class_lookup_method_in_list(struct objc_method_list* objc_method_list, SEL sel)
|
||||
{
|
||||
if (!objc_method_list) {
|
||||
return (Method)NULL;
|
||||
}
|
||||
|
||||
if (!selector_is_valid(sel)) {
|
||||
sel = sel_registerTypedName((char*)sel->id, sel->types);
|
||||
}
|
||||
|
||||
while (objc_method_list) {
|
||||
for (int i = 0; i < objc_method_list->method_count; i++) {
|
||||
SEL method_name = objc_method_list->method_list[i].method_name;
|
||||
if (method_name && method_name->id == sel->id) {
|
||||
return &objc_method_list->method_list[i];
|
||||
}
|
||||
}
|
||||
objc_method_list = objc_method_list->method_next;
|
||||
}
|
||||
|
||||
return (Method)NULL;
|
||||
}
|
||||
|
||||
static Method class_lookup_method_in_hierarchy(Class cls, SEL sel)
|
||||
{
|
||||
if (!selector_is_valid(sel)) {
|
||||
sel = sel_registerTypedName((char*)sel->id, sel->types);
|
||||
}
|
||||
|
||||
Method method;
|
||||
for (Class cli = cls; cli; cli = cli->superclass) {
|
||||
method = class_lookup_method_in_list(cli->methods, sel);
|
||||
if (method) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return (Method)NULL;
|
||||
}
|
||||
|
||||
// Add instance methods only to root class.
|
||||
static void class_root_add_instance_methods(Class cls)
|
||||
{
|
||||
int max_methods_allocated = 8;
|
||||
struct objc_method_list* new_list = (struct objc_method_list*)objc_malloc(sizeof(struct objc_method_list) + sizeof(struct objc_method) * max_methods_allocated);
|
||||
struct objc_method_list* objc_method_list = cls->methods;
|
||||
|
||||
new_list->method_count = 0;
|
||||
|
||||
while (objc_method_list) {
|
||||
for (int i = 0; i < objc_method_list->method_count; i++) {
|
||||
SEL method_name = objc_method_list->method_list[i].method_name;
|
||||
if (method_name) {
|
||||
// The instance method isn't a class method yet, so add it.
|
||||
if (!class_lookup_method_in_list(cls->get_isa()->methods, method_name)) {
|
||||
new_list->method_list[new_list->method_count++] = objc_method_list->method_list[i];
|
||||
if (new_list->method_count == max_methods_allocated) {
|
||||
max_methods_allocated += 8;
|
||||
new_list = (struct objc_method_list*)objc_realloc(new_list, sizeof(struct objc_method_list) + sizeof(struct objc_method) * max_methods_allocated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
objc_method_list = objc_method_list->method_next;
|
||||
}
|
||||
|
||||
if (new_list->method_count) {
|
||||
new_list->method_next = cls->get_isa()->methods;
|
||||
cls->get_isa()->methods = new_list;
|
||||
} else {
|
||||
objc_free(new_list);
|
||||
}
|
||||
|
||||
// TODO: Update dispatch table.
|
||||
}
|
||||
|
||||
static void class_send_initialize(Class cls)
|
||||
{
|
||||
assert(cls->is_class() && !cls->is_meta());
|
||||
if (!cls->is_initialized()) {
|
||||
cls->add_info(CLS_INITIALIZED);
|
||||
if (cls->superclass) {
|
||||
class_send_initialize(cls->superclass);
|
||||
}
|
||||
|
||||
SEL sel = sel_registerName("initialize");
|
||||
Method method = class_lookup_method_in_hierarchy(cls->get_isa(), sel);
|
||||
|
||||
if (method) {
|
||||
OBJC_DEBUGPRINT("start [%s +initialize]\n", cls->name);
|
||||
(*method->method_imp)((id)cls, sel);
|
||||
OBJC_DEBUGPRINT("end [%s +initialize]\n", cls->name);
|
||||
} else {
|
||||
OBJC_DEBUGPRINT("class %s has no +initialize\n", cls->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool class_can_resolve(Class cls)
|
||||
{
|
||||
if (cls->is_resolved()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!objc_getClass(ROOT_CLASS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* superclass = (char*)cls->superclass;
|
||||
return !superclass || (superclass && objc_getClass(superclass));
|
||||
}
|
||||
|
||||
bool class_resolve_links(Class cls)
|
||||
{
|
||||
assert(cls->is_class());
|
||||
|
||||
if (cls->is_resolved()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Class object_class = objc_getClass(ROOT_CLASS);
|
||||
if (!object_class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cls->get_isa()->set_isa(object_class);
|
||||
|
||||
if (!cls->superclass) {
|
||||
cls->superclass = nil;
|
||||
cls->get_isa()->superclass = nil;
|
||||
cls->set_info(CLS_RESOLVED);
|
||||
cls->get_isa()->set_info(CLS_RESOLVED);
|
||||
return true;
|
||||
}
|
||||
|
||||
Class supcls = objc_getClass((char*)cls->superclass);
|
||||
if (supcls) {
|
||||
// TODO: Fill subclass list
|
||||
cls->superclass = supcls;
|
||||
cls->get_isa()->superclass = supcls->get_isa();
|
||||
cls->set_info(CLS_RESOLVED);
|
||||
cls->get_isa()->set_info(CLS_RESOLVED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void class_resolve_all_unresolved()
|
||||
{
|
||||
for (int i = 0; i < unresolved_classes_next; i++) {
|
||||
class_resolve_links(unresolved_classes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool class_init(Class cls)
|
||||
{
|
||||
if (class_add(cls)) {
|
||||
selector_add_from_class(cls);
|
||||
selector_add_from_class(cls->get_isa());
|
||||
|
||||
class_disp_table_preinit(cls);
|
||||
class_disp_table_preinit(cls->get_isa());
|
||||
|
||||
// TODO: Init methods and dispatch tables.
|
||||
if (cls->is_root()) {
|
||||
class_root_add_instance_methods(cls);
|
||||
}
|
||||
|
||||
class_resolve_links(cls);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Class objc_getClass(const char* name)
|
||||
{
|
||||
Class cls = class_table_find(name);
|
||||
return cls;
|
||||
}
|
||||
|
||||
void class_add_from_module(struct objc_symtab* symtab)
|
||||
{
|
||||
for (int i = 0; i < symtab->cls_def_cnt; i++) {
|
||||
Class cls = (Class)symtab->defs[i];
|
||||
|
||||
// Fix clang flags
|
||||
if (cls->is_class()) {
|
||||
cls->set_info(CLS_CLASS);
|
||||
} else {
|
||||
cls->set_info(CLS_META);
|
||||
}
|
||||
|
||||
OBJC_DEBUGPRINT("Installing classes %x (%d of %d): %s :: %d\n", (uintptr_t)cls, i + 1, symtab->cls_def_cnt, cls->name, cls->get_info());
|
||||
|
||||
if (class_init(cls)) {
|
||||
if (!class_can_resolve(cls)) {
|
||||
unresolved_classes[unresolved_classes_next++] = cls;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OBJC_EXPORT Class objc_lookup_class(const char* name)
|
||||
{
|
||||
return objc_getClass(name);
|
||||
}
|
||||
|
||||
IMP class_get_implementation(Class cls, SEL sel)
|
||||
{
|
||||
// TODO: Can't init it here, since meta classes are passed here.
|
||||
// if (!cls->is_initialized()) {
|
||||
// class_send_initialize(cls);
|
||||
// }
|
||||
|
||||
sel = sel_registerTypedName((char*)sel->id, sel->types);
|
||||
Method method = class_lookup_method_in_hierarchy(cls, sel);
|
||||
|
||||
if (!method) {
|
||||
return nil_method;
|
||||
}
|
||||
|
||||
// TODO: Message forwarding
|
||||
|
||||
return method->method_imp;
|
||||
}
|
||||
31
libs/libobjc/src/init.mm
Normal file
31
libs/libobjc/src/init.mm
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <libobjc/class.h>
|
||||
#include <libobjc/module.h>
|
||||
#include <libobjc/objc.h>
|
||||
#include <libobjc/runtime.h>
|
||||
|
||||
// The function is called by a constructor of each module.
|
||||
OBJC_EXPORT void __objc_exec_class(struct objc_module* module)
|
||||
{
|
||||
static bool prepared_data_structures = false;
|
||||
|
||||
OBJC_DEBUGPRINT("Called __objc_exec_class, starting to init module\n");
|
||||
|
||||
if (!prepared_data_structures) {
|
||||
selector_table_init();
|
||||
class_table_init();
|
||||
prepared_data_structures = true;
|
||||
}
|
||||
|
||||
struct objc_symtab* symtab = module->symtab;
|
||||
struct objc_selector* selectors = symtab->refs;
|
||||
|
||||
if (selectors) {
|
||||
selector_add_from_module(selectors);
|
||||
}
|
||||
|
||||
class_add_from_module(symtab);
|
||||
|
||||
// TODO: Many things to init here.
|
||||
|
||||
class_resolve_all_unresolved();
|
||||
}
|
||||
9
libs/libobjc/src/memory.mm
Normal file
9
libs/libobjc/src/memory.mm
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <libobjc/memory.h>
|
||||
|
||||
id alloc_instance(Class cls)
|
||||
{
|
||||
size_t sz = cls->size();
|
||||
id obj = (id)objc_malloc(sz);
|
||||
obj->set_isa(cls);
|
||||
return obj;
|
||||
}
|
||||
11
libs/libobjc/src/msgsend_arm32.S
Normal file
11
libs/libobjc/src/msgsend_arm32.S
Normal file
@@ -0,0 +1,11 @@
|
||||
.extern objc_msg_lookup
|
||||
.global objc_msgSend
|
||||
|
||||
objc_msgSend:
|
||||
push {r0-r3}
|
||||
push {lr}
|
||||
bl objc_msg_lookup
|
||||
pop {lr}
|
||||
mov r12, r0 // imp pointer
|
||||
pop {r0-r3}
|
||||
bx r12
|
||||
19
libs/libobjc/src/msgsend_arm64.S
Normal file
19
libs/libobjc/src/msgsend_arm64.S
Normal file
@@ -0,0 +1,19 @@
|
||||
.extern objc_msg_lookup
|
||||
.global objc_msgSend
|
||||
|
||||
objc_msgSend:
|
||||
sub sp, sp, #0x50
|
||||
stp x0, x1, [sp]
|
||||
stp x2, x3, [sp, #0x10]
|
||||
stp x4, x5, [sp, #0x20]
|
||||
stp x6, x7, [sp, #0x30]
|
||||
stp x29, x30, [sp, #0x40]
|
||||
bl objc_msg_lookup
|
||||
mov x9, x0 // imp pointer
|
||||
ldp x0, x1, [sp]
|
||||
ldp x2, x3, [sp, #0x10]
|
||||
ldp x4, x5, [sp, #0x20]
|
||||
ldp x6, x7, [sp, #0x30]
|
||||
ldp x29, x30, [sp, #0x40]
|
||||
add sp, sp, #0x50
|
||||
br x9
|
||||
5
libs/libobjc/src/msgsend_riscv64.S
Normal file
5
libs/libobjc/src/msgsend_riscv64.S
Normal file
@@ -0,0 +1,5 @@
|
||||
.extern objc_msg_lookup
|
||||
.global objc_msgSend
|
||||
|
||||
objc_msgSend:
|
||||
|
||||
6
libs/libobjc/src/msgsend_x86.S
Normal file
6
libs/libobjc/src/msgsend_x86.S
Normal file
@@ -0,0 +1,6 @@
|
||||
extern objc_msg_lookup
|
||||
global objc_msgSend
|
||||
|
||||
objc_msgSend:
|
||||
call objc_msg_lookup
|
||||
jmp [eax]
|
||||
18
libs/libobjc/src/msgsend_x86_64.S
Normal file
18
libs/libobjc/src/msgsend_x86_64.S
Normal file
@@ -0,0 +1,18 @@
|
||||
extern objc_msg_lookup
|
||||
global objc_msgSend
|
||||
|
||||
objc_msgSend:
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push r8
|
||||
push r9
|
||||
call objc_msg_lookup
|
||||
pop r9
|
||||
pop r8
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
jmp [rax]
|
||||
109
libs/libobjc/src/selector.mm
Normal file
109
libs/libobjc/src/selector.mm
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <libobjc/memory.h>
|
||||
#include <libobjc/objc.h>
|
||||
#include <libobjc/runtime.h>
|
||||
#include <libobjc/selector.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct objc_selector* selector_pool_start;
|
||||
static struct objc_selector* selector_pool_next;
|
||||
|
||||
#define CONST_DATA true
|
||||
#define VOLATILE_DATA false
|
||||
|
||||
static SEL selector_table_add(const char* name, const char* types, bool const_data)
|
||||
{
|
||||
// Checking if we have this selector
|
||||
for (struct objc_selector* cur_sel = selector_pool_start; cur_sel != selector_pool_next; cur_sel++) {
|
||||
if (strcmp(name, (char*)cur_sel->id) == 0) {
|
||||
if (cur_sel->types == 0 || types == 0) {
|
||||
if (cur_sel->types == types) {
|
||||
return (SEL)cur_sel;
|
||||
}
|
||||
} else {
|
||||
if (strcmp(types, cur_sel->types) == 0) {
|
||||
return (SEL)cur_sel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SEL sel = (SEL)selector_pool_next++;
|
||||
if (const_data) {
|
||||
sel->id = (char*)name;
|
||||
sel->types = types;
|
||||
} else {
|
||||
int name_len = strlen(name);
|
||||
char* name_data = (char*)objc_malloc(name_len + 1);
|
||||
memcpy(name_data, name, name_len);
|
||||
name_data[name_len] = '\0';
|
||||
sel->id = name_data;
|
||||
sel->types = 0;
|
||||
|
||||
if (types) {
|
||||
int types_len = strlen(types);
|
||||
char* types_data = (char*)objc_malloc(types_len + 1);
|
||||
memcpy(types_data, types, types_len);
|
||||
types_data[types_len] = '\0';
|
||||
sel->types = types_data;
|
||||
}
|
||||
}
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
bool selector_is_valid(SEL sel)
|
||||
{
|
||||
return (uintptr_t)selector_pool_start < (uintptr_t)sel && (uintptr_t)sel < (uintptr_t)selector_pool_next;
|
||||
}
|
||||
|
||||
// TODO: We currently do it really stupid
|
||||
void selector_table_init()
|
||||
{
|
||||
selector_pool_start = selector_pool_next = (struct objc_selector*)objc_malloc(1024);
|
||||
}
|
||||
|
||||
void selector_add_from_module(struct objc_selector* selectors)
|
||||
{
|
||||
for (int i = 0; selectors[i].id; i++) {
|
||||
char* name = (char*)selectors[i].id;
|
||||
const char* types = selectors[i].types;
|
||||
SEL sel = selector_table_add(name, types, CONST_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void selector_add_from_method_list(struct objc_method_list* method_list)
|
||||
{
|
||||
for (int i = 0; i < method_list->method_count; i++) {
|
||||
Method method = &method_list->method_list[i];
|
||||
if (method->method_name) {
|
||||
char* name = (char*)method->method_name;
|
||||
const char* types = method->method_types;
|
||||
method->method_name = selector_table_add(name, types, CONST_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void selector_add_from_class(Class cls)
|
||||
{
|
||||
for (struct objc_method_list* ml = cls->methods; ml; ml = ml->method_next) {
|
||||
selector_add_from_method_list(ml);
|
||||
}
|
||||
}
|
||||
|
||||
SEL sel_registerName(const char* name)
|
||||
{
|
||||
if (!name) {
|
||||
return (SEL)NULL;
|
||||
}
|
||||
|
||||
return selector_table_add(name, 0, VOLATILE_DATA);
|
||||
}
|
||||
|
||||
SEL sel_registerTypedName(const char* name, const char* types)
|
||||
{
|
||||
if (!name || !types) {
|
||||
return (SEL)NULL;
|
||||
}
|
||||
|
||||
return selector_table_add(name, types, VOLATILE_DATA);
|
||||
}
|
||||
Reference in New Issue
Block a user