1 /****************************************************************************
2 * Copyright (C) 2012 by Matteo Franchin *
3 * *
4 * This file is part of Box. *
5 * *
6 * Box is free software: you can redistribute it and/or modify it *
7 * under the terms of the GNU Lesser General Public License as published *
8 * by the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * Box is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with Box. If not, see <http://www.gnu.org/licenses/>. *
18 ****************************************************************************/
19
20 #include <assert.h>
21
22 #include <box/types.h>
23 #include <box/mem.h>
24 #include <box/obj.h>
25 #include <box/callable.h>
26 #include <box/vm.h>
27 #include <box/vmproc.h>
28
29 #include <box/core_priv.h>
30 #include <box/callable_priv.h>
31 #include <box/vm_priv.h>
32
33 #include <box/vmsym.h>
34 #include <box/vmsymstuff.h>
35
36
37 /* Initialize a callable object as an undefined callable. */
38 void
BoxCallable_Init_As_Undefined(BoxCallable * cb)39 BoxCallable_Init_As_Undefined(BoxCallable *cb) {
40 cb->uid = NULL;
41 cb->kind = BOXCALLABLEKIND_UNDEFINED;
42 BoxPtr_Init(& cb->context);
43 }
44
45 /* Finalize a callable object. */
BoxCallable_Finish(BoxCallable * cb)46 void BoxCallable_Finish(BoxCallable *cb) {
47 if (cb->uid)
48 Box_Mem_Free(cb->uid);
49 BoxPtr_Unlink(& cb->context);
50 }
51
52 /* Create an undefined callable */
53 BOXOUT BoxCallable *
BoxCallable_Create_Undefined(BoxType * t_out,BoxType * t_in)54 BoxCallable_Create_Undefined(BoxType *t_out, BoxType *t_in) {
55 BoxType *t_cb = BoxType_Create_Callable(t_out, t_in);
56 BoxCallable *cb = NULL;
57 if (t_cb) {
58 cb = BoxSPtr_Raw_Alloc(t_cb, sizeof(BoxCallable));
59 if (cb)
60 BoxCallable_Init_As_Undefined(cb);
61
62 BoxSPtr_Unlink(t_cb);
63 }
64
65 return cb;
66 }
67
68 /* Create an undefined callable copying the type from an exising callable. */
69 BOXOUT BoxCallable *
BoxCallable_Create_Similar(BoxCallable * cb)70 BoxCallable_Create_Similar(BoxCallable *cb) {
71 BoxCallable *new_cb = NULL;
72 BoxType *t_cb = BoxSPtr_Get_Type(cb);
73 if (t_cb) {
74 new_cb = BoxSPtr_Raw_Alloc(t_cb, sizeof(BoxCallable));
75 if (new_cb)
76 BoxCallable_Init_As_Undefined(new_cb);
77 }
78
79 return new_cb;
80 }
81
82 /* Set the unique identifier for the callable. */
83 BoxBool
BoxCallable_Set_Uid(BoxCallable * cb,BoxUid * uid)84 BoxCallable_Set_Uid(BoxCallable *cb, BoxUid *uid) {
85 if (!(cb && !cb->uid))
86 return BOXBOOL_FALSE;
87
88 cb->uid = Box_Mem_Strdup(uid);
89 return BOXBOOL_TRUE;
90 }
91
92 /* Return the unique identifier associated to the given callable. */
93 BoxUid *
BoxCallable_Get_Uid(BoxCallable * cb)94 BoxCallable_Get_Uid(BoxCallable *cb) {
95 return (cb) ? cb->uid : NULL;
96 }
97
98 /* Set the callable context (useful for creating closures). */
99 void
BoxCallable_Set_Context(BoxCallable * cb,BOXIN BoxPtr * context)100 BoxCallable_Set_Context(BoxCallable *cb, BOXIN BoxPtr *context) {
101 (void) BoxPtr_Unlink(& cb->context);
102 if (context)
103 cb->context = *context;
104 else
105 BoxPtr_Init(& cb->context);
106 }
107
108 /* Initialize a callable object from a BoxCCall1 C function. */
109 BOXOUT BoxCallable *
BoxCallable_Define_From_CCall1(BOXIN BoxCallable * cb,BoxCCall1 call)110 BoxCallable_Define_From_CCall1(BOXIN BoxCallable *cb, BoxCCall1 call) {
111 if (!cb)
112 return NULL;
113
114 assert(cb->kind == BOXCALLABLEKIND_UNDEFINED);
115 cb->kind = BOXCALLABLEKIND_C_1;
116 cb->implem.c_call_1 = call;
117 return cb;
118 }
119
120 /* Initialize a callable object from a BoxCCall2 C function. */
121 BOXOUT BoxCallable *
BoxCallable_Define_From_CCall2(BOXIN BoxCallable * cb,BoxCCall2 call)122 BoxCallable_Define_From_CCall2(BOXIN BoxCallable *cb, BoxCCall2 call) {
123 if (!cb)
124 return NULL;
125
126 assert(cb->kind == BOXCALLABLEKIND_UNDEFINED);
127 cb->kind = BOXCALLABLEKIND_C_2;
128 cb->implem.c_call_2 = call;
129 return cb;
130 }
131
132 /* Initialize a callable object from a BoxCCallOld C function. */
133 BOXOUT BoxCallable *
BoxCallable_Define_From_CCallOld(BOXIN BoxCallable * cb,BoxCCallOld c_old)134 BoxCallable_Define_From_CCallOld(BOXIN BoxCallable *cb, BoxCCallOld c_old) {
135 if (!cb)
136 return NULL;
137
138 switch (cb->kind) {
139 case BOXCALLABLEKIND_UNDEFINED:
140 cb->kind = BOXCALLABLEKIND_C_OLD;
141 cb->implem.c_old = c_old;
142 return cb;
143
144 case BOXCALLABLEKIND_VM:
145 {
146 BoxVM *vm = cb->implem.vm_call.vm;
147 BoxVMCallNum call_num = cb->implem.vm_call.call_num;
148 switch (BoxVM_Get_Proc_Kind(vm, call_num)) {
149 case BOXVMPROCKIND_RESERVED:
150 if (BoxVM_Install_Proc_CCode(vm, call_num, c_old))
151 return cb;
152 break;
153
154 case BOXVMPROCKIND_FOREIGN:
155 #if 0
156 if (!BoxVM_Get_Callable_Implem(vm, call_num, & cb))
157 return BOXBOOL_FALSE;
158 break;
159 #endif
160
161 default:
162 break;
163 }
164 break;
165 }
166
167 default:
168 break;
169 }
170
171 (void) BoxCallable_Unlink(cb);
172 return NULL;
173 }
174
175 /* Initialize a callable object from a BoxCCall3 C function. */
BoxCallable_Define_From_CCall3(BoxCallable * cb,BoxCCall3 call)176 void BoxCallable_Define_From_CCall3(BoxCallable *cb, BoxCCall3 call) {
177 assert(cb->kind == BOXCALLABLEKIND_UNDEFINED);
178 cb->kind = BOXCALLABLEKIND_C_2;
179 cb->implem.c_call_3 = call;
180 }
181
182 /* Define a callable object from a BoxVM procedure. */
183 BOXOUT BoxCallable *
BoxCallable_Define_From_VM(BOXIN BoxCallable * cb,BoxVM * vm,BoxVMCallNum num)184 BoxCallable_Define_From_VM(BOXIN BoxCallable *cb, BoxVM *vm,
185 BoxVMCallNum num) {
186 if (!cb)
187 return NULL;
188
189 if (cb->kind != BOXCALLABLEKIND_UNDEFINED) {
190 (void) BoxSPtr_Unlink(cb);
191 return NULL;
192 }
193
194 cb->kind = BOXCALLABLEKIND_VM;
195 cb->implem.vm_call.vm = BoxVM_Link(vm);
196 cb->implem.vm_call.call_num = num;
197 return cb;
198 }
199
200 /* Return the call number for a VM callable. */
201 BoxBool
BoxCallable_Get_VM_Call_Num(BoxCallable * cb,BoxVM * vm,BoxVMCallNum * cn)202 BoxCallable_Get_VM_Call_Num(BoxCallable *cb, BoxVM *vm, BoxVMCallNum *cn) {
203 if (cb->kind == BOXCALLABLEKIND_VM && vm == cb->implem.vm_call.vm) {
204 if (cn)
205 *cn = cb->implem.vm_call.call_num;
206 return BOXBOOL_TRUE;
207 }
208
209 return BOXBOOL_FALSE;
210 }
211
212 /* Request a call number for a given callable. */
213 BoxBool
BoxCallable_Request_VM_Call_Num(BoxCallable * cb,BoxVM * vm,BoxVMCallNum * num,BOXOUT BoxCallable ** cb_out)214 BoxCallable_Request_VM_Call_Num(BoxCallable *cb, BoxVM *vm, BoxVMCallNum *num,
215 BOXOUT BoxCallable **cb_out) {
216 BoxVMCallNum new_num = BOXVMCALLNUM_NONE;
217 BoxCallable *new_cb = NULL;
218
219 switch (cb->kind) {
220 case BOXCALLABLEKIND_UNDEFINED:
221 {
222 /* Here we allocate a new VM call number and we leave it undefined.
223 * We define the undefined callable in cb and return it in cb_out.
224 */
225 new_num = BoxVM_Allocate_Call_Num(vm);
226 if (new_num == BOXVMCALLNUM_NONE)
227 break;
228
229 new_cb = BoxCallable_Define_From_VM(BoxCallable_Link(cb), vm, new_num);
230 if (!new_cb)
231 break;
232
233 *num = new_num;
234 *cb_out = new_cb;
235 return BOXBOOL_TRUE;
236 }
237
238 case BOXCALLABLEKIND_C_1:
239 case BOXCALLABLEKIND_C_2:
240 case BOXCALLABLEKIND_C_3:
241 case BOXCALLABLEKIND_C_OLD:
242 {
243 /* Here we just allocate a new call number, register it so that it points
244 * to the C implementation in cb and create a new BoxCallable for the
245 * VM call. This is finally what is returned in cb_out.
246 */
247
248 /* Allocate a new call number. */
249 new_num = BoxVM_Allocate_Call_Num(vm);
250 if (new_num == BOXVMCALLNUM_NONE)
251 break;
252
253 /* Create a new VM callable and define it. */
254 new_cb = BoxCallable_Create_Similar(cb);
255 if (!new_cb)
256 break;
257
258 new_cb = BoxCallable_Define_From_VM(new_cb, vm, new_num);
259 if (!new_cb)
260 break;
261
262 /* Register the callable with the VM. */
263 if (!BoxVM_Install_Proc_Callable(vm, new_num, cb))
264 break;
265
266 /* Set the procedure names, to make disassembly easier to read. */
267 (void) BoxVM_Set_Proc_Names(vm, new_num, BoxCallable_Get_Uid(cb), NULL);
268
269 *num = new_num;
270 *cb_out = new_cb;
271 return BOXBOOL_TRUE;
272 }
273
274 case BOXCALLABLEKIND_VM:
275 {
276 /* Check whether we do already have a call number for this VM. */
277 if (vm == cb->implem.vm_call.vm) {
278 /* We do: we can return it. */
279 *num = cb->implem.vm_call.call_num;
280 *cb_out = NULL;
281 return BOXBOOL_TRUE;
282
283 } else {
284 /* We don't have a call number for the required VM. */
285
286 /* This is the case where we have more than one VM and one of them
287 * may be calling a procedure defined in the other.
288 * While we want to support this in the future, for now we just abort.
289 */
290 abort();
291 }
292 }
293 break;
294 }
295
296 /* Failure: release resources and exit. */
297 if (new_num != BOXVMCALLNUM_NONE)
298 (void) BoxVM_Deallocate_Call_Num(vm, new_num);
299
300 if (!new_cb)
301 (void) BoxCallable_Unlink(new_cb);
302
303 return BOXBOOL_FALSE;
304 }
305
306 /* Whether the callable is implemented. */
307 BoxBool
BoxCallable_Is_Implemented(BoxCallable * cb)308 BoxCallable_Is_Implemented(BoxCallable *cb) {
309 /* The loop below may never return, if the BoxCallable is badly formed. */
310 while (1) {
311 switch (cb->kind) {
312 case BOXCALLABLEKIND_UNDEFINED:
313 return BOXBOOL_FALSE;
314
315 case BOXCALLABLEKIND_C_1:
316 case BOXCALLABLEKIND_C_2:
317 case BOXCALLABLEKIND_C_3:
318 case BOXCALLABLEKIND_C_OLD:
319 return BOXBOOL_TRUE;
320
321 case BOXCALLABLEKIND_VM:
322 {
323 BoxVM *vm = cb->implem.vm_call.vm;
324 BoxVMCallNum call_num = cb->implem.vm_call.call_num;
325 switch (BoxVM_Get_Proc_Kind(vm, call_num)) {
326 case BOXVMPROCKIND_UNDEFINED:
327 case BOXVMPROCKIND_RESERVED:
328 return BOXBOOL_FALSE;
329 case BOXVMPROCKIND_FOREIGN:
330 if (!BoxVM_Get_Callable_Implem(vm, call_num, & cb))
331 return BOXBOOL_FALSE;
332 break;
333 default:
334 return BOXBOOL_TRUE;
335 }
336 break;
337 }
338
339 default:
340 return BOXBOOL_FALSE;
341 }
342 }
343
344 /* Does not return. */
345 abort();
346 return BOXBOOL_FALSE;
347 }
348
349 /* Call an old-style C function. */
350 static BoxException *
My_Call_CallOld(BoxCallable * cb,BoxPtr * parent,BoxPtr * child)351 My_Call_CallOld(BoxCallable *cb, BoxPtr *parent, BoxPtr *child) {
352 BoxPtr dummy;
353 BoxVM vm;
354 BoxVMX vmx;
355
356 /* Dummy null pointer. */
357 BoxPtr_Init(& dummy);
358
359 /* Setup a fake VM. */
360 vmx.vm = & vm;
361 vm.box_vm_current = (parent) ? parent : & dummy;
362 vm.box_vm_arg1 = (child) ? child : & dummy;
363
364 if (cb->implem.c_old(& vmx) == BOXTASK_OK)
365 return NULL;
366
367 return BoxException_Create("Callable %s raised an exception", cb->uid);
368 }
369
370 /* Create a callable object from a BoxCCall2 C function. */
371 BOXOUT BoxException *
BoxCallable_Call1(BoxCallable * cb,BoxPtr * parent)372 BoxCallable_Call1(BoxCallable *cb, BoxPtr *parent) {
373 switch (cb->kind) {
374 case BOXCALLABLEKIND_UNDEFINED:
375 return BoxException_Create("Callable %s is undefined", cb->uid);
376
377 case BOXCALLABLEKIND_C_1:
378 return cb->implem.c_call_1(parent);
379
380 case BOXCALLABLEKIND_C_2:
381 {
382 BoxPtr null;
383 BoxPtr_Init(& null);
384 return cb->implem.c_call_2(parent, & null);
385 }
386
387 case BOXCALLABLEKIND_C_3:
388 {
389 BoxPtr callable, null;
390 BoxPtr_Init(& null);
391 BoxPtr_Init_From_SPtr(& callable, cb);
392 return cb->implem.c_call_3(& callable, parent, & null);
393 }
394
395 case BOXCALLABLEKIND_C_OLD:
396 return My_Call_CallOld(cb, parent, NULL);
397
398 case BOXCALLABLEKIND_VM:
399 {
400 BoxVMCallNum call_num = cb->implem.vm_call.call_num;
401 BoxVM *vm = cb->implem.vm_call.vm;
402 if (BoxVM_Module_Execute_With_Args(vm, call_num, parent, NULL)
403 == BOXTASK_OK)
404 return NULL;
405 break;
406 }
407 }
408
409 return BoxException_Create("Callable %s raised an exception", cb->uid);
410 }
411
412 /* Create a callable object from a BoxCCall2 C function. */
413 BOXOUT BoxException *
BoxCallable_Call2(BoxCallable * cb,BoxPtr * parent,BoxPtr * child)414 BoxCallable_Call2(BoxCallable *cb, BoxPtr *parent, BoxPtr *child) {
415 switch (cb->kind) {
416 case BOXCALLABLEKIND_UNDEFINED:
417 return BoxException_Create("Callable %s is undefined", cb->uid);
418
419 case BOXCALLABLEKIND_C_1:
420 return cb->implem.c_call_1(parent);
421
422 case BOXCALLABLEKIND_C_2:
423 return cb->implem.c_call_2(parent, child);
424
425 case BOXCALLABLEKIND_C_3:
426 {
427 BoxPtr callable;
428 BoxPtr_Init_From_SPtr(& callable, cb);
429 return cb->implem.c_call_3(& callable, parent, child);
430 }
431
432 case BOXCALLABLEKIND_C_OLD:
433 return My_Call_CallOld(cb, parent, child);
434
435 case BOXCALLABLEKIND_VM:
436 {
437 BoxVMCallNum call_num = cb->implem.vm_call.call_num;
438 BoxVM *vm = cb->implem.vm_call.vm;
439 if (BoxVM_Module_Execute_With_Args(vm, call_num, parent, child)
440 == BOXTASK_OK)
441 return NULL;
442 break;
443 }
444 }
445
446 return BoxException_Create("Callable %s raised an exception", cb->uid);
447 }
448
449 #include "messages.h"
450
451 BoxTask
BoxCallable_CallOld(BoxCallable * cb,BoxVMX * vmx)452 BoxCallable_CallOld(BoxCallable *cb, BoxVMX *vmx) {
453 BoxException *e = NULL;
454 char *es = NULL;
455
456 switch (cb->kind) {
457 case BOXCALLABLEKIND_C_2:
458 e = cb->implem.c_call_2(BoxVMX_Get_Parent(vmx), BoxVMX_Get_Child(vmx));
459 if (!e)
460 return BOXTASK_OK;
461 break;
462 case BOXCALLABLEKIND_C_OLD:
463 return cb->implem.c_old(vmx);
464 default:
465 MSG_FATAL("Call to new-style procedure is not supported, yet.");
466 return BOXTASK_FAILURE;
467 }
468
469 if (e) {
470 es = BoxException_Get_Str(e);
471 BoxException_Destroy(e);
472 BoxVM_Set_Fail_Msg(vmx->vm, es);
473 Box_Mem_Free(es);
474 }
475 return BOXTASK_FAILURE;
476 }
477