1#include "include/pw.h"
2#include "src/pw_alloc.h"
3#include "src/pw_interfaces_internal.h"
4
5#include <libpussy/arena.h>
6#include <libpussy/mmarray.h>
7
8static Arena* arena = nullptr;
9
10void* _pw_arena_alloc_bytes(unsigned size, unsigned alignment)
11{
12 void* result = _arena_fit(arena, size, alignment);
13 if (!result) {
14 pw_panic("Out of memory\n");
15 }
16 return result;
17}
18
19typedef struct {
20 char* name;
21 char** method_names;
22 unsigned num_methods;
23} InterfaceInfo;
24
25static InterfaceInfo** registered_interfaces = nullptr;
26static unsigned num_registered_interfaces = 0;
27
28static char s_interface_not_registered[] = "Interface %u is not registered yet\n";
29#define pw_panic_interface_not_registered(interface_id) pw_panic(s_interface_not_registered, (interface_id));
30
31[[noreturn]]
32void _pw_panic_no_interface(uint16_t type_id, uint16_t interface_id)
33{
34 char* iname;
35 if (registered_interfaces) {
36 iname = registered_interfaces[interface_id]->name;
37 } else {
38 iname = "unknown";
39 }
40 pw_panic("Interface %s (%u) is not defined for %s\n", iname, interface_id, _pw_types[type_id]->name);
41}
42
43PwInterface_Generic* __pw_do_lookup_interface(PwType* type, uint16_t interface_id)
44{
45 PwInterface_Generic** iface = type->other_interfaces;
46 if (iface) {
47 PwInterface_Generic** iface_end = iface + type->num_other_interfaces;
48 do {
49 if ((*iface)->id == interface_id) {
50 return *iface;
51 }
52 iface++;
53 } while (iface < iface_end);
54 }
55 return nullptr;
56}
57
58void _pw_undefined_interface()
59{
60 pw_set_status(PwStatus(PW_ERROR_INTERFACE_NOT_DEFINED));
61}
62
63void pw_destroy_compound2(PwValuePtr value, _PwCompoundChain* tail)
64/*
65 * Destroy value: call destructor (if refcount drops to zero for refcounted values) and make `value` Null.
66 */
67{
68 uint16_t type_id = value->type_id;
69
70 if (_pw_likely(type_id < PW_NUM_INTEGRAL_TYPES)) {
71 return;
72 }
73
74 if (_pw_unlikely(_pw_on_chain(value, tail))) {
75 return;
76 }
77
78 PwMethod_Basic_decref* meth_decref;
79 pw_method(type_id, Basic, decref, &meth_decref);
80 PwFunc_Basic_decref fn_decref = meth_decref->func;
81 if (_pw_unlikely(fn_decref)) {
82 if (fn_decref(meth_decref, value)) {
83 // refcount is still above zero
84 // clean value holder but do not call destructor
85 *value = PwNull();
86 return;
87 }
88 }
89
90 PwMethod_Basic_destroy* meth_destroy;
91 pw_method(type_id, Basic, destroy, &meth_destroy);
92 PwFunc_Basic_destroy fn_destroy = meth_destroy->func;
93 if (_pw_unlikely(fn_destroy)) {
94 fn_destroy(meth_destroy, value, tail);
95 }
96 *value = PwNull();
97}
98
99[[gnu::constructor]]
100void _pw_init_interfaces()
101{
102 if (registered_interfaces) {
103 return;
104 }
105
106 arena = create_arena(0);
107 registered_interfaces = mmarray_allocate(65536, 0, sizeof(InterfaceInfo*));
108
109 // register built-in interfaces
110
111# define X(name, ...) #name __VA_OPT__(,)
112 pw_assert(PwInterfaceId_Basic == pw_register_interface("Basic", PW_BASIC_INTERFACE_METHODS, nullptr ));
113 pw_assert(PwInterfaceId_RandomAccess == pw_register_interface("RandomAccess", PW_RANDOM_ACCESS_INTERFACE_METHODS, nullptr ));
114 pw_assert(PwInterfaceId_Reader == pw_register_interface("Reader", PW_READER_INTERFACE_METHODS, nullptr ));
115 pw_assert(PwInterfaceId_Writer == pw_register_interface("Writer", PW_WRITER_INTERFACE_METHODS, nullptr ));
116 pw_assert(PwInterfaceId_LineReader == pw_register_interface("LineReader", PW_LINE_READER_INTERFACE_METHODS, nullptr ));
117 pw_assert(PwInterfaceId_Append == pw_register_interface("Append", PW_APPEND_INTERFACE_METHODS, nullptr ));
118 pw_assert(PwInterfaceId_Fd == pw_register_interface("Fd", PW_FD_INTERFACE_METHODS, nullptr ));
119# undef X
120}
121
122uint16_t pw_register_interface(char* name, ...)
123{
124 _pw_init_interfaces();
125 if (mmarray_grow(registered_interfaces, 1)) { /* no op, address not changed */ }
126
127 uint16_t interface_id = num_registered_interfaces++;
128 InterfaceInfo* iinfo = registered_interfaces[interface_id] = _pw_arena_alloc(1, InterfaceInfo);
129 iinfo->name = name;
130 iinfo->num_methods = 0;
131
132 va_list ap;
133 va_list temp_ap;
134 va_start(ap);
135
136 // count method names
137 va_copy(temp_ap, ap);
138 for(;;) {
139 char* method_name = va_arg(temp_ap, char*);
140 if (!method_name) {
141 break;
142 }
143 iinfo->num_methods++;
144 }
145 va_end(temp_ap);
146
147 // allocate array for method names
148 char** method_names = _pw_arena_alloc(iinfo->num_methods, char*);
149 iinfo->method_names = method_names;
150
151 // init method names
152 for (;;) {
153 char* method_name = va_arg(ap, char*);
154 if (!method_name) {
155 break;
156 }
157 *method_names++ = method_name;
158 }
159 va_end(ap);
160 return interface_id;
161}
162
163bool pw_interface_exists(uint16_t interface_id)
164{
165 return interface_id < num_registered_interfaces;
166}
167
168char* pw_get_interface_name(uint16_t interface_id)
169{
170 if (interface_id >= num_registered_interfaces) {
171 pw_panic_interface_not_registered(interface_id);
172 }
173 return registered_interfaces[interface_id]->name;
174}
175
176unsigned _pw_get_num_interface_methods(uint16_t interface_id)
177{
178 if (interface_id >= num_registered_interfaces) {
179 pw_panic_interface_not_registered(interface_id);
180 }
181 return registered_interfaces[interface_id]->num_methods;
182}
183
184
185static PwInterface_Generic* find_interface_definition(PwType* type, uint16_t interface_id)
186// helper for _pw_make_interface
187{
188 unsigned n = type->num_interface_definitions;
189 if (n) {
190 PwInterface_Generic** idef = type->interface_definitions;
191 PwInterface_Generic** idef_end = idef + n;
192 while (idef < idef_end) {
193 if ((*idef)->id == interface_id) {
194 return *idef;
195 }
196 idef++;
197 }
198 }
199 return nullptr;
200}
201
202PwInterface_Generic* _pw_make_interface(PwType* type, uint16_t interface_id)
203{
204 if (interface_id >= num_registered_interfaces) {
205 pw_panic_interface_not_registered(interface_id);
206 }
207
208 InterfaceInfo* iinfo = registered_interfaces[interface_id];
209
210 // allocate and initialize interface structure
211
212 unsigned memsize = sizeof(PwInterface_Generic) + iinfo->num_methods * sizeof(PwMethod_Generic);
213 PwInterface_Generic* interface = _pw_arena_alloc_bytes(memsize, alignof(PwInterface_Generic));
214 interface->id = interface_id;
215 interface->num_methods = iinfo->num_methods;
216 interface->name = iinfo->name;
217 interface->method_names = iinfo->method_names;
218 interface->type = type;
219
220 // make interface methods
221
222 PwMethod_Generic* method = interface->methods;
223 PwMethod_Generic* method_end = method + interface->num_methods;
224 unsigned method_index = 0;
225 while (method < method_end) {
226 method->func = nullptr;
227 method->super = nullptr;
228 method->self = nullptr;
229 method->struct_offset = 0;
230 method->type_id = 0;
231
232 // build MRO chain
233
234 PwMethod_Generic* sub_method = method; // current method in the chain
235 unsigned* struct_offset = type->struct_offsets;
236
237 PwInterface_Generic* idef = find_interface_definition(type, interface_id);
238
239 if (idef && idef->methods[method_index].func) {
240 // have func defined for this type
241 sub_method->func = idef->methods[method_index].func;
242 sub_method->self = interface;
243 sub_method->struct_offset = struct_offset? *struct_offset : 0;
244 sub_method->type_id = type->id;
245 }
246 struct_offset++;
247
248 PwType** base_type = type->base_types;
249 PwType** base_type_end = base_type + type->num_base_types;
250 while (base_type < base_type_end) {
251 PwType* t = *base_type;
252 idef = find_interface_definition(t, interface_id);
253
254 if (idef && idef->methods[method_index].func) {
255
256 // have super func
257
258 if (sub_method->func) {
259 // sub_method already initialized, allocate new one
260 PwMethod_Generic* super_method = _pw_arena_alloc(1, PwMethod_Generic);
261 super_method->super = nullptr;
262 sub_method->super = super_method;
263 sub_method = super_method;
264 }
265 // initialize current method
266 sub_method->func = idef->methods[method_index].func;
267 sub_method->self = _pw_lookup_interface_t(t, interface_id);
268 if (!sub_method->self) {
269 pw_panic("Interface %s must be initialized for %s\n", iinfo->name, t->name);
270 }
271 pw_assert(sub_method->self->type->id == t->id);
272 sub_method->struct_offset = struct_offset? *struct_offset : 0;
273 sub_method->type_id = type->id;
274 }
275 base_type++;
276 struct_offset++;
277 }
278 method++;
279 method_index++;
280 }
281 return interface;
282}