Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/6model/serialization.c
Line
Count
Source (jump to first uncovered line)
1
#include <moar.h>
2
#include <sha1.h>
3
4
#ifndef MAX
5
1.89k
    #define MAX(x, y) ((y) > (x) ? (y) : (x))
6
#endif
7
8
/* Whether we deserialize lazily or not. */
9
#define MVM_SERIALIZATION_LAZY 1
10
11
/* Version of the serialization format that we are currently at and lowest
12
 * version we support. */
13
1.89k
#define CURRENT_VERSION 20
14
1.69k
#define MIN_VERSION     16
15
16
/* Various sizes (in bytes). */
17
1.69k
#define HEADER_SIZE                 (4 * 18)
18
2.25k
#define DEP_TABLE_ENTRY_SIZE        8
19
52.7k
#define STABLES_TABLE_ENTRY_SIZE    12
20
1.80M
#define OBJECTS_TABLE_ENTRY_SIZE    8
21
26.8k
#define CLOSURES_TABLE_ENTRY_SIZE   24
22
3.35k
#define CONTEXTS_TABLE_ENTRY_SIZE   16
23
1.81k
#define REPOS_TABLE_ENTRY_SIZE      16
24
25
/* Some guesses. */
26
104
#define DEFAULT_STABLE_DATA_SIZE        4096
27
104
#define STABLES_TABLE_ENTRIES_GUESS     16
28
104
#define OBJECT_SIZE_GUESS               8
29
104
#define CLOSURES_TABLE_ENTRIES_GUESS    16
30
104
#define CONTEXTS_TABLE_ENTRIES_GUESS    4
31
104
#define DEFAULT_CONTEXTS_DATA_SIZE      1024
32
104
#define DEFAULT_PARAM_INTERNS_DATA_SIZE 128
33
34
/* Possible reference types we can serialize. */
35
786k
#define REFVAR_NULL                 1
36
1.97M
#define REFVAR_OBJECT               2
37
248k
#define REFVAR_VM_NULL              3
38
2.10M
#define REFVAR_VM_INT               4
39
193k
#define REFVAR_VM_NUM               5
40
184k
#define REFVAR_VM_STR               6
41
17.2k
#define REFVAR_VM_ARR_VAR           7
42
2.47k
#define REFVAR_VM_ARR_STR           8
43
2.76k
#define REFVAR_VM_ARR_INT           9
44
110k
#define REFVAR_VM_HASH_STR_VAR      10
45
776k
#define REFVAR_STATIC_CODEREF       11
46
776k
#define REFVAR_CLONED_CODEREF       12
47
0
#define REFVAR_SC_REF               13
48
49
/* For the packed format, for "small" values of si and idx */
50
913k
#define OBJECTS_TABLE_ENTRY_SC_MASK     0x7FF
51
913k
#define OBJECTS_TABLE_ENTRY_SC_IDX_MASK 0x000FFFFF
52
343
#define OBJECTS_TABLE_ENTRY_SC_MAX      0x7FE
53
343
#define OBJECTS_TABLE_ENTRY_SC_IDX_MAX  0x000FFFFF
54
914k
#define OBJECTS_TABLE_ENTRY_SC_SHIFT    20
55
913k
#define OBJECTS_TABLE_ENTRY_SC_OVERFLOW 0x7FF
56
914k
#define OBJECTS_TABLE_ENTRY_IS_CONCRETE 0x80000000
57
58
/* In the main serialization data blobs we have 1 more bit to play with.
59
   The format is either 32 bits, with a packed value.
60
   or 32 bits with an overflow flag, 32 bits of ID, and 32 bits of index.
61
   The packed ID could be in the range 0..4094, the packed index 0..1048575.
62
   With these ranges, overflow isn't even needed for compiling the setting.
63
   An alternative format would be 8 bits of ID (so 0..254) and then 32 bits of
64
   index (0..65535), or 8 bits for an overflow flag, then 32 and 32.
65
   For this format, it turns out that currently for the setting, 296046 entries
66
   would pack into 3 bytes, and 59757 would overflow and need 9.
67
   296046 * 3 + 59757 * 9 == 1425951
68
   (296046 + 59757) * 4   == 1423212
69
   Hence that format is not quite as space efficient. */
70
71
1.71M
#define PACKED_SC_IDX_MASK  0x000FFFFF
72
515
#define PACKED_SC_MAX       0xFFE
73
515
#define PACKED_SC_IDX_MAX   0x000FFFFF
74
2.27M
#define PACKED_SC_SHIFT     20
75
2.27M
#define PACKED_SC_OVERFLOW  0xFFF
76
77
195
#define STRING_HEAP_LOC_MAX             0x7FFFFFFF
78
195
#define STRING_HEAP_LOC_PACKED_MAX      0x00007FFF
79
1.63M
#define STRING_HEAP_LOC_PACKED_OVERFLOW 0x00008000
80
0
#define STRING_HEAP_LOC_PACKED_LOW_MASK 0x0000FFFF
81
0
#define STRING_HEAP_LOC_PACKED_SHIFT    16
82
83
#define STABLE_BOOLIFICATION_SPEC_MODE_MASK 0x0F
84
25.4k
#define STABLE_HAS_CONTAINER_SPEC           0x10
85
25.4k
#define STABLE_HAS_INVOCATION_SPEC          0x20
86
25.4k
#define STABLE_HAS_HLL_OWNER                0x40
87
25.4k
#define STABLE_HAS_HLL_ROLE                 0x80
88
89
#define GROW_TABLE(table, alloc) \
90
107
        table = (char *)MVM_recalloc( \
91
107
            table, \
92
107
            alloc, \
93
107
            alloc * 2 \
94
107
        ); \
95
107
        alloc *= 2;
96
97
/* Endian translation (file format is little endian, so on big endian we need
98
 * to twiddle. */
99
#ifdef MVM_BIGENDIAN
100
static void switch_endian(char *bytes, size_t size)
101
{
102
    size_t low  = 0;
103
    size_t high = size - 1;
104
    while (high > low) {
105
        char tmp    = bytes[low];
106
        bytes[low]  = bytes[high];
107
        bytes[high] = tmp;
108
        low++;
109
        high--;
110
    }
111
}
112
#endif
113
114
/* Base64 encoding */
115
static char * base64_encode(const void *buf, size_t size)
116
104
{
117
104
    static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
118
104
119
104
    char* str = (char*) MVM_malloc((size+3)*4/3 + 1);
120
104
121
104
    char* p = str;
122
104
    const unsigned char* q = (const unsigned char*) buf;
123
104
    size_t i = 0;
124
104
125
7.49k
    while (i < size) {
126
7.39k
        int c = q[i++];
127
7.39k
        c *= 256;
128
7.39k
        if (i < size)
129
7.35k
            c += q[i];
130
7.39k
        i++;
131
7.39k
132
7.39k
        c *= 256;
133
7.39k
        if (i < size)
134
7.33k
            c += q[i];
135
7.39k
        i++;
136
7.39k
137
7.39k
        *p++ = base64[(c & 0x00fc0000) >> 18];
138
7.39k
        *p++ = base64[(c & 0x0003f000) >> 12];
139
7.39k
140
7.39k
        if (i > size + 1)
141
37
            *p++ = '=';
142
7.39k
        else
143
7.35k
            *p++ = base64[(c & 0x00000fc0) >> 6];
144
7.39k
145
7.39k
        if (i > size)
146
57
            *p++ = '=';
147
7.39k
        else
148
7.33k
            *p++ = base64[c & 0x0000003f];
149
7.39k
    }
150
104
151
104
    *p = 0;
152
104
153
104
    return str;
154
104
}
155
156
/* Base64 decoding */
157
static int POS(char c)
158
29.7k
{
159
29.7k
    /* XXX TODO: investigate whether enumerating all 256 cases of
160
29.7k
     * this in a switch/case can help the compiler turn it into a
161
29.7k
     * jump table instead of a bunch of comparisons (if it doesn't
162
29.7k
     * already, of course!)... */
163
29.7k
    if (c>='A' && c<='Z') return c - 'A';
164
5.63k
    if (c>='a' && c<='z') return c - 'a' + 26;
165
2.61k
    if (c>='0' && c<='9') return c - '0' + 52;
166
1.76k
    if (c == '+') return 62;
167
1.64k
    if (c == '/') return 63;
168
95
    if (c == '=') return -1;
169
0
    return -2;
170
95
}
171
static void * base64_decode(const char *s, size_t *data_len)
172
105
{
173
105
    const char *p;
174
105
    unsigned char *q, *data;
175
105
    /* XXX TODO: investigate whether putting these n[4] into 4
176
105
     * separate locals helps the compiler optimize them better.. */
177
105
    int n[4] = {-1, -1, -1, -1};
178
105
179
105
    size_t len = strlen(s);
180
105
    if (len % 4) {
181
0
        *data_len = 0;
182
0
        return NULL;
183
0
    }
184
105
    data = (unsigned char*) MVM_malloc(len/4*3);
185
105
    q = (unsigned char*) data;
186
105
187
7.55k
    for (p = s; *p; ) {
188
7.44k
        n[0] = POS(*p++);
189
7.44k
        n[1] = POS(*p++);
190
7.44k
        n[2] = POS(*p++);
191
7.44k
        n[3] = POS(*p++);
192
7.44k
193
7.44k
        /* XXX TODO: investigate jump table possibility here too,
194
7.44k
         * or at least collapse some of the branches... */
195
7.44k
        if (n[0] == -2
196
7.44k
         || n[1] == -2
197
7.44k
         || n[2] == -2
198
7.44k
         || n[3] == -2
199
7.44k
         || n[0] == -1
200
7.44k
         || n[1] == -1
201
7.44k
         || (n[2] == -1 && n[3] != -1)) {
202
0
            MVM_free(data);
203
0
            return NULL;
204
0
        }
205
7.44k
206
7.44k
        q[0] = (n[0] << 2) + (n[1] >> 4);
207
7.44k
        if (n[2] != -1)
208
7.40k
            q[1] = ((n[1] & 15) << 4) + (n[2] >> 2);
209
7.44k
        if (n[3] != -1)
210
7.38k
            q[2] = ((n[2] & 3) << 6) + n[3];
211
7.44k
        q += 3;
212
7.44k
    }
213
105
214
105
    *data_len = q-data - (n[2]==-1) - (n[3]==-1);
215
105
216
105
    return data;
217
105
}
218
219
220
/* ***************************************************************************
221
 * Serialization (writing related)
222
 * ***************************************************************************/
223
224
/* Writes an int32 into a buffer. */
225
3.03k
static void write_int32(char *buffer, size_t offset, MVMint32 value) {
226
3.03k
    memcpy(buffer + offset, &value, 4);
227
3.03k
#ifdef MVM_BIGENDIAN
228
    switch_endian(buffer + offset, 4);
229
#endif
230
3.03k
}
231
232
/* Writes a uint16 into a buffer. */
233
195
static void write_uint16(char *buffer, size_t offset, MVMuint16 value) {
234
195
    memcpy(buffer + offset, &value, 2);
235
195
#if MVM_BIGENDIAN
236
    switch_endian(buffer + offset, 2);
237
#endif
238
195
}
239
240
/* Writes an double into a buffer. */
241
9
static void write_double(char *buffer, size_t offset, double value) {
242
9
    memcpy(buffer + offset, &value, 8);
243
9
#ifdef MVM_BIGENDIAN
244
    switch_endian(buffer + offset, 8);
245
#endif
246
9
}
247
248
/* Adds an item to the MVMString heap if needed, and returns the index where
249
 * it may be found. */
250
577
static MVMint32 add_string_to_heap(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMString *s) {
251
577
    if (s == NULL) {
252
19
        /* We ensured that the first entry in the heap represents the null MVMString,
253
19
         * so can just hand back 0 here. */
254
19
        return 0;
255
19
    }
256
558
    else if (MVM_repr_exists_key(tc, writer->seen_strings, s)) {
257
56
        return (MVMint32)MVM_repr_at_key_int(tc, writer->seen_strings, s);
258
56
    }
259
502
    else {
260
502
        MVMint64 next_idx = MVM_repr_elems(tc, writer->root.string_heap);
261
502
        MVM_repr_bind_pos_s(tc, writer->root.string_heap, next_idx, s);
262
502
        MVM_repr_bind_key_int(tc, writer->seen_strings, s, next_idx);
263
502
        return (MVMint32)next_idx;
264
502
    }
265
577
}
266
267
/* Gets the ID of a serialization context. Returns 0 if it's the current
268
 * one, or its dependency table offset (base-1) otherwise. Note that if
269
 * it is not yet in the dependency table, it will be added. */
270
867
static MVMuint32 get_sc_id(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMSerializationContext *sc) {
271
867
    MVMint64 i, num_deps, offset;
272
867
273
867
    /* Easy if it's in the current SC. */
274
867
    if (writer->root.sc == sc)
275
390
        return 0;
276
867
277
867
    /* Otherwise, find it in our dependencies list. */
278
477
    num_deps = writer->root.num_dependencies;
279
567
    for (i = 0; i < num_deps; i++)
280
390
        if (writer->root.dependent_scs[i] == sc)
281
300
            return (MVMuint32)i + 1;
282
477
283
477
    /* Otherwise, need to add it to our dependencies list. Ensure there's
284
477
     * space in the dependencies table; grow if not. */
285
177
    offset = num_deps * DEP_TABLE_ENTRY_SIZE;
286
177
    if (offset + DEP_TABLE_ENTRY_SIZE > writer->dependencies_table_alloc) {
287
0
        GROW_TABLE(writer->root.dependencies_table, writer->dependencies_table_alloc);
288
0
    }
289
177
290
177
    /* Add dependency. */
291
177
    writer->root.dependent_scs = MVM_realloc(writer->root.dependent_scs, sizeof(MVMSerializationContext *) * (writer->root.num_dependencies + 1));
292
177
    writer->root.dependent_scs[writer->root.num_dependencies] = sc;
293
177
    write_int32(writer->root.dependencies_table, offset,
294
177
        add_string_to_heap(tc, writer, MVM_sc_get_handle(tc, sc)));
295
177
    write_int32(writer->root.dependencies_table, offset + 4,
296
177
        add_string_to_heap(tc, writer, MVM_sc_get_description(tc, sc)));
297
177
    writer->root.num_dependencies++;
298
177
    return writer->root.num_dependencies; /* Deliberately index + 1. */
299
477
}
300
301
498
#define OBJ_IS_NULL(obj) ((obj) == NULL)
302
303
/* Takes an STable. If it's already in an SC, returns information on how
304
 * to reference it. Otherwise, adds it to the current SC, effectively
305
 * placing it onto the work list. */
306
static void get_stable_ref_info(MVMThreadContext *tc, MVMSerializationWriter *writer,
307
348
                                MVMSTable *st, MVMuint32 *sc, MVMuint32 *sc_idx) {
308
348
    /* Add to this SC if needed. */
309
348
    if (MVM_sc_get_stable_sc(tc, st) == NULL) {
310
1
        MVM_sc_set_stable_sc(tc, st, writer->root.sc);
311
1
        MVM_sc_push_stable(tc, writer->root.sc, st);
312
1
    }
313
348
314
348
    /* Work out SC reference. */
315
348
    *sc     = get_sc_id(tc, writer, MVM_sc_get_stable_sc(tc, st));
316
348
    *sc_idx = (MVMuint32)MVM_sc_find_stable_idx(tc, MVM_sc_get_stable_sc(tc, st), st);
317
348
}
318
319
/* Expands current target storage as needed. */
320
4.78k
static void expand_storage_if_needed(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMint64 need) {
321
4.78k
    if (*(writer->cur_write_offset) + need > *(writer->cur_write_limit)) {
322
264
        *(writer->cur_write_limit) *= 2;
323
264
        *(writer->cur_write_buffer) = (char *)MVM_realloc(*(writer->cur_write_buffer),
324
264
            *(writer->cur_write_limit));
325
264
    }
326
4.78k
}
327
328
/* Writing function for null-terminated char array strings */
329
28
void MVM_serialization_write_cstr(MVMThreadContext *tc, MVMSerializationWriter *writer, char *string) {
330
28
    size_t len;
331
28
    if (string)
332
28
        len = strlen(string);
333
28
    else
334
0
        len = 0;
335
28
    if (len) {
336
21
        MVM_serialization_write_int(tc, writer, len);
337
21
        expand_storage_if_needed(tc, writer, len);
338
21
        memcpy(*(writer->cur_write_buffer) + *(writer->cur_write_offset), string, len);
339
21
        *(writer->cur_write_offset) += len;
340
7
    } else {
341
7
        MVM_serialization_write_int(tc, writer, 0);
342
7
    }
343
28
}
344
345
/* Writing function for variable sized integers. Writes out a 64 bit value
346
   using between 1 and 9 bytes. */
347
2.45k
void MVM_serialization_write_int(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMint64 value) {
348
2.45k
    MVMuint8 storage_needed;
349
2.45k
    char *buffer;
350
2.45k
    size_t offset;
351
2.45k
352
2.45k
    if (value >= -1 && value <= 126) {
353
1.16k
        storage_needed = 1;
354
1.29k
    } else {
355
650
        const MVMint64 abs_val = value < 0 ? -value - 1 : value;
356
1.29k
357
1.29k
        if (abs_val <= 0x7FF)
358
442
            storage_needed = 2;
359
852
        else if (abs_val <= 0x000000000007FFFF)
360
105
            storage_needed = 3;
361
747
        else if (abs_val <= 0x0000000007FFFFFF)
362
275
            storage_needed = 4;
363
472
        else if (abs_val <= 0x00000007FFFFFFFF)
364
104
            storage_needed = 5;
365
368
        else if (abs_val <= 0x000007FFFFFFFFFFLL)
366
104
            storage_needed = 6;
367
264
        else if (abs_val <= 0x0007FFFFFFFFFFFFLL)
368
104
            storage_needed = 7;
369
160
        else if (abs_val <= 0x07FFFFFFFFFFFFFFLL)
370
105
            storage_needed = 8;
371
160
        else
372
55
            storage_needed = 9;
373
1.29k
    }
374
2.45k
375
2.45k
    expand_storage_if_needed(tc, writer, storage_needed);
376
2.45k
377
2.45k
    buffer = *(writer->cur_write_buffer);
378
2.45k
    offset = *(writer->cur_write_offset);
379
2.45k
380
2.45k
    if (storage_needed == 1) {
381
1.16k
        buffer[offset] = 0x80 | (value + 129);
382
1.29k
    } else if (storage_needed == 9) {
383
55
        buffer[offset++] = 0x00;
384
55
        memcpy(buffer + offset, &value, 8);
385
55
#ifdef MVM_BIGENDIAN
386
        switch_endian(buffer + offset, 8);
387
#endif
388
1.23k
    } else {
389
1.23k
        MVMuint8 rest = storage_needed - 1;
390
1.23k
        MVMint64 nybble = value >> 8 * rest;
391
1.23k
        /* All the other high bits should be the same as the top bit of the
392
1.23k
           nybble we keep. Or we have a bug.  */
393
1.23k
        assert((nybble >> 3) == 0
394
1.23k
               || (nybble >> 3) == ~(MVMuint64)0);
395
1.23k
        buffer[offset++] = (rest << 4) | (nybble & 0xF);
396
1.23k
#ifdef MVM_BIGENDIAN
397
        memcpy(buffer + offset, (char *)&value + 8 - rest, rest);
398
        switch_endian(buffer + offset, rest);
399
#else
400
1.23k
        memcpy(buffer + offset, &value, rest);
401
1.23k
#endif
402
1.23k
    }
403
2.45k
404
2.45k
    *(writer->cur_write_offset) += storage_needed;
405
2.45k
}
406
407
/* Writing function for native numbers. */
408
9
void MVM_serialization_write_num(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMnum64 value) {
409
9
    expand_storage_if_needed(tc, writer, 8);
410
9
    write_double(*(writer->cur_write_buffer), *(writer->cur_write_offset), value);
411
9
    *(writer->cur_write_offset) += 8;
412
9
}
413
414
/* Writing function for native strings. */
415
195
void MVM_serialization_write_str(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMString *value) {
416
195
    MVMint32 heap_loc = add_string_to_heap(tc, writer, value);
417
195
418
195
    /* avoid warnings that heap_loc > STRING_HEAP_LOC_MAX is always false */
419
195
    if (!(heap_loc >= 0 && heap_loc <= STRING_HEAP_LOC_MAX))
420
0
        MVM_exception_throw_adhoc(tc,
421
0
                                  "Serialization error: string offset %d can't be serialized",
422
0
                                  heap_loc);
423
195
424
195
    if (heap_loc <= STRING_HEAP_LOC_PACKED_MAX) {
425
195
        expand_storage_if_needed(tc, writer, 2);
426
195
        write_uint16(*(writer->cur_write_buffer), *(writer->cur_write_offset),
427
195
                     heap_loc);
428
195
        *(writer->cur_write_offset) += 2;
429
0
    } else {
430
0
        expand_storage_if_needed(tc, writer, 4);
431
0
        write_uint16(*(writer->cur_write_buffer), *(writer->cur_write_offset),
432
0
                     (heap_loc >> STRING_HEAP_LOC_PACKED_SHIFT)
433
0
                     | STRING_HEAP_LOC_PACKED_OVERFLOW);
434
0
        *(writer->cur_write_offset) += 2;
435
0
        write_uint16(*(writer->cur_write_buffer), *(writer->cur_write_offset),
436
0
                     heap_loc & STRING_HEAP_LOC_PACKED_LOW_MASK);
437
0
        *(writer->cur_write_offset) += 2;
438
0
    }
439
195
}
440
441
/* Writes the ID, index pair that identifies an entry in a Serialization
442
   context. */
443
515
static void write_locate_sc_and_index(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMint32 sc_id, MVMint32 idx) {
444
515
    if (sc_id <= PACKED_SC_MAX && idx <= PACKED_SC_IDX_MAX) {
445
515
        MVMuint32 packed = (sc_id << PACKED_SC_SHIFT) | (idx & PACKED_SC_IDX_MASK);
446
515
        MVM_serialization_write_int(tc, writer, packed);
447
0
    } else {
448
0
        MVMuint32 packed = PACKED_SC_OVERFLOW << PACKED_SC_SHIFT;
449
0
        MVM_serialization_write_int(tc, writer, packed);
450
0
        MVM_serialization_write_int(tc, writer, sc_id);
451
0
        MVM_serialization_write_int(tc, writer, idx);
452
0
    }
453
515
}
454
455
/* Writes an object reference. */
456
486
static void write_obj_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *ref) {
457
486
    MVMint32 sc_id, idx;
458
486
459
486
    if (OBJ_IS_NULL(MVM_sc_get_obj_sc(tc, ref))) {
460
224
        /* This object doesn't belong to an SC yet, so it must be serialized as part of
461
224
         * this compilation unit. Add it to the work list. */
462
224
        MVM_sc_set_obj_sc(tc, ref, writer->root.sc);
463
224
        MVM_sc_push_object(tc, writer->root.sc, ref);
464
224
    }
465
486
    sc_id = get_sc_id(tc, writer, MVM_sc_get_obj_sc(tc, ref));
466
486
    idx   = (MVMint32)MVM_sc_find_object_idx(tc, MVM_sc_get_obj_sc(tc, ref), ref);
467
486
    write_locate_sc_and_index(tc, writer, sc_id, idx);
468
486
}
469
470
/* Writes an array where each item is a variant reference. */
471
7
static void write_array_var(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *arr) {
472
7
    MVMint32 elems = (MVMint32)MVM_repr_elems(tc, arr);
473
7
    MVMint32 i;
474
7
475
7
    /* Write out element count. */
476
7
    MVM_serialization_write_int(tc, writer, elems);
477
7
478
7
    /* Write elements. */
479
8
    for (i = 0; i < elems; i++)
480
1
        MVM_serialization_write_ref(tc, writer, MVM_repr_at_pos_o(tc, arr, i));
481
7
}
482
483
/* Writes an array where each item is an integer. */
484
1
static void write_array_int(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *arr) {
485
1
    MVMint32 elems = (MVMint32)MVM_repr_elems(tc, arr);
486
1
    MVMint32 i;
487
1
488
1
    /* Write out element count. */
489
1
    MVM_serialization_write_int(tc, writer, elems);
490
1
491
1
    /* Write elements. */
492
6
    for (i = 0; i < elems; i++)
493
5
        MVM_serialization_write_int(tc, writer, MVM_repr_at_pos_i(tc, arr, i));
494
1
}
495
496
/* Writes an array where each item is a MVMString. */
497
1
static void write_array_str(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *arr) {
498
1
    MVMint32 elems = (MVMint32)MVM_repr_elems(tc, arr);
499
1
    MVMint32 i;
500
1
501
1
    /* Write out element count. */
502
1
    MVM_serialization_write_int(tc, writer, elems);
503
1
504
1
    /* Write elements. */
505
4
    for (i = 0; i < elems; i++)
506
3
        MVM_serialization_write_str(tc, writer, MVM_repr_at_pos_s(tc, arr, i));
507
1
}
508
509
/* Writes a hash where each key is a MVMString and each value a variant reference. */
510
MVMThreadContext *cmp_tc;
511
195
static int cmp_strings(const void *s1, const void *s2) {
512
195
    return MVM_string_compare(cmp_tc, *(MVMString **)s1, *(MVMString **)s2);
513
195
}
514
61
static void write_hash_str_var(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *hash) {
515
61
    MVMint32 elems = (MVMint32)MVM_repr_elems(tc, hash);
516
61
    MVMString **keys = MVM_malloc(sizeof(MVMString *) * elems);
517
61
    MVMObject *iter = MVM_iter(tc, hash);
518
61
    MVMuint64 i = 0;
519
61
520
61
    /* Write out element count. */
521
61
    MVM_serialization_write_int(tc, writer, elems);
522
61
523
61
    /* Write elements, as key,value,key,value etc. */
524
175
    while (MVM_iter_istrue(tc, (MVMIter *)iter)) {
525
114
        MVM_repr_shift_o(tc, iter);
526
114
        keys[i++] = MVM_iterkey_s(tc, (MVMIter *)iter);
527
114
    }
528
61
    cmp_tc = tc;
529
61
    qsort(keys, elems, sizeof(MVMString*), cmp_strings);
530
175
    for (i = 0; i < elems; i++) {
531
114
        MVM_serialization_write_str(tc, writer, keys[i]);
532
114
        MVM_serialization_write_ref(tc, writer, MVM_repr_at_key_o(tc, hash, keys[i]));
533
114
    }
534
61
    MVM_free(keys);
535
61
}
536
537
/* Writes a reference to a code object in some SC. */
538
24
static void write_code_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *code) {
539
24
    MVMSerializationContext *sc = MVM_sc_get_obj_sc(tc, code);
540
24
    MVMint32  sc_id   = get_sc_id(tc, writer, sc);
541
24
    MVMint32  idx     = (MVMint32)MVM_sc_find_code_idx(tc, sc, code);
542
24
    write_locate_sc_and_index(tc, writer, sc_id, idx);
543
24
}
544
MVM_NO_RETURN void throw_closure_serialization_error(MVMThreadContext *tc, MVMCode *closure, const char *message) MVM_NO_RETURN_ATTRIBUTE;
545
0
MVM_NO_RETURN void throw_closure_serialization_error(MVMThreadContext *tc, MVMCode *closure, const char *message) {
546
0
    MVMString *file;
547
0
    MVMint32 line;
548
0
    MVM_gc_enter_from_allocator(tc); /* opportunity for creating a heap snapshot for debugging */
549
0
    MVM_code_location_out(tc, (MVMObject *)closure, &file, &line);
550
0
    {
551
0
        char *c_name = MVM_string_utf8_encode_C_string(tc,
552
0
                (closure->body.sf)->body.name);
553
0
        char *c_file = MVM_string_utf8_encode_C_string(tc, file);
554
0
        char *waste[] = { c_name, c_file, NULL };
555
0
        MVM_exception_throw_adhoc_free(tc, waste,
556
0
            "Serialization Error: %s '%s' (%s:%d)",
557
0
            message, c_name, c_file, line);
558
0
    }
559
0
}
560
561
/* Given a closure, locate the static code reference it was originally cloned
562
 * from. */
563
10
static MVMObject * closure_to_static_code_ref(MVMThreadContext *tc, MVMObject *closure, MVMint64 fatal) {
564
10
    MVMObject *scr = (MVMObject *)(((MVMCode *)closure)->body.sf)->body.static_code;
565
10
566
10
    if (scr == NULL || MVM_sc_get_obj_sc(tc, scr) == NULL) {
567
3
        if (fatal) {
568
0
            throw_closure_serialization_error(
569
0
                tc,
570
0
                (MVMCode *)closure,
571
0
                "missing static code ref for closure"
572
0
            );
573
0
        }
574
3
        return NULL;
575
3
    }
576
7
    return scr;
577
10
}
578
579
/* Takes an outer context that is potentially to be serialized. Checks if it
580
 * is of interest, and if so sets it up to be serialized. */
581
5
static MVMint32 get_serialized_context_idx(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMFrame *ctx, MVMCode *closure) {
582
5
     if (OBJ_IS_NULL(MVM_sc_get_frame_sc(tc, ctx))) {
583
5
        /* Make sure we should chase a level down. */
584
5
        if (OBJ_IS_NULL(closure_to_static_code_ref(tc, ctx->code_ref, 0))) {
585
3
            return 0;
586
3
        }
587
2
        else {
588
2
            if (writer->num_contexts == writer->alloc_contexts) {
589
1
                writer->alloc_contexts += 256;
590
1
                writer->contexts_list = MVM_realloc(writer->contexts_list,
591
1
                    writer->alloc_contexts * sizeof(MVMFrame *));
592
1
            }
593
2
            writer->contexts_list[writer->num_contexts++] = ctx;
594
2
            MVM_sc_set_frame_sc(tc, ctx, writer->root.sc);
595
2
            return (MVMint32)writer->num_contexts;
596
2
        }
597
5
    }
598
0
    else {
599
0
        MVMint64 i, c;
600
0
        if (MVM_sc_get_frame_sc(tc, ctx) != writer->root.sc)
601
0
            throw_closure_serialization_error(tc,
602
0
                closure,
603
0
                "reference to context outside of SC for"
604
0
            );
605
0
        c = writer->num_contexts;
606
0
        for (i = 0; i < c; i++)
607
0
            if (writer->contexts_list[i] == ctx)
608
0
                return (MVMint32)i + 1;
609
0
        throw_closure_serialization_error(
610
0
            tc,
611
0
            closure,
612
0
            "could not locate outer context in current SC for"
613
0
        );
614
0
    }
615
5
}
616
617
/* Takes a closure, that is to be serialized. Checks if it has an outer that is
618
 * of interest, and if so sets it up to be serialized. */
619
3
static MVMint32 get_serialized_outer_context_idx(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *closure) {
620
3
    if (((MVMCode *)closure)->body.is_compiler_stub)
621
0
        return 0;
622
3
    if (((MVMCode *)closure)->body.outer == NULL)
623
0
        return 0;
624
3
    return get_serialized_context_idx(tc, writer, ((MVMCode *)closure)->body.outer, (MVMCode *)closure);
625
3
}
626
627
/* Takes a closure that needs to be serialized. Makes an entry in the closures
628
 * table for it. Also adds it to this SC's code refs set and tags it with the
629
 * current SC. */
630
3
static void serialize_closure(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *closure) {
631
3
    MVMint32 static_sc_id, static_idx, context_idx;
632
3
633
3
    /* Locate the static code object. */
634
3
    MVMObject *static_code_ref = closure_to_static_code_ref(tc, closure, 1);
635
3
    MVMSerializationContext *static_code_sc = MVM_sc_get_obj_sc(tc, static_code_ref);
636
3
637
3
    /* Ensure there's space in the closures table; grow if not. */
638
3
    MVMint32 offset = writer->root.num_closures * CLOSURES_TABLE_ENTRY_SIZE;
639
3
    if (offset + CLOSURES_TABLE_ENTRY_SIZE > writer->closures_table_alloc) {
640
0
        GROW_TABLE(writer->root.closures_table, writer->closures_table_alloc);
641
0
    }
642
3
643
3
    /* Get the index of the context (which will add it to the todo list if
644
3
     * needed). */
645
3
    context_idx = get_serialized_outer_context_idx(tc, writer, closure);
646
3
647
3
    /* Add an entry to the closures table. */
648
3
    static_sc_id = get_sc_id(tc, writer, static_code_sc);
649
3
    static_idx   = (MVMint32)MVM_sc_find_code_idx(tc, static_code_sc, static_code_ref);
650
3
    write_int32(writer->root.closures_table, offset, static_sc_id);
651
3
    write_int32(writer->root.closures_table, offset + 4, static_idx);
652
3
    write_int32(writer->root.closures_table, offset + 8, context_idx);
653
3
654
3
    /* Check if it has a static code object. */
655
3
    if (((MVMCode *)closure)->body.code_object) {
656
0
        MVMObject *code_obj = (MVMObject *)((MVMCode *)closure)->body.code_object;
657
0
        write_int32(writer->root.closures_table, offset + 12, 1);
658
0
        if (!MVM_sc_get_obj_sc(tc, code_obj)) {
659
0
            MVM_sc_set_obj_sc(tc, code_obj, writer->root.sc);
660
0
            MVM_sc_push_object(tc, writer->root.sc, code_obj);
661
0
        }
662
0
        write_int32(writer->root.closures_table, offset + 16,
663
0
            get_sc_id(tc, writer, MVM_sc_get_obj_sc(tc, code_obj)));
664
0
        write_int32(writer->root.closures_table, offset + 20,
665
0
            (MVMint32)MVM_sc_find_object_idx(tc, MVM_sc_get_obj_sc(tc, code_obj), code_obj));
666
0
    }
667
3
    else {
668
3
        write_int32(writer->root.closures_table, offset + 12, 0);
669
3
    }
670
3
671
3
    /* Increment count of closures in the table. */
672
3
    writer->root.num_closures++;
673
3
674
3
    /* Add the closure to this SC, and mark it as as being in it. */
675
3
    MVM_repr_push_o(tc, writer->codes_list, closure);
676
3
    MVM_sc_set_obj_sc(tc, closure, writer->root.sc);
677
3
}
678
679
/* Writing function for references to things. */
680
2.04k
void MVM_serialization_write_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *ref) {
681
2.04k
    /* Work out what kind of thing we have and determine the discriminator. */
682
2.04k
    /* Note, we could use 0xFF as the sentinel value, and 0 as a "valid" value.
683
2.04k
     */
684
2.04k
    MVMuint8 discrim = 0;
685
2.04k
    if (ref == NULL) {
686
97
        discrim = REFVAR_NULL;
687
97
    }
688
1.95k
    else if (ref == tc->instance->VMNull) {
689
36
        discrim = REFVAR_VM_NULL;
690
36
    }
691
1.91k
    else if (REPR(ref)->ID == MVM_REPR_ID_MVMMultiCache) {
692
1
        discrim = REFVAR_VM_NULL;
693
1
    }
694
1.91k
    else if (REPR(ref)->ID == MVM_REPR_ID_MVMOSHandle) {
695
4
        discrim = REFVAR_VM_NULL;
696
4
    }
697
1.90k
    else if (REPR(ref)->ID == MVM_REPR_ID_Decoder && IS_CONCRETE(ref)) {
698
0
        discrim = REFVAR_VM_NULL;
699
0
    }
700
1.90k
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTInt) && IS_CONCRETE(ref)) {
701
1.34k
        discrim = REFVAR_VM_INT;
702
1.34k
    }
703
568
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTNum) && IS_CONCRETE(ref)) {
704
1
        discrim = REFVAR_VM_NUM;
705
1
    }
706
567
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTStr) && IS_CONCRETE(ref)) {
707
45
        discrim = REFVAR_VM_STR;
708
45
    }
709
522
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTArray) && IS_CONCRETE(ref)) {
710
7
        discrim = REFVAR_VM_ARR_VAR;
711
7
    }
712
515
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTIntArray) && IS_CONCRETE(ref)) {
713
1
        discrim = REFVAR_VM_ARR_INT;
714
1
    }
715
514
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTStrArray) && IS_CONCRETE(ref)) {
716
1
        discrim = REFVAR_VM_ARR_STR;
717
1
    }
718
513
    else if (STABLE(ref) == STABLE(tc->instance->boot_types.BOOTHash) && IS_CONCRETE(ref)) {
719
61
        discrim = REFVAR_VM_HASH_STR_VAR;
720
61
    }
721
452
    else if (REPR(ref)->ID == MVM_REPR_ID_MVMCode && IS_CONCRETE(ref)) {
722
24
        if (MVM_sc_get_obj_sc(tc, ref) && ((MVMCode *)ref)->body.is_static) {
723
18
            /* Static code reference. */
724
18
            discrim = REFVAR_STATIC_CODEREF;
725
18
        }
726
6
        else if (MVM_sc_get_obj_sc(tc, ref)) {
727
3
            /* Closure, but already seen and serialization already handled. */
728
3
            discrim = REFVAR_CLONED_CODEREF;
729
3
        }
730
3
        else {
731
3
            /* Closure but didn't see it yet. Take care of its serialization, which
732
3
             * gets it marked with this SC. Then it's just a normal code ref that
733
3
             * needs serializing. */
734
3
            serialize_closure(tc, writer, ref);
735
3
            discrim = REFVAR_CLONED_CODEREF;
736
3
        }
737
24
    }
738
428
    else if (REPR(ref)->ID == MVM_REPR_ID_SCRef && IS_CONCRETE(ref)) {
739
0
        discrim = REFVAR_SC_REF;
740
0
    }
741
428
    else {
742
428
        discrim = REFVAR_OBJECT;
743
428
    }
744
2.04k
745
2.04k
    /* Write the discriminator. */
746
2.04k
    expand_storage_if_needed(tc, writer, 1);
747
2.04k
    *(*(writer->cur_write_buffer) + *(writer->cur_write_offset)) = discrim;
748
2.04k
    ++*(writer->cur_write_offset);
749
2.04k
750
2.04k
    /* Now take appropriate action. */
751
2.04k
    switch (discrim) {
752
97
        case REFVAR_NULL: break;
753
428
        case REFVAR_OBJECT:
754
428
            write_obj_ref(tc, writer, ref);
755
428
            break;
756
41
        case REFVAR_VM_NULL:
757
41
            /* Nothing to do for these. */
758
41
            break;
759
1.34k
        case REFVAR_VM_INT:
760
1.34k
            MVM_serialization_write_int(tc, writer, MVM_repr_get_int(tc, ref));
761
1.34k
            break;
762
1
        case REFVAR_VM_NUM:
763
1
            MVM_serialization_write_num(tc, writer, MVM_repr_get_num(tc, ref));
764
1
            break;
765
45
        case REFVAR_VM_STR:
766
45
            MVM_serialization_write_str(tc, writer, MVM_repr_get_str(tc, ref));
767
45
            break;
768
7
        case REFVAR_VM_ARR_VAR:
769
7
            write_array_var(tc, writer, ref);
770
7
            break;
771
1
        case REFVAR_VM_ARR_STR:
772
1
            write_array_str(tc, writer, ref);
773
1
            break;
774
1
        case REFVAR_VM_ARR_INT:
775
1
            write_array_int(tc, writer, ref);
776
1
            break;
777
61
        case REFVAR_VM_HASH_STR_VAR:
778
61
            write_hash_str_var(tc, writer, ref);
779
61
            break;
780
24
        case REFVAR_STATIC_CODEREF:
781
24
        case REFVAR_CLONED_CODEREF:
782
24
            write_code_ref(tc, writer, ref);
783
24
            break;
784
0
        case REFVAR_SC_REF: {
785
0
            MVMString *handle = MVM_sc_get_handle(tc, (MVMSerializationContext *)ref);
786
0
            MVM_serialization_write_str(tc, writer, handle);
787
0
            break;
788
24
        }
789
0
        default:
790
0
            MVM_exception_throw_adhoc(tc,
791
0
                "Serialization Error: Unimplemented discriminator %d in MVM_serialization_read_ref",
792
0
                discrim);
793
2.04k
    }
794
2.04k
}
795
796
/* Writing function for references to STables. */
797
5
void MVM_serialization_write_stable_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMSTable *st) {
798
5
    MVMuint32 sc_id, idx;
799
5
    get_stable_ref_info(tc, writer, st, &sc_id, &idx);
800
5
    write_locate_sc_and_index(tc, writer, sc_id, idx);
801
5
}
802
803
/* Concatenates the various output segments into a single binary MVMString. */
804
104
static MVMString * concatenate_outputs(MVMThreadContext *tc, MVMSerializationWriter *writer) {
805
104
    char      *output      = NULL;
806
104
    char      *output_b64  = NULL;
807
104
    MVMuint32  output_size = 0;
808
104
    MVMuint32  offset      = 0;
809
104
    MVMString *result;
810
104
811
104
    /* Calculate total size. */
812
104
    output_size += MVM_ALIGN_SECTION(HEADER_SIZE);
813
104
    output_size += MVM_ALIGN_SECTION(writer->root.num_dependencies * DEP_TABLE_ENTRY_SIZE);
814
104
    output_size += MVM_ALIGN_SECTION(writer->root.num_stables * STABLES_TABLE_ENTRY_SIZE);
815
104
    output_size += MVM_ALIGN_SECTION(writer->stables_data_offset);
816
104
    output_size += MVM_ALIGN_SECTION(writer->root.num_objects * OBJECTS_TABLE_ENTRY_SIZE);
817
104
    output_size += MVM_ALIGN_SECTION(writer->objects_data_offset);
818
104
    output_size += MVM_ALIGN_SECTION(writer->root.num_closures * CLOSURES_TABLE_ENTRY_SIZE);
819
104
    output_size += MVM_ALIGN_SECTION(writer->root.num_contexts * CONTEXTS_TABLE_ENTRY_SIZE);
820
104
    output_size += MVM_ALIGN_SECTION(writer->contexts_data_offset);
821
104
    output_size += MVM_ALIGN_SECTION(writer->root.num_repos * REPOS_TABLE_ENTRY_SIZE);
822
104
    output_size += MVM_ALIGN_SECTION(writer->param_interns_data_offset);
823
104
824
104
    /* Allocate a buffer that size. */
825
104
    output = (char *)MVM_calloc(1, output_size);
826
104
827
104
    /* Write version into header. */
828
104
    write_int32(output, 0, CURRENT_VERSION);
829
104
    offset += MVM_ALIGN_SECTION(HEADER_SIZE);
830
104
831
104
    /* Put dependencies table in place and set location/rows in header. */
832
104
    write_int32(output, 4, offset);
833
104
    write_int32(output, 8, writer->root.num_dependencies);
834
104
    memcpy(output + offset, writer->root.dependencies_table,
835
104
        writer->root.num_dependencies * DEP_TABLE_ENTRY_SIZE);
836
104
    offset += MVM_ALIGN_SECTION(writer->root.num_dependencies * DEP_TABLE_ENTRY_SIZE);
837
104
838
104
    /* Put STables table in place, and set location/rows in header. */
839
104
    write_int32(output, 12, offset);
840
104
    write_int32(output, 16, writer->root.num_stables);
841
104
    memcpy(output + offset, writer->root.stables_table,
842
104
        writer->root.num_stables * STABLES_TABLE_ENTRY_SIZE);
843
104
    offset += MVM_ALIGN_SECTION(writer->root.num_stables * STABLES_TABLE_ENTRY_SIZE);
844
104
845
104
    /* Put STables data in place. */
846
104
    write_int32(output, 20, offset);
847
104
    memcpy(output + offset, writer->root.stables_data,
848
104
        writer->stables_data_offset);
849
104
    offset += MVM_ALIGN_SECTION(writer->stables_data_offset);
850
104
851
104
    /* Put objects table in place, and set location/rows in header. */
852
104
    write_int32(output, 24, offset);
853
104
    write_int32(output, 28, writer->root.num_objects);
854
104
    memcpy(output + offset, writer->root.objects_table,
855
104
        writer->root.num_objects * OBJECTS_TABLE_ENTRY_SIZE);
856
104
    offset += MVM_ALIGN_SECTION(writer->root.num_objects * OBJECTS_TABLE_ENTRY_SIZE);
857
104
858
104
    /* Put objects data in place. */
859
104
    write_int32(output, 32, offset);
860
104
    memcpy(output + offset, writer->root.objects_data,
861
104
        writer->objects_data_offset);
862
104
    offset += MVM_ALIGN_SECTION(writer->objects_data_offset);
863
104
864
104
    /* Put closures table in place, and set location/rows in header. */
865
104
    write_int32(output, 36, offset);
866
104
    write_int32(output, 40, writer->root.num_closures);
867
104
    memcpy(output + offset, writer->root.closures_table,
868
104
        writer->root.num_closures * CLOSURES_TABLE_ENTRY_SIZE);
869
104
    offset += MVM_ALIGN_SECTION(writer->root.num_closures * CLOSURES_TABLE_ENTRY_SIZE);
870
104
871
104
    /* Put contexts table in place, and set location/rows in header. */
872
104
    write_int32(output, 44, offset);
873
104
    write_int32(output, 48, writer->root.num_contexts);
874
104
    memcpy(output + offset, writer->root.contexts_table,
875
104
        writer->root.num_contexts * CONTEXTS_TABLE_ENTRY_SIZE);
876
104
    offset += MVM_ALIGN_SECTION(writer->root.num_contexts * CONTEXTS_TABLE_ENTRY_SIZE);
877
104
878
104
    /* Put contexts data in place. */
879
104
    write_int32(output, 52, offset);
880
104
    memcpy(output + offset, writer->root.contexts_data,
881
104
        writer->contexts_data_offset);
882
104
    offset += MVM_ALIGN_SECTION(writer->contexts_data_offset);
883
104
884
104
    /* Put repossessions table in place, and set location/rows in header. */
885
104
    write_int32(output, 56, offset);
886
104
    write_int32(output, 60, writer->root.num_repos);
887
104
    memcpy(output + offset, writer->root.repos_table,
888
104
        writer->root.num_repos * REPOS_TABLE_ENTRY_SIZE);
889
104
    offset += MVM_ALIGN_SECTION(writer->root.num_repos * REPOS_TABLE_ENTRY_SIZE);
890
104
891
104
    /* Put parameterized type intern data in place. */
892
104
    write_int32(output, 64, offset);
893
104
    write_int32(output, 68, writer->root.num_param_interns);
894
104
    memcpy(output + offset, writer->root.param_interns_data,
895
104
        writer->param_interns_data_offset);
896
104
    offset += MVM_ALIGN_SECTION(writer->param_interns_data_offset);
897
104
898
104
    /* Sanity check. */
899
104
    if (offset != output_size)
900
0
        MVM_exception_throw_adhoc(tc,
901
0
            "Serialization sanity check failed: offset != output_size");
902
104
903
104
    /* If we are compiling at present, then just stash the output for later
904
104
     * incorporation into the bytecode file. */
905
104
    if (tc->compiling_scs && MVM_repr_elems(tc, tc->compiling_scs) &&
906
0
            MVM_repr_at_pos_o(tc, tc->compiling_scs, 0) == (MVMObject *)writer->root.sc) {
907
0
        if (tc->serialized)
908
0
            MVM_free(tc->serialized);
909
0
        tc->serialized = output;
910
0
        tc->serialized_size = output_size;
911
0
        tc->serialized_string_heap = writer->root.string_heap;
912
0
        return NULL;
913
0
    }
914
104
915
104
    /* Base 64 encode. */
916
104
    output_b64 = base64_encode(output, output_size);
917
104
    MVM_free(output);
918
104
    if (output_b64 == NULL)
919
0
        MVM_exception_throw_adhoc(tc,
920
0
            "Serialization error: failed to convert to base64");
921
104
922
104
    /* Make a MVMString containing it. */
923
104
    result = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, output_b64);
924
104
    MVM_free(output_b64);
925
104
    return result;
926
104
}
927
928
/* Serializes the possibly-not-deserialized HOW. */
929
28
static void serialize_how_lazy(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMSTable *st) {
930
28
    if (st->HOW) {
931
28
        write_obj_ref(tc, writer, st->HOW);
932
28
    }
933
0
    else {
934
0
        MVMint32 sc_id = get_sc_id(tc, writer, st->HOW_sc);
935
0
        write_locate_sc_and_index(tc, writer, sc_id, st->HOW_idx);
936
0
    }
937
28
}
938
939
/* Adds an entry to the parameterized type intern section. */
940
static void add_param_intern(MVMThreadContext *tc, MVMSerializationWriter *writer,
941
1
                             MVMObject *type, MVMObject *ptype, MVMObject *params) {
942
1
    MVMint64 num_params, i;
943
1
944
1
    /* Save previous write buffer. */
945
1
    char      **orig_write_buffer = writer->cur_write_buffer;
946
1
    MVMuint32  *orig_write_offset = writer->cur_write_offset;
947
1
    MVMuint32  *orig_write_limit  = writer->cur_write_limit;
948
1
949
1
    /* Switch to intern data buffer. */
950
1
    writer->cur_write_buffer = &(writer->root.param_interns_data);
951
1
    writer->cur_write_offset = &(writer->param_interns_data_offset);
952
1
    writer->cur_write_limit  = &(writer->param_interns_data_alloc);
953
1
954
1
    /* Parametric type object reference. */
955
1
    write_obj_ref(tc, writer, ptype);
956
1
957
1
    /* Indexes in this SC of type object and STable. */
958
1
    expand_storage_if_needed(tc, writer, 12);
959
1
    if (MVM_sc_get_obj_sc(tc, type) != writer->root.sc)
960
0
        MVM_exception_throw_adhoc(tc,
961
0
            "Serialization error: parameterized type to intern not in current SC");
962
1
    write_int32(*(writer->cur_write_buffer), *(writer->cur_write_offset),
963
1
        MVM_sc_find_object_idx(tc, writer->root.sc, type));
964
1
    *(writer->cur_write_offset) += 4;
965
1
    if (MVM_sc_get_stable_sc(tc, STABLE(type)) != writer->root.sc)
966
0
        MVM_exception_throw_adhoc(tc,
967
0
            "Serialization error: STable of parameterized type to intern not in current SC");
968
1
    write_int32(*(writer->cur_write_buffer), *(writer->cur_write_offset),
969
1
        MVM_sc_find_stable_idx(tc, writer->root.sc, STABLE(type)));
970
1
    *(writer->cur_write_offset) += 4;
971
1
972
1
    /* Write parameter count and parameter object refs. */
973
1
    num_params = MVM_repr_elems(tc, params);
974
1
    write_int32(*(writer->cur_write_buffer), *(writer->cur_write_offset),
975
1
        (MVMint32)num_params);
976
1
    *(writer->cur_write_offset) += 4;
977
2
    for (i = 0; i < num_params; i++)
978
1
        write_obj_ref(tc, writer, MVM_repr_at_pos_o(tc, params, i));
979
1
980
1
    /* Increment number of parameterization interns. */
981
1
    writer->root.num_param_interns++;
982
1
983
1
    /* Restore original output buffer. */
984
1
    writer->cur_write_buffer = orig_write_buffer;
985
1
    writer->cur_write_offset = orig_write_offset;
986
1
    writer->cur_write_limit  = orig_write_limit;
987
1
}
988
989
/* This handles the serialization of an STable, and calls off to serialize
990
 * its representation data also. */
991
992
28
static void serialize_stable(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMSTable *st) {
993
28
    MVMint64  i;
994
28
    MVMuint8  flags;
995
28
996
28
    /* Ensure there's space in the STables table; grow if not. */
997
28
    MVMint32 offset = writer->root.num_stables * STABLES_TABLE_ENTRY_SIZE;
998
28
    if (offset + STABLES_TABLE_ENTRY_SIZE > writer->stables_table_alloc) {
999
0
        GROW_TABLE(writer->root.stables_table, writer->stables_table_alloc);
1000
0
    }
1001
28
1002
28
    /* Make STables table entry. */
1003
28
    write_int32(writer->root.stables_table, offset, add_string_to_heap(tc, writer, tc->instance->repr_list[st->REPR->ID]->name));
1004
28
    write_int32(writer->root.stables_table, offset + 4, writer->stables_data_offset);
1005
28
1006
28
    /* Increment count of stables in the table. */
1007
28
    writer->root.num_stables++;
1008
28
1009
28
    /* Make sure we're going to write to the correct place. */
1010
28
    writer->cur_write_buffer = &(writer->root.stables_data);
1011
28
    writer->cur_write_offset = &(writer->stables_data_offset);
1012
28
    writer->cur_write_limit  = &(writer->stables_data_alloc);
1013
28
1014
28
    /* Write HOW, WHAT and WHO. */
1015
28
    serialize_how_lazy(tc, writer, st);
1016
28
    write_obj_ref(tc, writer, st->WHAT);
1017
28
    MVM_serialization_write_ref(tc, writer, st->WHO);
1018
28
1019
28
    /* Method cache and v-table. */
1020
28
    if (!st->method_cache)
1021
8
        MVM_serialization_finish_deserialize_method_cache(tc, st);
1022
28
    MVM_serialization_write_ref(tc, writer, st->method_cache);
1023
28
1024
28
    /* Type check cache. */
1025
28
    MVM_serialization_write_int(tc, writer, st->type_check_cache_length);
1026
62
    for (i = 0; i < st->type_check_cache_length; i++)
1027
34
        MVM_serialization_write_ref(tc, writer, st->type_check_cache[i]);
1028
28
1029
28
    /* Mode flags.
1030
28
       These are stored as MVMuint16, but currently only the bottom 6 bits are
1031
28
       used. Whilst we could store these as 2 bytes, we don't actually gain any
1032
28
       future-proofing from that, because if we start assigning meaning to
1033
28
       currently unused bits, then we have to bump the serialisation version
1034
28
       *anyway*. If we didn't, older readers would encounter files with the
1035
28
       newly used bits, but ignore whatever semantics those bits were meant to
1036
28
       convey, and hence show buggy behaviour. And if we're bumping the
1037
28
       serialisation version, then we can increase the storage size.  */
1038
28
    if (st->mode_flags > 255) {
1039
0
        MVM_exception_throw_adhoc(tc,
1040
0
                                  "Serialization error: mode_flags %u out of range and can't be serialized",
1041
0
                                  st->mode_flags);
1042
0
    }
1043
28
    expand_storage_if_needed(tc, writer, 1);
1044
28
    *(*(writer->cur_write_buffer) + *(writer->cur_write_offset)) = st->mode_flags;
1045
28
    ++*(writer->cur_write_offset);
1046
28
1047
28
    /* Boolification spec. */
1048
28
    /* As this only needs 4 bits, also use the same byte to store various
1049
28
       NULL/not-NULL flag bits. */
1050
28
    if (st->boolification_spec) {
1051
15
        if (st->boolification_spec->mode >= 0xF) {
1052
0
            MVM_exception_throw_adhoc(tc,
1053
0
                                  "Serialization error: boolification spec mode %u out of range and can't be serialized",
1054
0
                                      st->boolification_spec->mode);
1055
0
        }
1056
15
        flags = st->boolification_spec->mode;
1057
13
    } else {
1058
13
        flags = 0xF;
1059
13
    }
1060
28
    if (st->container_spec != NULL)
1061
0
        flags |= STABLE_HAS_CONTAINER_SPEC;
1062
28
    if (st->invocation_spec != NULL)
1063
2
        flags |= STABLE_HAS_INVOCATION_SPEC;
1064
28
    if (st->hll_owner != NULL)
1065
1
        flags |= STABLE_HAS_HLL_OWNER;
1066
28
    if (st->hll_role != MVM_HLL_ROLE_NONE)
1067
1
        flags |= STABLE_HAS_HLL_ROLE;
1068
28
1069
28
    expand_storage_if_needed(tc, writer, 1);
1070
28
    *(*(writer->cur_write_buffer) + *(writer->cur_write_offset)) = flags;
1071
28
    ++*(writer->cur_write_offset);
1072
28
1073
28
    if (st->boolification_spec) {
1074
15
        MVM_serialization_write_ref(tc, writer, st->boolification_spec->method);
1075
15
    }
1076
28
1077
28
    /* Container spec. */
1078
28
    if (st->container_spec) {
1079
0
        /* Write container spec name. */
1080
0
        MVM_serialization_write_str(tc, writer,
1081
0
            MVM_string_ascii_decode_nt(tc, tc->instance->VMString,
1082
0
                st->container_spec->name));
1083
0
1084
0
        /* Give container spec a chance to serialize any data it wishes. */
1085
0
        st->container_spec->serialize(tc, st, writer);
1086
0
    }
1087
28
1088
28
    /* Invocation spec. */
1089
28
    if (st->invocation_spec) {
1090
2
        MVM_serialization_write_ref(tc, writer, st->invocation_spec->class_handle);
1091
2
        MVM_serialization_write_str(tc, writer, st->invocation_spec->attr_name);
1092
2
        MVM_serialization_write_int(tc, writer, st->invocation_spec->hint);
1093
2
        MVM_serialization_write_ref(tc, writer, st->invocation_spec->invocation_handler);
1094
2
        MVM_serialization_write_ref(tc, writer, st->invocation_spec->md_class_handle);
1095
2
        MVM_serialization_write_str(tc, writer, st->invocation_spec->md_cache_attr_name);
1096
2
        MVM_serialization_write_int(tc, writer, st->invocation_spec->md_cache_hint);
1097
2
        MVM_serialization_write_str(tc, writer, st->invocation_spec->md_valid_attr_name);
1098
2
        MVM_serialization_write_int(tc, writer, st->invocation_spec->md_valid_hint);
1099
2
    }
1100
28
1101
28
    /* HLL owner. */
1102
28
    if (st->hll_owner)
1103
1
        MVM_serialization_write_str(tc, writer, st->hll_owner->name);
1104
28
1105
28
    /* HLL role */
1106
28
    if (st->hll_role != MVM_HLL_ROLE_NONE) {
1107
1
        MVM_serialization_write_int(tc, writer, st->hll_role);
1108
1
    }
1109
28
1110
28
    /* If it's a parametric type, save parameterizer. */
1111
28
    if (st->mode_flags & MVM_PARAMETRIC_TYPE)
1112
2
        MVM_serialization_write_ref(tc, writer, st->paramet.ric.parameterizer);
1113
28
1114
28
    /* If it's a parameterized type, we may also need to make an intern table
1115
28
     * entry as well as writing out the parameter details. */
1116
28
    if (st->mode_flags & MVM_PARAMETERIZED_TYPE) {
1117
3
        MVMint64 i, num_params;
1118
3
1119
3
        /* To deserve an entry in the intern table, we need that both the type
1120
3
         * being parameterized and all of the arguments are from an SC other
1121
3
         * than the one we're currently serializing. Otherwise, there is no
1122
3
         * way the parameterized type in question could have been produced by
1123
3
         * another compilation unit. We keep a counter of things, which should
1124
3
         * add up to parameters + 1 if we need the intern entry. */
1125
3
        MVMuint32 internability = 0;
1126
3
1127
3
        /* Write a reference to the type being parameterized, and increment the
1128
3
         * internability if it's from a different SC (easier to check that after,
1129
3
         * as writing the ref will be sure to mark it as being in this one if it
1130
3
         * has no SC yet). */
1131
3
        MVMObject *ptype  = st->paramet.erized.parametric_type;
1132
3
        MVMObject *params = st->paramet.erized.parameters;
1133
3
        MVM_serialization_write_ref(tc, writer, ptype);
1134
3
        if (MVM_sc_get_obj_sc(tc, ptype) != writer->root.sc)
1135
1
            internability++;
1136
3
1137
3
        /* Write the parameters. We write them like an array, but an element at a
1138
3
         * time so we can check if an intern table entry is needed. */
1139
3
        num_params = MVM_repr_elems(tc, params);
1140
3
        /* This typically seems to have values between 1 and 3: */
1141
3
        MVM_serialization_write_int(tc, writer, num_params);
1142
6
        for (i = 0; i < num_params; i++) {
1143
3
            /* Save where we were before writing this parameter. */
1144
3
            size_t pre_write_mark = *(writer->cur_write_offset);
1145
3
1146
3
            /* Write parameter. */
1147
3
            MVMObject *parameter = MVM_repr_at_pos_o(tc, params, i);
1148
3
            MVM_serialization_write_ref(tc, writer, parameter);
1149
3
1150
3
            /* If what we write was an object reference and it's from another
1151
3
             * SC, add to the internability count. */
1152
3
            if (*(*(writer->cur_write_buffer) + pre_write_mark) == REFVAR_OBJECT)
1153
2
                if (MVM_sc_get_obj_sc(tc, parameter) != writer->root.sc)
1154
2
                    internability++;
1155
3
        }
1156
3
1157
3
        /* Make intern table entry if needed. */
1158
3
        if (internability == num_params + 1)
1159
1
            add_param_intern(tc, writer, st->WHAT, ptype, params);
1160
3
    }
1161
28
1162
28
    MVM_serialization_write_cstr(tc, writer, MVM_6model_get_stable_debug_name(tc, st));
1163
28
1164
28
    /* Store offset we save REPR data at. */
1165
28
    write_int32(writer->root.stables_table, offset + 8, writer->stables_data_offset);
1166
28
1167
28
    /* If the REPR has a function to serialize representation data, call it. */
1168
28
    if (st->REPR->serialize_repr_data)
1169
21
        st->REPR->serialize_repr_data(tc, st, writer);
1170
28
}
1171
1172
/* This handles the serialization of an object, which largely involves a
1173
 * delegation to its representation. */
1174
343
static void serialize_object(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *obj) {
1175
343
    MVMint32 offset;
1176
343
1177
343
    /* Get index of SC that holds the STable and its index. */
1178
343
    MVMuint32 sc;
1179
343
    MVMuint32 sc_idx;
1180
343
    MVMuint32 packed;
1181
343
    get_stable_ref_info(tc, writer, STABLE(obj), &sc, &sc_idx);
1182
343
1183
343
    /* Ensure there's space in the objects table; grow if not. */
1184
343
    offset = writer->root.num_objects * OBJECTS_TABLE_ENTRY_SIZE;
1185
343
    if (offset + OBJECTS_TABLE_ENTRY_SIZE > writer->objects_table_alloc) {
1186
107
        GROW_TABLE(writer->root.objects_table, writer->objects_table_alloc);
1187
107
    }
1188
343
1189
343
    /* Increment count of objects in the table. */
1190
343
    writer->root.num_objects++;
1191
343
1192
343
    /* Make sure we're going to write repr data to the correct place. */
1193
343
    writer->cur_write_buffer = &(writer->root.objects_data);
1194
343
    writer->cur_write_offset = &(writer->objects_data_offset);
1195
343
    writer->cur_write_limit  = &(writer->objects_data_alloc);
1196
343
1197
343
    packed = IS_CONCRETE(obj) ? OBJECTS_TABLE_ENTRY_IS_CONCRETE : 0;
1198
343
1199
343
    if (sc <= OBJECTS_TABLE_ENTRY_SC_MAX && sc_idx <= OBJECTS_TABLE_ENTRY_SC_IDX_MAX) {
1200
343
        packed |= (sc << OBJECTS_TABLE_ENTRY_SC_SHIFT) | sc_idx;
1201
0
    } else {
1202
0
        packed |= OBJECTS_TABLE_ENTRY_SC_OVERFLOW << OBJECTS_TABLE_ENTRY_SC_SHIFT;
1203
0
1204
0
        expand_storage_if_needed(tc, writer, 8);
1205
0
        write_int32(*(writer->cur_write_buffer), *(writer->cur_write_offset), sc);
1206
0
        *(writer->cur_write_offset) += 4;
1207
0
        write_int32(*(writer->cur_write_buffer), *(writer->cur_write_offset), sc_idx);
1208
0
        *(writer->cur_write_offset) += 4;
1209
0
    }
1210
343
1211
343
    /* Make objects table entry. */
1212
343
    write_int32(writer->root.objects_table, offset + 0, packed);
1213
343
    write_int32(writer->root.objects_table, offset + 4, writer->objects_data_offset);
1214
343
1215
343
    /* Delegate to its serialization REPR function. */
1216
343
    if (IS_CONCRETE(obj)) {
1217
316
        if (REPR(obj)->serialize)
1218
316
            REPR(obj)->serialize(tc, STABLE(obj), OBJECT_BODY(obj), writer);
1219
316
        else
1220
0
            MVM_exception_throw_adhoc(tc,
1221
0
                "Missing serialize REPR function for REPR %s (%s)", REPR(obj)->name, MVM_6model_get_debug_name(tc, obj));
1222
316
    }
1223
343
}
1224
1225
/* This handles the serialization of a context, which means serializing
1226
 * the stuff in its lexpad. */
1227
2
static void serialize_context(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMFrame *frame) {
1228
2
    MVMint32 i, offset, static_sc_id, static_idx;
1229
2
1230
2
    /* Grab lexpad, which we'll serialize later on. */
1231
2
    MVMStaticFrame *sf   = frame->static_info;
1232
2
    MVMLexicalRegistry **lexnames = sf->body.lexical_names_list;
1233
2
1234
2
    /* Locate the static code ref this context points to. */
1235
2
    MVMObject *static_code_ref = closure_to_static_code_ref(tc, frame->code_ref, 1);
1236
2
    MVMSerializationContext *static_code_sc  = MVM_sc_get_obj_sc(tc, static_code_ref);
1237
2
    if (OBJ_IS_NULL(static_code_sc))
1238
0
        MVM_exception_throw_adhoc(tc,
1239
0
            "Serialization Error: closure outer is a code object not in an SC");
1240
2
    static_sc_id = get_sc_id(tc, writer, static_code_sc);
1241
2
    static_idx   = (MVMint32)MVM_sc_find_code_idx(tc, static_code_sc, static_code_ref);
1242
2
1243
2
    /* Ensure there's space in the STables table; grow if not. */
1244
2
    offset = writer->root.num_contexts * CONTEXTS_TABLE_ENTRY_SIZE;
1245
2
    if (offset + CONTEXTS_TABLE_ENTRY_SIZE > writer->contexts_table_alloc) {
1246
0
        GROW_TABLE(writer->root.contexts_table, writer->contexts_table_alloc);
1247
0
    }
1248
2
1249
2
    /* Make contexts table entry. */
1250
2
    write_int32(writer->root.contexts_table, offset, static_sc_id);
1251
2
    write_int32(writer->root.contexts_table, offset + 4, static_idx);
1252
2
    write_int32(writer->root.contexts_table, offset + 8, writer->contexts_data_offset);
1253
2
1254
2
    /* See if there's any relevant outer context, and if so set it up to
1255
2
     * be serialized. */
1256
2
    if (frame->outer)
1257
2
        write_int32(writer->root.contexts_table, offset + 12,
1258
2
            get_serialized_context_idx(tc, writer, frame->outer, NULL));
1259
2
    else
1260
0
        write_int32(writer->root.contexts_table, offset + 12, 0);
1261
2
1262
2
    /* Increment count of stables in the table. */
1263
2
    writer->root.num_contexts++;
1264
2
1265
2
    /* Set up writer. */
1266
2
    writer->cur_write_buffer = &(writer->root.contexts_data);
1267
2
    writer->cur_write_offset = &(writer->contexts_data_offset);
1268
2
    writer->cur_write_limit  = &(writer->contexts_data_alloc);
1269
2
1270
2
    /* Serialize lexicals. */
1271
2
    MVM_serialization_write_int(tc, writer, sf->body.num_lexicals);
1272
4
    for (i = 0; i < sf->body.num_lexicals; i++) {
1273
2
        MVM_serialization_write_str(tc, writer, lexnames[i]->key);
1274
2
        switch (sf->body.lexical_types[i]) {
1275
0
            case MVM_reg_int8:
1276
0
            case MVM_reg_int16:
1277
0
            case MVM_reg_int32:
1278
0
                MVM_exception_throw_adhoc(tc, "unsupported lexical type %s", MVM_reg_get_debug_name(tc, sf->body.lexical_types[i]));
1279
0
                break;
1280
0
            case MVM_reg_int64:
1281
0
                MVM_serialization_write_int(tc, writer, frame->env[i].i64);
1282
0
                break;
1283
0
            case MVM_reg_num32:
1284
0
                MVM_exception_throw_adhoc(tc, "unsupported lexical type %s", MVM_reg_get_debug_name(tc, sf->body.lexical_types[i]));
1285
0
                break;
1286
0
            case MVM_reg_num64:
1287
0
                MVM_serialization_write_num(tc, writer, frame->env[i].n64);
1288
0
                break;
1289
0
            case MVM_reg_str:
1290
0
                MVM_serialization_write_str(tc, writer, frame->env[i].s);
1291
0
                break;
1292
2
            case MVM_reg_obj:
1293
2
                MVM_serialization_write_ref(tc, writer, frame->env[i].o);
1294
2
                break;
1295
0
            default:
1296
0
                MVM_exception_throw_adhoc(tc, "unsupported lexical type %s", MVM_reg_get_debug_name(tc, sf->body.lexical_types[i]));
1297
0
                break;
1298
2
        }
1299
2
    }
1300
2
}
1301
1302
/* Goes through the list of repossessions and serializes them all. */
1303
104
static void serialize_repossessions(MVMThreadContext *tc, MVMSerializationWriter *writer) {
1304
104
    MVMint64 i;
1305
104
1306
104
    /* Obtain list of repossession object indexes and original SCs. */
1307
104
    MVMObject *rep_indexes = writer->root.sc->body->rep_indexes;
1308
104
    MVMObject *rep_scs     = writer->root.sc->body->rep_scs;
1309
104
1310
104
    /* Allocate table space, provided we've actually something to do. */
1311
104
    writer->root.num_repos = MVM_repr_elems(tc, rep_indexes);
1312
104
    if (writer->root.num_repos == 0)
1313
100
        return;
1314
4
    writer->root.repos_table = (char *)MVM_malloc(writer->root.num_repos * REPOS_TABLE_ENTRY_SIZE);
1315
4
1316
4
    /* Make entries. */
1317
8
    for (i = 0; i < writer->root.num_repos; i++) {
1318
4
        MVMint32 offset  = (MVMint32)(i * REPOS_TABLE_ENTRY_SIZE);
1319
4
        MVMint32 obj_idx = (MVMint32)(MVM_repr_at_pos_i(tc, rep_indexes, i) >> 1);
1320
4
        MVMint32 is_st   = MVM_repr_at_pos_i(tc, rep_indexes, i) & 1;
1321
4
        MVMSerializationContext *orig_sc =
1322
4
            (MVMSerializationContext *)MVM_repr_at_pos_o(tc, rep_scs, i);
1323
4
1324
4
        /* Work out original object's SC location. */
1325
4
        MVMint32 orig_sc_id = get_sc_id(tc, writer, orig_sc);
1326
4
        MVMint32 orig_idx   = (MVMint32)(is_st
1327
1
            ? MVM_sc_find_stable_idx(tc, orig_sc, writer->root.sc->body->root_stables[obj_idx])
1328
3
            : MVM_sc_find_object_idx(tc, orig_sc, writer->root.sc->body->root_objects[obj_idx]));
1329
4
1330
4
        /* Write table row. */
1331
4
        write_int32(writer->root.repos_table, offset, is_st);
1332
4
        write_int32(writer->root.repos_table, offset + 4, obj_idx);
1333
4
        write_int32(writer->root.repos_table, offset + 8, orig_sc_id);
1334
4
        write_int32(writer->root.repos_table, offset + 12, orig_idx);
1335
4
    }
1336
4
}
1337
1338
104
static void serialize(MVMThreadContext *tc, MVMSerializationWriter *writer) {
1339
104
    MVMuint32 work_todo;
1340
305
    do {
1341
305
        /* Current work list sizes. */
1342
305
        MVMuint64 stables_todo  = writer->root.sc->body->num_stables;
1343
305
        MVMuint64 objects_todo  = writer->root.sc->body->num_objects;
1344
305
        MVMuint64 contexts_todo = writer->num_contexts;
1345
305
1346
305
        /* Reset todo flag - if we do some work we'll go round again as it
1347
305
         * may have generated more. */
1348
305
        work_todo = 0;
1349
305
1350
305
        /* Serialize any STables on the todo list. */
1351
333
        while (writer->stables_list_pos < stables_todo) {
1352
28
            serialize_stable(tc, writer, writer->root.sc->body->root_stables[writer->stables_list_pos]);
1353
28
            writer->stables_list_pos++;
1354
28
            work_todo = 1;
1355
28
        }
1356
305
1357
305
        /* Serialize any objects on the todo list. */
1358
648
        while (writer->objects_list_pos < objects_todo) {
1359
343
            serialize_object(tc, writer,
1360
343
                writer->root.sc->body->root_objects[writer->objects_list_pos]);
1361
343
            writer->objects_list_pos++;
1362
343
            work_todo = 1;
1363
343
        }
1364
305
1365
305
        /* Serialize any contexts on the todo list. */
1366
307
        while (writer->contexts_list_pos < contexts_todo) {
1367
2
            serialize_context(tc, writer, writer->contexts_list[writer->contexts_list_pos]);
1368
2
            writer->contexts_list_pos++;
1369
2
            work_todo = 1;
1370
2
        }
1371
305
    } while (work_todo);
1372
104
1373
104
    /* Finally, serialize repossessions table (this can't make any more
1374
104
     * work, so is done as a separate step here at the end). */
1375
104
    serialize_repossessions(tc, writer);
1376
104
}
1377
1378
104
MVMString * MVM_serialization_serialize(MVMThreadContext *tc, MVMSerializationContext *sc, MVMObject *empty_string_heap) {
1379
104
    MVMSerializationWriter *writer;
1380
104
    MVMString *result   = NULL;
1381
104
    MVMint32   sc_elems = (MVMint32)sc->body->num_objects;
1382
104
1383
104
    /* We don't sufficiently root things in here for the GC, so enforce gen2
1384
104
     * allocation. */
1385
104
    MVM_gc_allocate_gen2_default_set(tc);
1386
104
1387
104
    /* Set up writer with some initial settings. */
1388
104
    writer                      = MVM_calloc(1, sizeof(MVMSerializationWriter));
1389
104
    writer->root.version        = CURRENT_VERSION;
1390
104
    writer->root.sc             = sc;
1391
104
    writer->codes_list          = sc->body->root_codes;
1392
104
    writer->root.string_heap    = empty_string_heap;
1393
104
    writer->root.dependent_scs  = MVM_calloc(1, sizeof(MVMSerializationContext *));
1394
104
    writer->seen_strings        = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);
1395
104
1396
104
    /* Allocate initial memory space for storing serialized tables and data. */
1397
104
    writer->dependencies_table_alloc = DEP_TABLE_ENTRY_SIZE * 4;
1398
104
    writer->root.dependencies_table  = (char *)MVM_calloc(1, writer->dependencies_table_alloc);
1399
104
    writer->stables_table_alloc      = STABLES_TABLE_ENTRY_SIZE * STABLES_TABLE_ENTRIES_GUESS;
1400
104
    writer->root.stables_table       = (char *)MVM_calloc(1, writer->stables_table_alloc);
1401
104
    writer->objects_table_alloc      = OBJECTS_TABLE_ENTRY_SIZE * MAX(sc_elems, 1);
1402
104
    writer->root.objects_table       = (char *)MVM_calloc(1, writer->objects_table_alloc);
1403
104
    writer->stables_data_alloc       = DEFAULT_STABLE_DATA_SIZE;
1404
104
    writer->root.stables_data        = (char *)MVM_calloc(1, writer->stables_data_alloc);
1405
104
    writer->objects_data_alloc       = OBJECT_SIZE_GUESS * MAX(sc_elems, 1);
1406
104
    writer->root.objects_data        = (char *)MVM_calloc(1, writer->objects_data_alloc);
1407
104
    writer->closures_table_alloc     = CLOSURES_TABLE_ENTRY_SIZE * CLOSURES_TABLE_ENTRIES_GUESS;
1408
104
    writer->root.closures_table      = (char *)MVM_calloc(1, writer->closures_table_alloc);
1409
104
    writer->contexts_table_alloc     = CONTEXTS_TABLE_ENTRY_SIZE * CONTEXTS_TABLE_ENTRIES_GUESS;
1410
104
    writer->root.contexts_table      = (char *)MVM_calloc(1, writer->contexts_table_alloc);
1411
104
    writer->contexts_data_alloc      = DEFAULT_CONTEXTS_DATA_SIZE;
1412
104
    writer->root.contexts_data       = (char *)MVM_calloc(1, writer->contexts_data_alloc);
1413
104
    writer->param_interns_data_alloc = DEFAULT_PARAM_INTERNS_DATA_SIZE;
1414
104
    writer->root.param_interns_data  = (char *)MVM_calloc(1, writer->param_interns_data_alloc);
1415
104
1416
104
    /* Initialize MVMString heap so first entry is the NULL MVMString. */
1417
104
    MVM_repr_push_s(tc, empty_string_heap, NULL);
1418
104
1419
104
    /* Start serializing. */
1420
104
    serialize(tc, writer);
1421
104
1422
104
    /* Build a single result out of the serialized data; note if we're in the
1423
104
     * compiler pipeline this will return null and stash the output to write
1424
104
     * to a bytecode file later. */
1425
104
    result = concatenate_outputs(tc, writer);
1426
104
1427
104
    /* Clear up afterwards. */
1428
104
    MVM_free(writer->contexts_list);
1429
104
    MVM_free(writer->root.dependent_scs);
1430
104
    MVM_free(writer->root.dependencies_table);
1431
104
    MVM_free(writer->root.stables_table);
1432
104
    MVM_free(writer->root.stables_data);
1433
104
    MVM_free(writer->root.objects_table);
1434
104
    MVM_free(writer->root.objects_data);
1435
104
    MVM_free(writer->root.closures_table);
1436
104
    MVM_free(writer->root.contexts_table);
1437
104
    MVM_free(writer->root.contexts_data);
1438
104
    MVM_free(writer->root.param_interns_data);
1439
104
    MVM_free(writer->root.repos_table);
1440
104
    MVM_free(writer);
1441
104
1442
104
    /* Exit gen2 allocation. */
1443
104
    MVM_gc_allocate_gen2_default_clear(tc);
1444
104
1445
104
    return result;
1446
104
}
1447
1448
1449
/* ***************************************************************************
1450
 * Deserialization (reading related)
1451
 * ***************************************************************************/
1452
1453
/* Reads an int64 from a buffer. */
1454
0
static MVMint64 read_int64(const char *buffer, size_t offset) {
1455
0
    MVMint64 value;
1456
0
    memcpy(&value, buffer + offset, 8);
1457
0
#ifdef MVM_BIGENDIAN
1458
    switch_endian(&value, 8);
1459
#endif
1460
0
    return value;
1461
0
}
1462
1463
/* Reads an int32 from a buffer. */
1464
2.07M
static MVMint32 read_int32(const char *buffer, size_t offset) {
1465
2.07M
    MVMint32 value;
1466
2.07M
    memcpy(&value, buffer + offset, 4);
1467
2.07M
#ifdef MVM_BIGENDIAN
1468
    switch_endian(&value, 4);
1469
#endif
1470
2.07M
    return value;
1471
2.07M
}
1472
1473
1.63M
static MVMuint16 read_uint16(const char *buffer, size_t offset) {
1474
1.63M
    MVMuint16 value;
1475
1.63M
    memcpy(&value, buffer + offset, 2);
1476
1.63M
#ifdef MVM_BIGENDIAN
1477
    switch_endian(&value, 2);
1478
#endif
1479
1.63M
    return value;
1480
1.63M
}
1481
1482
/* Reads double from a buffer. */
1483
191k
static MVMnum64 read_double(const char *buffer, size_t offset) {
1484
191k
    MVMnum64 value;
1485
191k
    memcpy(&value, buffer + offset, 8);
1486
191k
#ifdef MVM_BIGENDIAN
1487
    switch_endian(&value, 8);
1488
#endif
1489
191k
    return value;
1490
191k
}
1491
1492
/* If deserialization should fail, cleans up before throwing an exception. */
1493
MVM_NO_RETURN static void fail_deserialize(MVMThreadContext *tc, char **waste, MVMSerializationReader *reader,
1494
                             const char *messageFormat, ...) MVM_NO_RETURN_ATTRIBUTE MVM_FORMAT(printf, 4, 5);
1495
MVM_NO_RETURN static void fail_deserialize(MVMThreadContext *tc, char **waste, MVMSerializationReader *reader,
1496
0
        const char *messageFormat, ...) {
1497
0
    va_list args;
1498
0
    if (reader->data_needs_free && reader->data)
1499
0
        MVM_free(reader->data);
1500
0
    if (reader->contexts)
1501
0
        MVM_free(reader->contexts);
1502
0
    if (reader->root.sc)
1503
0
        reader->root.sc->body->sr = NULL;
1504
0
    if (reader->root.dependent_scs)
1505
0
        MVM_free(reader->root.dependent_scs);
1506
0
    MVM_free(reader);
1507
0
    MVM_gc_allocate_gen2_default_clear(tc);
1508
0
    va_start(args, messageFormat);
1509
0
    MVM_exception_throw_adhoc_free_va(tc, waste, messageFormat, args);
1510
0
    va_end(args);
1511
0
}
1512
1513
/* Reads the item from the string heap at the specified index. */
1514
1.10M
static MVMString * read_string_from_heap(MVMThreadContext *tc, MVMSerializationReader *reader, MVMuint32 idx) {
1515
1.10M
    if (reader->root.string_heap) {
1516
298
        if (idx < MVM_repr_elems(tc, reader->root.string_heap))
1517
298
            return MVM_repr_at_pos_s(tc, reader->root.string_heap, idx);
1518
298
        else
1519
0
            fail_deserialize(tc, NULL, reader,
1520
0
                "Attempt to read past end of string heap (index %d)", idx);
1521
298
    }
1522
1.10M
    else {
1523
1.10M
        MVMCompUnit *cu = reader->root.string_comp_unit;
1524
1.10M
        if (idx == 0)
1525
0
            return NULL;
1526
1.10M
        idx--;
1527
1.10M
        if (idx < cu->body.num_strings)
1528
1.10M
            return MVM_cu_string(tc, cu, idx);
1529
1.10M
        else
1530
0
            fail_deserialize(tc, NULL, reader,
1531
0
                "Attempt to read past end of compilation unit string heap (index %d)", idx);
1532
1.10M
    }
1533
1.10M
}
1534
1535
/* Locates a serialization context; 0 is the current one, otherwise see the
1536
 * dependencies table. */
1537
2.67M
static MVMSerializationContext * locate_sc(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 sc_id) {
1538
2.67M
    MVMSerializationContext *sc;
1539
2.67M
    if (sc_id == 0)
1540
1.54M
        sc = reader->root.sc;
1541
1.12M
    else if (sc_id > 0 && sc_id - 1 < reader->root.num_dependencies)
1542
1.12M
        sc = reader->root.dependent_scs[sc_id - 1];
1543
1.12M
    else
1544
0
        fail_deserialize(tc, NULL, reader,
1545
0
            "Invalid dependencies table index encountered (index %d)", sc_id);
1546
2.67M
    return sc;
1547
2.67M
}
1548
1549
/* Ensure that we aren't going to read off the end of the buffer. */
1550
8.27M
MVM_STATIC_INLINE void assert_can_read(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 amount) {
1551
8.27M
    char *read_end = *(reader->cur_read_buffer) + *(reader->cur_read_offset) + amount;
1552
8.27M
    if (read_end > *(reader->cur_read_end))
1553
0
        fail_deserialize(tc, NULL, reader,
1554
0
            "Read past end of serialization data buffer");
1555
8.27M
}
1556
1557
/* Reading function for native integers. */
1558
0
MVMint64 MVM_serialization_read_int64(MVMThreadContext *tc, MVMSerializationReader *reader) {
1559
0
    MVMint64 result;
1560
0
    assert_can_read(tc, reader, 8);
1561
0
    result = read_int64(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1562
0
    *(reader->cur_read_offset) += 8;
1563
0
    return result;
1564
0
}
1565
1566
/* Reading function for variable-sized integers, using between 1 and 9 bytes of
1567
 * storage for an int64.
1568
 *
1569
 * The format chosen may not be quite the most space efficient for the values
1570
 * that we store, but the intent it is that close to smallest whilst very
1571
 * efficient to read. In particular, it doesn't require any looping, and
1572
 * has at most two length overrun checks.  */
1573
1574
6.41M
MVMint64 MVM_serialization_read_int(MVMThreadContext *tc, MVMSerializationReader *reader) {
1575
6.41M
    MVMint64 result;
1576
6.41M
    const MVMuint8 *read_at = (MVMuint8 *) *(reader->cur_read_buffer) + *(reader->cur_read_offset);
1577
6.41M
    MVMuint8 *const read_end = (MVMuint8 *) *(reader->cur_read_end);
1578
6.41M
    MVMuint8 first;
1579
6.41M
    MVMuint8 need;
1580
6.41M
1581
6.41M
    if (read_at >= read_end)
1582
0
        fail_deserialize(tc, NULL, reader,
1583
0
                         "Read past end of serialization data buffer");
1584
6.41M
1585
6.41M
    first = *read_at++;
1586
6.41M
1587
6.41M
    /* Top bit set means remaining 7 bits are a value between -1 and 126.
1588
6.41M
       (That turns out to be the most common 7 bit range that we serialize.)  */
1589
6.41M
    if (first & 0x80) {
1590
4.41M
        *(reader->cur_read_offset) += 1;
1591
4.41M
        /* Value we have is 128 to 255. Map it back to the range we need:  */
1592
4.41M
        return (MVMint64) first - 129;
1593
4.41M
    }
1594
6.41M
1595
6.41M
    /* Otherwise next 3 bits indicate how many more bytes follow. */
1596
1.99M
    need = first >> 4;
1597
1.99M
    if (!need) {
1598
55
        /* Have to read all 8 bytes. Ignore the bottom nybble.
1599
55
           In future, we may want to use it to also store 15 possible "common"
1600
55
           values. Not clear if that whould be best as a fixed table, a single
1601
55
           table sent as part of the serialization blob, or multiple tables for
1602
55
           different contexts (int32, int64, nativeint, others?)  */
1603
55
        if (read_at + 8 > read_end)
1604
0
            fail_deserialize(tc, NULL, reader,
1605
0
                             "Read past end of serialization data buffer");
1606
55
#ifdef MVM_CAN_UNALIGNED_INT64
1607
55
        *((MVMuint64*)&result) = *((MVMuint64*)read_at);
1608
55
#else
1609
        memcpy(&result, read_at, 8);
1610
#endif
1611
55
#ifdef MVM_BIGENDIAN
1612
        switch_endian(&result, 8);
1613
#endif
1614
55
        *(reader->cur_read_offset) += 9;
1615
55
        return result;
1616
55
    }
1617
1.99M
1618
1.99M
    if (read_at + need > read_end)
1619
0
        fail_deserialize(tc, NULL, reader,
1620
0
                         "Read past end of serialization data buffer");
1621
1.99M
1622
1.99M
    /* The bottom nybble of the first byte is the highest byte of the final
1623
1.99M
       value with any bits set. Right now the top nybble is garbage, but it
1624
1.99M
       gets flushed away with the sign extension shifting later.  */
1625
1.99M
    result = (MVMint64)first << 8 * need;
1626
1.99M
1627
1.99M
    /* The remaining 1 to 7 lower bytes follow next in the serialization stream.
1628
1.99M
     */
1629
1.99M
#ifdef MVM_BIGENDIAN
1630
    {
1631
        MVMuint8 *write_to = (MVMuint8 *)&result + 8 - need;
1632
        memcpy(write_to, read_at, need);
1633
        switch_endian(write_to, need);
1634
    }
1635
#else
1636
1.99M
#   ifdef MVM_CAN_UNALIGNED_INT64
1637
1.99M
    /* GCC and Clang both optimize this */
1638
1.99M
    switch (MVM_EXPECT(need, 2)) {
1639
105
        case 7:
1640
105
            ((MVMuint8*)&result)[6] = read_at[6];
1641
209
        case 6:
1642
209
            ((MVMuint8*)&result)[5] = read_at[5];
1643
313
        case 5:
1644
313
            ((MVMuint8*)&result)[4] = read_at[4];
1645
417
        case 4:
1646
417
            ((MVMuint8*)&result)[3] = read_at[3];
1647
486k
        case 3:
1648
486k
            ((MVMuint8*)&result)[2] = read_at[2];
1649
898k
        case 2:
1650
898k
            ((MVMuint8*)&result)[1] = read_at[1];
1651
1.99M
        case 1:
1652
1.99M
            ((MVMuint8*)&result)[0] = read_at[0];
1653
1.99M
            break;
1654
1.99M
    }
1655
1.99M
#   else
1656
    memcpy(&result, read_at, need);
1657
#   endif
1658
1.99M
#endif
1659
1.99M
1660
1.99M
    /* Having pieced the (unsigned) value back together, sign extend it:  */
1661
1.99M
    result = result << (64 - 4 - 8 * need);
1662
1.99M
    result = result >> (64 - 4 - 8 * need);
1663
1.99M
1664
1.99M
    *(reader->cur_read_offset) += need + 1;
1665
1.99M
    return result;
1666
1.99M
}
1667
1668
/* Reading function for native numbers. */
1669
191k
MVMnum64 MVM_serialization_read_num(MVMThreadContext *tc, MVMSerializationReader *reader) {
1670
191k
    MVMnum64 result;
1671
191k
    assert_can_read(tc, reader, 8);
1672
191k
    result = read_double(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1673
191k
    *(reader->cur_read_offset) += 8;
1674
191k
    return result;
1675
191k
}
1676
1677
/* Reading function for native strings.
1678
 *
1679
 * BEWARE - logic in this function is partly duplicated in the skip calculations
1680
 * of deserialize_method_cache_lazy(). See the note before
1681
 * MVM_serialization_read_ref(). */
1682
1.07M
MVMString * MVM_serialization_read_str(MVMThreadContext *tc, MVMSerializationReader *reader) {
1683
1.07M
    MVMint32 offset;
1684
1.07M
1685
1.07M
    assert_can_read(tc, reader, 2);
1686
1.07M
    offset = read_uint16(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1687
1.07M
    *(reader->cur_read_offset) += 2;
1688
1.07M
    if (offset & STRING_HEAP_LOC_PACKED_OVERFLOW) {
1689
0
        assert_can_read(tc, reader, 2);
1690
0
        offset ^= STRING_HEAP_LOC_PACKED_OVERFLOW;
1691
0
        offset <<= STRING_HEAP_LOC_PACKED_SHIFT;
1692
0
        offset |= read_uint16(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1693
0
        *(reader->cur_read_offset) += 2;
1694
0
    }
1695
1.07M
    return read_string_from_heap(tc, reader, offset);
1696
1.07M
}
1697
1698
/* Reading function for null-terminated char array strings */
1699
25.4k
char *MVM_serialization_read_cstr(MVMThreadContext *tc, MVMSerializationReader *reader) {
1700
25.4k
    MVMint64 len = MVM_serialization_read_int(tc, reader);
1701
25.4k
    char *strbuf = 0;
1702
25.4k
    if (len > 0) {
1703
25.4k
        const MVMuint8 *read_at = (MVMuint8 *) *(reader->cur_read_buffer) + *(reader->cur_read_offset);
1704
25.4k
        assert_can_read(tc, reader, len);
1705
25.4k
        strbuf = MVM_malloc(len + 1);
1706
25.4k
        if (strbuf == 0)
1707
0
            fail_deserialize(tc, NULL, reader, "Cannot read a c string: malloc failed.");
1708
25.4k
        memcpy(strbuf, read_at, len);
1709
25.4k
        strbuf[len] = 0;
1710
25.4k
        *(reader->cur_read_offset) += len;
1711
6
    } else if (len < 0) {
1712
0
        fail_deserialize(tc, NULL, reader, "Cannot read a c string with negative length %"PRIi64".", len);
1713
0
    }
1714
25.4k
    return strbuf;
1715
25.4k
}
1716
1717
/* The SC id,idx pair is used in various ways, but common to them all is to
1718
   look up the SC, then use the index to call some other function. Putting the
1719
   common parts into one function permits the serialized representation to be
1720
   changed, but frustratingly it requires two return values, which is a bit of
1721
   a pain in (real) C. Hence this rather ungainly function.
1722
1723
   BEWARE - logic in this function is partly duplicated in the skip calculations
1724
   of deserialize_method_cache_lazy(). See the note before
1725
   MVM_serialization_read_ref(). */
1726
1.71M
MVM_STATIC_INLINE MVMSerializationContext * read_locate_sc_and_index(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 *idx) {
1727
1.71M
    MVMint32 sc_id;
1728
1.71M
    MVMuint32 packed;
1729
1.71M
1730
1.71M
    if (reader->root.version >= 19) {
1731
1.71M
        packed = MVM_serialization_read_int(tc, reader);
1732
0
    } else {
1733
0
        assert_can_read(tc, reader, 4);
1734
0
        packed = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1735
0
        *(reader->cur_read_offset) += 4;
1736
0
    }
1737
1.71M
1738
1.71M
    sc_id = packed >> PACKED_SC_SHIFT;
1739
1.71M
    if (sc_id != PACKED_SC_OVERFLOW) {
1740
1.71M
        *idx = packed & PACKED_SC_IDX_MASK;
1741
0
    } else {
1742
0
        if (reader->root.version >= 19) {
1743
0
            sc_id = MVM_serialization_read_int(tc, reader);
1744
0
            *idx = MVM_serialization_read_int(tc, reader);
1745
0
        } else {
1746
0
            assert_can_read(tc, reader, 8);
1747
0
            sc_id = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1748
0
            *(reader->cur_read_offset) += 4;
1749
0
            *idx = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1750
0
            *(reader->cur_read_offset) += 4;
1751
0
        }
1752
0
    }
1753
1.71M
1754
1.71M
    return locate_sc(tc, reader, sc_id);
1755
1.71M
}
1756
1757
/* Reads in and resolves an object references.
1758
 *
1759
 * BEWARE - logic in this function is partly duplicated in the skip calculations
1760
 * of deserialize_method_cache_lazy(). See the note before
1761
 * MVM_serialization_read_ref(). */
1762
1.44M
static MVMObject * read_obj_ref(MVMThreadContext *tc, MVMSerializationReader *reader) {
1763
1.44M
    MVMint32 idx;
1764
1.44M
    MVMSerializationContext *sc = read_locate_sc_and_index(tc, reader, &idx);
1765
1.44M
    /* sequence point... */
1766
1.44M
    return MVM_sc_get_object(tc, sc, idx);
1767
1.44M
}
1768
1769
/* Reads in an array of variant references. */
1770
14.9k
static MVMObject * read_array_var(MVMThreadContext *tc, MVMSerializationReader *reader) {
1771
14.9k
    MVMObject *result = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
1772
14.9k
    MVMint32 elems, i;
1773
14.9k
1774
14.9k
    /* Read the element count. */
1775
14.9k
    elems = MVM_serialization_read_int(tc, reader);
1776
14.9k
1777
14.9k
    /* Read in the elements. */
1778
54.1k
    for (i = 0; i < elems; i++)
1779
39.2k
        MVM_repr_bind_pos_o(tc, result, i, MVM_serialization_read_ref(tc, reader));
1780
14.9k
1781
14.9k
    /* Set the SC. */
1782
14.9k
    MVM_sc_set_obj_sc(tc, result, reader->root.sc);
1783
14.9k
1784
14.9k
    return result;
1785
14.9k
}
1786
1787
/* Reads in an hash with string keys and variant references.
1788
 *
1789
 * BEWARE - logic in this function is partly duplicated in the skip calculations
1790
 * of deserialize_method_cache_lazy(). See the note before
1791
 * MVM_serialization_read_ref(). */
1792
82.1k
static MVMObject * read_hash_str_var(MVMThreadContext *tc, MVMSerializationReader *reader) {
1793
82.1k
    MVMObject *result = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);
1794
82.1k
    MVMint32 elems, i;
1795
82.1k
1796
82.1k
    /* Read the element count. */
1797
82.1k
    if (reader->root.version >= 19) {
1798
82.1k
        elems = MVM_serialization_read_int(tc, reader);
1799
0
    } else {
1800
0
        assert_can_read(tc, reader, 4);
1801
0
        elems = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1802
0
        *(reader->cur_read_offset) += 4;
1803
0
    }
1804
82.1k
1805
82.1k
    /* Read in the elements. */
1806
766k
    for (i = 0; i < elems; i++) {
1807
684k
        MVMString *key = MVM_serialization_read_str(tc, reader);
1808
684k
        MVM_repr_bind_key_o(tc, result, key, MVM_serialization_read_ref(tc, reader));
1809
684k
    }
1810
82.1k
1811
82.1k
    /* Set the SC. */
1812
82.1k
    MVM_sc_set_obj_sc(tc, result, reader->root.sc);
1813
82.1k
1814
82.1k
    return result;
1815
82.1k
}
1816
1817
/* Reads in an array of integers. */
1818
433
static MVMObject * read_array_int(MVMThreadContext *tc, MVMSerializationReader *reader) {
1819
433
    MVMObject *result = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIntArray);
1820
433
    MVMint64 elems, i;
1821
433
1822
433
    /* Read the element count. */
1823
433
    elems = MVM_serialization_read_int(tc, reader);
1824
433
1825
433
    /* Read in the elements. */
1826
576k
    for (i = 0; i < elems; i++)
1827
576k
        MVM_repr_bind_pos_i(tc, result, i, MVM_serialization_read_int(tc, reader));
1828
433
1829
433
    return result;
1830
433
}
1831
1832
/* Reads in an array of strings. */
1833
145
static MVMObject * read_array_str(MVMThreadContext *tc, MVMSerializationReader *reader) {
1834
145
    MVMObject *result = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTStrArray);
1835
145
    MVMint32 elems, i;
1836
145
1837
145
    /* Read the element count. */
1838
145
    if (reader->root.version >= 19) {
1839
145
        elems = MVM_serialization_read_int(tc, reader);
1840
0
    } else {
1841
0
        assert_can_read(tc, reader, 4);
1842
0
        elems = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
1843
0
        *(reader->cur_read_offset) += 4;
1844
0
    }
1845
145
1846
145
    /* Read in the elements. */
1847
127k
    for (i = 0; i < elems; i++)
1848
127k
        MVM_repr_bind_pos_s(tc, result, i, MVM_serialization_read_str(tc, reader));
1849
145
1850
145
    return result;
1851
145
}
1852
1853
/* Reads in a code reference.
1854
 *
1855
 * BEWARE - logic in this function is partly duplicated in the skip calculations
1856
 * of deserialize_method_cache_lazy(). See the note before
1857
 * MVM_serialization_read_ref(). */
1858
216k
static MVMObject * read_code_ref(MVMThreadContext *tc, MVMSerializationReader *reader) {
1859
216k
    MVMint32 idx;
1860
216k
    MVMSerializationContext *sc = read_locate_sc_and_index(tc, reader, &idx);
1861
216k
    return MVM_sc_get_code(tc, sc, idx);
1862
216k
}
1863
1864
/* Read the reference type discriminator from the buffer. */
1865
5.81M
MVM_STATIC_INLINE MVMuint8 read_discrim(MVMThreadContext *tc, MVMSerializationReader *reader) {
1866
5.81M
    assert_can_read(tc, reader, 1);
1867
5.81M
    return *(*(reader->cur_read_buffer) + *(reader->cur_read_offset));
1868
5.81M
}
1869
1870
/* Reading function for references.
1871
 *
1872
 * BEWARE - logic in this function is partly duplicated in
1873
 * deserialize_method_cache_lazy(). If you change the format (or sizes) of
1874
 * things read here (including of course, things read down the calltree) you may
1875
 * need to update the corresponding skip count logic in
1876
 * deserialize_method_cache_lazy().
1877
 */
1878
1879
5.22M
MVMObject * MVM_serialization_read_ref(MVMThreadContext *tc, MVMSerializationReader *reader) {
1880
5.22M
    MVMObject *result;
1881
5.22M
1882
5.22M
    /* Read the discriminator. */
1883
5.22M
    const int discrim_size = 1;
1884
5.22M
    const MVMuint8 discrim = read_discrim(tc, reader);
1885
5.22M
    *(reader->cur_read_offset) += discrim_size;
1886
5.22M
1887
5.22M
    /* Decide what to do based on it. */
1888
5.22M
    switch (discrim) {
1889
783k
        case REFVAR_NULL:
1890
783k
            return NULL;
1891
1.41M
        case REFVAR_OBJECT:
1892
1.41M
            return read_obj_ref(tc, reader);
1893
245k
        case REFVAR_VM_NULL:
1894
245k
            return tc->instance->VMNull;
1895
2.09M
        case REFVAR_VM_INT: {
1896
2.09M
            MVMint64 value;
1897
2.09M
            value = MVM_serialization_read_int(tc, reader);
1898
2.09M
            result = MVM_repr_box_int(tc, tc->instance->boot_types.BOOTInt, value);
1899
2.09M
            return result;
1900
783k
        }
1901
191k
        case REFVAR_VM_NUM:
1902
191k
            result = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTNum);
1903
191k
            MVM_repr_set_num(tc, result, MVM_serialization_read_num(tc, reader));
1904
191k
            return result;
1905
181k
        case REFVAR_VM_STR:
1906
181k
            result = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTStr);
1907
181k
            MVM_repr_set_str(tc, result, MVM_serialization_read_str(tc, reader));
1908
181k
            return result;
1909
14.9k
        case REFVAR_VM_ARR_VAR:
1910
14.9k
            result = read_array_var(tc, reader);
1911
14.9k
            if (reader->current_object) {
1912
14.9k
                MVM_repr_push_o(tc, reader->root.sc->body->owned_objects, result);
1913
14.9k
                MVM_repr_push_o(tc, reader->root.sc->body->owned_objects,
1914
14.9k
                    reader->current_object);
1915
14.9k
            }
1916
14.9k
            return result;
1917
145
        case REFVAR_VM_ARR_STR:
1918
145
            return read_array_str(tc, reader);
1919
433
        case REFVAR_VM_ARR_INT:
1920
433
            return read_array_int(tc, reader);
1921
82.1k
        case REFVAR_VM_HASH_STR_VAR:
1922
82.1k
            result = read_hash_str_var(tc, reader);
1923
82.1k
            if (reader->current_object) {
1924
49.8k
                MVM_repr_push_o(tc, reader->root.sc->body->owned_objects, result);
1925
49.8k
                MVM_repr_push_o(tc, reader->root.sc->body->owned_objects,
1926
49.8k
                    reader->current_object);
1927
49.8k
            }
1928
82.1k
            return result;
1929
216k
        case REFVAR_STATIC_CODEREF:
1930
216k
        case REFVAR_CLONED_CODEREF:
1931
216k
            return read_code_ref(tc, reader);
1932
0
        case REFVAR_SC_REF:
1933
0
            return (MVMObject *)MVM_sc_find_by_handle(tc,
1934
0
                MVM_serialization_read_str(tc, reader));
1935
0
        default:
1936
0
            fail_deserialize(tc, NULL, reader,
1937
0
                "Serialization Error: Unimplemented case of read_ref");
1938
5.22M
    }
1939
5.22M
}
1940
1941
/* Reading function for STable references. */
1942
30.2k
MVMSTable * MVM_serialization_read_stable_ref(MVMThreadContext *tc, MVMSerializationReader *reader) {
1943
30.2k
    MVMint32 idx;
1944
30.2k
    MVMSerializationContext *sc = read_locate_sc_and_index(tc, reader, &idx);
1945
30.2k
    return MVM_sc_get_stable(tc, sc, idx);
1946
30.2k
}
1947
1948
/* Checks the header looks sane and all of the places it points to make sense.
1949
 * Also dissects the input string into the tables and data segments and populates
1950
 * the reader data structure more fully. */
1951
static void check_and_dissect_input(MVMThreadContext *tc,
1952
1.69k
        MVMSerializationReader *reader, MVMString *data_str) {
1953
1.69k
    size_t  data_len;
1954
1.69k
    size_t  header_size;
1955
1.69k
    char   *data;
1956
1.69k
    char   *prov_pos;
1957
1.69k
    char   *data_end;
1958
1.69k
    if (data_str) {
1959
105
        /* Grab data from string. */
1960
105
        char *data_b64 = (char *)MVM_string_ascii_encode(tc, data_str, NULL, 0);
1961
105
        data = (char *)base64_decode(data_b64, &data_len);
1962
105
        MVM_free(data_b64);
1963
105
        reader->data_needs_free = 1;
1964
105
    }
1965
1.58k
    else {
1966
1.58k
        /* Try to get it from the current compilation unit. */
1967
1.58k
        data = (char *)(*tc->interp_cu)->body.serialized;
1968
1.58k
        if (!data)
1969
0
            fail_deserialize(tc, NULL, reader,
1970
0
                "Failed to find deserialization data in compilation unit");
1971
1.58k
        data_len = (*tc->interp_cu)->body.serialized_size;
1972
1.58k
    }
1973
1.69k
    prov_pos = data;
1974
1.69k
    data_end = data + data_len;
1975
1.69k
1976
1.69k
    /* Ensure we got the data. */
1977
1.69k
    if (data == NULL)
1978
0
        fail_deserialize(tc, NULL, reader,
1979
0
            "Failed to decode base64-encoded serialization data");
1980
1.69k
    reader->data = data;
1981
1.69k
1982
1.69k
    /* Ensure that we have enough space to read a version number and check it. */
1983
1.69k
    if (data_len < 4)
1984
0
        fail_deserialize(tc, NULL, reader,
1985
0
            "Serialized data too short to read a version number (< 4 bytes)");
1986
1.69k
    reader->root.version = read_int32(data, 0);
1987
1.69k
    if (reader->root.version < MIN_VERSION || reader->root.version > CURRENT_VERSION)
1988
0
        fail_deserialize(tc, NULL, reader,
1989
0
            "Unsupported serialization format version %d (current version is %d)",
1990
0
            reader->root.version, CURRENT_VERSION);
1991
1.69k
1992
1.69k
    /* Pick header size by version. */
1993
1.69k
    /* See blame history for the next line if you change the header size:  */
1994
1.69k
    header_size = HEADER_SIZE;
1995
1.69k
1996
1.69k
    /* Ensure that the data is at least as long as the header is expected to be. */
1997
1.69k
    if (data_len < header_size)
1998
0
        fail_deserialize(tc, NULL, reader,
1999
0
            "Serialized data shorter than header (< %"MVM_PRSz" bytes)", header_size);
2000
1.69k
    prov_pos += header_size;
2001
1.69k
2002
1.69k
    /* Get size and location of dependencies table. */
2003
1.69k
    reader->root.dependencies_table = data + read_int32(data, 4);
2004
1.69k
    reader->root.num_dependencies   = read_int32(data, 8);
2005
1.69k
    if (reader->root.dependencies_table < prov_pos)
2006
0
        fail_deserialize(tc, NULL, reader,
2007
0
            "Corruption detected (dependencies table starts before header ends)");
2008
1.69k
    prov_pos = reader->root.dependencies_table + reader->root.num_dependencies * DEP_TABLE_ENTRY_SIZE;
2009
1.69k
    if (prov_pos > data_end)
2010
0
        fail_deserialize(tc, NULL, reader,
2011
0
            "Corruption detected (dependencies table overruns end of data)");
2012
1.69k
2013
1.69k
    /* Get size and location of STables table. */
2014
1.69k
    reader->root.stables_table = data + read_int32(data, 12);
2015
1.69k
    reader->root.num_stables   = read_int32(data, 16);
2016
1.69k
    if (reader->root.stables_table < prov_pos)
2017
0
        fail_deserialize(tc, NULL, reader,
2018
0
            "Corruption detected (STables table starts before dependencies table ends)");
2019
1.69k
    prov_pos = reader->root.stables_table + reader->root.num_stables * STABLES_TABLE_ENTRY_SIZE;
2020
1.69k
    if (prov_pos > data_end)
2021
0
        fail_deserialize(tc, NULL, reader,
2022
0
            "Corruption detected (STables table overruns end of data)");
2023
1.69k
2024
1.69k
    /* Get location of STables data. */
2025
1.69k
    reader->root.stables_data = data + read_int32(data, 20);
2026
1.69k
    if (reader->root.stables_data < prov_pos)
2027
0
        fail_deserialize(tc, NULL, reader,
2028
0
            "Corruption detected (STables data starts before STables table ends)");
2029
1.69k
    prov_pos = reader->root.stables_data;
2030
1.69k
    if (prov_pos > data_end)
2031
0
        fail_deserialize(tc, NULL, reader,
2032
0
            "Corruption detected (STables data starts after end of data)");
2033
1.69k
2034
1.69k
    /* Get size and location of objects table. */
2035
1.69k
    reader->root.objects_table = data + read_int32(data, 24);
2036
1.69k
    reader->root.num_objects   = read_int32(data, 28);
2037
1.69k
    if (reader->root.objects_table < prov_pos)
2038
0
        fail_deserialize(tc, NULL, reader,
2039
0
            "Corruption detected (objects table starts before STables data ends)");
2040
1.69k
    prov_pos = reader->root.objects_table + reader->root.num_objects * OBJECTS_TABLE_ENTRY_SIZE;
2041
1.69k
    if (prov_pos > data_end)
2042
0
        fail_deserialize(tc, NULL, reader,
2043
0
            "Corruption detected (objects table overruns end of data)");
2044
1.69k
2045
1.69k
    /* Get location of objects data. */
2046
1.69k
    reader->root.objects_data = data + read_int32(data, 32);
2047
1.69k
    if (reader->root.objects_data < prov_pos)
2048
0
        fail_deserialize(tc, NULL, reader,
2049
0
            "Corruption detected (objects data starts before objects table ends)");
2050
1.69k
    prov_pos = reader->root.objects_data;
2051
1.69k
    if (prov_pos > data_end)
2052
0
        fail_deserialize(tc, NULL, reader,
2053
0
            "Corruption detected (objects data starts after end of data)");
2054
1.69k
2055
1.69k
    /* Get size and location of closures table. */
2056
1.69k
    reader->root.closures_table = data + read_int32(data, 36);
2057
1.69k
    reader->root.num_closures   = read_int32(data, 40);
2058
1.69k
    if (reader->root.closures_table < prov_pos)
2059
0
        fail_deserialize(tc, NULL, reader,
2060
0
            "Corruption detected (Closures table starts before objects data ends)");
2061
1.69k
    prov_pos = reader->root.closures_table + reader->root.num_closures * CLOSURES_TABLE_ENTRY_SIZE;
2062
1.69k
    if (prov_pos > data_end)
2063
0
        fail_deserialize(tc, NULL, reader,
2064
0
            "Corruption detected (Closures table overruns end of data)");
2065
1.69k
2066
1.69k
    /* Get size and location of contexts table. */
2067
1.69k
    reader->root.contexts_table = data + read_int32(data, 44);
2068
1.69k
    reader->root.num_contexts   = read_int32(data, 48);
2069
1.69k
    if (reader->root.contexts_table < prov_pos)
2070
0
        fail_deserialize(tc, NULL, reader,
2071
0
            "Corruption detected (contexts table starts before closures table ends)");
2072
1.69k
    prov_pos = reader->root.contexts_table + reader->root.num_contexts * CONTEXTS_TABLE_ENTRY_SIZE;
2073
1.69k
    if (prov_pos > data_end)
2074
0
        fail_deserialize(tc, NULL, reader,
2075
0
            "Corruption detected (contexts table overruns end of data)");
2076
1.69k
2077
1.69k
    /* Get location of contexts data. */
2078
1.69k
    reader->root.contexts_data = data + read_int32(data, 52);
2079
1.69k
    if (reader->root.contexts_data < prov_pos)
2080
0
        fail_deserialize(tc, NULL, reader,
2081
0
            "Corruption detected (contexts data starts before contexts table ends)");
2082
1.69k
    prov_pos = reader->root.contexts_data;
2083
1.69k
    if (prov_pos > data_end)
2084
0
        fail_deserialize(tc, NULL, reader,
2085
0
            "Corruption detected (contexts data starts after end of data)");
2086
1.69k
2087
1.69k
    /* Get size and location of repossessions table. */
2088
1.69k
    reader->root.repos_table = data + read_int32(data, 56);
2089
1.69k
    reader->root.num_repos   = read_int32(data, 60);
2090
1.69k
    if (reader->root.repos_table < prov_pos)
2091
0
        fail_deserialize(tc, NULL, reader,
2092
0
            "Corruption detected (repossessions table starts before contexts data ends)");
2093
1.69k
    prov_pos = reader->root.repos_table + reader->root.num_repos * REPOS_TABLE_ENTRY_SIZE;
2094
1.69k
    if (prov_pos > data_end)
2095
0
        fail_deserialize(tc, NULL, reader,
2096
0
            "Corruption detected (repossessions table overruns end of data)");
2097
1.69k
2098
1.69k
    /* Get location and number of entries in the interns data section. */
2099
1.69k
    reader->root.param_interns_data = data + read_int32(data, 64);
2100
1.69k
    reader->root.num_param_interns  = read_int32(data, 68);
2101
1.69k
    if (reader->root.param_interns_data < prov_pos)
2102
0
        fail_deserialize(tc, NULL, reader,
2103
0
            "Corruption detected (parameterization interns data starts before repossessions table ends)");
2104
1.69k
    prov_pos = reader->root.param_interns_data;
2105
1.69k
    if (prov_pos > data_end)
2106
0
        fail_deserialize(tc, NULL, reader,
2107
0
            "Corruption detected (parameterization interns data overruns end of data)");
2108
1.69k
2109
1.69k
    /* Set reading limits for data chunks. */
2110
1.69k
    reader->stables_data_end       = reader->root.objects_table;
2111
1.69k
    reader->objects_data_end       = reader->root.closures_table;
2112
1.69k
    reader->contexts_data_end      = reader->root.repos_table;
2113
1.69k
    reader->param_interns_data_end = data_end;
2114
1.69k
}
2115
2116
/* Goes through the dependencies table and resolves the dependencies that it
2117
 * contains to SerializationContexts. */
2118
1.69k
static void resolve_dependencies(MVMThreadContext *tc, MVMSerializationReader *reader) {
2119
1.69k
    char      *table_pos = reader->root.dependencies_table;
2120
1.69k
    MVMuint32  num_deps  = reader->root.num_dependencies;
2121
1.69k
    MVMuint32  i;
2122
1.69k
    reader->root.dependent_scs = MVM_malloc(MAX(num_deps, 1) * sizeof(MVMSerializationContext *));
2123
9.07k
    for (i = 0; i < num_deps; i++) {
2124
7.38k
        MVMString *handle = read_string_from_heap(tc, reader, read_int32(table_pos, 0));
2125
7.38k
        MVMSerializationContext *sc;
2126
7.38k
        sc = MVM_sc_find_by_handle(tc, handle);
2127
7.38k
        if (sc == NULL) {
2128
0
            MVMString *desc = read_string_from_heap(tc, reader, read_int32(table_pos, 4));
2129
0
            char *cname = MVM_string_ascii_encode(tc, desc, NULL, 0);
2130
0
            char *cdesc = NULL;
2131
0
            char *waste[] = { cname, NULL, NULL };
2132
0
            if (reader->root.sc->body->description) {
2133
0
                cdesc = MVM_string_ascii_encode(tc, reader->root.sc->body->description, NULL, 0);
2134
0
                waste[1] = cdesc;
2135
0
            }
2136
0
            else {
2137
0
                cdesc = "<unknown>";
2138
0
            }
2139
0
            if (!desc) desc = handle;
2140
0
            fail_deserialize(tc, waste, reader,
2141
0
                "Missing or wrong version of dependency '%s' (from '%s')",
2142
0
                cname,
2143
0
                cdesc);
2144
0
        }
2145
7.38k
        reader->root.dependent_scs[i] = sc;
2146
7.38k
        table_pos += 8;
2147
7.38k
    }
2148
1.69k
}
2149
2150
/* Allocates and STables that we need to deserialize, associating it with its
2151
 * REPR and getting its allocation size set up. */
2152
25.4k
static void stub_stable(MVMThreadContext *tc, MVMSerializationReader *reader, MVMuint32 i) {
2153
25.4k
    /* Save last read positions. */
2154
25.4k
    MVMint32   orig_stables_data_offset = reader->stables_data_offset;
2155
25.4k
    char     **orig_read_buffer         = reader->cur_read_buffer;
2156
25.4k
    MVMint32  *orig_read_offset         = reader->cur_read_offset;
2157
25.4k
    char     **orig_read_end            = reader->cur_read_end;
2158
25.4k
    char      *orig_read_buffer_val     = reader->cur_read_buffer ? *(reader->cur_read_buffer) : NULL;
2159
25.4k
    MVMint32   orig_read_offset_val     = reader->cur_read_offset ? *(reader->cur_read_offset) : 0;
2160
25.4k
    char      *orig_read_end_val        = reader->cur_read_end    ? *(reader->cur_read_end)    : NULL;
2161
25.4k
2162
25.4k
    /* Calculate location of STable's table row. */
2163
25.4k
    char *st_table_row = reader->root.stables_table + i * STABLES_TABLE_ENTRY_SIZE;
2164
25.4k
2165
25.4k
    /* Check we don't already have the STable (due to repossession). */
2166
25.4k
    MVMSTable *st = MVM_sc_try_get_stable(tc, reader->root.sc, i);
2167
25.4k
    if (!st) {
2168
25.4k
        /* Read in and look up representation. */
2169
25.4k
        const MVMREPROps *repr = MVM_repr_get_by_name(tc,
2170
25.4k
            read_string_from_heap(tc, reader, read_int32(st_table_row, 0)));
2171
25.4k
2172
25.4k
        /* Allocate and store stub STable. */
2173
25.4k
        st = MVM_gc_allocate_stable(tc, repr, NULL);
2174
25.4k
        MVM_sc_set_stable(tc, reader->root.sc, i, st);
2175
25.4k
    }
2176
25.4k
2177
25.4k
    /* Set the STable's SC. */
2178
25.4k
    MVM_sc_set_stable_sc(tc, st, reader->root.sc);
2179
25.4k
2180
25.4k
    /* Set STable read position, and set current read buffer to the
2181
25.4k
     * location of the REPR data. */
2182
25.4k
    reader->stables_data_offset = read_int32(st_table_row, 8);
2183
25.4k
    reader->cur_read_buffer     = &(reader->root.stables_data);
2184
25.4k
    reader->cur_read_offset     = &(reader->stables_data_offset);
2185
25.4k
    reader->cur_read_end        = &(reader->stables_data_end);
2186
25.4k
2187
25.4k
    if (st->REPR->deserialize_stable_size)
2188
25.4k
        st->REPR->deserialize_stable_size(tc, st, reader);
2189
25.4k
    else
2190
0
        fail_deserialize(tc, NULL, reader, "Missing deserialize_stable_size");
2191
25.4k
    if (st->size == 0)
2192
0
        fail_deserialize(tc, NULL, reader, "STable with size zero after deserialization");
2193
25.4k
2194
25.4k
    /* Restore original read positions. */
2195
25.4k
    reader->stables_data_offset = orig_stables_data_offset;
2196
25.4k
    reader->cur_read_buffer     = orig_read_buffer;
2197
25.4k
    reader->cur_read_offset     = orig_read_offset;
2198
25.4k
    reader->cur_read_end        = orig_read_end;
2199
25.4k
    if (reader->cur_read_buffer) {
2200
25.4k
        *(reader->cur_read_buffer)  = orig_read_buffer_val;
2201
25.4k
        *(reader->cur_read_offset)  = orig_read_offset_val;
2202
25.4k
        *(reader->cur_read_end)     = orig_read_end_val;
2203
25.4k
    }
2204
25.4k
}
2205
2206
/* This is slightly misnamed because it doesn't read objects_data_offset.
2207
   However, we never need that at the same time as we need the other data, so it
2208
   makes sense not to over generalise this code. */
2209
913k
static MVMSTable *read_object_table_entry(MVMThreadContext *tc, MVMSerializationReader *reader, MVMuint32 i, MVMint32 *concrete) {
2210
913k
    MVMint32 si;        /* The SC in the dependencies table, + 1 */
2211
913k
    MVMint32 si_idx;    /* The index in that SC */
2212
913k
    /* Calculate location of object's table row. */
2213
913k
    const char *const obj_table_row = reader->root.objects_table + i * OBJECTS_TABLE_ENTRY_SIZE;
2214
913k
    const MVMuint32 packed = read_int32(obj_table_row, 0);
2215
913k
2216
913k
    if (concrete)
2217
913k
        *concrete = packed & OBJECTS_TABLE_ENTRY_IS_CONCRETE;
2218
913k
2219
913k
    si = (packed >> OBJECTS_TABLE_ENTRY_SC_SHIFT) & OBJECTS_TABLE_ENTRY_SC_MASK;
2220
913k
    if (si == OBJECTS_TABLE_ENTRY_SC_OVERFLOW) {
2221
0
        const char *const overflow_data
2222
0
            = reader->root.objects_data + read_int32(obj_table_row, 4) - 8;
2223
0
        si = read_int32(overflow_data, 0);
2224
0
        si_idx = read_int32(overflow_data, 4);
2225
913k
    } else {
2226
913k
        si_idx = packed & OBJECTS_TABLE_ENTRY_SC_IDX_MASK;
2227
913k
    }
2228
913k
2229
913k
    /* Resolve the STable. */
2230
913k
    return MVM_sc_get_stable(tc, locate_sc(tc, reader, si), si_idx);
2231
913k
}
2232
2233
/* Stubs an object we need to deserialize, setting their REPR and type object
2234
 * flag. */
2235
913k
static void stub_object(MVMThreadContext *tc, MVMSerializationReader *reader, MVMuint32 i) {
2236
913k
    MVMint32 concrete;
2237
913k
    MVMSTable *st = read_object_table_entry(tc, reader, i, &concrete);
2238
913k
2239
913k
    /* Allocate and store stub object, unless it's already there due to a
2240
913k
     * repossession. */
2241
913k
    MVMObject *obj = MVM_sc_try_get_object(tc, reader->root.sc, i);
2242
913k
    if (!obj) {
2243
913k
        if (concrete)
2244
888k
            obj = st->REPR->allocate(tc, st);
2245
913k
        else
2246
25.4k
            obj = MVM_gc_allocate_type_object(tc, st);
2247
913k
        MVM_sc_set_object(tc, reader->root.sc, i, obj);
2248
913k
    }
2249
913k
2250
913k
    /* Set the object's SC. */
2251
913k
    MVM_sc_set_obj_sc(tc, obj, reader->root.sc);
2252
913k
}
2253
2254
/* Deserializes a context. */
2255
1.45k
static void deserialize_context(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 row) {
2256
1.45k
    MVMStaticFrame *sf;
2257
1.45k
    MVMFrame       *f;
2258
1.45k
    MVMint64        i, syms;
2259
1.45k
2260
1.45k
    /* Save last read positions. */
2261
1.45k
    MVMint32   orig_contexts_data_offset = reader->stables_data_offset;
2262
1.45k
    char     **orig_read_buffer          = reader->cur_read_buffer;
2263
1.45k
    MVMint32  *orig_read_offset          = reader->cur_read_offset;
2264
1.45k
    char     **orig_read_end             = reader->cur_read_end;
2265
1.45k
2266
1.45k
    /* Calculate location of context's table row. */
2267
1.45k
    char *table_row = reader->root.contexts_table + row * CONTEXTS_TABLE_ENTRY_SIZE;
2268
1.45k
2269
1.45k
    /* Resolve the reference to the static code object this context is for. */
2270
1.45k
    MVMuint32  static_sc_id = read_int32(table_row, 0);
2271
1.45k
    MVMuint32  static_idx   = read_int32(table_row, 4);
2272
1.45k
    MVMuint32  outer_idx    = read_int32(table_row, 12);
2273
1.45k
    MVMObject *static_code  = MVM_sc_get_code(tc,
2274
1.45k
        locate_sc(tc, reader, static_sc_id), static_idx);
2275
1.45k
2276
1.45k
    /* Create context. */
2277
1.45k
    sf = ((MVMCode *)static_code)->body.sf;
2278
1.45k
    f  = MVM_frame_create_context_only(tc, sf, static_code);
2279
1.45k
2280
1.45k
    /* Set context data read position, and set current read buffer to the correct thing. */
2281
1.45k
    reader->contexts_data_offset = read_int32(table_row, 8);
2282
1.45k
    reader->cur_read_buffer      = &(reader->root.contexts_data);
2283
1.45k
    reader->cur_read_offset      = &(reader->contexts_data_offset);
2284
1.45k
    reader->cur_read_end         = &(reader->contexts_data_end);
2285
1.45k
2286
1.45k
    /* Deserialize lexicals. */
2287
1.45k
    if (reader->root.version >= 19) {
2288
1.45k
        syms = MVM_serialization_read_int(tc, reader);
2289
0
    } else {
2290
0
        syms = MVM_serialization_read_int64(tc, reader);
2291
0
    }
2292
1.45k
2293
6.53k
    for (i = 0; i < syms; i++) {
2294
5.08k
        MVMString   *sym = MVM_serialization_read_str(tc, reader);
2295
5.08k
        MVMRegister *lex = MVM_frame_lexical(tc, f, sym);
2296
5.08k
        switch (MVM_frame_lexical_primspec(tc, f, sym)) {
2297
0
            case MVM_STORAGE_SPEC_BP_INT:
2298
0
                if (reader->root.version >= 19) {
2299
0
                    lex->i64 = MVM_serialization_read_int(tc, reader);
2300
0
                } else {
2301
0
                    lex->i64 = MVM_serialization_read_int64(tc, reader);
2302
0
                }
2303
0
2304
0
                break;
2305
0
            case MVM_STORAGE_SPEC_BP_NUM:
2306
0
                lex->n64 = MVM_serialization_read_num(tc, reader);
2307
0
                break;
2308
0
            case MVM_STORAGE_SPEC_BP_STR:
2309
0
                MVM_ASSIGN_REF(tc, &(f->header), lex->s,
2310
0
                    MVM_serialization_read_str(tc, reader));
2311
0
                break;
2312
5.08k
            default:
2313
5.08k
                MVM_ASSIGN_REF(tc, &(f->header), lex->o,
2314
5.08k
                    MVM_serialization_read_ref(tc, reader));
2315
5.08k
        }
2316
5.08k
    }
2317
1.45k
2318
1.45k
    /* Put context into contexts array (will be attached in a later pass). */
2319
1.45k
    reader->contexts[row] = f;
2320
1.45k
2321
1.45k
    /* Restore original read positions. */
2322
1.45k
    reader->contexts_data_offset = orig_contexts_data_offset;
2323
1.45k
    reader->cur_read_buffer      = orig_read_buffer;
2324
1.45k
    reader->cur_read_offset      = orig_read_offset;
2325
1.45k
    reader->cur_read_end         = orig_read_end;
2326
1.45k
2327
1.45k
    /* If we have an outer context... */
2328
1.45k
    if (outer_idx) {
2329
0
        /* Deserialize it if we don't already have it. */
2330
0
        if (!reader->contexts[outer_idx - 1])
2331
0
            deserialize_context(tc, reader, outer_idx - 1);
2332
0
2333
0
        /* Attach it. */
2334
0
        MVM_ASSIGN_REF(tc, &(f->header), f->outer, reader->contexts[outer_idx - 1]);
2335
0
    }
2336
1.45k
}
2337
2338
/* Deserializes a closure, though without attaching outer (that comes in a
2339
 * later step). */
2340
24.9k
static void deserialize_closure(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 i) {
2341
24.9k
    /* Calculate location of closure's table row. */
2342
24.9k
    char *table_row = reader->root.closures_table + i * CLOSURES_TABLE_ENTRY_SIZE;
2343
24.9k
2344
24.9k
    /* Resolve the reference to the static code object. */
2345
24.9k
    MVMuint32  static_sc_id = read_int32(table_row, 0);
2346
24.9k
    MVMuint32  static_idx   = read_int32(table_row, 4);
2347
24.9k
    MVMuint32  context_idx  = read_int32(table_row, 8);
2348
24.9k
    MVMObject *static_code  = MVM_sc_get_code(tc,
2349
24.9k
        locate_sc(tc, reader, static_sc_id), static_idx);
2350
24.9k
2351
24.9k
    /* Clone it and add it to the SC's code refs list. */
2352
24.9k
    MVMObject *closure = MVM_repr_clone(tc, static_code);
2353
24.9k
    MVM_repr_bind_pos_o(tc, reader->codes_list,
2354
24.9k
        reader->num_static_codes + i, closure);
2355
24.9k
2356
24.9k
    /* Tag it as being in this SC. */
2357
24.9k
    MVM_sc_set_obj_sc(tc, closure, reader->root.sc);
2358
24.9k
2359
24.9k
    /* See if there's a code object we need to attach. */
2360
24.9k
    if (read_int32(table_row, 12)) {
2361
24.9k
        MVMObject *obj = MVM_sc_get_object(tc,
2362
24.9k
            locate_sc(tc, reader, read_int32(table_row, 16)),
2363
24.9k
            read_int32(table_row, 20));
2364
24.9k
        MVM_ASSIGN_REF(tc, &(closure->header), ((MVMCode *)closure)->body.code_object, obj);
2365
24.9k
    }
2366
24.9k
2367
24.9k
    /* If we have an outer context... */
2368
24.9k
    if (context_idx) {
2369
24.4k
        /* Deserialize it if we don't already have it. */
2370
24.4k
        if (!reader->contexts[context_idx - 1])
2371
1.45k
            deserialize_context(tc, reader, context_idx - 1);
2372
24.4k
2373
24.4k
        /* Attach it. */
2374
24.4k
        MVM_ASSIGN_REF(tc, &(closure->header), ((MVMCode *)closure)->body.outer,
2375
24.4k
            reader->contexts[context_idx - 1]);
2376
24.4k
    }
2377
24.9k
}
2378
2379
/* Reads in what we need to lazily deserialize ->HOW later. */
2380
25.4k
static void deserialize_how_lazy(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
2381
25.4k
    MVMSerializationContext *sc = read_locate_sc_and_index(tc, reader, (MVMint32 *) &st->HOW_idx);
2382
25.4k
2383
25.4k
    MVM_ASSIGN_REF(tc, &(st->header), st->HOW_sc, sc);
2384
25.4k
}
2385
2386
/* calculate needed bytes for int, it is a simple version of MVM_serialization_read_int. */
2387
0
static MVMuint8 calculate_int_bytes(MVMThreadContext *tc, MVMSerializationReader *reader) {
2388
0
    const MVMuint8 *read_at = (MVMuint8 *) *(reader->cur_read_buffer) + *(reader->cur_read_offset);
2389
0
    MVMuint8 *const read_end = (MVMuint8 *) *(reader->cur_read_end);
2390
0
    MVMuint8 first;
2391
0
    MVMuint8 need;
2392
0
2393
0
    if (read_at >= read_end)
2394
0
        fail_deserialize(tc, NULL, reader,
2395
0
                         "Read past end of serialization data buffer");
2396
0
2397
0
    first = *read_at++;
2398
0
2399
0
    /* Top bit set means remaining 7 bits are a value between -1 and 126.
2400
0
       (That turns out to be the most common 7 bit range that we serialize.)  */
2401
0
    if (first & 0x80) {
2402
0
        return 1;
2403
0
    }
2404
0
2405
0
    /* Otherwise next 3 bits indicate how many more bytes follow. */
2406
0
    need = first >> 4;
2407
0
    if (!need) {
2408
0
      return 9;
2409
0
    }
2410
0
2411
0
    if (read_at + need > read_end)
2412
0
        fail_deserialize(tc, NULL, reader,
2413
0
                         "Read past end of serialization data buffer");
2414
0
2415
0
    return need + 1;
2416
0
}
2417
2418
/* Stashes what we need to deserialize the method cache lazily later, and then
2419
 * skips over it.
2420
 *
2421
 * This function is cruel and unforgiving if you change other parts of the
2422
 * serialization format, but don't remember (or realise) that you need to update
2423
 * its idea of sizes. Its "failure" mode is silent, and everything still passes
2424
 * tests. Only if you benchmark do you realise that everything takes longer,
2425
 * because the lazy paths are now no longer taken. */
2426
25.4k
static void deserialize_method_cache_lazy(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
2427
25.4k
    /* Peek ahead at the discriminator. */
2428
25.4k
    const int discrim_size = 1;
2429
25.4k
    const MVMuint8 discrim = read_discrim(tc, reader);
2430
25.4k
2431
25.4k
    /* We only know how to lazily handle a hash of code refs or code objects;
2432
25.4k
     * for anything else, don't do it lazily. */
2433
25.4k
    if (discrim == REFVAR_VM_HASH_STR_VAR) {
2434
23.0k
        MVMint32 elems, i, valid;
2435
23.0k
2436
23.0k
        /* Save the offset, then skip past discriminator. */
2437
23.0k
        MVMint32 before = *(reader->cur_read_offset);
2438
23.0k
        *(reader->cur_read_offset) += discrim_size;
2439
23.0k
2440
23.0k
        /* Check the elements are as expected. */
2441
23.0k
        if (reader->root.version >= 19) {
2442
23.0k
            elems = MVM_serialization_read_int(tc, reader);
2443
0
        } else {
2444
0
            assert_can_read(tc, reader, 4);
2445
0
            elems = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
2446
0
            *(reader->cur_read_offset) += 4;
2447
0
        }
2448
23.0k
        valid = 1;
2449
580k
        for (i = 0; i < elems; i++) {
2450
557k
            MVMuint32 packed;
2451
557k
            MVMuint8 inner_discrim;
2452
557k
            MVMint32 offset;
2453
557k
            /* Skip string. */
2454
557k
            assert_can_read(tc, reader, 2);
2455
557k
            offset = read_uint16(*(reader->cur_read_buffer), *(reader->cur_read_offset));
2456
557k
            *(reader->cur_read_offset) += 2;
2457
557k
            if (offset & STRING_HEAP_LOC_PACKED_OVERFLOW) {
2458
0
                assert_can_read(tc, reader, 2);
2459
0
                *(reader->cur_read_offset) += 2;
2460
0
            }
2461
557k
2462
557k
            /* Ensure we've a coderef or code object. */
2463
557k
            assert_can_read(tc, reader, discrim_size);
2464
557k
            inner_discrim = read_discrim(tc, reader);
2465
557k
            *(reader->cur_read_offset) += discrim_size;
2466
557k
            switch (inner_discrim) {
2467
557k
            case REFVAR_OBJECT:
2468
557k
            case REFVAR_STATIC_CODEREF:
2469
557k
            case REFVAR_CLONED_CODEREF:
2470
557k
                if (reader->root.version >= 19) {
2471
557k
                    packed = MVM_serialization_read_int(tc, reader);
2472
0
                } else {
2473
0
                    assert_can_read(tc, reader, 4);
2474
0
                    packed = read_int32(*(reader->cur_read_buffer),
2475
0
                                        *(reader->cur_read_offset) );
2476
0
                }
2477
557k
2478
557k
                if(packed == (PACKED_SC_OVERFLOW << PACKED_SC_SHIFT)) {
2479
0
                    if (reader->root.version >= 19) {
2480
0
                        *(reader->cur_read_offset) += calculate_int_bytes(tc, reader); /* for sc_id */
2481
0
                        *(reader->cur_read_offset) += calculate_int_bytes(tc, reader); /* for idx */
2482
0
                    } else {
2483
0
                        assert_can_read(tc, reader, 12);
2484
0
                        *(reader->cur_read_offset) += 12;
2485
0
                    }
2486
557k
                } else {
2487
557k
                    if (reader->root.version >= 19) {
2488
557k
2489
0
                    } else {
2490
0
                        *(reader->cur_read_offset) += 4;
2491
0
                    }
2492
557k
                }
2493
557k
                break;
2494
0
            case REFVAR_NULL:
2495
0
            case REFVAR_VM_NULL:
2496
0
            case REFVAR_VM_INT:
2497
0
            case REFVAR_VM_NUM:
2498
0
            case REFVAR_VM_STR:
2499
0
            case REFVAR_VM_ARR_VAR:
2500
0
            case REFVAR_VM_ARR_STR:
2501
0
            case REFVAR_VM_ARR_INT:
2502
0
            case REFVAR_VM_HASH_STR_VAR:
2503
0
                valid = 0;
2504
0
                *(reader->cur_read_offset) = before;
2505
0
                break;
2506
0
            default:
2507
0
                MVM_exception_throw_adhoc(tc,
2508
0
                                          "Serialization Error: Unimplemented discriminator %d in inner loop in deserialize_method_cache_lazy",
2509
0
                inner_discrim);
2510
557k
            }
2511
557k
            if (!valid)
2512
0
                break;
2513
557k
        }
2514
23.0k
2515
23.0k
        /* If all was valid then just stash what we need for later. */
2516
23.0k
        if (valid) {
2517
23.0k
            st->method_cache = NULL;
2518
23.0k
            MVM_ASSIGN_REF(tc, &(st->header), st->method_cache_sc, reader->root.sc);
2519
23.0k
            st->method_cache_offset = before;
2520
23.0k
            return;
2521
23.0k
        }
2522
2.32k
    } else {
2523
2.32k
        switch (discrim) {
2524
2.32k
        case REFVAR_OBJECT:
2525
2.32k
        case REFVAR_STATIC_CODEREF:
2526
2.32k
        case REFVAR_CLONED_CODEREF:
2527
2.32k
        case REFVAR_NULL:
2528
2.32k
        case REFVAR_VM_NULL:
2529
2.32k
        case REFVAR_VM_INT:
2530
2.32k
        case REFVAR_VM_NUM:
2531
2.32k
        case REFVAR_VM_STR:
2532
2.32k
        case REFVAR_VM_ARR_VAR:
2533
2.32k
        case REFVAR_VM_ARR_STR:
2534
2.32k
        case REFVAR_VM_ARR_INT:
2535
2.32k
        case REFVAR_VM_HASH_STR_VAR:
2536
2.32k
            break;
2537
0
        default:
2538
0
            MVM_exception_throw_adhoc(tc,
2539
0
                                      "Serialization Error: Unimplemented discriminator %d in deserialize_method_cache_lazy",
2540
0
                                      discrim);
2541
2.32k
        }
2542
2.32k
    }
2543
25.4k
2544
25.4k
    /* If we get here, fall back to eager deserialization. */
2545
2.32k
    MVM_ASSIGN_REF(tc, &(st->header), st->method_cache,
2546
2.32k
        MVM_serialization_read_ref(tc, reader));
2547
2.32k
}
2548
2549
/* Deserializes a single STable, along with its REPR data. */
2550
25.4k
static void deserialize_stable(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 i, MVMSTable *st) {
2551
25.4k
    /* Save last read positions. */
2552
25.4k
    MVMint32   orig_stables_data_offset = reader->stables_data_offset;
2553
25.4k
    char     **orig_read_buffer         = reader->cur_read_buffer;
2554
25.4k
    MVMint32  *orig_read_offset         = reader->cur_read_offset;
2555
25.4k
    char     **orig_read_end            = reader->cur_read_end;
2556
25.4k
    char      *orig_read_buffer_val     = reader->cur_read_buffer ? *(reader->cur_read_buffer) : NULL;
2557
25.4k
    MVMint32   orig_read_offset_val     = reader->cur_read_offset ? *(reader->cur_read_offset) : 0;
2558
25.4k
    char      *orig_read_end_val        = reader->cur_read_end    ? *(reader->cur_read_end)    : NULL;
2559
25.4k
2560
25.4k
    /* Calculate location of STable's table row. */
2561
25.4k
    char *st_table_row = reader->root.stables_table + i * STABLES_TABLE_ENTRY_SIZE;
2562
25.4k
    MVMuint8 flags;
2563
25.4k
    MVMuint8 mode;
2564
25.4k
2565
25.4k
    /* Set STable read position, and set current read buffer to the correct thing. */
2566
25.4k
    reader->stables_data_offset = read_int32(st_table_row, 4);
2567
25.4k
    reader->cur_read_buffer     = &(reader->root.stables_data);
2568
25.4k
    reader->cur_read_offset     = &(reader->stables_data_offset);
2569
25.4k
    reader->cur_read_end        = &(reader->stables_data_end);
2570
25.4k
2571
25.4k
    /* If the STable is being repossessed, clean up its existing data before we
2572
25.4k
     * write over it. */
2573
25.4k
    if (st->being_repossessed) {
2574
1
        if (st->REPR->gc_free_repr_data)
2575
1
            st->REPR->gc_free_repr_data(tc, st);
2576
1
        MVM_free(st->type_check_cache);
2577
1
        st->type_check_cache = NULL;
2578
1
        MVM_free(st->boolification_spec);
2579
1
        st->boolification_spec = NULL;
2580
1
        MVM_free(st->invocation_spec);
2581
1
        st->invocation_spec = NULL;
2582
1
        MVM_free(st->debug_name);
2583
1
        st->debug_name = NULL;
2584
1
        st->being_repossessed = 0;
2585
1
    }
2586
25.4k
2587
25.4k
    /* Read the HOW, WHAT and WHO. */
2588
25.4k
    deserialize_how_lazy(tc, st, reader);
2589
25.4k
    MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, read_obj_ref(tc, reader));
2590
25.4k
    MVM_ASSIGN_REF(tc, &(st->header), st->WHO, MVM_serialization_read_ref(tc, reader));
2591
25.4k
2592
25.4k
    /* Method cache. */
2593
25.4k
    deserialize_method_cache_lazy(tc, st, reader);
2594
25.4k
2595
25.4k
    /* Type check cache. */
2596
25.4k
    st->type_check_cache_length = MVM_serialization_read_int(tc, reader);
2597
25.4k
    if (st->type_check_cache_length > 0) {
2598
22.5k
        st->type_check_cache = (MVMObject **)MVM_malloc(st->type_check_cache_length * sizeof(MVMObject *));
2599
71.1k
        for (i = 0; i < st->type_check_cache_length; i++)
2600
48.6k
            MVM_ASSIGN_REF(tc, &(st->header), st->type_check_cache[i], MVM_serialization_read_ref(tc, reader));
2601
22.5k
    }
2602
25.4k
2603
25.4k
    /* Mode flags. */
2604
25.4k
    assert_can_read(tc, reader, 1);
2605
25.4k
    st->mode_flags = *(*(reader->cur_read_buffer) + *(reader->cur_read_offset));
2606
25.4k
    *(reader->cur_read_offset) += 1;
2607
25.4k
    if (st->mode_flags & MVM_PARAMETRIC_TYPE && st->mode_flags & MVM_PARAMETERIZED_TYPE)
2608
0
        fail_deserialize(tc, NULL, reader,
2609
0
            "STable mode flags cannot indicate both parametric and parameterized");
2610
25.4k
2611
25.4k
    /* Boolification spec. */
2612
25.4k
    assert_can_read(tc, reader, 1);
2613
25.4k
    flags = *(*(reader->cur_read_buffer) + *(reader->cur_read_offset));
2614
25.4k
    *(reader->cur_read_offset) += 1;
2615
25.4k
    mode = flags & 0xF;
2616
25.4k
    if (mode != 0xF) {
2617
12.5k
        st->boolification_spec = (MVMBoolificationSpec *)MVM_malloc(sizeof(MVMBoolificationSpec));
2618
12.5k
        st->boolification_spec->mode = mode;
2619
12.5k
        MVM_ASSIGN_REF(tc, &(st->header), st->boolification_spec->method, MVM_serialization_read_ref(tc, reader));
2620
12.5k
    }
2621
25.4k
2622
25.4k
    /* Container spec. */
2623
25.4k
    if (flags & STABLE_HAS_CONTAINER_SPEC) {
2624
0
        MVMString *name = MVM_serialization_read_str(tc, reader);
2625
0
        const MVMContainerConfigurer *cc = MVM_6model_get_container_config(tc, name);
2626
0
        if (!cc) {
2627
0
            char *cname = MVM_string_ascii_encode(tc, name, NULL, 0);
2628
0
            char *waste[] = { cname, NULL };
2629
0
            fail_deserialize(tc, waste, reader, "Could not look up the container config for '%s'",
2630
0
                cname);
2631
0
        }
2632
0
        cc->set_container_spec(tc, st);
2633
0
        st->container_spec->deserialize(tc, st, reader);
2634
0
    }
2635
25.4k
2636
25.4k
    /* Invocation spec. */
2637
25.4k
    if (flags & STABLE_HAS_INVOCATION_SPEC) {
2638
2
        st->invocation_spec = (MVMInvocationSpec *)MVM_calloc(1, sizeof(MVMInvocationSpec));
2639
2
        MVM_ASSIGN_REF(tc, &(st->header), st->invocation_spec->class_handle, MVM_serialization_read_ref(tc, reader));
2640
2
        MVM_ASSIGN_REF(tc, &(st->header), st->invocation_spec->attr_name, MVM_serialization_read_str(tc, reader));
2641
2
        st->invocation_spec->hint = MVM_serialization_read_int(tc, reader);
2642
2
        MVM_ASSIGN_REF(tc, &(st->header), st->invocation_spec->invocation_handler, MVM_serialization_read_ref(tc, reader));
2643
2
        MVM_ASSIGN_REF(tc, &(st->header), st->invocation_spec->md_class_handle, MVM_serialization_read_ref(tc, reader));
2644
2
        MVM_ASSIGN_REF(tc, &(st->header), st->invocation_spec->md_cache_attr_name, MVM_serialization_read_str(tc, reader));
2645
2
        st->invocation_spec->md_cache_hint = MVM_serialization_read_int(tc, reader);
2646
2
        MVM_ASSIGN_REF(tc, &(st->header), st->invocation_spec->md_valid_attr_name, MVM_serialization_read_str(tc, reader));
2647
2
        st->invocation_spec->md_valid_hint = MVM_serialization_read_int(tc, reader);
2648
2
    }
2649
25.4k
2650
25.4k
    /* HLL owner. */
2651
25.4k
    if (flags & STABLE_HAS_HLL_OWNER) {
2652
1
        st->hll_owner = MVM_hll_get_config_for(tc, MVM_serialization_read_str(tc, reader));
2653
1
    }
2654
25.4k
2655
25.4k
    /* HLL role. */
2656
25.4k
    if (flags & STABLE_HAS_HLL_ROLE) {
2657
1
        st->hll_role = MVM_serialization_read_int(tc, reader);
2658
1
    }
2659
25.4k
2660
25.4k
    /* If it's a parametric type... */
2661
25.4k
    if (st->mode_flags & MVM_PARAMETRIC_TYPE) {
2662
2
        /* Create empty lookup table, unless we were beat to it. */
2663
2
        if (!st->paramet.ric.lookup) {
2664
2
            MVMObject *lookup = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
2665
2
            MVM_ASSIGN_REF(tc, &(st->header), st->paramet.ric.lookup, lookup);
2666
2
        }
2667
2
2668
2
        /* Deserialize parameterizer. */
2669
2
        MVM_ASSIGN_REF(tc, &(st->header), st->paramet.ric.parameterizer,
2670
2
                       MVM_serialization_read_ref(tc, reader));
2671
2
    }
2672
25.4k
2673
25.4k
    /* If it's a parameterized type... */
2674
25.4k
    if (st->mode_flags & MVM_PARAMETERIZED_TYPE) {
2675
2
        MVMObject *lookup;
2676
2
2677
2
        /* Deserialize parametric type and parameters. */
2678
2
        MVMObject *ptype  = MVM_serialization_read_ref(tc, reader);
2679
2
        MVMObject *params = read_array_var(tc, reader);
2680
2
2681
2
        /* Attach them to the STable. */
2682
2
        MVM_ASSIGN_REF(tc, &(st->header), st->paramet.erized.parametric_type, ptype);
2683
2
        MVM_ASSIGN_REF(tc, &(st->header), st->paramet.erized.parameters, params);
2684
2
2685
2
        /* Add a mapping into the lookup list of the parameteric type. */
2686
2
        lookup = STABLE(ptype)->paramet.ric.lookup;
2687
2
        if (!lookup) {
2688
0
            lookup = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
2689
0
            MVM_ASSIGN_REF(tc, &(STABLE(ptype)->header), STABLE(ptype)->paramet.ric.lookup, lookup);
2690
0
        }
2691
2
        MVM_repr_push_o(tc, lookup, params);
2692
2
        MVM_repr_push_o(tc, lookup, st->WHAT);
2693
2
    }
2694
25.4k
2695
25.4k
    if (reader->root.version >= 18) {
2696
25.4k
        st->debug_name = MVM_serialization_read_cstr(tc, reader);
2697
0
    } else {
2698
0
        st->debug_name = 0;
2699
0
    }
2700
25.4k
2701
25.4k
    /* If the REPR has a function to deserialize representation data, call it. */
2702
25.4k
    if (st->REPR->deserialize_repr_data)
2703
21.0k
        st->REPR->deserialize_repr_data(tc, st, reader);
2704
25.4k
2705
25.4k
    /* Restore original read positions. */
2706
25.4k
    reader->stables_data_offset = orig_stables_data_offset;
2707
25.4k
    reader->cur_read_buffer     = orig_read_buffer;
2708
25.4k
    reader->cur_read_offset     = orig_read_offset;
2709
25.4k
    reader->cur_read_end        = orig_read_end;
2710
25.4k
    if (reader->cur_read_buffer) {
2711
25.4k
        *(reader->cur_read_buffer)  = orig_read_buffer_val;
2712
25.4k
        *(reader->cur_read_offset)  = orig_read_offset_val;
2713
25.4k
        *(reader->cur_read_end)     = orig_read_end_val;
2714
25.4k
    }
2715
25.4k
}
2716
2717
/* Deserializes a single object. */
2718
913k
static void deserialize_object(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint32 i, MVMObject *obj) {
2719
913k
    /* We've no more to do for type objects. */
2720
913k
    if (IS_CONCRETE(obj)) {
2721
888k
        /* Calculate location of object's table row. */
2722
888k
        char *obj_table_row = reader->root.objects_table + i * OBJECTS_TABLE_ENTRY_SIZE;
2723
888k
2724
888k
        /* Set current read buffer to the correct thing. */
2725
888k
        reader->cur_read_buffer = &(reader->root.objects_data);
2726
888k
        reader->cur_read_offset = &(reader->objects_data_offset);
2727
888k
        reader->cur_read_end    = &(reader->objects_data_end);
2728
888k
2729
888k
        /* Delegate to its deserialization REPR function. */
2730
888k
        reader->current_object = obj;
2731
888k
        reader->objects_data_offset = read_int32(obj_table_row, 4);
2732
888k
        if (REPR(obj)->deserialize)
2733
888k
            REPR(obj)->deserialize(tc, STABLE(obj), obj, OBJECT_BODY(obj), reader);
2734
888k
        else
2735
0
            fail_deserialize(tc, NULL, reader, "Missing deserialize REPR function for %s (%s)",
2736
0
                REPR(obj)->name, MVM_6model_get_debug_name(tc, obj));
2737
888k
        reader->current_object = NULL;
2738
888k
    }
2739
913k
}
2740
2741
/* Worklist manipulation functions. */
2742
939k
static void worklist_add_index(MVMThreadContext *tc, MVMDeserializeWorklist *wl, MVMuint32 index) {
2743
939k
    if (wl->num_indexes == wl->alloc_indexes) {
2744
3.58k
        if (wl->alloc_indexes)
2745
290
            wl->alloc_indexes *= 2;
2746
3.58k
        else
2747
3.29k
            wl->alloc_indexes = 128;
2748
3.58k
        wl->indexes = MVM_realloc(wl->indexes, wl->alloc_indexes * sizeof(MVMuint32));
2749
3.58k
    }
2750
939k
    wl->indexes[wl->num_indexes] = index;
2751
939k
    wl->num_indexes++;
2752
939k
}
2753
2.09M
static MVMuint32 worklist_has_work(MVMThreadContext *tc, MVMDeserializeWorklist *wl) {
2754
2.09M
    return wl->num_indexes > 0;
2755
2.09M
}
2756
939k
static MVMuint32 worklist_take_index(MVMThreadContext *tc, MVMDeserializeWorklist *wl) {
2757
939k
    wl->num_indexes--;
2758
939k
    return wl->indexes[wl->num_indexes];
2759
939k
}
2760
2761
/* Evaluates work lists until they are all empty. */
2762
63.3k
static void work_loop(MVMThreadContext *tc, MVMSerializationReader *sr) {
2763
63.3k
    MVMuint32 worked = 1;
2764
63.3k
2765
186k
    while (worked) {
2766
123k
        worked = 0;
2767
123k
2768
148k
        while (worklist_has_work(tc, &(sr->wl_stables))) {
2769
25.4k
            MVMuint32 index = worklist_take_index(tc, &(sr->wl_stables));
2770
25.4k
            deserialize_stable(tc, sr, index,
2771
25.4k
                sr->root.sc->body->root_stables[index]);
2772
25.4k
            worked = 1;
2773
25.4k
        }
2774
123k
2775
1.03M
        while (worklist_has_work(tc, &(sr->wl_objects)) &&
2776
913k
               !worklist_has_work(tc, &(sr->wl_stables))) {
2777
913k
            MVMuint32 index = worklist_take_index(tc, &(sr->wl_objects));
2778
913k
            deserialize_object(tc, sr, index,
2779
913k
                sr->root.sc->body->root_objects[index]);
2780
913k
            worked = 1;
2781
913k
        }
2782
123k
    }
2783
63.3k
}
2784
2785
/* Demands that we finish deserializing an object. */
2786
1.29M
MVMObject * MVM_serialization_demand_object(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx) {
2787
1.29M
    /* Obtain lock and check we didn't lose a race to deserialize this
2788
1.29M
     * object. */
2789
1.29M
    MVMSerializationReader *sr = sc->body->sr;
2790
1.29M
    MVMROOT(tc, sc, {
2791
1.29M
        MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)sc->body->mutex);
2792
1.29M
    });
2793
1.29M
    if (sc->body->root_objects[idx]) {
2794
380k
        MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2795
380k
        return sc->body->root_objects[idx];
2796
380k
    }
2797
1.29M
2798
1.29M
    /* Flag that we're working on some deserialization (and so will run the
2799
1.29M
     * loop). */
2800
913k
    sr->working++;
2801
913k
    MVM_gc_allocate_gen2_default_set(tc);
2802
913k
2803
913k
    /* Stub the object. */
2804
913k
    stub_object(tc, sr, idx);
2805
913k
2806
913k
    /* Add to worklist and process as needed. */
2807
913k
    worklist_add_index(tc, &(sr->wl_objects), idx);
2808
913k
    if (sr->working == 1)
2809
52.2k
        work_loop(tc, sr);
2810
913k
2811
913k
    /* Clear up. */
2812
913k
    MVM_gc_allocate_gen2_default_clear(tc);
2813
913k
    sr->working--;
2814
913k
    MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2815
913k
2816
913k
    /* Return the (perhaps just stubbed) object. */
2817
913k
    return sc->body->root_objects[idx];
2818
1.29M
}
2819
2820
/* Demands that we finish deserializing an STable. */
2821
39.0k
MVMSTable * MVM_serialization_demand_stable(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx) {
2822
39.0k
    /* Obtain lock and ensure we didn't lose a race to deserialize this
2823
39.0k
     * STable. */
2824
39.0k
    MVMSerializationReader *sr = sc->body->sr;
2825
39.0k
    MVMROOT(tc, sc, {
2826
39.0k
        MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)sc->body->mutex);
2827
39.0k
    });
2828
39.0k
    if (sc->body->root_stables[idx]) {
2829
13.6k
        MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2830
13.6k
        return sc->body->root_stables[idx];
2831
13.6k
    }
2832
39.0k
2833
39.0k
    /* Flag that we're working on some deserialization (and so will run the
2834
39.0k
     * loop). */
2835
25.4k
    sr->working++;
2836
25.4k
    MVM_gc_allocate_gen2_default_set(tc);
2837
25.4k
2838
25.4k
    /* Stub the STable. */
2839
25.4k
    stub_stable(tc, sr, idx);
2840
25.4k
2841
25.4k
    /* Add to worklist and process as needed. */
2842
25.4k
    worklist_add_index(tc, &(sr->wl_stables), idx);
2843
25.4k
    if (sr->working == 1)
2844
288
        work_loop(tc, sr);
2845
25.4k
2846
25.4k
    /* Clear up. */
2847
25.4k
    MVM_gc_allocate_gen2_default_clear(tc);
2848
25.4k
    sr->working--;
2849
25.4k
    MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2850
25.4k
2851
25.4k
    /* Return the (perhaps just stubbed) STable. */
2852
25.4k
    return sc->body->root_stables[idx];
2853
39.0k
}
2854
2855
/* Demands that we finish deserializing a coderef. */
2856
242k
MVMObject * MVM_serialization_demand_code(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx) {
2857
242k
    /* Obtain lock and ensure we didn't lose a race to deserialize this
2858
242k
     * code object. */
2859
242k
    MVMSerializationReader *sr = sc->body->sr;
2860
242k
    MVMROOT(tc, sc, {
2861
242k
        MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)sc->body->mutex);
2862
242k
    });
2863
242k
    if (!MVM_is_null(tc, MVM_repr_at_pos_o(tc, sr->codes_list, idx))) {
2864
217k
        MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2865
217k
        return MVM_repr_at_pos_o(tc, sr->codes_list, idx);
2866
217k
    }
2867
242k
2868
242k
    /* Flag that we're working on some deserialization (and so will run the
2869
242k
     * loop). */
2870
24.9k
    sr->working++;
2871
24.9k
    MVM_gc_allocate_gen2_default_set(tc);
2872
24.9k
2873
24.9k
    /* Deserialize the code object. */
2874
24.9k
    deserialize_closure(tc, sr, idx - sr->num_static_codes);
2875
24.9k
2876
24.9k
    /* Add to worklist and process as needed. */
2877
24.9k
    if (sr->working == 1)
2878
0
        work_loop(tc, sr);
2879
24.9k
2880
24.9k
    /* Clear up. */
2881
24.9k
    MVM_gc_allocate_gen2_default_clear(tc);
2882
24.9k
    sr->working--;
2883
24.9k
    MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2884
24.9k
2885
24.9k
    /* Return the (perhaps just stubbed) STable. */
2886
24.9k
    return MVM_repr_at_pos_o(tc, sr->codes_list, idx);
2887
242k
}
2888
2889
/* Forces us to complete deserialization of a particular STable before work
2890
 * can go on. */
2891
306
void MVM_serialization_force_stable(MVMThreadContext *tc, MVMSerializationReader *sr, MVMSTable *st) {
2892
306
    /* We'll always have the WHAT if we finished deserializing. */
2893
306
    if (!st->WHAT) {
2894
0
        /* Not finished. Try to find the index. */
2895
0
        MVMDeserializeWorklist *wl = &(sr->wl_stables);
2896
0
        MVMint32  found = 0;
2897
0
        MVMuint32 i;
2898
0
        for (i = 0; i < wl->num_indexes; i++) {
2899
0
            MVMuint32 index = wl->indexes[i];
2900
0
            if (!found) {
2901
0
                if (sr->root.sc->body->root_stables[index] == st) {
2902
0
                    /* Found it; finish deserialize. */
2903
0
                    deserialize_stable(tc, sr, index,
2904
0
                        sr->root.sc->body->root_stables[index]);
2905
0
                    found = 1;
2906
0
                }
2907
0
            }
2908
0
            else {
2909
0
                /* After the found index; steal from list. */
2910
0
                wl->indexes[i - 1] = index;
2911
0
            }
2912
0
        }
2913
0
        if (found)
2914
0
            wl->num_indexes--;
2915
0
    }
2916
306
}
2917
2918
/* Finishes deserializing the method cache. */
2919
26.9M
void MVM_serialization_finish_deserialize_method_cache(MVMThreadContext *tc, MVMSTable *st) {
2920
26.9M
    MVMSerializationContext *sc = st->method_cache_sc;
2921
26.9M
    if (sc && sc->body->sr) {
2922
9.03k
        /* Acquire mutex and ensure we didn't lose a race to do this. */
2923
9.03k
        MVMSerializationReader *sr = sc->body->sr;
2924
9.03k
        MVMROOT2(tc, st, sc, {
2925
9.03k
            MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)sc->body->mutex);
2926
9.03k
        });
2927
9.03k
        if (st->method_cache_sc) {
2928
9.03k
            MVMObject *cache;
2929
9.03k
2930
9.03k
            /* Set reader's position. */
2931
9.03k
            sr->stables_data_offset    = st->method_cache_offset;
2932
9.03k
            sr->cur_read_buffer        = &(sr->root.stables_data);
2933
9.03k
            sr->cur_read_offset        = &(sr->stables_data_offset);
2934
9.03k
            sr->cur_read_end           = &(sr->stables_data_end);
2935
9.03k
2936
9.03k
            /* Flag that we're working on some deserialization (and so will run the
2937
9.03k
            * loop). */
2938
9.03k
            sr->working++;
2939
9.03k
            MVM_gc_allocate_gen2_default_set(tc);
2940
9.03k
2941
9.03k
            /* Deserialize what we need. */
2942
9.03k
            cache = MVM_serialization_read_ref(tc, sr);
2943
9.03k
            if (sr->working == 1)
2944
9.03k
                work_loop(tc, sr);
2945
9.03k
            MVM_ASSIGN_REF(tc, &(st->header), st->method_cache, cache);
2946
9.03k
2947
9.03k
            /* Clear up. */
2948
9.03k
            MVM_gc_allocate_gen2_default_clear(tc);
2949
9.03k
            sr->working--;
2950
9.03k
            st->method_cache_sc = NULL;
2951
9.03k
        }
2952
9.03k
        MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)sc->body->mutex);
2953
9.03k
    }
2954
26.9M
}
2955
2956
/* Repossess an object or STable. Ignores those not matching the specified
2957
 * type (where 0 = object, 1 = STable). */
2958
static void repossess(MVMThreadContext *tc, MVMSerializationReader *reader, MVMint64 i,
2959
8
                      MVMObject *repo_conflicts, MVMint32 type) {
2960
8
    MVMuint32 slot;
2961
8
2962
8
    /* Calculate location of table row. */
2963
8
    char *table_row = reader->root.repos_table + i * REPOS_TABLE_ENTRY_SIZE;
2964
8
2965
8
    /* Do appropriate type of repossession, provided it matches the type of
2966
8
     * thing we're current repossessing. */
2967
8
    MVMint32 repo_type = read_int32(table_row, 0);
2968
8
    if (repo_type != type)
2969
4
        return;
2970
4
    if (repo_type == 0) {
2971
3
        MVMSTable *updated_st;
2972
3
2973
3
        /* Get object to repossess. */
2974
3
        MVMSerializationContext *orig_sc = locate_sc(tc, reader, read_int32(table_row, 8));
2975
3
        MVMObject *orig_obj = MVM_sc_get_object(tc, orig_sc, read_int32(table_row, 12));
2976
3
2977
3
        /* If we have a reposession conflict, make a copy of the original object
2978
3
         * and reference it from the conflicts list. Push the original (about to
2979
3
         * be overwritten) object reference too. */
2980
3
        if (MVM_sc_get_obj_sc(tc, orig_obj) != orig_sc) {
2981
0
            MVMROOT(tc, orig_obj, {
2982
0
                MVMObject *backup = NULL;
2983
0
                MVMROOT(tc, backup, {
2984
0
                    if (IS_CONCRETE(orig_obj)) {
2985
0
                        backup = REPR(orig_obj)->allocate(tc, STABLE(orig_obj));
2986
0
                        REPR(orig_obj)->copy_to(tc, STABLE(orig_obj), OBJECT_BODY(orig_obj), backup, OBJECT_BODY(backup));
2987
0
                    }
2988
0
                    else
2989
0
                        backup = MVM_gc_allocate_type_object(tc, STABLE(orig_obj));
2990
0
                });
2991
0
2992
0
                MVM_SC_WB_OBJ(tc, backup);
2993
0
                MVM_repr_push_o(tc, repo_conflicts, backup);
2994
0
                MVM_repr_push_o(tc, repo_conflicts, orig_obj);
2995
0
            });
2996
0
        }
2997
3
2998
3
        /* Put it into objects root set at the apporpriate slot. */
2999
3
        slot = read_int32(table_row, 4);
3000
3
        MVM_sc_set_object(tc, reader->root.sc, slot, orig_obj);
3001
3
        MVM_sc_set_obj_sc(tc, orig_obj, reader->root.sc);
3002
3
        MVM_sc_set_idx_in_sc(&(orig_obj->header), slot);
3003
3
3004
3
        /* Clear it up, since we'll re-allocate all the bits inside
3005
3
         * it on deserialization. */
3006
3
        if (REPR(orig_obj)->gc_free) {
3007
3
            REPR(orig_obj)->gc_free(tc, orig_obj);
3008
3
            /* Ensure the object is clean in case the deserialization never happens */
3009
3
            memset(OBJECT_BODY(orig_obj), 0, orig_obj->header.size - sizeof(MVMObject));
3010
3
        }
3011
3
3012
3
        /* The object's STable may have changed as a result of the
3013
3
         * repossession (perhaps due to mixing in to it), so put the
3014
3
         * STable it should now have in place. */
3015
3
        updated_st = read_object_table_entry(tc, reader, slot, NULL);
3016
3
        MVM_ASSIGN_REF(tc, &(orig_obj->header), orig_obj->st, updated_st);
3017
3
3018
3
        /* Put this on the list of things we should deserialize right away. */
3019
3
        worklist_add_index(tc, &(reader->wl_objects), slot);
3020
3
    }
3021
1
    else if (repo_type == 1) {
3022
1
        /* Get STable to repossess. */
3023
1
        MVMSerializationContext *orig_sc = locate_sc(tc, reader, read_int32(table_row, 8));
3024
1
        MVMSTable *orig_st = MVM_sc_get_stable(tc, orig_sc, read_int32(table_row, 12));
3025
1
3026
1
        /* Make sure we don't have a reposession conflict. */
3027
1
        if (MVM_sc_get_stable_sc(tc, orig_st) != orig_sc)
3028
0
            fail_deserialize(tc, NULL, reader,
3029
0
                "STable conflict detected during deserialization.\n"
3030
0
                "(Probable attempt to load a mutated module or modules that cannot be loaded together).");
3031
1
3032
1
        /* Put it into STables root set at the apporpriate slot. */
3033
1
        slot = read_int32(table_row, 4);
3034
1
        MVM_sc_set_stable(tc, reader->root.sc, slot, orig_st);
3035
1
        MVM_sc_set_stable_sc(tc, orig_st, reader->root.sc);
3036
1
        MVM_sc_set_idx_in_sc(&(orig_st->header), slot);
3037
1
3038
1
        /* Flag as being repossessed, so we can clear up memory at the point
3039
1
         * we replaced the STable data with the updated data. */
3040
1
        orig_st->being_repossessed = 1;
3041
1
3042
1
        /* Put this on the list of things we should deserialize right away. */
3043
1
        worklist_add_index(tc, &(reader->wl_stables), slot);
3044
1
    }
3045
0
    else {
3046
0
        fail_deserialize(tc, NULL, reader, "Unknown repossession type");
3047
0
    }
3048
4
}
3049
3050
/* This goes through the entries in the parameterized types interning section,
3051
 * if any. For each, if we already deserialized the parameterization from a
3052
 * different compilation unit or created it in something we already compiled,
3053
 * we just use that existing parameterization. */
3054
1.69k
static void resolve_param_interns(MVMThreadContext *tc, MVMSerializationReader *reader) {
3055
1.69k
    MVMint32 iidx;
3056
1.69k
3057
1.69k
    /* Switch to reading the parameterization segment. */
3058
1.69k
    reader->cur_read_buffer = &(reader->root.param_interns_data);
3059
1.69k
    reader->cur_read_offset = &(reader->param_interns_data_offset);
3060
1.69k
    reader->cur_read_end    = &(reader->param_interns_data_end);
3061
1.69k
3062
1.69k
    /* Go over all the interns we have. */
3063
1.69k
    for (iidx = 0; iidx < reader->root.num_param_interns; iidx++) {
3064
2
        MVMObject *params, *matching;
3065
2
        MVMint32   num_params, i;
3066
2
3067
2
        /* Resolve the parametric type. */
3068
2
        MVMObject *ptype = read_obj_ref(tc, reader);
3069
2
3070
2
        /* Read indexes where type object and STable will get placed if a
3071
2
         * matching intern is found. */
3072
2
        MVMint32 type_idx = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
3073
2
        MVMint32 st_idx   = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset) + 4);
3074
2
        *(reader->cur_read_offset) += 8;
3075
2
3076
2
        /* Read parameters and push into array. */
3077
2
        num_params = read_int32(*(reader->cur_read_buffer), *(reader->cur_read_offset));
3078
2
        *(reader->cur_read_offset) += 4;
3079
2
        params = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
3080
4
        for (i = 0; i < num_params; i++)
3081
2
            MVM_repr_push_o(tc, params, read_obj_ref(tc, reader));
3082
2
3083
2
        /* Try to find a matching parameterization. */
3084
2
        matching = MVM_6model_parametric_try_find_parameterization(tc, STABLE(ptype), params);
3085
2
        if (matching) {
3086
2
            MVM_sc_set_object(tc, reader->root.sc, type_idx, matching);
3087
2
            MVM_sc_set_stable(tc, reader->root.sc, st_idx, STABLE(matching));
3088
2
        }
3089
2
    }
3090
1.69k
}
3091
3092
/* Takes serialized data, an empty SerializationContext to deserialize it into,
3093
 * a strings heap and the set of static code refs for the compilation unit.
3094
 * Deserializes the data into the required objects and STables. */
3095
void MVM_serialization_deserialize(MVMThreadContext *tc, MVMSerializationContext *sc,
3096
        MVMObject *string_heap, MVMObject *codes_static,
3097
1.69k
        MVMObject *repo_conflicts, MVMString *data) {
3098
1.69k
    MVMint32 scodes, i;
3099
1.69k
3100
1.69k
    /* Allocate and set up reader. */
3101
1.69k
    MVMSerializationReader *reader = MVM_calloc(1, sizeof(MVMSerializationReader));
3102
1.69k
    reader->root.sc          = sc;
3103
1.69k
3104
1.69k
    /* If we've been given a NULL string heap, use that of the current
3105
1.69k
     * compilation unit. */
3106
1.69k
    if (MVM_is_null(tc, string_heap))
3107
1.58k
        reader->root.string_comp_unit = *(tc->interp_cu);
3108
1.69k
    else
3109
105
        reader->root.string_heap = string_heap;
3110
1.69k
3111
1.69k
    /* Store reader inside serialization context; it'll need it for lazy
3112
1.69k
     * deserialization. */
3113
1.69k
    sc->body->sr = reader;
3114
1.69k
3115
1.69k
    /* Put code root list into SC. We'll end up mutating it, but that's
3116
1.69k
     * probably fine. */
3117
1.69k
    MVM_sc_set_code_list(tc, sc, codes_static);
3118
1.69k
    reader->codes_list = codes_static;
3119
1.69k
    scodes = (MVMint32)MVM_repr_elems(tc, codes_static);
3120
1.69k
    reader->num_static_codes = scodes;
3121
1.69k
3122
1.69k
    /* Mark all the static code refs we've been provided with as static. */
3123
242k
    for (i = 0; i < scodes; i++) {
3124
240k
        MVMObject *scr = MVM_repr_at_pos_o(tc, reader->codes_list, i);
3125
240k
        ((MVMCode *)scr)->body.is_static = 1;
3126
240k
        MVM_sc_set_obj_sc(tc, scr, sc);
3127
240k
    }
3128
1.69k
3129
1.69k
    /* During deserialization, we allocate directly in generation 2. This
3130
1.69k
     * is because these objects are almost certainly going to be long lived,
3131
1.69k
     * but also because if we know that we won't end up moving the objects
3132
1.69k
     * we are working on during a deserialization run, it's a bunch easier
3133
1.69k
     * to have those partially constructed objects floating around. */
3134
1.69k
    MVM_gc_allocate_gen2_default_set(tc);
3135
1.69k
3136
1.69k
    /* Read header and dissect the data into its parts. */
3137
1.69k
    check_and_dissect_input(tc, reader, data);
3138
1.69k
3139
1.69k
    /* Resolve the SCs in the dependencies table. */
3140
1.69k
    resolve_dependencies(tc, reader);
3141
1.69k
3142
1.69k
    /* Size objects, STables, and contexts arrays. */
3143
1.69k
    if (sc->body->root_objects)
3144
8
        MVM_free(sc->body->root_objects);
3145
1.69k
    if (sc->body->root_stables)
3146
2
        MVM_free(sc->body->root_stables);
3147
1.69k
    sc->body->root_objects  = MVM_calloc(reader->root.num_objects, sizeof(MVMObject *));
3148
1.69k
    sc->body->num_objects   = reader->root.num_objects;
3149
1.69k
    sc->body->alloc_objects = reader->root.num_objects;
3150
1.69k
    sc->body->root_stables  = MVM_calloc(reader->root.num_stables, sizeof(MVMSTable *));
3151
1.69k
    sc->body->num_stables   = reader->root.num_stables;
3152
1.69k
    sc->body->alloc_stables = reader->root.num_stables;
3153
1.69k
    reader->contexts        = MVM_calloc(reader->root.num_contexts, sizeof(MVMFrame *));
3154
1.69k
3155
1.69k
    /* Increase size of code refs list to include closures we'll later
3156
1.69k
     * deserialize. */
3157
1.69k
    REPR(codes_static)->pos_funcs.set_elems(tc, STABLE(codes_static),
3158
1.69k
        codes_static, OBJECT_BODY(codes_static),
3159
1.69k
        scodes + reader->root.num_closures);
3160
1.69k
3161
1.69k
    /* Handle any type parameterization interning, menaing we should not
3162
1.69k
     * deserialize our own versions of things. */
3163
1.69k
    resolve_param_interns(tc, reader);
3164
1.69k
3165
1.69k
    /* If we're repossessing STables and objects from other SCs, then first
3166
1.69k
      * get those raw objects into our root set. Note we do all the STables,
3167
1.69k
      * then all the objects, since the objects may, post-repossession, refer
3168
1.69k
      * to a repossessed STable. */
3169
1.69k
     for (i = 0; i < reader->root.num_repos; i++)
3170
4
        repossess(tc, reader, i, repo_conflicts, 1);
3171
1.69k
     for (i = 0; i < reader->root.num_repos; i++)
3172
4
        repossess(tc, reader, i, repo_conflicts, 0);
3173
1.69k
3174
1.69k
    /* Enter the work loop to deal with the things we immediately need to
3175
1.69k
     * handle in order to complete repossession object deserialization. */
3176
1.69k
    reader->working = 1;
3177
1.69k
    work_loop(tc, reader);
3178
1.69k
    reader->working = 0;
3179
1.69k
3180
1.69k
    /* Clear serialized data reference in CU. */
3181
1.69k
    if ((*tc->interp_cu)->body.serialized) {
3182
1.58k
        (*tc->interp_cu)->body.serialized = NULL;
3183
1.58k
        (*tc->interp_cu)->body.serialized_size = 0;
3184
1.58k
    }
3185
1.69k
3186
1.69k
    /* If lazy deserialization is disabled, deserialize everything. */
3187
1.69k
#if !MVM_SERIALIZATION_LAZY
3188
    for (i = 0; i < sc->body->num_objects; i++)
3189
        MVM_serialization_demand_object(tc, sc, i);
3190
    for (i = 0; i < sc->body->num_stables; i++)
3191
        MVM_serialization_demand_stable(tc, sc, i);
3192
#endif
3193
1.69k
3194
1.69k
    /* Restore normal GC allocation. */
3195
1.69k
    MVM_gc_allocate_gen2_default_clear(tc);
3196
1.69k
}
3197
3198
/*
3199
3200
=item sha1
3201
3202
Computes the SHA-1 hash of string.
3203
3204
=cut
3205
3206
*/
3207
147
MVMString * MVM_sha1(MVMThreadContext *tc, MVMString *str) {
3208
147
    /* Grab the string as UTF8 bytes. */
3209
147
    MVMuint64 output_size;
3210
147
    char *utf8_string = MVM_string_utf8_encode(tc, str, &output_size, 0);
3211
147
3212
147
    /* Compute its SHA-1 and encode it. */
3213
147
    SHA1Context      context;
3214
147
    char          output[80];
3215
147
    SHA1Init(&context);
3216
147
    SHA1Update(&context, (unsigned char*) utf8_string, (size_t) output_size);
3217
147
    SHA1Final(&context, output);
3218
147
3219
147
    /* Free the C-MVMString and put result into a new MVMString. */
3220
147
    MVM_free(utf8_string);
3221
147
    return MVM_string_ascii_decode(tc, tc->instance->VMString, output, 40);
3222
147
}