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 #include <string.h>
22 
23 #include <box/mem.h>
24 #include <box/obj.h>
25 #include <box/core.h>
26 #include <box/messages.h>
27 #include <box/exception.h>
28 
29 #include <box/types_priv.h>
30 
31 
32 /**
33  * @brief Get the header from a pointer as returned by BoxObj_Alloc().
34  */
35 #define MY_GET_HEADER_FROM_OBJ(obj) \
36   ((BoxObjHeader *) ((char *) (obj) - sizeof(BoxObjHeader)))
37 
38 /**
39  * @brief Get the object (as returned by BoxObj_Alloc()) from the header.
40  *
41  * This is the inverse of #MY_GET_HEADER_FROM_OBJ.
42  */
43 #define MY_GET_OBJ_FROM_HEADER(hdr) \
44   ((void *) ((char *) (hdr) + sizeof(BoxObjHeader)))
45 
46 
47 /* Forward references */
48 static BoxBool My_Init_Obj(BoxPtr *src, BoxType *t);
49 static void My_Finish_Obj(BoxPtr *src, BoxType *t);
50 static BoxBool My_Copy_Obj(BoxPtr *dst, BoxPtr *src, BoxType *t);
51 
52 /* Initialize a subtype. */
53 void
BoxSubtype_Init(BoxSubtype * st)54 BoxSubtype_Init(BoxSubtype *st) {
55   BoxPtr_Nullify(& st->parent);
56   BoxPtr_Nullify(& st->child);
57 }
58 
59 /* Finalize a subtype. */
60 void
BoxSubtype_Finish(BoxSubtype * st)61 BoxSubtype_Finish(BoxSubtype *st) {
62   (void) BoxPtr_Unlink(& st->parent);
63   (void) BoxPtr_Unlink(& st->child);
64 }
65 
66 /* Finalize a subtype. */
67 void
BoxSubtype_Copy(BoxSubtype * dst,BoxSubtype * src)68 BoxSubtype_Copy(BoxSubtype *dst, BoxSubtype *src) {
69   *dst = *src;
70 }
71 
72 /* Finalize an Any object. */
BoxAny_Finish(BoxAny * any)73 void BoxAny_Finish(BoxAny *any)
74 {
75   (void) BoxPtr_Unlink(& any->ptr);
76   BoxPtr_Init(& any->ptr);
77   /*TODO: unlink type as well!*/
78   any->type = NULL;
79 }
80 
81 /* Copy an Any object. */
BoxAny_Copy(BoxAny * dst,BoxAny * src)82 void BoxAny_Copy(BoxAny *dst, BoxAny *src) {
83   *dst = *src;
84   (void) BoxPtr_Link(& src->ptr);
85   /* Note that the type is implicitly referenced by the object. */
86 }
87 
88 /* Change the boxed object stored inside the given any object. */
BoxAny_Box(BoxPtr * any,BoxPtr * obj,BoxType * t,BoxBool safe)89 BoxBool BoxAny_Box(BoxPtr *any, BoxPtr *obj, BoxType *t, BoxBool safe) {
90   BoxAny *any_sptr = BoxPtr_Get_Target(any);
91   BoxPtr obj_copy, *new_ptr = NULL;
92 
93   /* If obj is a NULL-block object, we must copy it now. */
94   if (safe && BoxPtr_Get_Target(obj) && !BoxPtr_Get_Block(obj)) {
95     if (!BoxPtr_Create_Obj(& obj_copy, t))
96       return BOXBOOL_FALSE;
97     if (!BoxPtr_Copy_Obj(& obj_copy, obj, t)) {
98       (void) BoxPtr_Unlink(& obj_copy);
99       return BOXBOOL_FALSE;
100     }
101     new_ptr = & obj_copy;
102   } else
103     new_ptr = BoxPtr_Link(obj);
104 
105   BoxPtr_Unlink(& any_sptr->ptr);
106   any_sptr->ptr = *new_ptr;
107   any_sptr->type = t;
108   return BOXBOOL_TRUE;
109 }
110 
111 /* Retrieve the boxed object stored inside the given any object. */
BoxAny_Unbox(BOXOUT BoxPtr * obj,BoxPtr * any,BoxType * t)112 BoxBool BoxAny_Unbox(BOXOUT BoxPtr *obj, BoxPtr *any, BoxType *t) {
113   return BOXBOOL_FALSE;
114 }
115 
116 /* Initialize a block of memory addressed by src and with type t. */
117 static BoxBool
My_Init_Obj(BoxPtr * src,BoxType * t)118 My_Init_Obj(BoxPtr *src, BoxType *t) {
119   while (1) {
120     switch (t->type_class) {
121     case BOXTYPECLASS_SUBTYPE_NODE:
122       BoxSubtype_Init((BoxSubtype *) BoxPtr_Get_Target(src));
123       return BOXBOOL_TRUE;
124 
125     case BOXTYPECLASS_PRIMARY:
126     case BOXTYPECLASS_INTRINSIC:
127       return BOXBOOL_TRUE;
128 
129     case BOXTYPECLASS_IDENT:
130       {
131         BoxCallable *callable;
132         BoxType *node;
133         BoxType *rt;
134 
135         /* Initialise first from the type we are deriving from. */
136         rt = BoxType_Resolve(t, BOXTYPERESOLVE_IDENT, 1);
137         if (!My_Init_Obj(src, rt))
138           return BOXBOOL_FALSE;
139 
140         node = BoxType_Find_Own_Combination_With_Id(t, BOXCOMBTYPE_AT,
141                                                     BOXTYPEID_INIT, NULL);
142         if (node && BoxType_Get_Combination_Info(node, NULL, & callable))
143         {
144           /* Now do our own initialization... */
145           BoxException *excp = BoxCallable_Call1(callable, src);
146           if (!excp)
147             return BOXBOOL_TRUE;
148 
149           BoxException_Destroy(excp);
150           My_Finish_Obj(src, rt);
151           return BOXBOOL_FALSE;
152         }
153 
154         return BOXBOOL_TRUE;
155       }
156 
157     case BOXTYPECLASS_RAISED:
158       t = BoxType_Resolve(t, BOXTYPERESOLVE_RAISED, 0);
159       break;
160 
161     case BOXTYPECLASS_STRUCTURE:
162       {
163         BoxBool success;
164         BoxTypeIter ti;
165         BoxType *node;
166         size_t idx, failure_idx;
167 
168         /* If things go wrong, failure_idx will be set to the index of
169          * the member at which a failure occurred. This will be used to
170          * finalize all the previous members of the structure (in order to
171          * avoid ending up with a partially initialized structure).
172          */
173         success = BOXBOOL_TRUE;
174 
175         for (BoxTypeIter_Init(& ti, t), idx = 0;
176              BoxTypeIter_Get_Next(& ti, & node);
177              idx++) {
178           size_t offset;
179           BoxType *type;
180           BoxPtr member_ptr;
181 
182           BoxType_Get_Structure_Member(node, NULL, & offset, NULL, & type);
183           BoxPtr_Add_Offset(& member_ptr, src, offset);
184           if (!My_Init_Obj(& member_ptr, type))
185           {
186             success = BOXBOOL_FALSE;
187             failure_idx = idx;
188             break;
189           }
190         }
191 
192         BoxTypeIter_Finish(& ti);
193 
194         /* No failure, exit successfully! */
195         if (success)
196           return BOXBOOL_TRUE;
197 
198         /* Failure: finalize whatever was initialized and exit with failure. */
199         for (BoxTypeIter_Init(& ti, t), idx = 0;
200              BoxTypeIter_Get_Next(& ti, & node) && idx < failure_idx;
201              idx++) {
202           size_t offset;
203           BoxType *type;
204           BoxPtr member_ptr;
205 
206           BoxType_Get_Structure_Member(node, NULL, & offset, NULL, & type);
207           BoxPtr_Add_Offset(& member_ptr, src, offset);
208           My_Finish_Obj(& member_ptr, type);
209         }
210 
211         BoxTypeIter_Finish(& ti);
212       }
213 
214       return BOXBOOL_FALSE;
215 
216     case BOXTYPECLASS_SPECIES:
217       t = BoxType_Resolve(t, BOXTYPERESOLVE_SPECIES, 0);
218       break;
219 
220     case BOXTYPECLASS_ENUM:
221       /* TO BE IMPLEMENTED */
222       return BOXBOOL_FALSE;
223 
224     case BOXTYPECLASS_FUNCTION:
225       /* TO BE IMPLEMENTED */
226       return BOXBOOL_FALSE;
227 
228     case BOXTYPECLASS_POINTER:
229       BoxPtr_Init((BoxPtr *) BoxPtr_Get_Target(src));
230       return BOXBOOL_TRUE;
231 
232     case BOXTYPECLASS_ANY:
233       BoxAny_Init((BoxAny *) BoxPtr_Get_Target(src));
234       return BOXBOOL_TRUE;
235 
236     default:
237       return BOXBOOL_FALSE;
238     }
239   }
240 
241   return BOXBOOL_TRUE;
242 }
243 
244 /* Generic finalization function for objects. */
245 static void
My_Finish_Obj(BoxPtr * src,BoxType * t)246 My_Finish_Obj(BoxPtr *src, BoxType *t) {
247   while (1) {
248     switch (t->type_class) {
249     case BOXTYPECLASS_SUBTYPE_NODE:
250       BoxSubtype_Finish((BoxSubtype *) BoxPtr_Get_Target(src));
251       return;
252 
253     case BOXTYPECLASS_PRIMARY:
254     case BOXTYPECLASS_INTRINSIC:
255       return;
256 
257     case BOXTYPECLASS_IDENT:
258       {
259         BoxCallable *callable;
260         BoxType *node =
261           BoxType_Find_Own_Combination_With_Id(t, BOXCOMBTYPE_AT,
262                                                BOXTYPEID_FINISH, NULL);
263 
264         if (node && BoxType_Get_Combination_Info(node, NULL, & callable))
265         {
266           /* Finalize the object. Ignore exceptions! */
267           BoxException *excp = BoxCallable_Call1(callable, src);
268           if (excp)
269             BoxException_Destroy(excp);
270         }
271 
272         t = BoxType_Resolve(t, BOXTYPERESOLVE_IDENT, 1);
273       }
274       break;
275 
276     case BOXTYPECLASS_RAISED:
277       t = BoxType_Resolve(t, BOXTYPERESOLVE_RAISED, 0);
278       break;
279 
280     case BOXTYPECLASS_STRUCTURE:
281       {
282         BoxTypeIter ti;
283         BoxType *node;
284 
285         for (BoxTypeIter_Init(& ti, t);
286              BoxTypeIter_Get_Next(& ti, & node);) {
287           size_t offset;
288           BoxType *type;
289           BoxPtr member_ptr;
290 
291           BoxType_Get_Structure_Member(node, NULL, & offset, NULL, & type);
292           BoxPtr_Add_Offset(& member_ptr, src, offset);
293           My_Finish_Obj(& member_ptr, type);
294         }
295 
296         BoxTypeIter_Finish(& ti);
297         return;
298       }
299 
300     case BOXTYPECLASS_SPECIES:
301       t = BoxType_Resolve(t, BOXTYPERESOLVE_SPECIES, 0);
302       break;
303 
304     case BOXTYPECLASS_ENUM:
305       /* TO BE IMPLEMENTED */
306     case BOXTYPECLASS_FUNCTION:
307       /* TO BE IMPLEMENTED */
308       return;
309 
310     case BOXTYPECLASS_POINTER:
311       (void) BoxPtr_Unlink((BoxPtr *) BoxPtr_Get_Target(src));
312       BoxPtr_Init((BoxPtr *) BoxPtr_Get_Target(src));
313       return;
314 
315     case BOXTYPECLASS_ANY:
316       BoxAny_Finish((BoxAny *) BoxPtr_Get_Target(src));
317       return;
318 
319     default:
320       return;
321     }
322   }
323 }
324 
325 /* Generic function to copy objects. */
326 static BoxBool
My_Copy_Obj(BoxPtr * dst,BoxPtr * src,BoxType * t)327 My_Copy_Obj(BoxPtr *dst, BoxPtr *src, BoxType *t) {
328   /* Check we are not copying the object over itself. */
329   if (dst->ptr == src->ptr)
330     return BOXBOOL_TRUE;
331 
332   /* We do a loop to avoid nested calls when a type needs to be resolved: we
333    * rather resolve and try again.
334    */
335   while (1) {
336     switch (t->type_class) {
337     case BOXTYPECLASS_PRIMARY:
338     case BOXTYPECLASS_INTRINSIC:
339       (void) memcpy(dst->ptr, src->ptr, BoxType_Get_Size(t));
340       return BOXBOOL_TRUE;
341 
342     case BOXTYPECLASS_IDENT:
343       {
344         BoxCallable *callable;
345         BoxType *node;
346         BoxType *rt;
347 
348         /* First, copy the derived type. */
349         rt = BoxType_Resolve(t, BOXTYPERESOLVE_IDENT, 1);
350 
351         node = BoxType_Find_Own_Combination(t, BOXCOMBTYPE_COPY, t, NULL);
352 
353         if (!node)
354           return My_Copy_Obj(dst, src, rt);
355 
356         if (node && BoxType_Get_Combination_Info(node, NULL, & callable))
357         {
358           /* Now do our own initialization... */
359           BoxException *excp = BoxCallable_Call2(callable, dst, src);
360           if (!excp)
361             return BOXBOOL_TRUE;
362 
363           BoxException_Destroy(excp);
364           My_Finish_Obj(dst, rt);
365           return BOXBOOL_FALSE;
366         }
367       }
368 
369       return BOXBOOL_TRUE;
370 
371     case BOXTYPECLASS_RAISED:
372       t = BoxType_Resolve(t, BOXTYPERESOLVE_RAISED, 0);
373       assert(t->type_class != BOXTYPECLASS_RAISED);
374       break;
375 
376     case BOXTYPECLASS_STRUCTURE:
377       {
378         BoxBool success;
379         BoxTypeIter ti;
380         BoxType *node;
381         size_t idx, failure_idx;
382 
383         /* If something goes wrong, idx contains the index for which copy
384          * failed, so that we can finalise all the partially copied members.
385          */
386         success = BOXBOOL_TRUE;
387 
388         for (BoxTypeIter_Init(& ti, t), idx = 0;
389              BoxTypeIter_Get_Next(& ti, & node);
390              idx++) {
391           size_t offset;
392           BoxType *memb_type;
393           BoxPtr dst_memb_ptr, src_memb_ptr;
394 
395           BoxType_Get_Structure_Member(node, NULL, & offset, NULL,
396                                        & memb_type);
397           BoxPtr_Add_Offset(& dst_memb_ptr, dst, offset);
398           BoxPtr_Add_Offset(& src_memb_ptr, src, offset);
399           if (!My_Copy_Obj(& dst_memb_ptr, & src_memb_ptr, memb_type)) {
400             success = BOXBOOL_FALSE;
401             failure_idx = idx;
402             break;
403           }
404         }
405 
406         BoxTypeIter_Finish(& ti);
407 
408         /* No failure, exit successfully! */
409         if (success)
410           return BOXBOOL_TRUE;
411 
412         /* Failure: finalize whatever was copied and exit with failure. */
413         for (BoxTypeIter_Init(& ti, t), idx = 0;
414              BoxTypeIter_Get_Next(& ti, & node) && idx < failure_idx;
415              idx++) {
416           size_t offset;
417           BoxType *memb_type;
418           BoxPtr dst_memb_ptr;
419 
420           BoxType_Get_Structure_Member(node, NULL, & offset, NULL,
421                                        & memb_type);
422           BoxPtr_Add_Offset(& dst_memb_ptr, dst, offset);
423           My_Finish_Obj(& dst_memb_ptr, memb_type);
424         }
425 
426         BoxTypeIter_Finish(& ti);
427 
428         return BOXBOOL_FALSE;
429       }
430 
431     case BOXTYPECLASS_SPECIES:
432       t = BoxType_Resolve(t, BOXTYPERESOLVE_SPECIES, 0);
433       assert(t->type_class != BOXTYPECLASS_SPECIES);
434       break;
435 
436     case BOXTYPECLASS_ENUM:
437     case BOXTYPECLASS_FUNCTION:
438     case BOXTYPECLASS_POINTER:
439       return BOXBOOL_FALSE;
440 
441     case BOXTYPECLASS_ANY:
442       BoxAny_Copy((BoxAny *) BoxPtr_Get_Target(dst),
443                   (BoxAny *) BoxPtr_Get_Target(src));
444       return BOXBOOL_TRUE;
445 
446     default:
447       MSG_FATAL("Unexpected type class (%d) in My_Copy_Obj", t->type_class);
448       return BOXBOOL_FALSE;
449     }
450   }
451 
452   /* Should never get here... */
453   return BOXBOOL_FALSE;
454 }
455 
456 /* Add a reference to an object and return it. */
BoxSPtr_Link(BoxSPtr src)457 BoxSPtr BoxSPtr_Link(BoxSPtr src) {
458   if (src) {
459     BoxObjHeader *head = MY_GET_HEADER_FROM_OBJ(src);
460     assert(head->num_refs > 0);
461     head->num_refs++;
462     return src;
463   } else
464     return NULL;
465 }
466 
467 /* This function allows unlinking an object and performing some extra
468  * operations before object destruction.
469  */
BoxSPtr_Unlink_Begin(BoxSPtr src)470 BoxBool BoxSPtr_Unlink_Begin(BoxSPtr src) {
471   BoxObjHeader *head = MY_GET_HEADER_FROM_OBJ(src);
472   BoxSPtr ret;
473 
474   if (head->num_refs == 1)
475     return BOXBOOL_TRUE;
476 
477   assert(head->num_refs > 1);
478   ret = BoxSPtr_Unlink(src);
479   assert(ret);
480   return BOXBOOL_FALSE;
481 }
482 
483 /* Function to be used in conjunction with BoxSPtr_Unlink_Begin. */
BoxSPtr_Unlink_End(BoxSPtr src)484 void BoxSPtr_Unlink_End(BoxSPtr src) {
485   BoxSPtr ret = BoxSPtr_Unlink(src);
486   assert(!ret);
487 }
488 
489 /* Reference the given object. */
BoxPtr_Link(BoxPtr * src)490 BoxPtr *BoxPtr_Link(BoxPtr *src) {
491   if (!BoxPtr_Is_Detached(src)) {
492     BoxObjHeader *head = BoxPtr_Get_Block(src);
493     assert(head->num_refs >= 1);
494     head->num_refs++;
495   }
496 
497   return src;
498 }
499 
500 /* Remove a reference to an object, destroying it, if unreferenced. */
BoxPtr_Unlink(BoxPtr * src)501 BoxBool BoxPtr_Unlink(BoxPtr *src) {
502   BoxObjHeader *head = BoxPtr_Get_Block(src);
503   size_t num_refs;
504 
505   if (!head)
506     return BOXBOOL_TRUE;
507 
508   num_refs = head->num_refs;
509 
510   if (num_refs > 1) {
511     head->num_refs--;
512     return BOXBOOL_TRUE;
513 
514   } else {
515     BoxPtr src_container;
516 
517     assert(num_refs == 1);
518 
519     /* Destroy the containing object. */
520     src_container.block = src->block;
521     src_container.ptr = MY_GET_OBJ_FROM_HEADER(head);
522     My_Finish_Obj(& src_container, head->type);
523 
524     if (head->type)
525       (void) BoxType_Unlink(head->type);
526     head->num_refs = 0;
527     Box_Mem_Free(head);
528     return BOXBOOL_FALSE;
529   }
530 }
531 
532 /* Get the type of the allocated object. */
533 BoxType *
BoxSPtr_Get_Type(BoxSPtr obj)534 BoxSPtr_Get_Type(BoxSPtr obj) {
535   if (obj) {
536     BoxObjHeader *head = MY_GET_HEADER_FROM_OBJ(obj);
537     assert(head->num_refs >= 1);
538     return head->type;
539   } else
540     return NULL;
541 }
542 
543 /* Remove a reference to an object, destroying it, if unreferenced. */
BoxSPtr_Unlink(BoxSPtr src)544 BoxSPtr BoxSPtr_Unlink(BoxSPtr src) {
545   if (src) {
546     BoxPtr src_ptr;
547     BoxPtr_Init_From_SPtr(& src_ptr, src);
548 
549     if (BoxPtr_Unlink(& src_ptr))
550       return src;
551   }
552 
553   return NULL;
554 }
555 
556 /* Allocate space for an object of the given type. */
BoxSPtr_Alloc(BoxType * t)557 BoxSPtr BoxSPtr_Alloc(BoxType *t) {
558   size_t obj_size = BoxType_Get_Size(t);
559   return BoxSPtr_Raw_Alloc(t, obj_size);
560 }
561 
562 /* Raw allocation function. */
BoxSPtr_Raw_Alloc(BoxType * t,size_t obj_size)563 BOXOUT BoxSPtr BoxSPtr_Raw_Alloc(BoxType *t, size_t obj_size) {
564   size_t total_size;
565   if (Box_Mem_Sum(& total_size, sizeof(BoxObjHeader), obj_size)) {
566     void *whole = Box_Mem_Alloc(total_size);
567     if (whole) {
568       BoxObjHeader *head = whole;
569       void *ptr = MY_GET_OBJ_FROM_HEADER(whole);
570       head->num_refs = 1;
571       head->type = (t) ? BoxSPtr_Link(t) : NULL;
572       return ptr;
573     }
574   }
575 
576   return NULL;
577 }
578 
579 /* Allocate and initialize an object of the given type; return its pointer. */
BoxSPtr_Create(BoxType * t)580 BoxSPtr BoxSPtr_Create(BoxType *t) {
581   BoxSPtr obj = BoxSPtr_Alloc(t);
582 
583   BoxPtr obj_ptr;
584   BoxPtr_Init_From_SPtr(& obj_ptr, obj);
585 
586   if (obj) {
587     if (My_Init_Obj(& obj_ptr, t))
588       return obj;
589 
590     (void) BoxSPtr_Unlink(obj);
591   }
592 
593   return NULL;
594 }
595 
596 /* Create a new object of the given type and return a pointer to it. */
597 BoxBool
BoxPtr_Create_Obj(BOXOUT BoxPtr * ptr,BoxType * t)598 BoxPtr_Create_Obj(BOXOUT BoxPtr *ptr, BoxType *t) {
599   BoxSPtr obj = BoxSPtr_Create(t);
600   BoxPtr_Init_From_SPtr(ptr, obj);
601   return (obj != NULL);
602 }
603 
604 /* Copy an object of the given type. */
605 BoxBool
BoxPtr_Copy_Obj(BoxPtr * dst,BoxPtr * src,BoxType * t)606 BoxPtr_Copy_Obj(BoxPtr *dst, BoxPtr *src, BoxType *t) {
607   return My_Copy_Obj(dst, src, t);
608 }
609 
610 /* Try to convert a BoxPtr object to a simple single pointer. */
611 void *
BoxPtr_Get_SPtr(const BoxPtr * src)612 BoxPtr_Get_SPtr(const BoxPtr *src) {
613   if (src) {
614     void *sptr = (char *) src->block + sizeof(BoxObjHeader);
615     if (sptr == src->ptr)
616       return sptr;
617   }
618 
619   return NULL;
620 }
621 
622 /* Combine two objects from their types and pointers. */
623 BoxBool
Box_Combine(BoxType * t_parent,BoxPtr * parent,BoxType * t_child,BoxPtr * child,BoxException ** exception)624 Box_Combine(BoxType *t_parent, BoxPtr *parent,
625             BoxType *t_child, BoxPtr *child, BoxException **exception) {
626   BoxType *comb_node, *expand_type;
627   BoxTypeCmp expand;
628   BoxCallable *cb;
629 
630   if (!(t_parent && t_child))
631     return BOXBOOL_FALSE;
632 
633   comb_node = BoxType_Find_Combination(t_parent, BOXCOMBTYPE_AT, t_child,
634                                        & expand);
635   if (!comb_node)
636     return BOXBOOL_FALSE;
637 
638   if (!BoxType_Get_Combination_Info(comb_node, & expand_type, & cb))
639     MSG_FATAL("Failed getting combination info in dynamic call.");
640 
641   if (expand == BOXTYPECMP_MATCHING) {
642     *exception = BoxException_Create_Raw("Dynamic expansion of type is not "
643                                          "yet implemented");
644     return BOXBOOL_TRUE;
645   }
646 
647   /* Make sure we are not passing NULL objects... */
648   if (!(parent && child)) {
649     if (!parent && !BoxType_Is_Empty(t_parent)) {
650       *exception = BoxException_Create_Raw("Empty parent in dynamic "
651                                            "combination");
652       return BOXBOOL_TRUE;
653     }
654 
655     if (!child && !BoxType_Is_Empty(t_child)) {
656       *exception = BoxException_Create_Raw("Empty child in dynamic "
657                                            "combination");
658       return BOXBOOL_TRUE;
659     }
660   }
661 
662   *exception = BoxCallable_Call2(cb, parent, child);
663   return BOXBOOL_TRUE;
664 }
665 
666 /* Combine two objects boxed as BoxAny objects. */
667 BoxBool
Box_Combine_Any(BoxAny * parent,BoxAny * child,BoxException ** except)668 Box_Combine_Any(BoxAny *parent, BoxAny *child, BoxException **except) {
669   return Box_Combine(BoxAny_Get_Type(parent), BoxAny_Get_Obj(parent),
670                      BoxAny_Get_Type(child), BoxAny_Get_Obj(child), except);
671 }
672