Coverage Report

Created: 2017-04-15 07:07

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