Coverage Report

Created: 2018-06-21 18:56

/home/travis/build/MoarVM/MoarVM/src/6model/reprs/CUnion.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* This representation's function pointer table. */
4
static const MVMREPROps CUnion_this_repr;
5
6
/* Locates all of the attributes. Puts them onto a flattened, ordered
7
 * list of attributes (populating the passed flat_list). Also builds
8
 * the index mapping for doing named lookups. Note index is not related
9
 * to the storage position. */
10
0
static MVMObject * index_mapping_and_flat_list(MVMThreadContext *tc, MVMObject *mro, MVMCUnionREPRData *repr_data) {
11
0
    MVMInstance *instance  = tc->instance;
12
0
    MVMObject *flat_list, *class_list, *attr_map_list;
13
0
    MVMint32  num_classes, i, current_slot = 0;
14
0
    MVMCUnionNameMap *result;
15
0
16
0
    MVMint32 mro_idx = MVM_repr_elems(tc, mro);
17
0
    flat_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
18
0
    class_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
19
0
    attr_map_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
20
0
21
0
    /* Walk through the parents list. */
22
0
    while (mro_idx)
23
0
    {
24
0
        /* Get current class in MRO. */
25
0
        MVMObject *type_info     = MVM_repr_at_pos_o(tc, mro, --mro_idx);
26
0
        MVMObject *current_class = MVM_repr_at_pos_o(tc, type_info, 0);
27
0
28
0
        /* Get its local parents; make sure we're not doing MI. */
29
0
        MVMObject *parents     = MVM_repr_at_pos_o(tc, type_info, 2);
30
0
        MVMint32  num_parents = MVM_repr_elems(tc, parents);
31
0
        if (num_parents <= 1) {
32
0
            /* Get attributes and iterate over them. */
33
0
            MVMObject *attributes     = MVM_repr_at_pos_o(tc, type_info, 1);
34
0
            MVMIter * const attr_iter = (MVMIter *)MVM_iter(tc, attributes);
35
0
            MVMObject *attr_map = NULL;
36
0
37
0
            if (MVM_iter_istrue(tc, attr_iter))
38
0
                attr_map = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
39
0
40
0
            while (MVM_iter_istrue(tc, attr_iter)) {
41
0
                MVMObject *current_slot_obj = MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, current_slot);
42
0
                MVMObject *attr, *name_obj;
43
0
                MVMString *name;
44
0
45
0
                MVM_repr_shift_o(tc, (MVMObject *)attr_iter);
46
0
47
0
                /* Get attribute. */
48
0
                attr = MVM_iterval(tc, attr_iter);
49
0
50
0
                /* Get its name. */
51
0
                name_obj = MVM_repr_at_key_o(tc, attr, instance->str_consts.name);
52
0
                name     = MVM_repr_get_str(tc, name_obj);
53
0
54
0
                MVM_repr_bind_key_o(tc, attr_map, name, current_slot_obj);
55
0
56
0
                current_slot++;
57
0
58
0
                /* Push attr onto the flat list. */
59
0
                MVM_repr_push_o(tc, flat_list, attr);
60
0
            }
61
0
62
0
            /* Add to class list and map list. */
63
0
            MVM_repr_push_o(tc, class_list, current_class);
64
0
            MVM_repr_push_o(tc, attr_map_list, attr_map);
65
0
        }
66
0
        else {
67
0
            MVM_exception_throw_adhoc(tc,
68
0
                "CUnion representation does not support multiple inheritance");
69
0
        }
70
0
    }
71
0
72
0
    /* We can now form the name map. */
73
0
    num_classes = MVM_repr_elems(tc, class_list);
74
0
    result = (MVMCUnionNameMap *) MVM_malloc(sizeof(MVMCUnionNameMap) * (1 + num_classes));
75
0
76
0
    for (i = 0; i < num_classes; i++) {
77
0
        result[i].class_key = MVM_repr_at_pos_o(tc, class_list, i);
78
0
        result[i].name_map  = MVM_repr_at_pos_o(tc, attr_map_list, i);
79
0
    }
80
0
81
0
    /* set the end to be NULL, it's useful for iteration. */
82
0
    result[i].class_key = NULL;
83
0
84
0
    repr_data->name_to_index_mapping = result;
85
0
86
0
    return flat_list;
87
0
}
88
89
/* This works out an allocation strategy for the object. It takes care of
90
 * "inlining" storage of attributes that are natively typed, as well as
91
 * noting unbox targets. */
92
0
static void compute_allocation_strategy(MVMThreadContext *tc, MVMObject *repr_info, MVMCUnionREPRData *repr_data) {
93
0
    /* Compute index mapping table and get flat list of attributes. */
94
0
    MVMObject *flat_list = index_mapping_and_flat_list(tc, repr_info, repr_data);
95
0
96
0
    /* If we have no attributes in the index mapping, then just the header. */
97
0
    if (repr_data->name_to_index_mapping[0].class_key == NULL) {
98
0
        repr_data->struct_size = 1; /* avoid 0-byte malloc */
99
0
        repr_data->struct_align = ALIGNOF(void *);
100
0
    }
101
0
102
0
    /* Otherwise, we need to compute the allocation strategy.  */
103
0
    else {
104
0
        /* The structure itself will as big as the biggest element.
105
0
         * So we keep track of that biggest element. */
106
0
        MVMint32 total_size = 0;
107
0
108
0
        /* Get number of attributes and set up various counters. */
109
0
        MVMint32 num_attrs        = MVM_repr_elems(tc, flat_list);
110
0
        MVMint32 info_alloc       = num_attrs == 0 ? 1 : num_attrs;
111
0
        MVMint32 cur_obj_attr     = 0;
112
0
        MVMint32 cur_init_slot    = 0;
113
0
        MVMint32 i;
114
0
115
0
        /* Allocate location/offset arrays and GC mark info arrays. */
116
0
        repr_data->num_attributes      = num_attrs;
117
0
        repr_data->attribute_locations = (MVMint32 *)   MVM_malloc(info_alloc * sizeof(MVMint32));
118
0
        repr_data->struct_offsets      = (MVMint32 *)   MVM_malloc(info_alloc * sizeof(MVMint32));
119
0
        repr_data->flattened_stables   = (MVMSTable **) MVM_calloc(info_alloc, sizeof(MVMObject *));
120
0
        repr_data->member_types        = (MVMObject **) MVM_calloc(info_alloc, sizeof(MVMObject *));
121
0
        repr_data->struct_align        = 0;
122
0
123
0
        /* Go over the attributes and arrange their allocation. */
124
0
        for (i = 0; i < num_attrs; i++) {
125
0
            /* Fetch its type; see if it's some kind of unboxed type. */
126
0
            MVMObject *attr  = MVM_repr_at_pos_o(tc, flat_list, i);
127
0
            MVMObject *type  = MVM_repr_at_key_o(tc, attr, tc->instance->str_consts.type);
128
0
            MVMObject *inlined_val = MVM_repr_at_key_o(tc, attr, tc->instance->str_consts.inlined);
129
0
            MVMint64 inlined = !MVM_is_null(tc, inlined_val) && MVM_repr_get_int(tc, inlined_val);
130
0
            MVMint32   bits  = sizeof(void *) * 8;
131
0
            MVMint32   align = ALIGNOF(void *);
132
0
            if (!MVM_is_null(tc, type)) {
133
0
                /* See if it's a type that we know how to handle in a C struct. */
134
0
                const MVMStorageSpec *spec = REPR(type)->get_storage_spec(tc, STABLE(type));
135
0
                MVMint32  type_id    = REPR(type)->ID;
136
0
                if (spec->inlineable == MVM_STORAGE_SPEC_INLINED &&
137
0
                        (spec->boxed_primitive == MVM_STORAGE_SPEC_BP_INT ||
138
0
                         spec->boxed_primitive == MVM_STORAGE_SPEC_BP_NUM)) {
139
0
                    /* It's a boxed int or num; pretty easy. It'll just live in the
140
0
                     * body of the struct. Instead of masking in i here (which
141
0
                     * would be the parallel to how we handle boxed types) we
142
0
                     * repurpose it to store the bit-width of the type, so
143
0
                     * that get_attribute_ref can find it later. */
144
0
                    bits = spec->bits;
145
0
                    align = spec->align;
146
0
147
0
                    repr_data->attribute_locations[i] = (bits << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_IN_STRUCT;
148
0
                    repr_data->flattened_stables[i] = STABLE(type);
149
0
                    if (REPR(type)->initialize) {
150
0
                        if (!repr_data->initialize_slots)
151
0
                            repr_data->initialize_slots = (MVMint32 *) MVM_calloc(info_alloc + 1, sizeof(MVMint32));
152
0
                        repr_data->initialize_slots[cur_init_slot] = i;
153
0
                        cur_init_slot++;
154
0
                    }
155
0
                }
156
0
                else if (spec->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) {
157
0
                    /* It's a string of some kind.  */
158
0
                    repr_data->num_child_objs++;
159
0
                    repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_STRING;
160
0
                    repr_data->member_types[i] = type;
161
0
                    repr_data->flattened_stables[i] = STABLE(type);
162
0
                    if (REPR(type)->initialize) {
163
0
                        if (!repr_data->initialize_slots)
164
0
                            repr_data->initialize_slots = (MVMint32 *) MVM_calloc(info_alloc + 1, sizeof(MVMint32));
165
0
                        repr_data->initialize_slots[cur_init_slot] = i;
166
0
                        cur_init_slot++;
167
0
                    }
168
0
                }
169
0
                else if (type_id == MVM_REPR_ID_MVMCArray) {
170
0
                    /* It's a CArray of some kind.  */
171
0
                    repr_data->num_child_objs++;
172
0
                    repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CARRAY;
173
0
                    repr_data->member_types[i] = type;
174
0
                }
175
0
                else if (type_id == MVM_REPR_ID_MVMCStruct) {
176
0
                    /* It's a CStruct. */
177
0
                    repr_data->num_child_objs++;
178
0
                    repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CSTRUCT;
179
0
                    repr_data->member_types[i] = type;
180
0
                    if (inlined) {
181
0
                        MVMCStructREPRData *cstruct_repr_data = (MVMCStructREPRData *)STABLE(type)->REPR_data;
182
0
                        bits                                  = cstruct_repr_data->struct_size * 8;
183
0
                        align                                 = cstruct_repr_data->struct_align;
184
0
                        repr_data->attribute_locations[i]    |= MVM_CUNION_ATTR_INLINED;
185
0
                    }
186
0
                }
187
0
                else if (type_id == MVM_REPR_ID_MVMCPPStruct) {
188
0
                    /* It's a CPPStruct. */
189
0
                    repr_data->num_child_objs++;
190
0
                    repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CPPSTRUCT;
191
0
                    repr_data->member_types[i] = type;
192
0
                    if (inlined) {
193
0
                        MVMCPPStructREPRData *cppstruct_repr_data = (MVMCPPStructREPRData *)STABLE(type)->REPR_data;
194
0
                        bits                                      = cppstruct_repr_data->struct_size * 8;
195
0
                        align                                     = cppstruct_repr_data->struct_align;
196
0
                        repr_data->attribute_locations[i]        |= MVM_CUNION_ATTR_INLINED;
197
0
                    }
198
0
                }
199
0
                else if (type_id == MVM_REPR_ID_MVMCUnion) {
200
0
                    /* It's a CUnion. */
201
0
                    repr_data->num_child_objs++;
202
0
                    repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CUNION;
203
0
                    repr_data->member_types[i] = type;
204
0
                    if (inlined) {
205
0
                        MVMCUnionREPRData *cunion_repr_data = (MVMCUnionREPRData *)STABLE(type)->REPR_data;
206
0
                        bits                                = cunion_repr_data->struct_size * 8;
207
0
                        align                               = cunion_repr_data->struct_align;
208
0
                        repr_data->attribute_locations[i]  |= MVM_CUNION_ATTR_INLINED;
209
0
                    }
210
0
                }
211
0
                else if (type_id == MVM_REPR_ID_MVMCPointer) {
212
0
                    /* It's a CPointer. */
213
0
                    repr_data->num_child_objs++;
214
0
                    repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CPTR;
215
0
                    repr_data->member_types[i] = type;
216
0
                }
217
0
                else {
218
0
                    MVM_exception_throw_adhoc(tc,
219
0
                        "CUnion representation only handles attributes of type:\n"
220
0
                        "  (u)int8, (u)int16, (u)int32, (u)int64, (u)long, (u)longlong, num32, num64, (s)size_t, bool, Str\n"
221
0
                        "  and types with representation: CArray, CPointer, CStruct, CPPStruct and CUnion");
222
0
                }
223
0
            }
224
0
            else {
225
0
                MVM_exception_throw_adhoc(tc,
226
0
                    "CUnion representation requires the types of all attributes to be specified");
227
0
            }
228
0
229
0
            if (bits % 8) {
230
0
                 MVM_exception_throw_adhoc(tc,
231
0
                    "CUnion only supports native types that are a multiple of 8 bits wide (was passed: %"PRId32")", bits);
232
0
            }
233
0
234
0
            if (align > repr_data->struct_align)
235
0
                repr_data->struct_align = align;
236
0
237
0
            repr_data->struct_offsets[i] = 0;
238
0
            if (bits / 8 > total_size)
239
0
                total_size = bits / 8;
240
0
        }
241
0
242
0
        /* Finally, put computed allocation size in place; it's body size plus
243
0
         * header size. Also number of markables and sentinels. */
244
0
        repr_data->struct_size = total_size;
245
0
        if (repr_data->initialize_slots)
246
0
            repr_data->initialize_slots[cur_init_slot] = -1;
247
0
    }
248
0
}
249
250
/* Helper for reading an int at the specified offset. */
251
0
static MVMint32 get_int_at_offset(void *data, MVMint32 offset) {
252
0
    void *location = (char *)data + offset;
253
0
    return *((MVMint32 *)location);
254
0
}
255
256
/* Helper for writing an int at the specified offset. */
257
0
static void set_int_at_offset(void *data, MVMint32 offset, MVMint32 value) {
258
0
    void *location = (char *)data + offset;
259
0
    *((MVMint32 *)location) = value;
260
0
}
261
262
/* Helper for reading a num at the specified offset. */
263
0
static MVMnum32 get_num_at_offset(void *data, MVMint32 offset) {
264
0
    void *location = (char *)data + offset;
265
0
    return *((MVMnum32 *)location);
266
0
}
267
268
/* Helper for writing a num at the specified offset. */
269
0
static void set_num_at_offset(void *data, MVMint32 offset, MVMnum32 value) {
270
0
    void *location = (char *)data + offset;
271
0
    *((MVMnum32 *)location) = value;
272
0
}
273
274
/* Helper for reading a pointer at the specified offset. */
275
0
static void * get_ptr_at_offset(void *data, MVMint32 offset) {
276
0
    void *location = (char *)data + offset;
277
0
    return *((void **)location);
278
0
}
279
280
/* Helper for writing a pointer at the specified offset. */
281
0
static void set_ptr_at_offset(void *data, MVMint32 offset, void *value) {
282
0
    void *location = (char *)data + offset;
283
0
    *((void **)location) = value;
284
0
}
285
286
/* Helper for finding a slot number. */
287
0
static MVMint32 try_get_slot(MVMThreadContext *tc, MVMCUnionREPRData *repr_data, MVMObject *class_key, MVMString *name) {
288
0
    if (repr_data->name_to_index_mapping) {
289
0
        MVMCUnionNameMap *cur_map_entry = repr_data->name_to_index_mapping;
290
0
        while (cur_map_entry->class_key != NULL) {
291
0
            if (cur_map_entry->class_key == class_key) {
292
0
                MVMObject *slot_obj = MVM_repr_at_key_o(tc, cur_map_entry->name_map, name);
293
0
                if (IS_CONCRETE(slot_obj))
294
0
                    return MVM_repr_get_int(tc, slot_obj);
295
0
                break;
296
0
            }
297
0
            cur_map_entry++;
298
0
        }
299
0
    }
300
0
    return -1;
301
0
}
302
303
/* Creates a new type object of this representation, and associates it with
304
 * the given HOW. */
305
0
static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) {
306
0
    MVMSTable *st  = MVM_gc_allocate_stable(tc, &CUnion_this_repr, HOW);
307
0
308
0
    MVMROOT(tc, st, {
309
0
        MVMObject *obj = MVM_gc_allocate_type_object(tc, st);
310
0
        MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj);
311
0
        st->size = sizeof(MVMCUnion);
312
0
    });
313
0
314
0
    return st->WHAT;
315
0
}
316
317
/* Composes the representation. */
318
0
static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *repr_info) {
319
0
    /* Compute allocation strategy. */
320
0
    MVMCUnionREPRData *repr_data = MVM_calloc(1, sizeof(MVMCUnionREPRData));
321
0
    MVMObject *attr_info = MVM_repr_at_key_o(tc, repr_info, tc->instance->str_consts.attribute);
322
0
    MVM_gc_allocate_gen2_default_set(tc);
323
0
    compute_allocation_strategy(tc, attr_info, repr_data);
324
0
    MVM_gc_allocate_gen2_default_clear(tc);
325
0
    st->REPR_data = repr_data;
326
0
}
327
328
/* Initialize a new instance. */
329
0
static void initialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
330
0
    MVMCUnionREPRData * repr_data = (MVMCUnionREPRData *)st->REPR_data;
331
0
332
0
    /* Allocate object body. */
333
0
    MVMCUnionBody *body = (MVMCUnionBody *)data;
334
0
    body->cunion = MVM_calloc(1, repr_data->struct_size > 0 ? repr_data->struct_size : 1);
335
0
336
0
    /* Allocate child obj array. */
337
0
    if (repr_data->num_child_objs > 0)
338
0
        body->child_objs = (MVMObject **)MVM_calloc(repr_data->num_child_objs,
339
0
            sizeof(MVMObject *));
340
0
341
0
    /* Initialize the slots. */
342
0
    if (repr_data->initialize_slots) {
343
0
        MVMint32 i;
344
0
        for (i = 0; repr_data->initialize_slots[i] >= 0; i++) {
345
0
            MVMint32  offset = repr_data->struct_offsets[repr_data->initialize_slots[i]];
346
0
            MVMSTable *st     = repr_data->flattened_stables[repr_data->initialize_slots[i]];
347
0
            st->REPR->initialize(tc, st, root, (char *)body->cunion + offset);
348
0
        }
349
0
    }
350
0
}
351
352
/* Copies to the body of one object to another. */
353
0
static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) {
354
0
    MVMCUnionREPRData * repr_data = (MVMCUnionREPRData *) st->REPR_data;
355
0
    MVMCUnionBody *src_body = (MVMCUnionBody *)src;
356
0
    MVMCUnionBody *dest_body = (MVMCUnionBody *)dest;
357
0
    MVM_exception_throw_adhoc(tc, "cloning a CUnion is NYI");
358
0
}
359
360
/* Helper for complaining about attribute access errors. */
361
MVM_NO_RETURN static void no_such_attribute(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name) MVM_NO_RETURN_ATTRIBUTE;
362
0
static void no_such_attribute(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name) {
363
0
    char *c_name = MVM_string_utf8_encode_C_string(tc, name);
364
0
    char *waste[] = { c_name, NULL };
365
0
    MVM_exception_throw_adhoc_free(tc, waste, "Can not %s non-existent attribute '%s'",
366
0
        action, c_name);
367
0
}
368
369
/* Helper to die because this type doesn't support attributes. */
370
MVM_NO_RETURN static void die_no_attrs(MVMThreadContext *tc) MVM_NO_RETURN_ATTRIBUTE;
371
0
static void die_no_attrs(MVMThreadContext *tc) {
372
0
    MVM_exception_throw_adhoc(tc,
373
0
        "CUnion representation attribute not yet fully implemented");
374
0
}
375
376
static void get_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root,
377
        void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint,
378
0
        MVMRegister *result_reg, MVMuint16 kind) {
379
0
    MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data;
380
0
    MVMCUnionBody *body = (MVMCUnionBody *)data;
381
0
    MVMint64 slot;
382
0
383
0
    if (!repr_data)
384
0
        MVM_exception_throw_adhoc(tc, "CUnion: must compose before using get_attribute");
385
0
386
0
    slot = hint >= 0 ? hint : try_get_slot(tc, repr_data, class_handle, name);
387
0
    if (slot >= 0) {
388
0
        MVMSTable *attr_st = repr_data->flattened_stables[slot];
389
0
        switch (kind) {
390
0
        case MVM_reg_obj: {
391
0
            MVMint32 type      = repr_data->attribute_locations[slot] & MVM_CUNION_ATTR_MASK;
392
0
            MVMint32 real_slot = repr_data->attribute_locations[slot] >> MVM_CUNION_ATTR_SHIFT;
393
0
394
0
            if (type == MVM_CUNION_ATTR_IN_STRUCT) {
395
0
                MVM_exception_throw_adhoc(tc,
396
0
                    "CUnion can't perform boxed get on flattened attributes yet");
397
0
            }
398
0
            else {
399
0
                MVMObject *typeobj = repr_data->member_types[slot];
400
0
                MVMObject *obj     = body->child_objs[real_slot];
401
0
                if (!obj) {
402
0
                    /* No cached object. */
403
0
                    if (repr_data->attribute_locations[slot] & MVM_CUNION_ATTR_INLINED) {
404
0
                        if (type == MVM_CUNION_ATTR_CSTRUCT) {
405
0
                            obj = MVM_nativecall_make_cstruct(tc, typeobj,
406
0
                                (char *)body->cunion + repr_data->struct_offsets[slot]);
407
0
                        }
408
0
                        else if (type == MVM_CUNION_ATTR_CPPSTRUCT) {
409
0
                            obj = MVM_nativecall_make_cppstruct(tc, typeobj,
410
0
                                (char *)body->cunion + repr_data->struct_offsets[slot]);
411
0
                        }
412
0
                        else if (type == MVM_CUNION_ATTR_CUNION) {
413
0
                            obj = MVM_nativecall_make_cunion(tc, typeobj,
414
0
                                (char *)body->cunion + repr_data->struct_offsets[slot]);
415
0
                        }
416
0
                    }
417
0
                    else {
418
0
                        void *cobj = get_ptr_at_offset(body->cunion, repr_data->struct_offsets[slot]);
419
0
                        if (cobj) {
420
0
                            MVMObject **child_objs = body->child_objs;
421
0
                            if (type == MVM_CUNION_ATTR_CARRAY) {
422
0
                                obj = MVM_nativecall_make_carray(tc, typeobj, cobj);
423
0
                            }
424
0
                            else if(type == MVM_CUNION_ATTR_CSTRUCT) {
425
0
                                obj = MVM_nativecall_make_cstruct(tc, typeobj, cobj);
426
0
                            }
427
0
                            else if(type == MVM_CUNION_ATTR_CPPSTRUCT) {
428
0
                                obj = MVM_nativecall_make_cppstruct(tc, typeobj, cobj);
429
0
                            }
430
0
                            else if(type == MVM_CUNION_ATTR_CUNION) {
431
0
                                obj = MVM_nativecall_make_cunion(tc, typeobj, cobj);
432
0
                            }
433
0
                            else if(type == MVM_CUNION_ATTR_CPTR) {
434
0
                                obj = MVM_nativecall_make_cpointer(tc, typeobj, cobj);
435
0
                            }
436
0
                            else if(type == MVM_CUNION_ATTR_STRING) {
437
0
                                MVMROOT(tc, typeobj, {
438
0
                                    MVMString *str = MVM_string_utf8_decode(tc, tc->instance->VMString,
439
0
                                        cobj, strlen(cobj));
440
0
                                    obj = MVM_repr_box_str(tc, typeobj, str);
441
0
                                });
442
0
                            }
443
0
                            child_objs[real_slot] = obj;
444
0
                        }
445
0
                        else {
446
0
                            obj = typeobj;
447
0
                        }
448
0
                    }
449
0
                }
450
0
                result_reg->o = obj;
451
0
            }
452
0
            break;
453
0
        }
454
0
        case MVM_reg_int64: {
455
0
            if (attr_st)
456
0
                result_reg->i64 = attr_st->REPR->box_funcs.get_int(tc, attr_st, root,
457
0
                    ((char *)body->cunion) + repr_data->struct_offsets[slot]);
458
0
            else
459
0
                MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute");
460
0
            break;
461
0
        }
462
0
        case MVM_reg_num64: {
463
0
            if (attr_st)
464
0
                result_reg->n64 = attr_st->REPR->box_funcs.get_num(tc, attr_st, root,
465
0
                    ((char *)body->cunion) + repr_data->struct_offsets[slot]);
466
0
            else
467
0
                MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute");
468
0
            break;
469
0
        }
470
0
        case MVM_reg_str: {
471
0
            if (attr_st)
472
0
                result_reg->s = attr_st->REPR->box_funcs.get_str(tc, attr_st, root,
473
0
                    ((char *)body->cunion) + repr_data->struct_offsets[slot]);
474
0
            else
475
0
                MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute");
476
0
            if (!result_reg->s)
477
0
                result_reg->s = tc->instance->str_consts.empty;
478
0
            break;
479
0
        }
480
0
        default:
481
0
            MVM_exception_throw_adhoc(tc, "CUnion: invalid kind in attribute get");
482
0
        }
483
0
    }
484
0
    else {
485
0
        no_such_attribute(tc, "bind", class_handle, name);
486
0
    }
487
0
}
488
489
/* Binds the given value to the specified attribute. */
490
static void bind_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root,
491
        void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint,
492
0
        MVMRegister value_reg, MVMuint16 kind) {
493
0
    MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data;
494
0
    MVMCUnionBody *body = (MVMCUnionBody *)data;
495
0
    MVMint64 slot;
496
0
497
0
    if (!repr_data)
498
0
        MVM_exception_throw_adhoc(tc, "CUnion: must compose before using bind_attribute");
499
0
500
0
    slot = hint >= 0 ? hint : try_get_slot(tc, repr_data, class_handle, name);
501
0
    if (slot >= 0) {
502
0
        MVMSTable *attr_st = repr_data->flattened_stables[slot];
503
0
        switch (kind) {
504
0
        case MVM_reg_obj: {
505
0
            MVMObject *value = value_reg.o;
506
0
            MVMint32   type  = repr_data->attribute_locations[slot] & MVM_CUNION_ATTR_MASK;
507
0
508
0
            if (type == MVM_CUNION_ATTR_IN_STRUCT) {
509
0
                MVM_exception_throw_adhoc(tc,
510
0
                    "CUnion can't perform boxed bind on flattened attributes yet");
511
0
            }
512
0
            else {
513
0
                MVMint32   real_slot = repr_data->attribute_locations[slot] >> MVM_CUNION_ATTR_SHIFT;
514
0
515
0
                if (IS_CONCRETE(value)) {
516
0
                    void *cobj       = NULL;
517
0
518
0
                    MVM_ASSIGN_REF(tc, &(root->header), body->child_objs[real_slot], value);
519
0
520
0
                    /* Set cobj to correct pointer based on type of value. */
521
0
                    if (type == MVM_CUNION_ATTR_CARRAY) {
522
0
                        if (REPR(value)->ID != MVM_REPR_ID_MVMCArray)
523
0
                            MVM_exception_throw_adhoc(tc,
524
0
                                "Can only store CArray attribute in CArray slot in CUnion");
525
0
                        cobj = ((MVMCArray *)value)->body.storage;
526
0
                    }
527
0
                    else if (type == MVM_CUNION_ATTR_CSTRUCT) {
528
0
                        if (REPR(value)->ID != MVM_REPR_ID_MVMCStruct)
529
0
                            MVM_exception_throw_adhoc(tc,
530
0
                                "Can only store CStruct attribute in CStruct slot in CUnion");
531
0
                        cobj = ((MVMCStruct *)value)->body.cstruct;
532
0
                    }
533
0
                    else if (type == MVM_CUNION_ATTR_CPTR) {
534
0
                        if (REPR(value)->ID != MVM_REPR_ID_MVMCPointer)
535
0
                            MVM_exception_throw_adhoc(tc,
536
0
                                "Can only store CPointer attribute in CPointer slot in CUnion");
537
0
                        cobj = ((MVMCPointer *)value)->body.ptr;
538
0
                    }
539
0
                    else if (type == MVM_CUNION_ATTR_STRING) {
540
0
                        MVMString *str  = MVM_repr_get_str(tc, value);
541
0
                        cobj = MVM_string_utf8_encode_C_string(tc, str);
542
0
                    }
543
0
544
0
                    set_ptr_at_offset(body->cunion, repr_data->struct_offsets[slot], cobj);
545
0
                }
546
0
                else {
547
0
                    body->child_objs[real_slot] = NULL;
548
0
                    set_ptr_at_offset(body->cunion, repr_data->struct_offsets[slot], NULL);
549
0
                }
550
0
            }
551
0
            break;
552
0
        }
553
0
        case MVM_reg_int64: {
554
0
            if (attr_st)
555
0
                attr_st->REPR->box_funcs.set_int(tc, attr_st, root,
556
0
                    ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.i64);
557
0
            else
558
0
                MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute");
559
0
            break;
560
0
        }
561
0
        case MVM_reg_num64: {
562
0
            if (attr_st)
563
0
                attr_st->REPR->box_funcs.set_num(tc, attr_st, root,
564
0
                    ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.n64);
565
0
            else
566
0
                MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute");
567
0
            break;
568
0
        }
569
0
        case MVM_reg_str: {
570
0
            if (attr_st)
571
0
                attr_st->REPR->box_funcs.set_str(tc, attr_st, root,
572
0
                    ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.s);
573
0
            else
574
0
                MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute");
575
0
            break;
576
0
        }
577
0
        default:
578
0
            MVM_exception_throw_adhoc(tc, "CUnion: invalid kind in attribute bind");
579
0
        }
580
0
    }
581
0
    else {
582
0
        no_such_attribute(tc, "bind", class_handle, name);
583
0
    }
584
0
}
585
586
587
/* Checks if an attribute has been initialized. */
588
0
static MVMint64 is_attribute_initialized(MVMThreadContext *tc, MVMSTable *st, void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint) {
589
0
    die_no_attrs(tc);
590
0
}
591
592
/* Gets the hint for the given attribute ID. */
593
0
static MVMint64 hint_for(MVMThreadContext *tc, MVMSTable *st, MVMObject *class_handle, MVMString *name) {
594
0
    return MVM_NO_HINT;
595
0
}
596
597
/* Adds held objects to the GC worklist. */
598
0
static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) {
599
0
    MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *) st->REPR_data;
600
0
    MVMCUnionBody *body = (MVMCUnionBody *)data;
601
0
    MVMint32 i;
602
0
    for (i = 0; i < repr_data->num_child_objs; i++)
603
0
        MVM_gc_worklist_add(tc, worklist, &body->child_objs[i]);
604
0
}
605
606
/* Marks the representation data in an STable.*/
607
0
static void gc_mark_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMGCWorklist *worklist) {
608
0
    MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data;
609
0
    if (repr_data) {
610
0
        MVMint32 i;
611
0
        if (repr_data->name_to_index_mapping) {
612
0
            MVMCUnionNameMap *map = repr_data->name_to_index_mapping;
613
0
            for (i = 0; map[i].class_key; i++) {
614
0
                MVM_gc_worklist_add(tc, worklist, &map[i].class_key);
615
0
                MVM_gc_worklist_add(tc, worklist, &map[i].name_map);
616
0
            }
617
0
        }
618
0
619
0
        if (repr_data->flattened_stables) {
620
0
            MVMSTable **flattened_stables = repr_data->flattened_stables;
621
0
            for (i = 0; i < repr_data->num_attributes; i++)
622
0
                MVM_gc_worklist_add(tc, worklist, &flattened_stables[i]);
623
0
        }
624
0
625
0
        if (repr_data->member_types) {
626
0
            MVMObject **member_types = repr_data->member_types;
627
0
            for (i = 0; i < repr_data->num_attributes; i++)
628
0
                MVM_gc_worklist_add(tc, worklist, &member_types[i]);
629
0
        }
630
0
    }
631
0
}
632
633
/* Free representation data. */
634
0
static void gc_free_repr_data(MVMThreadContext *tc, MVMSTable *st) {
635
0
    MVM_free(st->REPR_data);
636
0
}
637
638
/* This is called to do any cleanup of resources when an object gets
639
 * embedded inside another one. Never called on a top-level object. */
640
0
static void gc_cleanup(MVMThreadContext *tc, MVMSTable *st, void *data) {
641
0
    MVMCUnionBody *body = (MVMCUnionBody *)data;
642
0
    if (body->child_objs)
643
0
        MVM_free(body->child_objs);
644
0
    /* XXX For some reason, this causes crashes at the moment. Need to
645
0
     * work out why. */
646
0
    /*if (body->cunion)
647
0
        MVM_free(body->cunion);*/
648
0
}
649
650
/* Called by the VM in order to free memory associated with this object. */
651
0
static void gc_free(MVMThreadContext *tc, MVMObject *obj) {
652
0
    gc_cleanup(tc, STABLE(obj), OBJECT_BODY(obj));
653
0
}
654
655
static const MVMStorageSpec storage_spec = {
656
    MVM_STORAGE_SPEC_REFERENCE, /* inlineable */
657
    sizeof(void*) * 8,          /* bits */
658
    ALIGNOF(void*),             /* align */
659
    MVM_STORAGE_SPEC_BP_NONE,   /* boxed_primitive */
660
    0,                          /* can_box */
661
    0,                          /* is_unsigned */
662
};
663
664
/* Gets the storage specification for this representation. */
665
0
static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) {
666
0
    return &storage_spec;
667
0
}
668
669
/* Serializes the REPR data. */
670
0
static void serialize_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMSerializationWriter *writer) {
671
0
    MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data;
672
0
    MVMint32 i, num_classes, num_slots;
673
0
674
0
    MVM_serialization_write_int(tc, writer, repr_data->struct_size);
675
0
    MVM_serialization_write_int(tc, writer, repr_data->struct_align);
676
0
    MVM_serialization_write_int(tc, writer, repr_data->num_attributes);
677
0
    MVM_serialization_write_int(tc, writer, repr_data->num_child_objs);
678
0
    for(i = 0; i < repr_data->num_attributes; i++){
679
0
        MVM_serialization_write_int(tc, writer, repr_data->attribute_locations[i]);
680
0
        MVM_serialization_write_int(tc, writer, repr_data->struct_offsets[i]);
681
0
682
0
        MVM_serialization_write_int(tc, writer, repr_data->flattened_stables[i] != NULL);
683
0
        if (repr_data->flattened_stables[i])
684
0
            MVM_serialization_write_stable_ref(tc, writer, repr_data->flattened_stables[i]);
685
0
686
0
        MVM_serialization_write_ref(tc, writer, repr_data->member_types[i]);
687
0
    }
688
0
689
0
    i=0;
690
0
    while (repr_data->name_to_index_mapping[i].class_key)
691
0
        i++;
692
0
    num_classes = i;
693
0
    MVM_serialization_write_int(tc, writer, num_classes);
694
0
    for(i = 0; i < num_classes; i++){
695
0
        MVM_serialization_write_ref(tc, writer, repr_data->name_to_index_mapping[i].class_key);
696
0
        MVM_serialization_write_ref(tc, writer, repr_data->name_to_index_mapping[i].name_map);
697
0
    }
698
0
699
0
    i=0;
700
0
    while(repr_data->initialize_slots && repr_data->initialize_slots[i] != -1)
701
0
        i++;
702
0
    num_slots = i;
703
0
    MVM_serialization_write_int(tc, writer, num_slots);
704
0
    for(i = 0; i < num_slots; i++){
705
0
        MVM_serialization_write_int(tc, writer, repr_data->initialize_slots[i]);
706
0
    }
707
0
}
708
709
/* Deserializes the REPR data. */
710
0
static void deserialize_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
711
0
    MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *) MVM_malloc(sizeof(MVMCUnionREPRData));
712
0
    MVMint32 i, num_classes, num_slots;
713
0
714
0
    repr_data->struct_size = MVM_serialization_read_int(tc, reader);
715
0
    if (reader->root.version >= 17) {
716
0
        repr_data->struct_align = MVM_serialization_read_int(tc, reader);
717
0
    }
718
0
    repr_data->num_attributes = MVM_serialization_read_int(tc, reader);
719
0
    repr_data->num_child_objs = MVM_serialization_read_int(tc, reader);
720
0
721
0
    repr_data->attribute_locations = (MVMint32 *)MVM_malloc(sizeof(MVMint32) * repr_data->num_attributes);
722
0
    repr_data->struct_offsets      = (MVMint32 *)MVM_malloc(sizeof(MVMint32) * repr_data->num_attributes);
723
0
    repr_data->flattened_stables   = (MVMSTable **)MVM_malloc(repr_data->num_attributes * sizeof(MVMSTable *));
724
0
    repr_data->member_types        = (MVMObject **)MVM_malloc(repr_data->num_attributes * sizeof(MVMObject *));
725
0
726
0
    for(i = 0; i < repr_data->num_attributes; i++) {
727
0
        repr_data->attribute_locations[i] = MVM_serialization_read_int(tc, reader);
728
0
        repr_data->struct_offsets[i] = MVM_serialization_read_int(tc, reader);
729
0
730
0
        if(MVM_serialization_read_int(tc, reader)){
731
0
            MVM_ASSIGN_REF(tc, &(st->header), repr_data->flattened_stables[i], MVM_serialization_read_stable_ref(tc, reader));
732
0
        }
733
0
        else {
734
0
            repr_data->flattened_stables[i] = NULL;
735
0
        }
736
0
737
0
        repr_data->member_types[i] = MVM_serialization_read_ref(tc, reader);
738
0
    }
739
0
740
0
    num_classes = MVM_serialization_read_int(tc, reader);
741
0
    repr_data->name_to_index_mapping = (MVMCUnionNameMap *)MVM_malloc(sizeof(MVMCUnionNameMap) * (1 + num_classes));
742
0
    for(i = 0; i < num_classes; i++){
743
0
        repr_data->name_to_index_mapping[i].class_key = MVM_serialization_read_ref(tc, reader);
744
0
        repr_data->name_to_index_mapping[i].name_map = MVM_serialization_read_ref(tc, reader);
745
0
    }
746
0
    repr_data->name_to_index_mapping[i].class_key = NULL;
747
0
    repr_data->name_to_index_mapping[i].name_map = NULL;
748
0
749
0
    num_slots = MVM_serialization_read_int(tc, reader);
750
0
    repr_data->initialize_slots = (MVMint32 *)MVM_malloc(sizeof(MVMint32) * (1 + num_slots));
751
0
    for(i = 0; i < num_slots; i++){
752
0
        repr_data->initialize_slots[i] = MVM_serialization_read_int(tc, reader);
753
0
    }
754
0
    repr_data->initialize_slots[i] = -1;
755
0
756
0
    st->REPR_data = repr_data;
757
0
}
758
759
0
static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
760
0
    st->size = sizeof(MVMCUnion);
761
0
}
762
763
/* Initializes the representation. */
764
144
const MVMREPROps * MVMCUnion_initialize(MVMThreadContext *tc) {
765
144
    return &CUnion_this_repr;
766
144
}
767
768
static const MVMREPROps CUnion_this_repr = {
769
    type_object_for,
770
    MVM_gc_allocate_object,
771
    initialize,
772
    copy_to,
773
    {
774
        get_attribute,
775
        bind_attribute,
776
        hint_for,
777
        is_attribute_initialized,
778
        MVM_REPR_DEFAULT_ATTRIBUTE_AS_ATOMIC
779
    },   /* attr_funcs */
780
    MVM_REPR_DEFAULT_BOX_FUNCS,
781
    MVM_REPR_DEFAULT_POS_FUNCS,
782
    MVM_REPR_DEFAULT_ASS_FUNCS,
783
    MVM_REPR_DEFAULT_ELEMS,
784
    get_storage_spec,
785
    NULL, /* change_type */
786
    NULL, /* serialize */
787
    NULL, /* deserialize */
788
    serialize_repr_data,
789
    deserialize_repr_data,
790
    deserialize_stable_size,
791
    gc_mark,
792
    gc_free,
793
    gc_cleanup,
794
    gc_mark_repr_data,
795
    gc_free_repr_data,
796
    compose,
797
    NULL, /* spesh */
798
    "CUnion", /* name */
799
    MVM_REPR_ID_MVMCUnion,
800
    NULL, /* unmanaged_size */
801
    NULL, /* describe_refs */
802
};