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