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