Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/6model/reprs/P6opaque.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
#ifndef MAX
4
    #define MAX(x,y) ((x)>(y)?(x):(y))
5
#endif
6
7
142k
#define P6OMAX(x, y) ((y) > (x) ? (y) : (x))
8
#define REFVAR_VM_HASH_STR_VAR 10
9
1.36k
#define MVM_P6OPAQUE_NO_UNBOX_SLOT 0xFFFF
10
11
/* This representation's function pointer table. */
12
static const MVMREPROps P6opaque_this_repr;
13
14
/* Helpers for reading/writing values. */
15
7.31M
MVM_STATIC_INLINE MVMObject * get_obj_at_offset(void *data, MVMint64 offset) {
16
7.31M
    void *location = (char *)data + offset;
17
7.31M
    return *((MVMObject **)location);
18
7.31M
}
19
5.52M
MVM_STATIC_INLINE void set_obj_at_offset(MVMThreadContext *tc, MVMObject *root, void *data, MVMint64 offset, MVMObject *value) {
20
5.52M
    void *location = (char *)data + offset;
21
5.52M
    MVM_ASSIGN_REF(tc, &(root->header), *((MVMObject **)location), value);
22
5.52M
}
23
24
/* Helper for finding a slot number. */
25
5.95M
static MVMint64 try_get_slot(MVMThreadContext *tc, MVMP6opaqueREPRData *repr_data, MVMObject *class_key, MVMString *name) {
26
5.95M
    if (repr_data->name_to_index_mapping) {
27
5.95M
        MVMP6opaqueNameMap *cur_map_entry = repr_data->name_to_index_mapping;
28
17.8M
        while (cur_map_entry->class_key != NULL) {
29
17.7M
            if (cur_map_entry->class_key == class_key) {
30
5.95M
                MVMint16 i;
31
30.1M
                for (i = 0; i < cur_map_entry->num_attrs; i++) {
32
30.1M
                    if (MVM_string_equal(tc, cur_map_entry->names[i], name)) {
33
5.94M
                        return cur_map_entry->slots[i];
34
5.94M
                    }
35
30.1M
                }
36
5.95M
            }
37
11.8M
            cur_map_entry++;
38
11.8M
        }
39
5.95M
    }
40
5.56k
    return -1;
41
5.95M
}
42
43
/* Creates a new type object of this representation, and associates it with
44
 * the given HOW. */
45
1.50k
static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) {
46
1.50k
    MVMSTable *st = MVM_gc_allocate_stable(tc, &P6opaque_this_repr, HOW);
47
1.50k
48
1.50k
    MVMROOT(tc, st, {
49
1.50k
        MVMObject *obj = MVM_gc_allocate_type_object(tc, st);
50
1.50k
        MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj);
51
1.50k
        st->size = 0; /* Is updated later. */
52
1.50k
    });
53
1.50k
54
1.50k
    return st->WHAT;
55
1.50k
}
56
57
/* Creates a new instance based on the type object. */
58
770k
static MVMObject * allocate(MVMThreadContext *tc, MVMSTable *st) {
59
770k
    if (st->size)
60
770k
        return MVM_gc_allocate_object(tc, st);
61
770k
    else
62
0
        MVM_exception_throw_adhoc(tc, "P6opaque: must compose %s before allocating", MVM_6model_get_stable_debug_name(tc, st));
63
770k
}
64
65
/* Initializes a new instance. */
66
552k
static void initialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
67
552k
    MVMP6opaqueREPRData * repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
68
552k
    data = MVM_p6opaque_real_data(tc, data);
69
552k
    if (repr_data) {
70
552k
        MVMint64 i;
71
552k
        for (i = 0; repr_data->initialize_slots[i] >= 0; i++) {
72
9
            MVMint64   offset = repr_data->attribute_offsets[repr_data->initialize_slots[i]];
73
9
            MVMSTable *st     = repr_data->flattened_stables[repr_data->initialize_slots[i]];
74
9
            st->REPR->initialize(tc, st, root, (char *)data + offset);
75
9
        }
76
552k
    }
77
0
    else {
78
0
        MVM_exception_throw_adhoc(tc, "P6opaque: must compose %s before using initialize", MVM_6model_get_stable_debug_name(tc, st));
79
0
    }
80
552k
}
81
82
/* Copies the body of one object to another. */
83
856
static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) {
84
856
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
85
856
    MVMuint16 i;
86
856
    src = MVM_p6opaque_real_data(tc, src);
87
856
88
856
    /* Flattened in REPRs need a chance to copy 'emselves. */
89
6.76k
    for (i = 0; i < repr_data->num_attributes; i++) {
90
5.90k
        MVMSTable *st_copy = repr_data->flattened_stables[i];
91
5.90k
        MVMuint16  offset  = repr_data->attribute_offsets[i];
92
5.90k
        if (st_copy) {
93
840
            st_copy->REPR->copy_to(tc, st_copy, (char*)src + offset, dest_root, (char*)dest + offset);
94
840
        }
95
5.06k
        else {
96
5.06k
            MVMObject *ref = get_obj_at_offset(src, offset);
97
5.06k
            if (ref)
98
1.70k
                set_obj_at_offset(tc, dest_root, dest, offset, ref);
99
5.06k
        }
100
5.90k
    }
101
856
}
102
103
/* Called by the VM to mark any GCable items. */
104
1.09M
static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) {
105
1.09M
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
106
1.09M
    MVMint64 i;
107
1.09M
    data = MVM_p6opaque_real_data(tc, data);
108
1.09M
109
1.09M
    /* Mark objects. */
110
9.59M
    for (i = 0; i < repr_data->gc_obj_mark_offsets_count; i++) {
111
8.50M
        MVMuint16 offset = repr_data->gc_obj_mark_offsets[i];
112
8.50M
        MVM_gc_worklist_add(tc, worklist, (char *)data + offset);
113
8.50M
    }
114
1.09M
115
1.09M
    /* Mark any nested reprs that need it. */
116
2.30M
    for (i = 0; repr_data->gc_mark_slots[i] >= 0; i++) {
117
1.21M
        MVMuint16  offset = repr_data->attribute_offsets[repr_data->gc_mark_slots[i]];
118
1.21M
        MVMSTable *st     = repr_data->flattened_stables[repr_data->gc_mark_slots[i]];
119
1.21M
        st->REPR->gc_mark(tc, st, (char *)data + offset, worklist);
120
1.21M
    }
121
1.09M
}
122
123
/* Called by the VM in order to free memory associated with this object. */
124
2.84M
static void gc_free(MVMThreadContext *tc, MVMObject *obj) {
125
2.84M
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)STABLE(obj)->REPR_data;
126
2.84M
    MVMint64 i;
127
2.84M
    void *data = MVM_p6opaque_real_data(tc, OBJECT_BODY(obj));
128
2.84M
129
2.84M
    /* Cleanup any nested reprs that need it. */
130
2.84M
    for (i = 0; repr_data->gc_cleanup_slots[i] >= 0; i++) {
131
0
        MVMuint16  offset = repr_data->attribute_offsets[repr_data->gc_cleanup_slots[i]];
132
0
        MVMSTable *st     = repr_data->flattened_stables[repr_data->gc_cleanup_slots[i]];
133
0
        st->REPR->gc_cleanup(tc, st, (char *)data + offset);
134
0
    }
135
2.84M
136
2.84M
    /* If we replaced the object body, free the replacement. */
137
2.84M
    MVM_free(((MVMP6opaque *)obj)->body.replaced);
138
2.84M
}
139
140
/* Marks the representation data in an STable.*/
141
11.7k
static void gc_mark_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMGCWorklist *worklist) {
142
11.7k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
143
11.7k
144
11.7k
    /* May not be composed yet. */
145
11.7k
    if (repr_data == NULL)
146
3
        return;
147
11.7k
148
11.6k
    if (repr_data->flattened_stables) {
149
10.9k
        int i;
150
52.1k
        for (i = 0; i < repr_data->num_attributes; i++)
151
41.1k
            if (repr_data->flattened_stables[i])
152
9.70k
                MVM_gc_worklist_add(tc, worklist, &repr_data->flattened_stables[i]);
153
10.9k
    }
154
11.6k
155
11.6k
    if (repr_data->auto_viv_values) {
156
6.10k
        int i;
157
47.2k
        for (i = 0; i < repr_data->num_attributes; i++)
158
41.1k
            if (repr_data->auto_viv_values[i])
159
25.1k
                MVM_gc_worklist_add(tc, worklist, &repr_data->auto_viv_values[i]);
160
6.10k
    }
161
11.6k
162
11.6k
    if (repr_data->name_to_index_mapping) {
163
11.6k
        MVMP6opaqueNameMap *cur_map_entry = repr_data->name_to_index_mapping;
164
35.2k
        while (cur_map_entry->class_key != NULL) {
165
23.5k
            MVMint16 i;
166
64.7k
            for (i = 0; i < cur_map_entry->num_attrs; i++) {
167
41.1k
                MVM_gc_worklist_add(tc, worklist, &cur_map_entry->names[i]);
168
41.1k
            }
169
23.5k
            MVM_gc_worklist_add(tc, worklist, &cur_map_entry->class_key);
170
23.5k
            cur_map_entry++;
171
23.5k
        }
172
11.6k
    }
173
11.6k
}
174
175
/* Marks the representation data in an STable.*/
176
1
static void gc_free_repr_data(MVMThreadContext *tc, MVMSTable *st) {
177
1
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
178
1
179
1
    /* May not have survived to composition. */
180
1
    if (repr_data == NULL)
181
0
        return;
182
1
183
1
    if (repr_data->name_to_index_mapping) {
184
1
        MVMP6opaqueNameMap *cur_map_entry = repr_data->name_to_index_mapping;
185
3
        while (cur_map_entry->class_key != NULL) {
186
2
            MVM_free(cur_map_entry->names);
187
2
            MVM_free(cur_map_entry->slots);
188
2
            cur_map_entry++;
189
2
        }
190
1
        MVM_free(repr_data->name_to_index_mapping);
191
1
    }
192
1
193
1
    MVM_free(repr_data->attribute_offsets);
194
1
    MVM_free(repr_data->flattened_stables);
195
1
    MVM_free(repr_data->auto_viv_values);
196
1
    MVM_free(repr_data->unbox_slots);
197
1
    MVM_free(repr_data->gc_obj_mark_offsets);
198
1
    MVM_free(repr_data->initialize_slots);
199
1
    MVM_free(repr_data->gc_mark_slots);
200
1
    MVM_free(repr_data->gc_cleanup_slots);
201
1
202
1
    MVM_free(st->REPR_data);
203
1
}
204
205
/* Helper for complaining about attribute access errors. */
206
MVM_NO_RETURN static void no_such_attribute(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name, MVMSTable *target_type) MVM_NO_RETURN_ATTRIBUTE;
207
4
MVM_NO_RETURN static void no_such_attribute(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name, MVMSTable *target_type) {
208
4
    char *c_name = MVM_string_utf8_encode_C_string(tc, name);
209
4
    char *waste[] = { c_name, NULL };
210
4
    MVM_exception_throw_adhoc_free(tc, waste, "P6opaque: no such attribute '%s' on type %s in a %s when trying to %s", c_name, MVM_6model_get_debug_name(tc, class_handle), MVM_6model_get_stable_debug_name(tc, target_type), action);
211
4
}
212
213
MVM_NO_RETURN static void invalid_access_kind(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name, const char *kind_desc) MVM_NO_RETURN_ATTRIBUTE;
214
0
MVM_NO_RETURN static void invalid_access_kind(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name, const char *kind_desc) {
215
0
    char *c_name = MVM_string_utf8_encode_C_string(tc, name);
216
0
    char *waste[] = { c_name, NULL };
217
0
    MVM_exception_throw_adhoc_free(tc, waste, "P6opaque: invalid %s attribute '%s' in type %s for kind %s", action, c_name, MVM_6model_get_debug_name(tc, class_handle), kind_desc);
218
0
}
219
220
/* Gets the current value for an attribute. */
221
static void get_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root,
222
        void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint,
223
11.3M
        MVMRegister *result_reg, MVMuint16 kind) {
224
11.3M
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
225
11.3M
    MVMint64 slot;
226
11.3M
    data = MVM_p6opaque_real_data(tc, data);
227
11.3M
228
11.3M
    if (!repr_data)
229
0
        MVM_exception_throw_adhoc(tc, "P6opaque: must compose %s before using get_attribute", MVM_6model_get_stable_debug_name(tc, st));
230
11.3M
231
11.3M
    /* Try the slot allocation first. */
232
8.43M
    slot = hint >= 0 && hint < repr_data->num_attributes && !(repr_data->mi) ? hint :
233
2.87M
        try_get_slot(tc, repr_data, class_handle, name);
234
11.3M
    if (slot >= 0) {
235
11.3M
        MVMSTable *attr_st = repr_data->flattened_stables[slot];
236
11.3M
        switch (kind) {
237
6.26M
        case MVM_reg_obj:
238
6.26M
        {
239
6.26M
            if (!attr_st) {
240
6.21M
                MVMObject *result = get_obj_at_offset(data, repr_data->attribute_offsets[slot]);
241
6.21M
                if (result) {
242
5.89M
                    result_reg->o = result;
243
5.89M
                }
244
329k
                else {
245
329k
                    /* Maybe we know how to auto-viv it to a container. */
246
329k
                    if (repr_data->auto_viv_values) {
247
329k
                        MVMObject *value = repr_data->auto_viv_values[slot];
248
329k
                        if (value != NULL) {
249
321k
                            if (IS_CONCRETE(value)) {
250
2
                                MVMROOT2(tc, value, root, {
251
2
                                    MVMObject *cloned = REPR(value)->allocate(tc, STABLE(value));
252
2
                                    /* Ordering here matters. We write the object into the
253
2
                                    * register before calling copy_to. This is because
254
2
                                    * if copy_to allocates, obj may have moved after
255
2
                                    * we called it. This saves us having to put things on
256
2
                                    * the temporary stack. The GC will know to update it
257
2
                                    * in the register if it moved. */
258
2
                                    result_reg->o = cloned;
259
2
                                    REPR(value)->copy_to(tc, STABLE(value), OBJECT_BODY(value),
260
2
                                        cloned, OBJECT_BODY(cloned));
261
2
                                    set_obj_at_offset(tc, root, MVM_p6opaque_real_data(tc, OBJECT_BODY(root)),
262
2
                                        repr_data->attribute_offsets[slot], result_reg->o);
263
2
                                });
264
2
                            }
265
321k
                            else {
266
321k
                                set_obj_at_offset(tc, root, data, repr_data->attribute_offsets[slot], value);
267
321k
                                result_reg->o = value;
268
321k
                            }
269
321k
                        }
270
8.05k
                        else {
271
8.05k
                            result_reg->o = tc->instance->VMNull;
272
8.05k
                        }
273
329k
                    }
274
1
                    else {
275
1
                        result_reg->o = tc->instance->VMNull;
276
1
                    }
277
329k
                }
278
6.21M
            }
279
48.1k
            else {
280
48.1k
                MVMROOT2(tc, root, attr_st, {
281
48.1k
                    /* Need to produce a boxed version of this attribute. */
282
48.1k
                    MVMObject *cloned = attr_st->REPR->allocate(tc, attr_st);
283
48.1k
284
48.1k
                    /* Ordering here matters too. see comments above */
285
48.1k
                    result_reg->o = cloned;
286
48.1k
                    attr_st->REPR->copy_to(tc, attr_st,
287
48.1k
                        (char *)MVM_p6opaque_real_data(tc, OBJECT_BODY(root)) + repr_data->attribute_offsets[slot],
288
48.1k
                        cloned, OBJECT_BODY(cloned));
289
48.1k
                });
290
48.1k
            }
291
6.26M
            break;
292
6.26M
        }
293
3.45M
        case MVM_reg_int64: {
294
3.45M
            if (attr_st)
295
3.45M
                result_reg->i64 = attr_st->REPR->box_funcs.get_int(tc, attr_st, root,
296
3.45M
                    (char *)data + repr_data->attribute_offsets[slot]);
297
3.45M
            else
298
0
                invalid_access_kind(tc, "native access", class_handle, name, "int64");
299
3.45M
            break;
300
6.26M
        }
301
511
        case MVM_reg_num64: {
302
511
            if (attr_st)
303
511
                result_reg->n64 = attr_st->REPR->box_funcs.get_num(tc, attr_st, root,
304
511
                    (char *)data + repr_data->attribute_offsets[slot]);
305
511
            else
306
0
                invalid_access_kind(tc, "native access", class_handle, name, "num64");
307
511
            break;
308
6.26M
        }
309
1.57M
        case MVM_reg_str: {
310
1.57M
            if (attr_st)
311
1.57M
                result_reg->s = attr_st->REPR->box_funcs.get_str(tc, attr_st, root,
312
1.57M
                    (char *)data + repr_data->attribute_offsets[slot]);
313
1.57M
            else
314
0
                invalid_access_kind(tc, "native access", class_handle, name, "str");
315
1.57M
            break;
316
6.26M
        }
317
0
        default: {
318
0
            MVM_exception_throw_adhoc(tc, "P6opaque: invalid kind in attribute lookup in %s", MVM_6model_get_stable_debug_name(tc, st));
319
0
        }
320
11.3M
        }
321
11.3M
    }
322
18.4E
    else {
323
18.4E
        /* Otherwise, complain that the attribute doesn't exist. */
324
18.4E
        no_such_attribute(tc, "get a value", class_handle, name, st);
325
18.4E
    }
326
11.3M
}
327
328
/* Binds the given value to the specified attribute. */
329
static void bind_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root,
330
        void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint,
331
6.71M
        MVMRegister value_reg, MVMuint16 kind) {
332
6.71M
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
333
6.71M
    MVMint64 slot;
334
6.71M
    data = MVM_p6opaque_real_data(tc, data);
335
6.71M
336
6.71M
    if (!repr_data)
337
0
        MVM_exception_throw_adhoc(tc, "P6opaque: must compose %s before using bind_attribute_boxed", MVM_6model_get_stable_debug_name(tc, st));
338
6.71M
339
6.71M
    /* Try the slot allocation first. */
340
3.67M
    slot = hint >= 0 && hint < repr_data->num_attributes && !(repr_data->mi) ? hint :
341
3.03M
        try_get_slot(tc, repr_data, class_handle, name);
342
6.71M
    if (slot >= 0) {
343
6.71M
        MVMSTable *attr_st = repr_data->flattened_stables[slot];
344
6.71M
        switch (kind) {
345
3.82M
        case MVM_reg_obj: {
346
3.82M
            MVMObject *value = value_reg.o;
347
3.82M
            if (attr_st) {
348
0
                MVMSTable *value_st = STABLE(value);
349
0
                if (attr_st == value_st)
350
0
                    value_st->REPR->copy_to(tc, attr_st, OBJECT_BODY(value), root,
351
0
                        (char *)data + repr_data->attribute_offsets[slot]);
352
0
                else
353
0
                    MVM_exception_throw_adhoc(tc,
354
0
                        "P6opaque: representation mismatch when storing value (of type %s) to attribute (of type %s)",
355
0
                        MVM_6model_get_stable_debug_name(tc, value_st), MVM_6model_get_stable_debug_name(tc, attr_st));
356
0
            }
357
3.82M
            else {
358
3.82M
                set_obj_at_offset(tc, root, data, repr_data->attribute_offsets[slot], value);
359
3.82M
            }
360
3.82M
            break;
361
3.82M
        }
362
2.57M
        case MVM_reg_int64: {
363
2.57M
            if (attr_st)
364
2.57M
                attr_st->REPR->box_funcs.set_int(tc, attr_st, root,
365
2.57M
                    (char *)data + repr_data->attribute_offsets[slot],
366
2.57M
                    value_reg.i64);
367
2.57M
            else
368
0
                invalid_access_kind(tc, "native bind to", class_handle, name, "int64");
369
2.57M
            break;
370
3.82M
        }
371
1.05k
        case MVM_reg_num64: {
372
1.05k
            if (attr_st)
373
1.05k
                attr_st->REPR->box_funcs.set_num(tc, attr_st, root,
374
1.05k
                    (char *)data + repr_data->attribute_offsets[slot],
375
1.05k
                    value_reg.n64);
376
1.05k
            else
377
0
                invalid_access_kind(tc, "native bind to", class_handle, name, "num64");
378
1.05k
            break;
379
3.82M
        }
380
313k
        case MVM_reg_str: {
381
313k
            if (attr_st)
382
313k
                attr_st->REPR->box_funcs.set_str(tc, attr_st, root,
383
313k
                    (char *)data + repr_data->attribute_offsets[slot],
384
313k
                    value_reg.s);
385
313k
            else
386
0
                invalid_access_kind(tc, "native bind to", class_handle, name, "str");
387
313k
            break;
388
3.82M
        }
389
0
        default: {
390
0
            MVM_exception_throw_adhoc(tc, "P6opaque: invalid kind in attribute bind in %s", MVM_6model_get_stable_debug_name(tc, st));
391
0
        }
392
6.71M
        }
393
6.71M
    }
394
2
    else {
395
2
        /* Otherwise, complain that the attribute doesn't exist. */
396
2
        no_such_attribute(tc, "bind a value", class_handle, name, st);
397
2
    }
398
6.71M
}
399
400
/* Checks if an attribute has been initialized. */
401
9
static MVMint64 is_attribute_initialized(MVMThreadContext *tc, MVMSTable *st, void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint) {
402
9
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
403
9
    MVMint64 slot;
404
9
405
9
    if (!repr_data)
406
0
        MVM_exception_throw_adhoc(tc, "P6opaque: must compose %s before using bind_attribute_boxed", MVM_6model_get_stable_debug_name(tc, st));
407
9
408
9
    data = MVM_p6opaque_real_data(tc, data);
409
9
    /* This can stay commented out until we actually pass something other than NO_HINT
410
9
    slot = hint >= 0 && hint < repr_data->num_attributes && !(repr_data->mi) ? hint :
411
9
        try_get_slot(tc, repr_data, class_handle, name);
412
9
    */
413
9
    slot = try_get_slot(tc, repr_data, class_handle, name);
414
9
    if (slot >= 0)
415
9
        return NULL != get_obj_at_offset(data, repr_data->attribute_offsets[slot]);
416
9
    else
417
0
        no_such_attribute(tc, "check if it's initialized", class_handle, name, st);
418
0
    return 0;
419
9
}
420
421
/* Gets the hint for the given attribute ID. */
422
10.2k
static MVMint64 hint_for(MVMThreadContext *tc, MVMSTable *st, MVMObject *class_key, MVMString *name) {
423
10.2k
    MVMint64 slot;
424
10.2k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
425
10.2k
    if (!repr_data)
426
0
        return MVM_NO_HINT;
427
10.2k
    slot = try_get_slot(tc, repr_data, class_key, name);
428
5.60k
    return slot >= 0 ? slot : MVM_NO_HINT;
429
10.2k
}
430
431
/* Gets an architecture atomic sized native integer attribute as an atomic
432
 * reference. */
433
static AO_t * attribute_as_atomic(MVMThreadContext *tc, MVMSTable *st, void *data,
434
                                  MVMObject *class_handle, MVMString *name,
435
0
                                  MVMuint16 kind) {
436
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
437
0
    MVMint64 slot;
438
0
    data = MVM_p6opaque_real_data(tc, data);
439
0
    if (!repr_data)
440
0
        MVM_exception_throw_adhoc(tc,
441
0
            "P6opaque: must compose %s before using get_attribute", MVM_6model_get_stable_debug_name(tc, st));
442
0
    slot = try_get_slot(tc, repr_data, class_handle, name);
443
0
    if (slot >= 0) {
444
0
        if (kind == MVM_reg_int64) {
445
0
            MVMSTable *attr_st = repr_data->flattened_stables[slot];
446
0
            if (attr_st) {
447
0
                const MVMStorageSpec *ss = attr_st->REPR->get_storage_spec(tc, attr_st);
448
0
                if (ss->inlineable && ss->boxed_primitive == MVM_STORAGE_SPEC_BP_INT &&
449
0
                        ss->bits / 8 == sizeof(AO_t))
450
0
                    return (AO_t *)((char *)data + repr_data->attribute_offsets[slot]);
451
0
            }
452
0
            MVM_exception_throw_adhoc(tc,
453
0
                "Can only do an atomic integer operation on an atomicint attribute");
454
0
        }
455
0
        else if (kind == MVM_reg_obj) {
456
0
            return (AO_t *)((char *)data + repr_data->attribute_offsets[slot]);
457
0
        }
458
0
        else {
459
0
            MVM_exception_throw_adhoc(tc,
460
0
                "Can only perform atomic operations on object or atomicint attributes");
461
0
        }
462
0
    }
463
0
    else {
464
0
        no_such_attribute(tc, "get atomic reference to", class_handle, name, st);
465
0
    }
466
0
}
467
468
/* Used with boxing. Sets an integer value, for representations that can hold
469
 * one. */
470
45
static void set_int(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMint64 value) {
471
45
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
472
45
    data = MVM_p6opaque_real_data(tc, data);
473
45
    if (repr_data->unbox_int_slot >= 0) {
474
45
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_int_slot];
475
45
        st->REPR->box_funcs.set_int(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_int_slot], value);
476
45
    }
477
0
    else {
478
0
        MVM_exception_throw_adhoc(tc,
479
0
            "This type cannot box a native integer: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
480
0
    }
481
45
}
482
483
/* Used with boxing. Gets an integer value, for representations that can
484
 * hold one. */
485
11
static MVMint64 get_int(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
486
11
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
487
11
    data = MVM_p6opaque_real_data(tc, data);
488
11
    if (repr_data->unbox_int_slot >= 0) {
489
11
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_int_slot];
490
11
        return st->REPR->box_funcs.get_int(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_int_slot]);
491
11
    }
492
0
    else {
493
0
        MVM_exception_throw_adhoc(tc,
494
0
            "This type cannot unbox to a native integer: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
495
0
    }
496
11
}
497
498
/* Used with boxing. Sets a floating point value, for representations that can
499
 * hold one. */
500
7
static void set_num(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMnum64 value) {
501
7
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
502
7
    data = MVM_p6opaque_real_data(tc, data);
503
7
    if (repr_data->unbox_num_slot >= 0) {
504
7
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_num_slot];
505
7
        st->REPR->box_funcs.set_num(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_num_slot], value);
506
7
    }
507
0
    else {
508
0
        MVM_exception_throw_adhoc(tc,
509
0
            "This type cannot box a native number: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
510
0
    }
511
7
}
512
513
/* Used with boxing. Gets a floating point value, for representations that can
514
 * hold one. */
515
6
static MVMnum64 get_num(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
516
6
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
517
6
    data = MVM_p6opaque_real_data(tc, data);
518
6
    if (repr_data->unbox_num_slot >= 0) {
519
6
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_num_slot];
520
6
        return st->REPR->box_funcs.get_num(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_num_slot]);
521
6
    }
522
0
    else {
523
0
        MVM_exception_throw_adhoc(tc,
524
0
            "This type cannot unbox to a native number: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
525
0
    }
526
6
}
527
528
/* Used with boxing. Sets a string value, for representations that can hold
529
 * one. */
530
13
static void set_str(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMString *value) {
531
13
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
532
13
    data = MVM_p6opaque_real_data(tc, data);
533
13
    if (repr_data->unbox_str_slot >= 0) {
534
13
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_str_slot];
535
13
        st->REPR->box_funcs.set_str(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_str_slot], value);
536
13
    }
537
0
    else {
538
0
        MVM_exception_throw_adhoc(tc,
539
0
            "This type cannot box a native string: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
540
0
    }
541
13
}
542
543
/* Used with boxing. Gets a string value, for representations that can hold
544
 * one. */
545
12
static MVMString * get_str(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
546
12
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
547
12
    data = MVM_p6opaque_real_data(tc, data);
548
12
    if (repr_data->unbox_str_slot >= 0) {
549
12
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_str_slot];
550
12
        return st->REPR->box_funcs.get_str(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_str_slot]);
551
12
    }
552
0
    else {
553
0
        MVM_exception_throw_adhoc(tc,
554
0
            "This type cannot unbox to a native string: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
555
0
    }
556
12
}
557
558
/* Used with boxing. Sets an unsigned integer value, for representations that can hold
559
 * one. */
560
0
static void set_uint(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMuint64 value) {
561
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
562
0
    data = MVM_p6opaque_real_data(tc, data);
563
0
    if (repr_data->unbox_int_slot >= 0) {
564
0
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_int_slot];
565
0
        st->REPR->box_funcs.set_uint(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_int_slot], value);
566
0
    }
567
0
    else {
568
0
        MVM_exception_throw_adhoc(tc,
569
0
            "This type cannot box a native integer: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
570
0
    }
571
0
}
572
573
/* Used with boxing. Gets an unsigned integer value, for representations that can
574
 * hold one. */
575
0
static MVMuint64 get_uint(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
576
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
577
0
    data = MVM_p6opaque_real_data(tc, data);
578
0
    if (repr_data->unbox_int_slot >= 0) {
579
0
        MVMSTable *st = repr_data->flattened_stables[repr_data->unbox_int_slot];
580
0
        return st->REPR->box_funcs.get_uint(tc, st, root, (char *)data + repr_data->attribute_offsets[repr_data->unbox_int_slot]);
581
0
    }
582
0
    else {
583
0
        MVM_exception_throw_adhoc(tc,
584
0
            "This type cannot unbox to a native integer: P6opaque, %s", MVM_6model_get_stable_debug_name(tc, st));
585
0
    }
586
0
}
587
588
21
static void * get_boxed_ref(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMuint32 repr_id) {
589
21
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
590
21
    data = MVM_p6opaque_real_data(tc, data);
591
21
    if (repr_data->unbox_slots) {
592
21
        MVMuint16 offset = repr_data->unbox_slots[repr_id];
593
21
        if (offset != MVM_P6OPAQUE_NO_UNBOX_SLOT)
594
21
            return (char *)data + repr_data->attribute_offsets[offset];
595
21
    }
596
21
597
0
    MVM_exception_throw_adhoc(tc,
598
0
        "P6opaque: get_boxed_ref could not unbox for the representation '%s' of type %s", MVM_repr_get_by_id(tc, repr_id)->name, MVM_6model_get_stable_debug_name(tc, st));
599
0
}
600
601
static const MVMStorageSpec default_storage_spec = {
602
    MVM_STORAGE_SPEC_REFERENCE, /* inlineable */
603
    0,                          /* bits */
604
    ALIGNOF(void *),            /* align */
605
    MVM_STORAGE_SPEC_BP_NONE,   /* boxed_primitive */
606
    0,                          /* can_box */
607
    0,                          /* is_unsigned */
608
};
609
610
611
/* Gets the storage specification for this representation. */
612
235k
static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) {
613
235k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
614
235k
    if (repr_data)
615
235k
        return &repr_data->storage_spec;
616
0
    return &default_storage_spec;
617
235k
}
618
619
20.2k
static void mk_storage_spec(MVMThreadContext *tc, MVMP6opaqueREPRData * repr_data, MVMStorageSpec *spec) {
620
20.2k
621
20.2k
    spec->inlineable      = MVM_STORAGE_SPEC_REFERENCE;
622
20.2k
    spec->boxed_primitive = MVM_STORAGE_SPEC_BP_NONE;
623
20.2k
    spec->can_box         = 0;
624
20.2k
625
20.2k
    if (repr_data->unbox_int_slot >= 0)
626
10
        spec->can_box += MVM_STORAGE_SPEC_CAN_BOX_INT;
627
20.2k
    if (repr_data->unbox_num_slot >= 0)
628
4
        spec->can_box += MVM_STORAGE_SPEC_CAN_BOX_NUM;
629
20.2k
    if (repr_data->unbox_str_slot >= 0)
630
5
        spec->can_box += MVM_STORAGE_SPEC_CAN_BOX_STR;
631
20.2k
}
632
633
/* Compose the representation. */
634
19
static MVMuint16 * allocate_unbox_slots() {
635
19
    MVMuint16 *slots = MVM_malloc(MVM_REPR_MAX_COUNT * sizeof(MVMuint16));
636
19
    MVMuint16 i;
637
1.23k
    for (i = 0; i < MVM_REPR_MAX_COUNT; i++)
638
1.21k
        slots[i] = MVM_P6OPAQUE_NO_UNBOX_SLOT;
639
19
    return slots;
640
19
}
641
1.49k
static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info_hash) {
642
1.49k
    MVMint64   mro_pos, mro_count, num_parents, total_attrs, num_attrs,
643
1.49k
               cur_slot, cur_type, cur_alloc_addr, cur_obj_attr,
644
1.49k
               cur_init_slot, cur_mark_slot, cur_cleanup_slot,
645
1.49k
               unboxed_type, i;
646
1.49k
    MVMObject *info;
647
1.49k
    MVMP6opaqueREPRData *repr_data;
648
1.49k
649
1.49k
    MVMStringConsts       str_consts = tc->instance->str_consts;
650
1.49k
    MVMString        * const str_avc = str_consts.auto_viv_container;
651
1.49k
    MVMString       * const str_name = str_consts.name;
652
1.49k
    MVMString       * const str_type = str_consts.type;
653
1.49k
    MVMString    * const str_ass_del = str_consts.associative_delegate;
654
1.49k
    MVMString    * const str_pos_del = str_consts.positional_delegate;
655
1.49k
    MVMString  * const str_attribute = str_consts.attribute;
656
1.49k
    MVMString * const str_box_target = str_consts.box_target;
657
1.49k
658
1.49k
    /* Check not already composed. */
659
1.49k
    if (st->REPR_data)
660
0
        MVM_exception_throw_adhoc(tc, "Type %s is already composed", MVM_6model_get_stable_debug_name(tc, st));
661
1.49k
662
1.49k
    /* Allocate the representation data. */
663
1.49k
    repr_data = (MVMP6opaqueREPRData *)MVM_calloc(1, sizeof(MVMP6opaqueREPRData));
664
1.49k
665
1.49k
    /* Find attribute information. */
666
1.49k
    info = MVM_repr_at_key_o(tc, info_hash, str_attribute);
667
1.49k
    if (MVM_is_null(tc, info))
668
0
        MVM_exception_throw_adhoc(tc, "P6opaque: missing attribute protocol in compose of %s", MVM_6model_get_stable_debug_name(tc, st));
669
1.49k
670
1.49k
    /* In this first pass, we'll loop over the MRO entries, looking for
671
1.49k
     * if there is any multiple inheritance and counting the number of
672
1.49k
     * attributes. */
673
1.49k
    mro_count   = REPR(info)->elems(tc, STABLE(info), info, OBJECT_BODY(info));
674
1.49k
    mro_pos     = mro_count;
675
1.49k
    total_attrs = 0;
676
4.05k
    while (mro_pos--) {
677
2.56k
        /* Get info for the class at the current position. */
678
2.56k
        MVMObject *class_info = MVM_repr_at_pos_o(tc, info, mro_pos);
679
2.56k
680
2.56k
        /* Get its list of attributes and parents. */
681
2.56k
        MVMObject *attr_list = MVM_repr_at_pos_o(tc, class_info, 1);
682
2.56k
        MVMObject *parent_list = MVM_repr_at_pos_o(tc, class_info, 2);
683
2.56k
684
2.56k
        /* If there's more than one parent, set the multiple inheritance
685
2.56k
         * flag (this means we have non-linear layout). */
686
2.56k
        num_parents = REPR(parent_list)->elems(tc, STABLE(parent_list),
687
2.56k
            parent_list, OBJECT_BODY(parent_list));
688
2.56k
        if (num_parents > 1)
689
0
            repr_data->mi = 1;
690
2.56k
691
2.56k
        /* Add attribute count to the running total. */
692
2.56k
        total_attrs += REPR(attr_list)->elems(tc, STABLE(attr_list),
693
2.56k
            attr_list, OBJECT_BODY(attr_list));
694
2.56k
    }
695
1.49k
696
1.49k
    /* Fill out and allocate other things we now can. */
697
1.49k
    repr_data->num_attributes = total_attrs;
698
1.49k
    if (total_attrs) {
699
372
        repr_data->attribute_offsets   = MVM_malloc(total_attrs * sizeof(MVMuint16));
700
372
        repr_data->flattened_stables   = (MVMSTable **)MVM_calloc(total_attrs, sizeof(MVMSTable *));
701
372
        repr_data->auto_viv_values     = (MVMObject **)MVM_calloc(total_attrs, sizeof(MVMObject *));
702
372
        repr_data->gc_obj_mark_offsets = MVM_malloc(total_attrs * sizeof(MVMuint16));
703
372
    }
704
1.49k
    repr_data->name_to_index_mapping = (MVMP6opaqueNameMap *)MVM_calloc((mro_count + 1), sizeof(MVMP6opaqueNameMap));
705
1.49k
    repr_data->initialize_slots      = MVM_malloc((total_attrs + 1) * sizeof(MVMuint16));
706
1.49k
    repr_data->gc_mark_slots         = MVM_malloc((total_attrs + 1) * sizeof(MVMuint16));
707
1.49k
    repr_data->gc_cleanup_slots      = MVM_malloc((total_attrs + 1) * sizeof(MVMuint16));
708
1.49k
709
1.49k
    /* -1 indicates no unboxing or delegate possible for a type. */
710
1.49k
    repr_data->unbox_int_slot = -1;
711
1.49k
    repr_data->unbox_num_slot = -1;
712
1.49k
    repr_data->unbox_str_slot = -1;
713
1.49k
    repr_data->pos_del_slot   = -1;
714
1.49k
    repr_data->ass_del_slot   = -1;
715
1.49k
716
1.49k
    /* Second pass populates the rest of the REPR data. */
717
1.49k
    mro_pos          = mro_count;
718
1.49k
    cur_slot         = 0;
719
1.49k
    cur_type         = 0;
720
1.49k
    cur_alloc_addr   = sizeof(MVMP6opaqueBody);
721
1.49k
    cur_obj_attr     = 0;
722
1.49k
    cur_init_slot    = 0;
723
1.49k
    cur_mark_slot    = 0;
724
1.49k
    cur_cleanup_slot = 0;
725
4.05k
    while (mro_pos--) {
726
2.56k
        /* Get info for the class at the current position. */
727
2.56k
        MVMObject *class_info = MVM_repr_at_pos_o(tc, info, mro_pos);
728
2.56k
        MVMObject *type_obj = MVM_repr_at_pos_o(tc, class_info, 0);
729
2.56k
        MVMObject *attr_list = MVM_repr_at_pos_o(tc, class_info, 1);
730
2.56k
731
2.56k
        /* Set up name map entry. */
732
2.56k
        MVMP6opaqueNameMap *name_map = &repr_data->name_to_index_mapping[cur_type];
733
2.56k
        num_attrs = REPR(attr_list)->elems(tc, STABLE(attr_list),
734
2.56k
            attr_list, OBJECT_BODY(attr_list));
735
2.56k
        MVM_ASSIGN_REF(tc, &(st->header), name_map->class_key, type_obj);
736
2.56k
        name_map->num_attrs = num_attrs;
737
2.56k
        if (num_attrs) {
738
934
            name_map->names = MVM_malloc(num_attrs * sizeof(MVMString *));
739
934
            name_map->slots = MVM_malloc(num_attrs * sizeof(MVMuint16));
740
934
        }
741
2.56k
742
2.56k
        /* Go over the attributes. */
743
5.64k
        for (i = 0; i < num_attrs; i++) {
744
3.08k
            MVMObject *attr_info = MVM_repr_at_pos_o(tc, attr_list, i);
745
3.08k
746
3.08k
            /* Extract name, type and if it's a box target. */
747
3.08k
            MVMObject *name_obj = MVM_repr_at_key_o(tc, attr_info, str_name);
748
3.08k
            MVMObject *type = MVM_repr_at_key_o(tc, attr_info, str_type);
749
3.08k
            MVMint64 is_box_target = REPR(attr_info)->ass_funcs.exists_key(tc,
750
3.08k
                STABLE(attr_info), attr_info, OBJECT_BODY(attr_info), (MVMObject *)str_box_target);
751
3.08k
            MVMint8 inlined = 0;
752
3.08k
            MVMuint32 bits;
753
3.08k
            MVMuint32 align;
754
3.08k
755
3.08k
            /* Ensure we have a name. */
756
3.08k
            if (MVM_is_null(tc, name_obj))
757
0
                MVM_exception_throw_adhoc(tc, "P6opaque: %s missing attribute name for attribute %"PRId64, MVM_6model_get_stable_debug_name(tc, st), i);
758
3.08k
759
3.08k
            if (REPR(name_obj)->ID == MVM_REPR_ID_MVMString) {
760
5
                MVM_ASSIGN_REF(tc, &(st->header), name_map->names[i], (MVMString *)name_obj);
761
5
            }
762
3.07k
            else {
763
3.07k
                MVM_ASSIGN_REF(tc, &(st->header), name_map->names[i], MVM_repr_get_str(tc, name_obj));
764
3.07k
            }
765
3.08k
            name_map->slots[i] = cur_slot;
766
3.08k
767
3.08k
            /* Consider the type. */
768
3.08k
            unboxed_type = MVM_STORAGE_SPEC_BP_NONE;
769
3.08k
            bits         = sizeof(MVMObject *) * 8;
770
3.08k
            align        = ALIGNOF(void *);
771
3.08k
            if (!MVM_is_null(tc, type)) {
772
1.43k
                /* Get the storage spec of the type and see what it wants. */
773
1.43k
                const MVMStorageSpec *spec = REPR(type)->get_storage_spec(tc, STABLE(type));
774
1.43k
                if (spec->inlineable == MVM_STORAGE_SPEC_INLINED) {
775
1.43k
                    /* Yes, it's something we'll flatten. */
776
1.43k
                    unboxed_type = spec->boxed_primitive;
777
1.43k
                    bits = spec->bits;
778
1.43k
                    align = spec->align;
779
1.43k
                    MVM_ASSIGN_REF(tc, &(st->header), repr_data->flattened_stables[cur_slot], STABLE(type));
780
1.43k
                    inlined = 1;
781
1.43k
782
1.43k
                    /* Does it need special initialization? */
783
1.43k
                    if (REPR(type)->initialize) {
784
3
                        repr_data->initialize_slots[cur_init_slot] = cur_slot;
785
3
                        cur_init_slot++;
786
3
                    }
787
1.43k
788
1.43k
                    /* Does it have special GC needs? */
789
1.43k
                    if (REPR(type)->gc_mark) {
790
563
                        repr_data->gc_mark_slots[cur_mark_slot] = cur_slot;
791
563
                        cur_mark_slot++;
792
563
                    }
793
1.43k
                    if (REPR(type)->gc_cleanup) {
794
3
                        repr_data->gc_cleanup_slots[cur_cleanup_slot] = cur_slot;
795
3
                        cur_cleanup_slot++;
796
3
                    }
797
1.43k
798
1.43k
                    /* Is it a target for box/unbox operations? */
799
1.43k
                    if (is_box_target) {
800
17
                        /* If it boxes a primitive, note that. */
801
17
                        switch (unboxed_type) {
802
8
                            case MVM_STORAGE_SPEC_BP_INT:
803
8
                                if (repr_data->unbox_int_slot >= 0)
804
0
                                    MVM_exception_throw_adhoc(tc,
805
0
                                        "While composing %s: Duplicate box_target for native int: attributes %d and %"PRId64, MVM_6model_get_stable_debug_name(tc, st), repr_data->unbox_int_slot, i);
806
8
                                repr_data->unbox_int_slot = cur_slot;
807
8
                                break;
808
4
                            case MVM_STORAGE_SPEC_BP_NUM:
809
4
                                if (repr_data->unbox_num_slot >= 0)
810
0
                                    MVM_exception_throw_adhoc(tc,
811
0
                                        "While composing %s: Duplicate box_target for native num: attributes %d and %"PRId64, MVM_6model_get_stable_debug_name(tc, st), repr_data->unbox_num_slot, i);
812
4
                                repr_data->unbox_num_slot = cur_slot;
813
4
                                break;
814
5
                            case MVM_STORAGE_SPEC_BP_STR:
815
5
                                if (repr_data->unbox_str_slot >= 0)
816
0
                                    MVM_exception_throw_adhoc(tc,
817
0
                                        "While composing %s: Duplicate box_target for native str: attributes %d and %"PRId64, MVM_6model_get_stable_debug_name(tc, st), repr_data->unbox_str_slot, i);
818
5
                                repr_data->unbox_str_slot = cur_slot;
819
5
                                break;
820
0
                            default:
821
0
                                /* nothing, just suppress 'missing default' warning */
822
0
                                break;
823
17
                        }
824
17
825
17
                        /* Also list in the by-repr unbox list. */
826
17
                        if (repr_data->unbox_slots == NULL)
827
17
                            repr_data->unbox_slots = allocate_unbox_slots();
828
17
                        repr_data->unbox_slots[REPR(type)->ID] = cur_slot;
829
17
                    }
830
1.43k
                }
831
1.43k
            }
832
3.08k
833
3.08k
            /* C structure needs careful alignment. If cur_alloc_addr is not
834
3.08k
             * aligned to align bytes (cur_alloc_addr % align), make sure it is
835
3.08k
             * before we add the next element. */
836
3.08k
            if (cur_alloc_addr % align) {
837
0
                cur_alloc_addr += align - cur_alloc_addr % align;
838
0
            }
839
3.08k
840
3.08k
            /* Attribute will live at the current position in the object. */
841
3.08k
            repr_data->attribute_offsets[cur_slot] = cur_alloc_addr;
842
3.08k
843
3.08k
            /* Handle object attributes, which need marking and may have auto-viv needs. */
844
3.08k
            if (!inlined) {
845
1.65k
                repr_data->gc_obj_mark_offsets[cur_obj_attr] = cur_alloc_addr;
846
1.65k
                if (MVM_repr_exists_key(tc, attr_info, str_avc))
847
1.64k
                    MVM_ASSIGN_REF(tc, &(st->header), repr_data->auto_viv_values[cur_slot],
848
1.65k
                        MVM_repr_at_key_o(tc, attr_info, str_avc));
849
1.65k
                cur_obj_attr++;
850
1.65k
            }
851
3.08k
852
3.08k
            /* Is it a positional or associative delegate? */
853
3.08k
            if (MVM_repr_exists_key(tc, attr_info, str_pos_del)) {
854
185
                if (repr_data->pos_del_slot != -1)
855
0
                    MVM_exception_throw_adhoc(tc,
856
0
                        "While composing %s: Duplicate positional delegate attributes: %d and %"PRId64"", MVM_6model_get_stable_debug_name(tc, st), repr_data->pos_del_slot, cur_slot);
857
185
                if (unboxed_type == MVM_STORAGE_SPEC_BP_NONE)
858
185
                    repr_data->pos_del_slot = cur_slot;
859
185
                else
860
0
                    MVM_exception_throw_adhoc(tc,
861
0
                        "While composing %s: Positional delegate attribute must be a reference type", MVM_6model_get_stable_debug_name(tc, st));
862
185
            }
863
3.08k
            if (MVM_repr_exists_key(tc, attr_info, str_ass_del)) {
864
16
                if (repr_data->ass_del_slot != -1)
865
0
                    MVM_exception_throw_adhoc(tc,
866
0
                        "While composing %s: Duplicate associative delegate attributes: %d and %"PRId64, MVM_6model_get_stable_debug_name(tc, st), repr_data->pos_del_slot, cur_slot);
867
16
                if (unboxed_type == MVM_STORAGE_SPEC_BP_NONE)
868
16
                    repr_data->ass_del_slot = cur_slot;
869
16
                else
870
0
                    MVM_exception_throw_adhoc(tc,
871
0
                        "While composing %s: Associative delegate attribute must be a reference type", MVM_6model_get_stable_debug_name(tc, st));
872
16
            }
873
3.08k
874
3.08k
            /* Add the required space for this type. */
875
3.08k
            cur_alloc_addr += bits / 8;
876
3.08k
877
3.08k
            /* Increment slot count. */
878
3.08k
            cur_slot++;
879
3.08k
        }
880
2.56k
881
2.56k
        /* Increment name map type index. */
882
2.56k
        cur_type++;
883
2.56k
    }
884
1.49k
885
1.49k
    /* Add allocated amount for body to have total object size. */
886
1.49k
    st->size = sizeof(MVMP6opaque) + (cur_alloc_addr - sizeof(MVMP6opaqueBody));
887
1.49k
888
1.49k
    /* Add sentinels/counts. */
889
1.49k
    repr_data->gc_obj_mark_offsets_count = cur_obj_attr;
890
1.49k
    repr_data->initialize_slots[cur_init_slot] = -1;
891
1.49k
    repr_data->gc_mark_slots[cur_mark_slot] = -1;
892
1.49k
    repr_data->gc_cleanup_slots[cur_cleanup_slot] = -1;
893
1.49k
894
1.49k
    /* Add storage spec */
895
1.49k
    mk_storage_spec(tc, repr_data, &repr_data->storage_spec);
896
1.49k
897
1.49k
    /* Install representation data. */
898
1.49k
    st->REPR_data = repr_data;
899
1.49k
}
900
901
/* Set the size of the STable. */
902
18.7k
static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
903
18.7k
    /* To calculate size, we need number of attributes and to know about
904
18.7k
     * anything flattend in. */
905
18.7k
    MVMint64  num_attributes = MVM_serialization_read_int(tc, reader);
906
18.7k
    MVMuint32 cur_offset = sizeof(MVMP6opaque);
907
18.7k
    MVMint64  i;
908
89.6k
    for (i = 0; i < num_attributes; i++) {
909
70.8k
        if (MVM_serialization_read_int(tc, reader)) {
910
15.1k
            MVMSTable *st = MVM_serialization_read_stable_ref(tc, reader);
911
15.1k
            const MVMStorageSpec *ss = st->REPR->get_storage_spec(tc, st);
912
15.1k
            if (ss->inlineable) {
913
15.1k
                /* TODO: Review if/when we get sub-byte things. */
914
15.1k
                if (cur_offset % ss->align) {
915
0
                    cur_offset += ss->align - cur_offset % ss->align;
916
0
                }
917
15.1k
                cur_offset += ss->bits / 8;
918
15.1k
            }
919
15.1k
            else
920
0
                cur_offset += sizeof(MVMObject *);
921
15.1k
        }
922
55.7k
        else {
923
55.7k
            cur_offset += sizeof(MVMObject *);
924
55.7k
        }
925
70.8k
    }
926
18.7k
927
18.7k
    st->size = cur_offset;
928
18.7k
}
929
930
/* Serializes the REPR data. */
931
17
static void serialize_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMSerializationWriter *writer) {
932
17
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
933
17
    MVMuint16 i, num_classes;
934
17
935
17
    if (!repr_data)
936
0
        MVM_exception_throw_adhoc(tc,
937
0
            "Representation for %s must be composed before it can be serialized", MVM_6model_get_stable_debug_name(tc, st));
938
17
939
17
    MVM_serialization_write_int(tc, writer, repr_data->num_attributes);
940
17
941
26
    for (i = 0; i < repr_data->num_attributes; i++) {
942
9
        MVM_serialization_write_int(tc, writer, repr_data->flattened_stables[i] != NULL);
943
9
        if (repr_data->flattened_stables[i])
944
5
            MVM_serialization_write_stable_ref(tc, writer, repr_data->flattened_stables[i]);
945
9
    }
946
17
947
17
    MVM_serialization_write_int(tc, writer, repr_data->mi);
948
17
949
17
    if (repr_data->auto_viv_values) {
950
7
        MVM_serialization_write_int(tc, writer, 1);
951
16
        for (i = 0; i < repr_data->num_attributes; i++)
952
9
            MVM_serialization_write_ref(tc, writer, repr_data->auto_viv_values[i]);
953
7
    }
954
10
    else {
955
10
        MVM_serialization_write_int(tc, writer, 0);
956
10
    }
957
17
958
17
    MVM_serialization_write_int(tc, writer, repr_data->unbox_int_slot);
959
17
    MVM_serialization_write_int(tc, writer, repr_data->unbox_num_slot);
960
17
    MVM_serialization_write_int(tc, writer, repr_data->unbox_str_slot);
961
17
962
17
    if (repr_data->unbox_slots) {
963
2
        MVMuint32 num_written = 0;
964
2
        MVM_serialization_write_int(tc, writer, 1);
965
130
        for (i = 0; i < MVM_REPR_MAX_COUNT; i++) {
966
128
            if (repr_data->unbox_slots[i] != MVM_P6OPAQUE_NO_UNBOX_SLOT) {
967
2
                MVM_serialization_write_int(tc, writer, i);
968
2
                MVM_serialization_write_int(tc, writer, repr_data->unbox_slots[i]);
969
2
                num_written++;
970
2
            }
971
128
        }
972
2
        for (i = num_written; i < repr_data->num_attributes; i++) {
973
0
            MVM_serialization_write_int(tc, writer, 0);
974
0
            MVM_serialization_write_int(tc, writer, 0);
975
0
        }
976
2
    }
977
15
    else {
978
15
        MVM_serialization_write_int(tc, writer, 0);
979
15
    }
980
17
981
17
    i = 0;
982
47
    while (repr_data->name_to_index_mapping[i].class_key)
983
30
        i++;
984
17
    num_classes = i;
985
17
    MVM_serialization_write_int(tc, writer, num_classes);
986
47
    for (i = 0; i < num_classes; i++) {
987
30
        const MVMuint32 num_attrs = repr_data->name_to_index_mapping[i].num_attrs;
988
30
        MVMuint32 j;
989
30
        MVM_serialization_write_ref(tc, writer, repr_data->name_to_index_mapping[i].class_key);
990
30
        MVM_serialization_write_int(tc, writer, num_attrs);
991
39
        for (j = 0; j < num_attrs; j++) {
992
9
            MVM_serialization_write_str(tc, writer, repr_data->name_to_index_mapping[i].names[j]);
993
9
            MVM_serialization_write_int(tc, writer, repr_data->name_to_index_mapping[i].slots[j]);
994
9
        }
995
30
    }
996
17
997
17
    MVM_serialization_write_int(tc, writer, repr_data->pos_del_slot);
998
17
    MVM_serialization_write_int(tc, writer, repr_data->ass_del_slot);
999
17
}
1000
1001
/* Deserializes representation data. */
1002
18.7k
static void deserialize_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
1003
18.7k
    MVMuint16 i, j, num_classes, cur_offset;
1004
18.7k
    MVMint16 cur_initialize_slot, cur_gc_mark_slot, cur_gc_cleanup_slot;
1005
18.7k
1006
18.7k
    MVMP6opaqueREPRData *repr_data = MVM_malloc(sizeof(MVMP6opaqueREPRData));
1007
18.7k
1008
18.7k
    repr_data->num_attributes = (MVMuint16)MVM_serialization_read_int(tc, reader);
1009
18.7k
1010
18.7k
    repr_data->flattened_stables = (MVMSTable **)MVM_malloc(P6OMAX(repr_data->num_attributes, 1) * sizeof(MVMSTable *));
1011
89.6k
    for (i = 0; i < repr_data->num_attributes; i++)
1012
70.8k
        if (MVM_serialization_read_int(tc, reader)) {
1013
15.1k
            MVM_ASSIGN_REF(tc, &(st->header), repr_data->flattened_stables[i], MVM_serialization_read_stable_ref(tc, reader));
1014
15.1k
        }
1015
55.7k
        else {
1016
55.7k
            repr_data->flattened_stables[i] = NULL;
1017
55.7k
        }
1018
18.7k
1019
18.7k
    repr_data->mi = MVM_serialization_read_int(tc, reader);
1020
18.7k
1021
18.7k
    if (MVM_serialization_read_int(tc, reader)) {
1022
10.3k
        repr_data->auto_viv_values = (MVMObject **)MVM_malloc(P6OMAX(repr_data->num_attributes, 1) * sizeof(MVMObject *));
1023
81.2k
        for (i = 0; i < repr_data->num_attributes; i++)
1024
70.8k
            MVM_ASSIGN_REF(tc, &(st->header), repr_data->auto_viv_values[i], MVM_serialization_read_ref(tc, reader));
1025
8.38k
    } else {
1026
8.38k
        repr_data->auto_viv_values = NULL;
1027
8.38k
    }
1028
18.7k
1029
18.7k
    repr_data->unbox_int_slot = MVM_serialization_read_int(tc, reader);
1030
18.7k
    repr_data->unbox_num_slot = MVM_serialization_read_int(tc, reader);
1031
18.7k
    repr_data->unbox_str_slot = MVM_serialization_read_int(tc, reader);
1032
18.7k
1033
18.7k
    if (MVM_serialization_read_int(tc, reader)) {
1034
2
        repr_data->unbox_slots = allocate_unbox_slots();
1035
4
        for (i = 0; i < repr_data->num_attributes; i++) {
1036
2
            MVMuint16 repr_id = MVM_serialization_read_int(tc, reader);
1037
2
            MVMuint16 slot = MVM_serialization_read_int(tc, reader);
1038
2
            if (repr_id)
1039
2
                repr_data->unbox_slots[repr_id] = slot;
1040
2
        }
1041
18.7k
    } else {
1042
18.7k
        repr_data->unbox_slots = NULL;
1043
18.7k
    }
1044
18.7k
1045
18.7k
    num_classes = (MVMuint16)MVM_serialization_read_int(tc, reader);
1046
18.7k
    repr_data->name_to_index_mapping = (MVMP6opaqueNameMap *)MVM_malloc((num_classes + 1) * sizeof(MVMP6opaqueNameMap));
1047
56.8k
    for (i = 0; i < num_classes; i++) {
1048
38.0k
        MVMint32 num_attrs = 0;
1049
38.0k
1050
38.0k
        MVM_ASSIGN_REF(tc, &(st->header), repr_data->name_to_index_mapping[i].class_key,
1051
38.0k
            MVM_serialization_read_ref(tc, reader));
1052
38.0k
1053
38.0k
        num_attrs = MVM_serialization_read_int(tc, reader);
1054
38.0k
        repr_data->name_to_index_mapping[i].names = (MVMString **)MVM_malloc(P6OMAX(num_attrs, 1) * sizeof(MVMString *));
1055
38.0k
        repr_data->name_to_index_mapping[i].slots = (MVMuint16 *)MVM_malloc(P6OMAX(num_attrs, 1) * sizeof(MVMuint16));
1056
108k
        for (j = 0; j < num_attrs; j++) {
1057
70.8k
            MVM_ASSIGN_REF(tc, &(st->header), repr_data->name_to_index_mapping[i].names[j],
1058
70.8k
                MVM_serialization_read_str(tc, reader));
1059
70.8k
1060
70.8k
            repr_data->name_to_index_mapping[i].slots[j] = (MVMuint16)MVM_serialization_read_int(tc, reader);
1061
70.8k
        }
1062
38.0k
1063
38.0k
        repr_data->name_to_index_mapping[i].num_attrs = num_attrs;
1064
38.0k
    }
1065
18.7k
1066
18.7k
    /* set the last one to be NULL */
1067
18.7k
    repr_data->name_to_index_mapping[i].class_key = NULL;
1068
18.7k
1069
18.7k
    repr_data->pos_del_slot = (MVMint16)MVM_serialization_read_int(tc, reader);
1070
18.7k
    repr_data->ass_del_slot = (MVMint16)MVM_serialization_read_int(tc, reader);
1071
18.7k
1072
18.7k
    /* Re-calculate the remaining info, which is platform specific or
1073
18.7k
     * derived information. */
1074
18.7k
    repr_data->attribute_offsets   = (MVMuint16 *)MVM_malloc(P6OMAX(repr_data->num_attributes, 1) * sizeof(MVMuint16));
1075
18.7k
    repr_data->gc_obj_mark_offsets = (MVMuint16 *)MVM_malloc(P6OMAX(repr_data->num_attributes, 1) * sizeof(MVMuint16));
1076
18.7k
    repr_data->initialize_slots    = (MVMint16 *)MVM_malloc((repr_data->num_attributes + 1) * sizeof(MVMint16));
1077
18.7k
    repr_data->gc_mark_slots       = (MVMint16 *)MVM_malloc((repr_data->num_attributes + 1) * sizeof(MVMint16));
1078
18.7k
    repr_data->gc_cleanup_slots    = (MVMint16 *)MVM_malloc((repr_data->num_attributes + 1) * sizeof(MVMint16));
1079
18.7k
    repr_data->gc_obj_mark_offsets_count = 0;
1080
18.7k
    cur_offset          = sizeof(MVMP6opaqueBody);
1081
18.7k
    cur_initialize_slot = 0;
1082
18.7k
    cur_gc_mark_slot    = 0;
1083
18.7k
    cur_gc_cleanup_slot = 0;
1084
89.6k
    for (i = 0; i < repr_data->num_attributes; i++) {
1085
70.8k
        if (repr_data->flattened_stables[i] == NULL) {
1086
55.7k
            /* Store position. */
1087
55.7k
            repr_data->attribute_offsets[i] = cur_offset;
1088
55.7k
1089
55.7k
            /* Reference type. Needs marking. */
1090
55.7k
            repr_data->gc_obj_mark_offsets[repr_data->gc_obj_mark_offsets_count] = cur_offset;
1091
55.7k
            repr_data->gc_obj_mark_offsets_count++;
1092
55.7k
1093
55.7k
            /* Increment by pointer size. */
1094
55.7k
            cur_offset += sizeof(MVMObject *);
1095
55.7k
        }
1096
15.1k
        else {
1097
15.1k
            /* Store position. */
1098
15.1k
            MVMSTable *cur_st = repr_data->flattened_stables[i];
1099
15.1k
            const MVMStorageSpec *spec = cur_st->REPR->get_storage_spec(tc, cur_st);
1100
15.1k
            /* Set up flags for initialization and GC. */
1101
15.1k
            if (cur_st->REPR->initialize)
1102
1
                repr_data->initialize_slots[cur_initialize_slot++] = i;
1103
15.1k
            if (cur_st->REPR->gc_mark)
1104
4.18k
                repr_data->gc_mark_slots[cur_gc_mark_slot++] = i;
1105
15.1k
            if (cur_st->REPR->gc_cleanup)
1106
1
                repr_data->gc_cleanup_slots[cur_gc_cleanup_slot++] = i;
1107
15.1k
1108
15.1k
            if (spec->align == 0) {
1109
0
                MVM_exception_throw_adhoc(tc, "Serialization error: Storage Spec of P6opaque must not have align set to 0.");
1110
0
            }
1111
15.1k
1112
15.1k
            if (cur_offset % spec->align) {
1113
0
                cur_offset += spec->align - cur_offset % spec->align;
1114
0
            }
1115
15.1k
1116
15.1k
            repr_data->attribute_offsets[i] = cur_offset;
1117
15.1k
1118
15.1k
            /* Increment by size reported by representation. */
1119
15.1k
            cur_offset += spec->bits / 8;
1120
15.1k
        }
1121
70.8k
    }
1122
18.7k
    repr_data->initialize_slots[cur_initialize_slot] = -1;
1123
18.7k
    repr_data->gc_mark_slots[cur_gc_mark_slot] = -1;
1124
18.7k
    repr_data->gc_cleanup_slots[cur_gc_cleanup_slot] = -1;
1125
18.7k
1126
18.7k
    mk_storage_spec(tc, repr_data, &repr_data->storage_spec);
1127
18.7k
1128
18.7k
    st->REPR_data = repr_data;
1129
18.7k
}
1130
1131
/* Deserializes the data. */
1132
217k
static void deserialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMSerializationReader *reader) {
1133
217k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1134
217k
    MVMuint16 num_attributes = repr_data->num_attributes;
1135
217k
    MVMuint16 i;
1136
1.75M
    for (i = 0; i < num_attributes; i++) {
1137
1.53M
        MVMuint16 a_offset = repr_data->attribute_offsets[i];
1138
1.53M
        MVMSTable *a_st = repr_data->flattened_stables[i];
1139
1.53M
        if (a_st)
1140
160k
            a_st->REPR->deserialize(tc, a_st, root, (char *)data + a_offset, reader);
1141
1.53M
        else
1142
1.37M
            set_obj_at_offset(tc, root, data, a_offset, MVM_serialization_read_ref(tc, reader));
1143
1.53M
    }
1144
217k
}
1145
1146
/* Serializes the object's body. */
1147
110
static void serialize(MVMThreadContext *tc, MVMSTable *st, void *data, MVMSerializationWriter *writer) {
1148
110
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1149
110
    MVMuint16 num_attributes;
1150
110
    MVMuint16 i;
1151
110
1152
110
    if (!repr_data)
1153
0
        MVM_exception_throw_adhoc(tc,
1154
0
            "Representation of %s must be composed before it can be serialized", MVM_6model_get_stable_debug_name(tc, st));
1155
110
1156
110
    num_attributes = repr_data->num_attributes;
1157
110
1158
110
    data = MVM_p6opaque_real_data(tc, data);
1159
110
1160
529
    for (i = 0; i < num_attributes; i++) {
1161
419
        MVMuint16  a_offset = repr_data->attribute_offsets[i];
1162
419
        MVMSTable *a_st     = repr_data->flattened_stables[i];
1163
419
        if (a_st) {
1164
23
            if (a_st->REPR->serialize)
1165
23
                a_st->REPR->serialize(tc, a_st, (char *)data + a_offset, writer);
1166
23
            else
1167
0
                MVM_exception_throw_adhoc(tc, "Missing serialize REPR function for REPR %s in type %s", a_st->REPR->name, MVM_6model_get_stable_debug_name(tc, a_st));
1168
23
        }
1169
419
        else
1170
396
            MVM_serialization_write_ref(tc, writer, get_obj_at_offset(data, a_offset));
1171
419
    }
1172
110
}
1173
1174
/* Performs a change of type, where possible. */
1175
2.45k
static void change_type(MVMThreadContext *tc, MVMObject *obj, MVMObject *new_type) {
1176
2.45k
    MVMP6opaqueREPRData *cur_repr_data = (MVMP6opaqueREPRData *)STABLE(obj)->REPR_data;
1177
2.45k
    MVMP6opaqueREPRData *new_repr_data = (MVMP6opaqueREPRData *)STABLE(new_type)->REPR_data;
1178
2.45k
    MVMP6opaqueNameMap *cur_map_entry, *new_map_entry;
1179
2.45k
1180
2.45k
    /* Ensure we don't have a type object. */
1181
2.45k
    if (!IS_CONCRETE(obj))
1182
0
        MVM_exception_throw_adhoc(tc,
1183
0
            "Cannot change the type of a %s type object", MVM_6model_get_debug_name(tc, obj));
1184
2.45k
1185
2.45k
    /* Ensure that the REPR of the new type is also P6opaque. */
1186
2.45k
    if (REPR(new_type)->ID != REPR(obj)->ID)
1187
1
        MVM_exception_throw_adhoc(tc,
1188
1
            "New type for %s must have a matching representation (P6opaque vs %s)", MVM_6model_get_debug_name(tc, obj), REPR(new_type)->name);
1189
2.45k
1190
2.45k
    /* Ensure the MRO prefixes match up. */
1191
2.45k
    cur_map_entry = cur_repr_data->name_to_index_mapping;
1192
2.45k
    new_map_entry = new_repr_data->name_to_index_mapping;
1193
4.92k
    while (cur_map_entry->class_key != NULL && cur_map_entry->num_attrs == 0)
1194
2.46k
        cur_map_entry++;
1195
4.93k
    while (new_map_entry->class_key != NULL && new_map_entry->num_attrs == 0)
1196
2.47k
        new_map_entry++;
1197
7.34k
    while (cur_map_entry->class_key != NULL) {
1198
4.88k
        if (new_map_entry->class_key == NULL || new_map_entry->class_key != cur_map_entry->class_key)
1199
1
            MVM_exception_throw_adhoc(tc,
1200
1
                "Incompatible MROs in P6opaque rebless for types %s and %s", MVM_6model_get_debug_name(tc, obj), MVM_6model_get_debug_name(tc, new_type));
1201
4.88k
        cur_map_entry++;
1202
4.88k
        new_map_entry++;
1203
4.88k
    }
1204
2.45k
1205
2.45k
    /* Resize if needed. */
1206
2.45k
    if (STABLE(obj)->size != STABLE(new_type)->size) {
1207
2.44k
        /* Get current object body. */
1208
2.44k
        MVMP6opaqueBody *body = (MVMP6opaqueBody *)OBJECT_BODY(obj);
1209
2.44k
        void            *old  = body->replaced ? body->replaced : body;
1210
2.44k
1211
2.44k
        /* Allocate new memory. */
1212
2.44k
        size_t  new_size = STABLE(new_type)->size - sizeof(MVMObject);
1213
2.44k
        void   *new = MVM_malloc(new_size);
1214
2.44k
        memset((char *)new + (STABLE(obj)->size - sizeof(MVMObject)),
1215
2.44k
            0, new_size - (STABLE(obj)->size - sizeof(MVMObject)));
1216
2.44k
1217
2.44k
        /* Copy existing to new.
1218
2.44k
         * XXX Need more care here, as may have to re-barrier pointers. */
1219
2.44k
        memcpy(new, old, STABLE(obj)->size - sizeof(MVMObject));
1220
2.44k
1221
2.44k
        /* Pointer switch, taking care of existing body issues. */
1222
2.44k
        if (body->replaced) {
1223
0
            body->replaced = new;
1224
0
            MVM_free(old);
1225
0
        }
1226
2.44k
        else {
1227
2.44k
            body->replaced = new;
1228
2.44k
        }
1229
2.44k
    }
1230
2.45k
1231
2.45k
    /* Finally, ready to switch over the STable. */
1232
2.45k
    MVM_ASSIGN_REF(tc, &(obj->header), obj->st, STABLE(new_type));
1233
2.45k
}
1234
1235
0
static void die_no_pos_del(MVMThreadContext *tc, MVMSTable *st) {
1236
0
    MVM_exception_throw_adhoc(tc, "This type (%s) does not support positional operations", MVM_6model_get_stable_debug_name(tc, st));
1237
0
}
1238
1239
164k
static void at_pos(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMint64 index, MVMRegister *value, MVMuint16 kind) {
1240
164k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1241
164k
    MVMObject *del;
1242
164k
    if (repr_data->pos_del_slot == -1)
1243
0
        die_no_pos_del(tc, st);
1244
164k
    data = MVM_p6opaque_real_data(tc, data);
1245
164k
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1246
164k
    REPR(del)->pos_funcs.at_pos(tc, STABLE(del), del, OBJECT_BODY(del), index, value, kind);
1247
164k
}
1248
17.8k
void MVM_P6opaque_at_pos(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMint64 index, MVMRegister *value, MVMuint16 kind) {
1249
17.8k
    return at_pos(tc, st, root, data, index, value, kind);
1250
17.8k
}
1251
1252
33.6k
static void bind_pos(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMint64 index, MVMRegister value, MVMuint16 kind) {
1253
33.6k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1254
33.6k
    MVMObject *del;
1255
33.6k
    if (repr_data->pos_del_slot == -1)
1256
0
        die_no_pos_del(tc, st);
1257
33.6k
    data = MVM_p6opaque_real_data(tc, data);
1258
33.6k
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1259
33.6k
    REPR(del)->pos_funcs.bind_pos(tc, STABLE(del), del, OBJECT_BODY(del), index, value, kind);
1260
33.6k
}
1261
1262
0
static void set_elems(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMuint64 count) {
1263
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1264
0
    MVMObject *del;
1265
0
    if (repr_data->pos_del_slot == -1)
1266
0
        die_no_pos_del(tc, st);
1267
0
    data = MVM_p6opaque_real_data(tc, data);
1268
0
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1269
0
    REPR(del)->pos_funcs.set_elems(tc, STABLE(del), del, OBJECT_BODY(del), count);
1270
0
}
1271
1272
553
static void push(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMRegister value, MVMuint16 kind) {
1273
553
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1274
553
    MVMObject *del;
1275
553
    if (repr_data->pos_del_slot == -1)
1276
0
        die_no_pos_del(tc, st);
1277
553
    data = MVM_p6opaque_real_data(tc, data);
1278
553
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1279
553
    REPR(del)->pos_funcs.push(tc, STABLE(del), del, OBJECT_BODY(del), value, kind);
1280
553
}
1281
1282
1
static void pop(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMRegister *value, MVMuint16 kind) {
1283
1
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1284
1
    MVMObject *del;
1285
1
    if (repr_data->pos_del_slot == -1)
1286
0
        die_no_pos_del(tc, st);
1287
1
    data = MVM_p6opaque_real_data(tc, data);
1288
1
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1289
1
    REPR(del)->pos_funcs.pop(tc, STABLE(del), del, OBJECT_BODY(del), value, kind);
1290
1
}
1291
1292
18.7k
static void unshift(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMRegister value, MVMuint16 kind) {
1293
18.7k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1294
18.7k
    MVMObject *del;
1295
18.7k
    if (repr_data->pos_del_slot == -1)
1296
0
        die_no_pos_del(tc, st);
1297
18.7k
    data = MVM_p6opaque_real_data(tc, data);
1298
18.7k
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1299
18.7k
    REPR(del)->pos_funcs.unshift(tc, STABLE(del), del, OBJECT_BODY(del), value, kind);
1300
18.7k
}
1301
1302
1
static void shift(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMRegister *value, MVMuint16 kind) {
1303
1
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1304
1
    MVMObject *del;
1305
1
    if (repr_data->pos_del_slot == -1)
1306
0
        die_no_pos_del(tc, st);
1307
1
    data = MVM_p6opaque_real_data(tc, data);
1308
1
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1309
1
    REPR(del)->pos_funcs.shift(tc, STABLE(del), del, OBJECT_BODY(del), value, kind);
1310
1
}
1311
1312
0
static void oslice(MVMThreadContext *tc, MVMSTable *st, MVMObject *src, void *data, MVMObject *dest, MVMint64 start, MVMint64 end) {
1313
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1314
0
    MVMObject *del;
1315
0
    if (repr_data->pos_del_slot == -1)
1316
0
        die_no_pos_del(tc, st);
1317
0
    data = MVM_p6opaque_real_data(tc, data);
1318
0
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1319
0
    REPR(del)->pos_funcs.slice(tc, STABLE(del), del, OBJECT_BODY(del), dest, start, end);
1320
0
}
1321
1322
0
static void osplice(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *target_array, MVMint64 offset, MVMuint64 elems) {
1323
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1324
0
    MVMObject *del;
1325
0
    if (repr_data->pos_del_slot == -1)
1326
0
        die_no_pos_del(tc, st);
1327
0
    data = MVM_p6opaque_real_data(tc, data);
1328
0
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1329
0
    REPR(del)->pos_funcs.splice(tc, STABLE(del), del, OBJECT_BODY(del), target_array, offset, elems);
1330
0
}
1331
1332
0
static void die_no_ass_del(MVMThreadContext *tc, MVMSTable *st) {
1333
0
    MVM_exception_throw_adhoc(tc, "This type (%s) does not support associative operations", MVM_6model_get_stable_debug_name(tc, st));
1334
0
}
1335
1336
761k
static void at_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key, MVMRegister *result, MVMuint16 kind) {
1337
761k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1338
761k
    MVMObject *del;
1339
761k
    if (repr_data->ass_del_slot == -1)
1340
0
        die_no_ass_del(tc, st);
1341
761k
    data = MVM_p6opaque_real_data(tc, data);
1342
761k
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->ass_del_slot]);
1343
761k
    REPR(del)->ass_funcs.at_key(tc, STABLE(del), del, OBJECT_BODY(del), key, result, kind);
1344
761k
}
1345
1346
4
static void bind_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key, MVMRegister value, MVMuint16 kind) {
1347
4
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1348
4
    MVMObject *del;
1349
4
    if (repr_data->ass_del_slot == -1)
1350
0
        die_no_ass_del(tc, st);
1351
4
    data = MVM_p6opaque_real_data(tc, data);
1352
4
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->ass_del_slot]);
1353
4
    REPR(del)->ass_funcs.bind_key(tc, STABLE(del), del, OBJECT_BODY(del), key, value, kind);
1354
4
}
1355
1356
38.2k
static MVMint64 exists_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key) {
1357
38.2k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1358
38.2k
    MVMObject *del;
1359
38.2k
    if (repr_data->ass_del_slot == -1)
1360
0
        die_no_ass_del(tc, st);
1361
38.2k
    data = MVM_p6opaque_real_data(tc, data);
1362
38.2k
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->ass_del_slot]);
1363
38.2k
    return REPR(del)->ass_funcs.exists_key(tc, STABLE(del), del, OBJECT_BODY(del), key);
1364
38.2k
}
1365
1366
76.5k
static void delete_key(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key) {
1367
76.5k
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1368
76.5k
    MVMObject *del;
1369
76.5k
    if (repr_data->ass_del_slot == -1)
1370
0
        die_no_ass_del(tc, st);
1371
76.5k
    data = MVM_p6opaque_real_data(tc, data);
1372
76.5k
    del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->ass_del_slot]);
1373
76.5k
    REPR(del)->ass_funcs.delete_key(tc, STABLE(del), del, OBJECT_BODY(del), key);
1374
76.5k
}
1375
1376
483
static MVMuint64 elems(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
1377
483
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1378
483
    data = MVM_p6opaque_real_data(tc, data);
1379
483
    if (repr_data->pos_del_slot >= 0) {
1380
483
        MVMObject *del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->pos_del_slot]);
1381
483
        return REPR(del)->elems(tc, STABLE(del), del, OBJECT_BODY(del));
1382
483
    }
1383
0
    else if (repr_data->ass_del_slot >= 0) {
1384
0
        MVMObject *del = get_obj_at_offset(data, repr_data->attribute_offsets[repr_data->ass_del_slot]);
1385
0
        return REPR(del)->elems(tc, STABLE(del), del, OBJECT_BODY(del));
1386
0
    }
1387
0
    else {
1388
0
        MVM_exception_throw_adhoc(tc, "This type (%s) does not support elems", MVM_6model_get_stable_debug_name(tc, st));
1389
0
    }
1390
483
}
1391
1392
/* Bytecode specialization for this REPR. */
1393
34.3k
static MVMString * spesh_attr_name(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand o, MVMint32 indirect) {
1394
34.3k
    if (indirect) {
1395
1
        MVMSpeshFacts *name_facts = MVM_spesh_get_and_use_facts(tc, g, o);
1396
1
        if (name_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE)
1397
1
            return name_facts->value.s;
1398
1
        else
1399
0
            return NULL;
1400
1
    }
1401
34.3k
    else {
1402
34.3k
        return MVM_spesh_get_string(tc, g, o);
1403
34.3k
    }
1404
34.3k
}
1405
37.0k
static void spesh(MVMThreadContext *tc, MVMSTable *st, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
1406
37.0k
    MVMP6opaqueREPRData * repr_data = (MVMP6opaqueREPRData *)st->REPR_data;
1407
37.0k
    MVMuint16             opcode    = ins->info->opcode;
1408
37.0k
    if (!repr_data)
1409
0
        return;
1410
37.0k
    switch (opcode) {
1411
748
    case MVM_OP_create: {
1412
748
        /* Create can be optimized if there are no initialization slots. */
1413
748
        if (repr_data->initialize_slots[0] < 0 && !(st->mode_flags & MVM_FINALIZE_TYPE)) {
1414
748
            MVMSpeshOperand target   = ins->operands[0];
1415
748
            MVMSpeshOperand type     = ins->operands[1];
1416
748
            ins->info                = MVM_op_get_op(MVM_OP_sp_fastcreate);
1417
748
            ins->operands            = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
1418
748
            ins->operands[0]         = target;
1419
748
            ins->operands[1].lit_i16 = st->size;
1420
748
            ins->operands[2].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)st);
1421
748
            MVM_spesh_get_facts(tc, g, type)->usages--;
1422
748
        }
1423
748
        break;
1424
748
    }
1425
14.2k
    case MVM_OP_getattr_o:
1426
14.2k
    case MVM_OP_getattrs_o: {
1427
14.2k
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1428
14.2k
        MVMSpeshFacts *ch_facts  = MVM_spesh_get_and_use_facts(tc, g, ins->operands[2]);
1429
14.2k
        MVMString     *name      = spesh_attr_name(tc, g, ins->operands[3], opcode == MVM_OP_getattrs_o);
1430
14.2k
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1431
13.7k
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1432
13.7k
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1433
13.7k
            if (slot >= 0 && !repr_data->flattened_stables[slot]) {
1434
13.7k
                if (repr_data->auto_viv_values && repr_data->auto_viv_values[slot]) {
1435
12.9k
                    MVMObject *av_value = repr_data->auto_viv_values[slot];
1436
12.9k
                    if (IS_CONCRETE(av_value)) {
1437
0
                        ins->info = MVM_op_get_op(MVM_OP_sp_p6ogetvc_o);
1438
0
                    }
1439
12.9k
                    else {
1440
12.9k
                        ins->info = MVM_op_get_op(MVM_OP_sp_p6ogetvt_o);
1441
12.9k
                    }
1442
12.9k
                    if (opcode == MVM_OP_getattrs_o)
1443
1
                        MVM_spesh_get_facts(tc, g, ins->operands[3])->usages--;
1444
12.9k
                    MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1445
12.9k
                    ins->operands[2].lit_i16 = repr_data->attribute_offsets[slot];
1446
12.9k
                    ins->operands[3].lit_i16 = MVM_spesh_add_spesh_slot(tc, g,
1447
12.9k
                        (MVMCollectable *)av_value);
1448
12.9k
                }
1449
812
                else {
1450
812
                    if (opcode == MVM_OP_getattrs_o)
1451
0
                        MVM_spesh_get_facts(tc, g, ins->operands[3])->usages--;
1452
812
                    MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1453
812
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6oget_o);
1454
812
                    ins->operands[2].lit_i16 = repr_data->attribute_offsets[slot];
1455
812
                    MVM_spesh_manipulate_remove_handler_successors(tc, bb);
1456
812
                }
1457
13.7k
            }
1458
13.7k
        }
1459
14.2k
        break;
1460
14.2k
    }
1461
7.42k
    case MVM_OP_getattr_i:
1462
7.42k
    case MVM_OP_getattrs_i: {
1463
7.42k
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1464
7.42k
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[2]);
1465
7.42k
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[3], opcode == MVM_OP_getattrs_i);
1466
7.42k
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1467
7.05k
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1468
7.05k
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1469
7.05k
            if (slot >= 0 && repr_data->flattened_stables[slot]) {
1470
7.05k
                MVMSTable      *flat_st = repr_data->flattened_stables[slot];
1471
7.05k
                const MVMStorageSpec *flat_ss = flat_st->REPR->get_storage_spec(tc, flat_st);
1472
7.05k
                if (flat_st->REPR->ID == MVM_REPR_ID_P6int && flat_ss->bits == 64) {
1473
7.05k
                    if (opcode == MVM_OP_getattrs_i)
1474
0
                        MVM_spesh_get_facts(tc, g, ins->operands[3])->usages--;
1475
7.05k
                    MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1476
7.05k
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6oget_i);
1477
7.05k
                    ins->operands[2].lit_i16 = repr_data->attribute_offsets[slot];
1478
7.05k
                }
1479
7.05k
            }
1480
7.05k
        }
1481
7.42k
        break;
1482
7.42k
    }
1483
1
    case MVM_OP_getattr_n:
1484
1
    case MVM_OP_getattrs_n: {
1485
1
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1486
1
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[2]);
1487
1
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[3], opcode == MVM_OP_getattrs_n);
1488
1
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1489
1
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1490
1
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1491
1
            if (slot >= 0 && repr_data->flattened_stables[slot]) {
1492
1
                MVMSTable      *flat_st = repr_data->flattened_stables[slot];
1493
1
                const MVMStorageSpec *flat_ss = flat_st->REPR->get_storage_spec(tc, flat_st);
1494
1
                if (flat_st->REPR->ID == MVM_REPR_ID_P6num && flat_ss->bits == 64) {
1495
1
                    if (opcode == MVM_OP_getattrs_n)
1496
0
                        MVM_spesh_get_facts(tc, g, ins->operands[3])->usages--;
1497
1
                    MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1498
1
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6oget_n);
1499
1
                    ins->operands[2].lit_i16 = repr_data->attribute_offsets[slot];
1500
1
                }
1501
1
            }
1502
1
        }
1503
1
        break;
1504
1
    }
1505
2.13k
    case MVM_OP_getattr_s:
1506
2.13k
    case MVM_OP_getattrs_s: {
1507
2.13k
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1508
2.13k
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[2]);
1509
2.13k
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[3], opcode == MVM_OP_getattrs_s);
1510
2.13k
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1511
2.13k
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1512
2.13k
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1513
2.13k
            if (slot >= 0 && repr_data->flattened_stables[slot]) {
1514
2.13k
                MVMSTable      *flat_st = repr_data->flattened_stables[slot];
1515
2.13k
                if (flat_st->REPR->ID == MVM_REPR_ID_P6str) {
1516
2.13k
                    if (opcode == MVM_OP_getattrs_s)
1517
0
                        MVM_spesh_get_facts(tc, g, ins->operands[3])->usages--;
1518
2.13k
                    MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1519
2.13k
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6oget_s);
1520
2.13k
                    ins->operands[2].lit_i16 = repr_data->attribute_offsets[slot];
1521
2.13k
                }
1522
2.13k
            }
1523
2.13k
        }
1524
2.13k
        break;
1525
2.13k
    }
1526
5.23k
    case MVM_OP_bindattr_o:
1527
5.23k
    case MVM_OP_bindattrs_o: {
1528
5.23k
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]);
1529
5.23k
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1530
5.23k
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[2], opcode == MVM_OP_bindattrs_o);
1531
5.23k
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1532
4.91k
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1533
4.91k
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1534
4.91k
            if (slot >= 0 && !repr_data->flattened_stables[slot]) {
1535
4.91k
                if (opcode == MVM_OP_bindattrs_o)
1536
0
                    MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1537
4.91k
                MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
1538
4.91k
                ins->info = MVM_op_get_op(MVM_OP_sp_p6obind_o);
1539
4.91k
                ins->operands[1].lit_i16 = repr_data->attribute_offsets[slot];
1540
4.91k
                ins->operands[2] = ins->operands[3];
1541
4.91k
            }
1542
4.91k
        }
1543
5.23k
        break;
1544
5.23k
    }
1545
4.74k
    case MVM_OP_bindattr_i:
1546
4.74k
    case MVM_OP_bindattrs_i: {
1547
4.74k
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]);
1548
4.74k
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1549
4.74k
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[2], opcode == MVM_OP_bindattrs_i);
1550
4.74k
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1551
4.66k
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1552
4.66k
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1553
4.66k
            if (slot >= 0 && repr_data->flattened_stables[slot]) {
1554
4.66k
                MVMSTable      *flat_st = repr_data->flattened_stables[slot];
1555
4.66k
                const MVMStorageSpec *flat_ss = flat_st->REPR->get_storage_spec(tc, flat_st);
1556
4.66k
                if (flat_st->REPR->ID == MVM_REPR_ID_P6int && flat_ss->bits == 64) {
1557
4.66k
                    if (opcode == MVM_OP_bindattrs_i)
1558
0
                        MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1559
4.66k
                    MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
1560
4.66k
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6obind_i);
1561
4.66k
                    ins->operands[1].lit_i16 = repr_data->attribute_offsets[slot];
1562
4.66k
                    ins->operands[2] = ins->operands[3];
1563
4.66k
                }
1564
4.66k
            }
1565
4.66k
        }
1566
4.74k
        break;
1567
4.74k
    }
1568
3
    case MVM_OP_bindattr_n:
1569
3
    case MVM_OP_bindattrs_n: {
1570
3
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]);
1571
3
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1572
3
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[2], opcode == MVM_OP_bindattrs_n);
1573
3
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1574
3
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1575
3
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1576
3
            if (slot >= 0 && repr_data->flattened_stables[slot]) {
1577
3
                MVMSTable      *flat_st = repr_data->flattened_stables[slot];
1578
3
                const MVMStorageSpec *flat_ss = flat_st->REPR->get_storage_spec(tc, flat_st);
1579
3
                if (flat_st->REPR->ID == MVM_REPR_ID_P6num && flat_ss->bits == 64) {
1580
3
                    if (opcode == MVM_OP_bindattrs_n)
1581
0
                        MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1582
3
                    MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
1583
3
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6obind_n);
1584
3
                    ins->operands[1].lit_i16 = repr_data->attribute_offsets[slot];
1585
3
                    ins->operands[2] = ins->operands[3];
1586
3
                }
1587
3
            }
1588
3
        }
1589
3
        break;
1590
3
    }
1591
548
    case MVM_OP_bindattr_s:
1592
548
    case MVM_OP_bindattrs_s: {
1593
548
        MVMSpeshFacts *obj_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[0]);
1594
548
        MVMSpeshFacts *ch_facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]);
1595
548
        MVMString     *name     = spesh_attr_name(tc, g, ins->operands[2], opcode == MVM_OP_bindattrs_s);
1596
548
        if (name && ch_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && ch_facts->type
1597
547
            && obj_facts->flags & MVM_SPESH_FACT_CONCRETE) {
1598
547
            MVMint64 slot = try_get_slot(tc, repr_data, ch_facts->type, name);
1599
547
            if (slot >= 0 && repr_data->flattened_stables[slot]) {
1600
547
                MVMSTable      *flat_st = repr_data->flattened_stables[slot];
1601
547
                if (flat_st->REPR->ID == MVM_REPR_ID_P6str) {
1602
547
                    if (opcode == MVM_OP_bindattrs_s)
1603
0
                        MVM_spesh_get_facts(tc, g, ins->operands[2])->usages--;
1604
547
                    MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--;
1605
547
                    ins->info = MVM_op_get_op(MVM_OP_sp_p6obind_s);
1606
547
                    ins->operands[1].lit_i16 = repr_data->attribute_offsets[slot];
1607
547
                    ins->operands[2] = ins->operands[3];
1608
547
                }
1609
547
            }
1610
547
        }
1611
548
        break;
1612
548
    }
1613
37.0k
    }
1614
37.0k
}
1615
1616
/* Initializes the representation. */
1617
144
const MVMREPROps * MVMP6opaque_initialize(MVMThreadContext *tc) {
1618
144
    return &P6opaque_this_repr;
1619
144
}
1620
1621
static const MVMREPROps P6opaque_this_repr = {
1622
    type_object_for,
1623
    allocate,
1624
    initialize,
1625
    copy_to,
1626
    {
1627
        get_attribute,
1628
        bind_attribute,
1629
        hint_for,
1630
        is_attribute_initialized,
1631
        attribute_as_atomic
1632
    },    /* attr_funcs */
1633
    {
1634
        set_int,
1635
        get_int,
1636
        set_num,
1637
        get_num,
1638
        set_str,
1639
        get_str,
1640
        set_uint,
1641
        get_uint,
1642
        get_boxed_ref
1643
    },    /* box_funcs */
1644
    {
1645
        at_pos,
1646
        bind_pos,
1647
        set_elems,
1648
        push,
1649
        pop,
1650
        unshift,
1651
        shift,
1652
        oslice,
1653
        osplice,
1654
        MVM_REPR_DEFAULT_AT_POS_MULTIDIM,
1655
        MVM_REPR_DEFAULT_BIND_POS_MULTIDIM,
1656
        MVM_REPR_DEFAULT_DIMENSIONS,
1657
        MVM_REPR_DEFAULT_SET_DIMENSIONS,
1658
        MVM_REPR_DEFAULT_GET_ELEM_STORAGE_SPEC,
1659
        MVM_REPR_DEFAULT_POS_AS_ATOMIC,
1660
        MVM_REPR_DEFAULT_POS_AS_ATOMIC_MULTIDIM
1661
    },    /* pos_funcs */
1662
    {
1663
        at_key,
1664
        bind_key,
1665
        exists_key,
1666
        delete_key,
1667
        NULL
1668
    },    /* ass_funcs */
1669
    elems,
1670
    get_storage_spec,
1671
    change_type,
1672
    serialize,
1673
    deserialize, /* deserialize */
1674
    serialize_repr_data,
1675
    deserialize_repr_data,
1676
    deserialize_stable_size,
1677
    gc_mark,
1678
    gc_free,
1679
    NULL, /* gc_cleanup */
1680
    gc_mark_repr_data,
1681
    gc_free_repr_data,
1682
    compose,
1683
    spesh,
1684
    "P6opaque", /* name */
1685
    MVM_REPR_ID_P6opaque,
1686
    NULL, /* unmanaged_size */
1687
    NULL, /* describe_refs */
1688
};
1689
1690
/* Get the pointer offset of an attribute. Used for optimizing access to it on
1691
 * precisely known types. */
1692
size_t MVM_p6opaque_attr_offset(MVMThreadContext *tc, MVMObject *type,
1693
714
                                MVMObject *class_handle, MVMString *name) {
1694
714
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)type->st->REPR_data;
1695
714
    size_t slot = try_get_slot(tc, repr_data, class_handle, name);
1696
714
    return repr_data->attribute_offsets[slot];
1697
714
}
1698
1699
#ifdef DEBUG_HELPERS
1700
/* This is meant to be called in a debugging session and not used anywhere else.
1701
 * Plese don't delete. */
1702
0
static void dump_p6opaque(MVMThreadContext *tc, MVMObject *obj, int nested) {
1703
0
    MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)STABLE(obj)->REPR_data;
1704
0
    MVMP6opaqueBody *data = MVM_p6opaque_real_data(tc, OBJECT_BODY(obj));
1705
0
    if (repr_data) {
1706
0
        MVMint16 const num_attributes = repr_data->num_attributes;
1707
0
        MVMint16 cur_attribute = 0;
1708
0
        MVMP6opaqueNameMap * const name_to_index_mapping = repr_data->name_to_index_mapping;
1709
0
        if (!IS_CONCRETE(obj)) {
1710
0
            fprintf(stderr, "(%s", MVM_6model_get_debug_name(tc, obj));
1711
0
            fprintf(stderr, nested ? ")" : ")\n");
1712
0
            return;
1713
0
        }
1714
0
        fprintf(stderr, "%s.new(", MVM_6model_get_debug_name(tc, obj));
1715
0
        if (name_to_index_mapping != NULL) {
1716
0
            MVMint16 i;
1717
0
            MVMP6opaqueNameMap *cur_map_entry = name_to_index_mapping;
1718
0
1719
0
            while (cur_map_entry->class_key != NULL) {
1720
0
                MVMint16 i;
1721
0
                MVMint64 slot;
1722
0
                if (cur_map_entry->num_attrs > 0) {
1723
0
                    fprintf(stderr, "#`(from %s) ", MVM_6model_get_stable_debug_name(tc, cur_map_entry->class_key->st));
1724
0
                }
1725
0
                for (i = 0; i < cur_map_entry->num_attrs; i++) {
1726
0
                    char * name = MVM_string_utf8_encode_C_string(tc, cur_map_entry->names[i]);
1727
0
                    fprintf(stderr, "%s", name);
1728
0
                    MVM_free(name);
1729
0
1730
0
                    slot = cur_map_entry->slots[i];
1731
0
                    if (slot >= 0) {
1732
0
                        MVMuint16 const offset = repr_data->attribute_offsets[slot];
1733
0
                        MVMSTable * const attr_st = repr_data->flattened_stables[slot];
1734
0
                        if (attr_st == NULL) {
1735
0
                            MVMObject *value = get_obj_at_offset(data, offset);
1736
0
                            if (value != NULL) {
1737
0
                                fprintf(stderr, "=");
1738
0
                                dump_p6opaque(tc, value, 1);
1739
0
                            }
1740
0
                        }
1741
0
                        else {
1742
0
                            if (attr_st->REPR->ID == MVM_REPR_ID_P6str) {
1743
0
                                char * const str = MVM_string_utf8_encode_C_string(tc, (MVMString *)get_obj_at_offset(data, offset));
1744
0
                                fprintf(stderr, "='%s'", str);
1745
0
                                MVM_free(str);
1746
0
                            }
1747
0
                            else if (attr_st->REPR->ID == MVM_REPR_ID_P6int) {
1748
0
                                MVMint64 val = attr_st->REPR->box_funcs.get_int(tc, attr_st, obj, (char *)data + offset);
1749
0
                                fprintf(stderr, "=%ld", val);
1750
0
                            }
1751
0
                            else {
1752
0
                                fprintf(stderr, "[%d]=%s", repr_data->attribute_offsets[slot], MVM_6model_get_stable_debug_name(tc, attr_st));
1753
0
                            }
1754
0
                            /*MVMString * const s = attr_st->REPR->box_funcs.get_str(tc, attr_st, obj, (char *)data + offset);*/
1755
0
                            /*char * const str = MVM_string_utf8_encode_C_string(tc, s);*/
1756
0
                            /*fprintf(stderr, "='%s'", str);*/
1757
0
                            /*MVM_free(str);*/
1758
0
                        }
1759
0
                    }
1760
0
                    if (cur_attribute++ < num_attributes - 1)
1761
0
                        fprintf(stderr, ", ");
1762
0
                }
1763
0
                cur_map_entry++;
1764
0
            }
1765
0
        }
1766
0
        fprintf(stderr, nested ? ")" : ")\n");
1767
0
    }
1768
0
    else {
1769
0
        fprintf(stderr, "%s%s", MVM_6model_get_debug_name(tc, obj), nested ? "" : "\n");
1770
0
    }
1771
0
}
1772
1773
0
void MVM_dump_p6opaque(MVMThreadContext *tc, MVMObject *obj) {
1774
0
    dump_p6opaque(tc, obj, 0);
1775
0
}
1776
#endif