1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include <assert.h>
10#include <libobjc/class.h>
11#include <libobjc/memory.h>
12#include <libobjc/module.h>
13#include <libobjc/objc.h>
14#include <libobjc/runtime.h>
15#include <libobjc/selector.h>
16#include <string.h>
17
18struct class_node {
19 const char* name;
20 int length;
21 Class cls;
22};
23
24// FIXME: Allocate it dynamically.
25static class_node class_tabel_storage[512];
26static int class_table_next_free = 0;
27
28static Class unresolved_classes[128];
29static int unresolved_classes_next = 0;
30
31void class_table_init()
32{
33}
34
35Class class_table_find(const char* name)
36{
37 int len = strlen(name);
38 for (int i = 0; i < class_table_next_free; i++) {
39 if (len == class_tabel_storage[i].length) {
40 if (strcmp(name, class_tabel_storage[i].name) == 0) {
41 return class_tabel_storage[i].cls;
42 }
43 }
44 }
45 return Nil;
46}
47
48static void class_table_add(const char* name, Class cls)
49{
50 int place = class_table_next_free++;
51 class_tabel_storage[place].name = name;
52 class_tabel_storage[place].length = strlen(name);
53 class_tabel_storage[place].cls = cls;
54}
55
56bool class_add(Class cls)
57{
58 Class fnd = class_table_find(cls->name);
59 if (fnd) {
60 return false;
61 } else {
62 static int next_class_num = 1;
63 cls->set_number(next_class_num);
64 cls->get_isa()->set_number(next_class_num);
65 class_table_add(cls->name, cls);
66 next_class_num++;
67 return true;
68 }
69}
70
71static inline void class_disp_table_preinit(Class cls)
72{
73 cls->disp_table = DISPATCH_TABLE_NOT_INITIALIZED;
74}
75
76static Method class_lookup_method_in_list(struct objc_method_list* objc_method_list, SEL sel)
77{
78 if (!objc_method_list) {
79 return (Method)NULL;
80 }
81
82 if (!selector_is_valid(sel)) {
83 sel = sel_registerTypedName((char*)sel->id, sel->types);
84 }
85
86 while (objc_method_list) {
87 for (int i = 0; i < objc_method_list->method_count; i++) {
88 SEL method_name = objc_method_list->method_list[i].method_name;
89 if (method_name && method_name->id == sel->id) {
90 return &objc_method_list->method_list[i];
91 }
92 }
93 objc_method_list = objc_method_list->method_next;
94 }
95
96 return (Method)NULL;
97}
98
99static Method class_lookup_method_in_hierarchy(Class cls, SEL sel)
100{
101 if (!selector_is_valid(sel)) {
102 sel = sel_registerTypedName((char*)sel->id, sel->types);
103 }
104
105 Method method;
106 for (Class cli = cls; cli; cli = cli->superclass) {
107 method = class_lookup_method_in_list(cli->methods, sel);
108 if (method) {
109 return method;
110 }
111 }
112
113 return (Method)NULL;
114}
115
116// Add instance methods only to root class.
117static void class_root_add_instance_methods(Class cls)
118{
119 int max_methods_allocated = 8;
120 struct objc_method_list* new_list = (struct objc_method_list*)objc_malloc(sizeof(struct objc_method_list) + sizeof(struct objc_method[max_methods_allocated]));
121 struct objc_method_list* objc_method_list = cls->methods;
122
123 new_list->method_count = 0;
124
125 while (objc_method_list) {
126 for (int i = 0; i < objc_method_list->method_count; i++) {
127 SEL method_name = objc_method_list->method_list[i].method_name;
128 if (method_name) {
129 // The instance method isn't a class method yet, so add it.
130 if (!class_lookup_method_in_list(cls->get_isa()->methods, method_name)) {
131 new_list->method_list[new_list->method_count++] = objc_method_list->method_list[i];
132 if (new_list->method_count == max_methods_allocated) {
133 max_methods_allocated += 8;
134 new_list = (struct objc_method_list*)objc_realloc(new_list, sizeof(struct objc_method_list) + sizeof(struct objc_method[max_methods_allocated]));
135 }
136 }
137 }
138 }
139 objc_method_list = objc_method_list->method_next;
140 }
141
142 if (new_list->method_count) {
143 new_list->method_next = cls->get_isa()->methods;
144 cls->get_isa()->methods = new_list;
145 } else {
146 objc_free(new_list);
147 }
148
149 // TODO: Update dispatch table.
150}
151
152static void class_send_initialize(Class cls)
153{
154 assert(cls->is_class() && !cls->is_meta());
155 if (!cls->is_initialized()) {
156 cls->add_info(CLS_INITIALIZED);
157 if (cls->superclass) {
158 class_send_initialize(cls->superclass);
159 }
160
161 SEL sel = sel_registerName("initialize");
162 Method method = class_lookup_method_in_hierarchy(cls->get_isa(), sel);
163
164 if (method) {
165 OBJC_DEBUGPRINT("start [%s +initialize]\n", cls->name);
166 (*method->method_imp)((id)cls, sel);
167 OBJC_DEBUGPRINT("end [%s +initialize]\n", cls->name);
168 } else {
169 OBJC_DEBUGPRINT("class %s has no +initialize\n", cls->name);
170 }
171 }
172}
173
174bool class_can_resolve(Class cls)
175{
176 if (cls->is_resolved()) {
177 return true;
178 }
179
180 if (!objc_getClass(ROOT_CLASS)) {
181 return false;
182 }
183
184 const char* superclass = (char*)cls->superclass;
185 return !superclass || (superclass && objc_getClass(superclass));
186}
187
188bool class_resolve_links(Class cls)
189{
190 assert(cls->is_class());
191
192 if (cls->is_resolved()) {
193 return true;
194 }
195
196 Class object_class = objc_getClass(ROOT_CLASS);
197 if (!object_class) {
198 return false;
199 }
200
201 cls->get_isa()->set_isa(object_class);
202
203 if (!cls->superclass) {
204 cls->superclass = nil;
205 cls->get_isa()->superclass = nil;
206 cls->set_info(CLS_RESOLVED);
207 cls->get_isa()->set_info(CLS_RESOLVED);
208 return true;
209 }
210
211 Class supcls = objc_getClass((char*)cls->superclass);
212 if (supcls) {
213 // TODO: Fill subclass list
214 cls->superclass = supcls;
215 cls->get_isa()->superclass = supcls->get_isa();
216 cls->set_info(CLS_RESOLVED);
217 cls->get_isa()->set_info(CLS_RESOLVED);
218 return true;
219 }
220
221 return false;
222}
223
224void class_resolve_all_unresolved()
225{
226 for (int i = 0; i < unresolved_classes_next; i++) {
227 class_resolve_links(unresolved_classes[i]);
228 }
229}
230
231bool class_init(Class cls)
232{
233 if (class_add(cls)) {
234 selector_add_from_class(cls);
235 selector_add_from_class(cls->get_isa());
236
237 class_disp_table_preinit(cls);
238 class_disp_table_preinit(cls->get_isa());
239
240 // TODO: Init methods and dispatch tables.
241 if (cls->is_root()) {
242 class_root_add_instance_methods(cls);
243 }
244
245 class_resolve_links(cls);
246
247 return true;
248 }
249
250 return false;
251}
252
253Class objc_getClass(const char* name)
254{
255 Class cls = class_table_find(name);
256 return cls;
257}
258
259void class_add_from_module(struct objc_symtab* symtab)
260{
261 for (int i = 0; i < symtab->cls_def_cnt; i++) {
262 Class cls = (Class)symtab->defs[i];
263
264 // Fix clang flags
265 if (cls->is_class()) {
266 cls->set_info(CLS_CLASS);
267 } else {
268 cls->set_info(CLS_META);
269 }
270
271 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());
272
273 if (class_init(cls)) {
274 if (!class_can_resolve(cls)) {
275 unresolved_classes[unresolved_classes_next++] = cls;
276 }
277 }
278 }
279}
280
281OBJC_EXPORT Class objc_lookup_class(const char* name)
282{
283 return objc_getClass(name);
284}
285
286IMP class_get_implementation(Class cls, SEL sel)
287{
288 // TODO: Can't init it here, since meta classes are passed here.
289 // if (!cls->is_initialized()) {
290 // class_send_initialize(cls);
291 // }
292
293 sel = sel_registerTypedName((char*)sel->id, sel->types);
294 Method method = class_lookup_method_in_hierarchy(cls, sel);
295
296 if (!method) {
297 return nil_method;
298 }
299
300 // TODO: Message forwarding
301
302 return method->method_imp;
303}