~ubuntu-branches/ubuntu/vivid/nqp/vivid-proposed

« back to all changes in this revision

Viewing changes to src/vm/jvm/runtime/org/perl6/nqp/sixmodel/SerializationWriter.java

  • Committer: Package Import Robot
  • Author(s): Alessandro Ghedini
  • Date: 2013-11-01 12:09:18 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20131101120918-kx51sl0sxl3exsxi
Tags: 2013.10-1
* New upstream release
* Bump versioned (Build-)Depends on parrot
* Update patches
* Install new README.pod
* Fix vcs-field-not-canonical
* Do not install rubyish examples
* Do not Depends on parrot-devel anymore
* Add 07_disable-serialization-tests.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.perl6.nqp.sixmodel;
 
2
 
 
3
import java.nio.ByteBuffer;
 
4
import java.nio.ByteOrder;
 
5
import java.util.ArrayList;
 
6
import java.util.HashMap;
 
7
import java.util.List;
 
8
import java.util.Map;
 
9
 
 
10
import org.perl6.nqp.runtime.Base64;
 
11
import org.perl6.nqp.runtime.CallFrame;
 
12
import org.perl6.nqp.runtime.CodeRef;
 
13
import org.perl6.nqp.runtime.ExceptionHandling;
 
14
import org.perl6.nqp.runtime.ThreadContext;
 
15
import org.perl6.nqp.sixmodel.reprs.CallCapture;
 
16
import org.perl6.nqp.sixmodel.reprs.IOHandle;
 
17
import org.perl6.nqp.sixmodel.reprs.MultiCache;
 
18
 
 
19
public class SerializationWriter {
 
20
    /* The current version of the serialization format. */
 
21
    private final int CURRENT_VERSION = 8;
 
22
    
 
23
    /* Various sizes (in bytes). */
 
24
    private final int HEADER_SIZE               = 4 * 16;
 
25
    private final int STABLES_TABLE_ENTRY_SIZE  = 12;
 
26
    private final int OBJECTS_TABLE_ENTRY_SIZE  = 16;
 
27
    private final int CLOSURES_TABLE_ENTRY_SIZE = 24;
 
28
    private final int CONTEXTS_TABLE_ENTRY_SIZE = 16;
 
29
    private final int REPOS_TABLE_ENTRY_SIZE    = 16;
 
30
    
 
31
    /* Possible reference types we can serialize. */
 
32
    private final short REFVAR_NULL               = 1;
 
33
    private final short REFVAR_OBJECT             = 2;
 
34
    private final short REFVAR_VM_NULL            = 3;
 
35
    private final short REFVAR_VM_INT             = 4;
 
36
    private final short REFVAR_VM_NUM             = 5;
 
37
    private final short REFVAR_VM_STR             = 6;
 
38
    private final short REFVAR_VM_ARR_VAR         = 7;
 
39
    private final short REFVAR_VM_ARR_STR         = 8;
 
40
    private final short REFVAR_VM_ARR_INT         = 9;
 
41
    private final short REFVAR_VM_HASH_STR_VAR    = 10;
 
42
    private final short REFVAR_STATIC_CODEREF     = 11;
 
43
    private final short REFVAR_CLONED_CODEREF     = 12;
 
44
    
 
45
    private ThreadContext tc;
 
46
    private SerializationContext sc;
 
47
    private ArrayList<String> sh;
 
48
    private HashMap<String, Integer> stringMap;
 
49
    
 
50
    private ArrayList<SerializationContext> dependentSCs;
 
51
    private ArrayList<CallFrame> contexts;
 
52
    
 
53
    private static final int DEPS = 0;
 
54
    private static final int STABLES = 1;
 
55
    private static final int STABLE_DATA = 2;
 
56
    private static final int OBJECTS = 3;
 
57
    private static final int OBJECT_DATA = 4;
 
58
    private static final int CLOSURES = 5;
 
59
    private static final int CONTEXTS = 6;
 
60
    private static final int CONTEXT_DATA = 7;
 
61
    private static final int REPOS = 8;
 
62
    private ByteBuffer[] outputs;
 
63
    private int currentBuffer;
 
64
    
 
65
    private int numClosures;
 
66
    private int sTablesListPos;
 
67
    private int objectsListPos;
 
68
    private int contextsListPos;
 
69
    
 
70
    public SerializationWriter(ThreadContext tc, SerializationContext sc, ArrayList<String> sh) {
 
71
        this.tc = tc;
 
72
        this.sc = sc;
 
73
        this.sh = sh;
 
74
        this.stringMap = new HashMap<String, Integer>();
 
75
        this.dependentSCs = new ArrayList<SerializationContext>();
 
76
        this.contexts = new ArrayList<CallFrame>();
 
77
        this.outputs = new ByteBuffer[9];
 
78
        this.outputs[DEPS] = ByteBuffer.allocate(128);
 
79
        this.outputs[STABLES] = ByteBuffer.allocate(512);
 
80
        this.outputs[STABLE_DATA] = ByteBuffer.allocate(1024);
 
81
        this.outputs[OBJECTS] = ByteBuffer.allocate(2048);
 
82
        this.outputs[OBJECT_DATA] = ByteBuffer.allocate(8912);
 
83
        this.outputs[CLOSURES] = ByteBuffer.allocate(128);
 
84
        this.outputs[CONTEXTS] = ByteBuffer.allocate(128);
 
85
        this.outputs[CONTEXT_DATA] = ByteBuffer.allocate(1024);
 
86
        this.outputs[REPOS] = ByteBuffer.allocate(64);
 
87
        this.outputs[DEPS].order(ByteOrder.LITTLE_ENDIAN);
 
88
        this.outputs[STABLES].order(ByteOrder.LITTLE_ENDIAN);
 
89
        this.outputs[STABLE_DATA].order(ByteOrder.LITTLE_ENDIAN);
 
90
        this.outputs[OBJECTS].order(ByteOrder.LITTLE_ENDIAN);
 
91
        this.outputs[OBJECT_DATA].order(ByteOrder.LITTLE_ENDIAN);
 
92
        this.outputs[CLOSURES].order(ByteOrder.LITTLE_ENDIAN);
 
93
        this.outputs[CONTEXTS].order(ByteOrder.LITTLE_ENDIAN);
 
94
        this.outputs[CONTEXT_DATA].order(ByteOrder.LITTLE_ENDIAN);
 
95
        this.outputs[REPOS].order(ByteOrder.LITTLE_ENDIAN);
 
96
        this.currentBuffer = 0;
 
97
        this.numClosures = 0;
 
98
        this.sTablesListPos = 0;
 
99
        this.objectsListPos = 0;
 
100
        this.contextsListPos = 0;
 
101
    }
 
102
 
 
103
    public String serialize() {
 
104
        /* Initialize string heap so first entry is the NULL string. */
 
105
        sh.add(null);
 
106
 
 
107
        /* Start serializing. */
 
108
        serializationLoop();
 
109
 
 
110
        /* Build a single result string out of the serialized data. */
 
111
        return concatenateOutputs();
 
112
    }
 
113
    
 
114
    private int addStringToHeap(String s) {
 
115
        /* We ensured that the first entry in the heap represents the null string,
 
116
         * so can just hand back 0 here. */
 
117
        if (s == null)
 
118
            return 0;
 
119
        
 
120
        /* Did we already see it? */
 
121
        Integer idx = stringMap.get(s);
 
122
        if (idx != null)
 
123
            return idx;
 
124
        
 
125
        /* Otherwise, need to add it to the heap. */
 
126
        int newIdx = sh.size();
 
127
        sh.add(s);
 
128
        stringMap.put(s, newIdx);
 
129
        return newIdx;
 
130
    }
 
131
 
 
132
    /* Gets the ID of a serialization context. Returns 0 if it's the current
 
133
     * one, or its dependency table offset (base-1) otherwise. Note that if
 
134
     * it is not yet in the dependency table, it will be added. */
 
135
    private int getSCId(SerializationContext sc) {
 
136
        /* Easy if it's in the current SC. */
 
137
        if (sc == this.sc)
 
138
            return 0;
 
139
        
 
140
        /* If not, try to find it in our dependencies list. */
 
141
        int found = dependentSCs.indexOf(sc);
 
142
        if (found >= 0)
 
143
            return found + 1;
 
144
 
 
145
        /* Otherwise, need to add it to our dependencies list. */
 
146
        dependentSCs.add(sc);
 
147
        growToHold(DEPS, 8);
 
148
        outputs[DEPS].putInt(addStringToHeap(sc.handle));
 
149
        outputs[DEPS].putInt(addStringToHeap(sc.description));
 
150
        return dependentSCs.size(); /* Deliberately index + 1. */
 
151
    }
 
152
    
 
153
    /* Takes an STable. If it's already in an SC, returns information on how
 
154
     * to reference it. Otherwise, adds it to the current SC, effectively
 
155
     * placing it onto the work list. */
 
156
    private int[] getSTableRefInfo(STable st) {
 
157
        /* Add to this SC if needed. */
 
158
        if (st.sc == null) {
 
159
            st.sc = this.sc;
 
160
            this.sc.root_stables.add(st);
 
161
        }
 
162
        
 
163
        /* Work out SC reference. */
 
164
        int[] result = new int[2];
 
165
        result[0] = getSCId(st.sc);
 
166
        result[1] = st.sc.root_stables.indexOf(st);
 
167
        return result;
 
168
    }
 
169
    
 
170
    /* Writing function for native integers. */
 
171
    public void writeInt(long value) {
 
172
        this.growToHold(currentBuffer, 8);
 
173
        outputs[currentBuffer].putLong(value);
 
174
    }
 
175
    
 
176
    /* Writing function for 32-bit native integers. */
 
177
    public void writeInt32(int value) {
 
178
        this.growToHold(currentBuffer, 4);
 
179
        outputs[currentBuffer].putInt(value);
 
180
    }
 
181
 
 
182
    /* Writing function for native numbers. */
 
183
    public void writeNum(double value) {
 
184
        this.growToHold(currentBuffer, 8);
 
185
        outputs[currentBuffer].putDouble(value);
 
186
    }
 
187
 
 
188
    /* Writing function for native strings. */
 
189
    public void writeStr(String value) {
 
190
        int heapLoc = addStringToHeap(value);
 
191
        this.growToHold(currentBuffer, 4);
 
192
        outputs[currentBuffer].putInt(heapLoc);
 
193
    }
 
194
 
 
195
    /* Writes an object reference. */
 
196
    public void writeObjRef(SixModelObject ref) {
 
197
        if (ref.sc == null) {
 
198
            /* This object doesn't belong to an SC yet, so it must be serialized as part of
 
199
             * this compilation unit. Add it to the work list. */
 
200
            ref.sc = this.sc;
 
201
            this.sc.root_objects.add(ref);
 
202
        }
 
203
        
 
204
        /* Write SC index, then object index. */
 
205
        this.growToHold(currentBuffer, 8);
 
206
        outputs[currentBuffer].putInt(getSCId(ref.sc));
 
207
        outputs[currentBuffer].putInt(ref.sc.root_objects.indexOf(ref));
 
208
    }
 
209
    
 
210
    public void writeList(List<SixModelObject> list) {
 
211
        growToHold(currentBuffer, 6);
 
212
        outputs[currentBuffer].putShort(REFVAR_VM_ARR_VAR);
 
213
        outputs[currentBuffer].putInt(list.size());
 
214
        for (SixModelObject item : list)
 
215
            writeRef(item);
 
216
    }
 
217
    
 
218
    public void writeHash(Map<String, SixModelObject> hash) {
 
219
        growToHold(currentBuffer, 6);
 
220
        outputs[currentBuffer].putShort(REFVAR_VM_HASH_STR_VAR);
 
221
        outputs[currentBuffer].putInt(hash.size());
 
222
        for (String key : hash.keySet()) {
 
223
            writeStr(key);
 
224
            writeRef(hash.get(key));
 
225
        }
 
226
    }
 
227
 
 
228
    public void writeIntHash(HashMap<String, Integer> hash) {
 
229
        growToHold(currentBuffer, 6);
 
230
        outputs[currentBuffer].putShort(REFVAR_VM_HASH_STR_VAR);
 
231
        outputs[currentBuffer].putInt(hash.size());
 
232
        for (String key : hash.keySet()) {
 
233
            writeStr(key);
 
234
            growToHold(currentBuffer, 10);
 
235
            outputs[currentBuffer].putShort(REFVAR_VM_INT);
 
236
            outputs[currentBuffer].putLong((int)hash.get(key));
 
237
        }
 
238
    }
 
239
 
 
240
    private void writeCodeRef(SixModelObject ref) {
 
241
        SerializationContext codeSC = ref.sc;
 
242
        int scId = getSCId(codeSC);
 
243
        int idx = codeSC.root_codes.indexOf(ref);
 
244
        growToHold(currentBuffer, 8);
 
245
        outputs[currentBuffer].putInt(scId);
 
246
        outputs[currentBuffer].putInt(idx);
 
247
    }
 
248
    
 
249
    /* Writing function for references to things. */
 
250
    public void writeRef(SixModelObject ref) {
 
251
        /* Work out what kind of thing we have and determine the discriminator. */
 
252
        short discrim = 0;
 
253
        if (ref == null) {
 
254
            discrim = REFVAR_VM_NULL;
 
255
        }
 
256
        else if (ref.st.REPR instanceof IOHandle) {
 
257
            /* Can't serialize handles. */
 
258
            discrim = REFVAR_VM_NULL;
 
259
        }
 
260
        else if (ref.st.REPR instanceof CallCapture) {
 
261
            /* This is a hack for Rakudo's sake; it keeps a CallCapture around in
 
262
             * the lexpad, for no really good reason. */
 
263
            discrim = REFVAR_VM_NULL;
 
264
        }
 
265
        else if (ref.st.REPR instanceof MultiCache) {
 
266
            /* These are re-computed each time. */
 
267
            discrim = REFVAR_VM_NULL;
 
268
        }
 
269
        else if (ref.st.WHAT == tc.gc.BOOTInt) {
 
270
            discrim = REFVAR_VM_INT;
 
271
        }
 
272
        else if (ref.st.WHAT == tc.gc.BOOTNum) {
 
273
            discrim = REFVAR_VM_NUM;
 
274
        }
 
275
        else if (ref.st.WHAT == tc.gc.BOOTStr) {
 
276
            discrim = REFVAR_VM_STR;
 
277
        }
 
278
        else if (ref.st.WHAT == tc.gc.BOOTArray) {
 
279
            discrim = REFVAR_VM_ARR_VAR;
 
280
        }
 
281
        else if (ref.st.WHAT == tc.gc.BOOTIntArray) {
 
282
            discrim = REFVAR_VM_ARR_INT;
 
283
        }
 
284
        else if (ref.st.WHAT == tc.gc.BOOTStrArray) {
 
285
            discrim = REFVAR_VM_ARR_STR;
 
286
        }
 
287
        else if (ref.st.WHAT == tc.gc.BOOTHash) {
 
288
            discrim = REFVAR_VM_HASH_STR_VAR;
 
289
        }
 
290
        else if (ref instanceof CodeRef) {
 
291
            if (ref.sc != null && ((CodeRef)ref).isStaticCodeRef) {
 
292
                /* Static code reference. */
 
293
                discrim = REFVAR_STATIC_CODEREF;
 
294
            }
 
295
            else if (ref.sc != null) {
 
296
                /* Closure, but already seen and serialization already handled. */
 
297
                discrim = REFVAR_CLONED_CODEREF;
 
298
            }
 
299
            else {
 
300
                /* Closure but didn't see it yet. Take care of it serialization, which
 
301
                 * gets it marked with this SC. Then it's just a normal code ref that
 
302
                 * needs serializing. */
 
303
                serializeClosure((CodeRef)ref);
 
304
                discrim = REFVAR_CLONED_CODEREF;
 
305
            }
 
306
        }
 
307
        else {
 
308
            /* Just a normal object, with no special serialization needs. */
 
309
            discrim = REFVAR_OBJECT;
 
310
        }
 
311
 
 
312
        /* Write the discriminator. */
 
313
        growToHold(currentBuffer, 2);
 
314
        outputs[currentBuffer].putShort(discrim);
 
315
        
 
316
        /* Now take appropriate action. */
 
317
        switch (discrim) {
 
318
            case REFVAR_NULL:
 
319
            case REFVAR_VM_NULL:
 
320
                /* Nothing to do for these. */
 
321
                break;
 
322
            case REFVAR_OBJECT:
 
323
                writeObjRef(ref);
 
324
                break;
 
325
            case REFVAR_VM_INT:
 
326
                writeInt(ref.get_int(tc));
 
327
                break;
 
328
            case REFVAR_VM_NUM:
 
329
                writeNum(ref.get_num(tc));
 
330
                break;
 
331
            case REFVAR_VM_STR:
 
332
                writeStr(ref.get_str(tc));
 
333
                break;
 
334
            case REFVAR_VM_ARR_VAR:
 
335
            case REFVAR_VM_ARR_INT:
 
336
            case REFVAR_VM_ARR_STR:
 
337
            case REFVAR_VM_HASH_STR_VAR:
 
338
                /* These all delegate to the REPR. */
 
339
                ref.st.REPR.serialize(tc, this, ref);
 
340
                break;
 
341
            case REFVAR_STATIC_CODEREF:
 
342
            case REFVAR_CLONED_CODEREF:
 
343
                writeCodeRef(ref);
 
344
                break;
 
345
            default:
 
346
                throw new RuntimeException("Serialization Error: Unimplemented object type writeRef");
 
347
        }
 
348
    }
 
349
 
 
350
    /* Writing function for references to STables. */
 
351
    public void writeSTableRef(STable st) {
 
352
        int[] idxs = getSTableRefInfo(st);
 
353
        growToHold(currentBuffer, 8);
 
354
        outputs[currentBuffer].putInt(idxs[0]);
 
355
        outputs[currentBuffer].putInt(idxs[1]);
 
356
    }
 
357
    
 
358
    /* Concatenates the various output segments into a single binary string. */
 
359
    private String concatenateOutputs() {
 
360
        int output_size = 0;
 
361
        int offset      = 0;
 
362
        
 
363
        /* Calculate total size. */
 
364
        output_size += HEADER_SIZE;
 
365
        output_size += outputs[DEPS].position();
 
366
        output_size += outputs[STABLES].position();
 
367
        output_size += outputs[STABLE_DATA].position();
 
368
        output_size += outputs[OBJECTS].position();
 
369
        output_size += outputs[OBJECT_DATA].position();
 
370
        output_size += outputs[CLOSURES].position();
 
371
        output_size += outputs[CONTEXTS].position();
 
372
        output_size += outputs[CONTEXT_DATA].position();
 
373
        output_size += outputs[REPOS].position();
 
374
        
 
375
        /* Allocate a buffer that size. */
 
376
        ByteBuffer output = ByteBuffer.allocate(output_size);
 
377
        output.order(ByteOrder.LITTLE_ENDIAN);
 
378
        
 
379
        /* Write version into header. */
 
380
        output.putInt(CURRENT_VERSION);
 
381
        offset += HEADER_SIZE;
 
382
        
 
383
        /* Put dependencies table in place and set location/rows in header. */
 
384
        output.putInt(offset);
 
385
        output.putInt(this.dependentSCs.size());
 
386
        output.position(offset);
 
387
        output.put(usedBytes(outputs[DEPS]));
 
388
        offset += outputs[DEPS].position();
 
389
        
 
390
        /* Put STables table in place, and set location/rows in header. */
 
391
        output.position(12);
 
392
        output.putInt(offset);
 
393
        output.putInt(this.sc.root_stables.size());
 
394
        output.position(offset);
 
395
        output.put(usedBytes(outputs[STABLES]));
 
396
        offset += outputs[STABLES].position();
 
397
        
 
398
        /* Put STables data in place. */
 
399
        output.position(20);
 
400
        output.putInt(offset);
 
401
        output.position(offset);
 
402
        output.put(usedBytes(outputs[STABLE_DATA]));
 
403
        offset += outputs[STABLE_DATA].position();
 
404
        
 
405
        /* Put objects table in place, and set location/rows in header. */
 
406
        output.position(24);
 
407
        output.putInt(offset);
 
408
        output.putInt(this.sc.root_objects.size());
 
409
        output.position(offset);
 
410
        output.put(usedBytes(outputs[OBJECTS]));
 
411
        offset += outputs[OBJECTS].position();
 
412
        
 
413
        /* Put objects data in place. */
 
414
        output.position(32);
 
415
        output.putInt(offset);
 
416
        output.position(offset);
 
417
        output.put(usedBytes(outputs[OBJECT_DATA]));
 
418
        offset += outputs[OBJECT_DATA].position();
 
419
        
 
420
        /* Put closures table in place, and set location/rows in header. */
 
421
        output.position(36);
 
422
        output.putInt(offset);
 
423
        output.putInt(this.numClosures);
 
424
        output.position(offset);
 
425
        output.put(usedBytes(outputs[CLOSURES]));
 
426
        offset += outputs[CLOSURES].position();
 
427
 
 
428
        /* Put contexts table in place, and set location/rows in header. */
 
429
        output.position(44);
 
430
        output.putInt(offset);
 
431
        output.putInt(this.contexts.size());
 
432
        output.position(offset);
 
433
        output.put(usedBytes(outputs[CONTEXTS]));
 
434
        offset += outputs[CONTEXTS].position();
 
435
        
 
436
        /* Put contexts data in place. */
 
437
        output.position(52);
 
438
        output.putInt(offset);
 
439
        output.position(offset);
 
440
        output.put(usedBytes(outputs[CONTEXT_DATA]));
 
441
        offset += outputs[CONTEXT_DATA].position();
 
442
        
 
443
        /* Put repossessions table in place, and set location/rows in header. */
 
444
        output.position(56);
 
445
        output.putInt(offset);
 
446
        output.putInt(this.sc.rep_scs.size());
 
447
        output.position(offset);
 
448
        output.put(usedBytes(outputs[REPOS]));
 
449
        offset += outputs[REPOS].position();
 
450
        
 
451
        /* Sanity check. */
 
452
        if (offset != output_size)
 
453
            throw new RuntimeException("Serialization sanity check failed: offset != output_size");
 
454
        
 
455
        /* Base 64 encode and return. */
 
456
        return Base64.encode(output);
 
457
    }
 
458
    
 
459
    /* Grabs an array of the bytes actually populated in the specified buffer. */
 
460
    private byte[] usedBytes(ByteBuffer bb) {
 
461
        byte[] result = new byte[bb.position()];
 
462
        bb.position(0);
 
463
        bb.get(result);
 
464
        bb.position(result.length);
 
465
        return result;
 
466
    }
 
467
    
 
468
    /* This handles the serialization of an object, which largely involves a
 
469
     * delegation to its representation. */
 
470
    private void serializeObject(SixModelObject obj) {
 
471
        /* Get index of SC that holds the STable and its index. */
 
472
        int[] ref = getSTableRefInfo(obj.st);
 
473
        int sc = ref[0];
 
474
        int sc_idx = ref[1];
 
475
        
 
476
        /* Ensure there's space in the objects table; grow if not. */
 
477
        growToHold(OBJECTS, OBJECTS_TABLE_ENTRY_SIZE);
 
478
        
 
479
        /* Make objects table entry. */
 
480
        outputs[OBJECTS].putInt(sc);
 
481
        outputs[OBJECTS].putInt(sc_idx);
 
482
        outputs[OBJECTS].putInt(outputs[OBJECT_DATA].position());
 
483
        outputs[OBJECTS].putInt(obj instanceof TypeObject ? 0 : 1);
 
484
        
 
485
        /* Make sure we're going to write to the correct place. */
 
486
        currentBuffer = OBJECT_DATA;
 
487
        
 
488
        /* Delegate to its serialization REPR function. */
 
489
        if (!(obj instanceof TypeObject))
 
490
            obj.st.REPR.serialize(tc, this, obj);
 
491
    }
 
492
 
 
493
    private void serializeStable(STable st) {
 
494
        /* Ensure there's space in the STables table. */
 
495
        growToHold(STABLES, STABLES_TABLE_ENTRY_SIZE);
 
496
        
 
497
        /* Make STables table entry. */
 
498
        outputs[STABLES].putInt(addStringToHeap(st.REPR.name));
 
499
        outputs[STABLES].putInt(outputs[STABLE_DATA].position());
 
500
        
 
501
        /* Make sure we're going to write to the correct place. */
 
502
        currentBuffer = STABLE_DATA;
 
503
        
 
504
        /* Write HOW, WHAT and WHO. */
 
505
        writeObjRef(st.HOW);
 
506
        writeObjRef(st.WHAT);
 
507
        writeRef(st.WHO);
 
508
 
 
509
        /* Method cache and v-table. */
 
510
        growToHold(currentBuffer, 2);
 
511
        if (st.MethodCache != null) {
 
512
            writeHash(st.MethodCache);
 
513
        }
 
514
        else {
 
515
            outputs[currentBuffer].putShort(REFVAR_VM_NULL);
 
516
        }
 
517
        int vtl = st.VTable == null ? 0 : st.VTable.length;
 
518
        writeInt(vtl);
 
519
        for (int i = 0; i < vtl; i++)
 
520
            writeRef(st.VTable[i]);
 
521
        
 
522
        /* Type check cache. */
 
523
        int tcl = st.TypeCheckCache == null ? 0 : st.TypeCheckCache.length;
 
524
        writeInt(tcl);
 
525
        for (int i = 0; i < tcl; i++)
 
526
            writeRef(st.TypeCheckCache[i]);
 
527
        
 
528
        /* Mode flags. */
 
529
        writeInt(st.ModeFlags);
 
530
        
 
531
        /* Boolification spec. */
 
532
        writeInt(st.BoolificationSpec == null ? 0 : 1);
 
533
        if (st.BoolificationSpec != null) {
 
534
            writeInt(st.BoolificationSpec.Mode);
 
535
            writeRef(st.BoolificationSpec.Method);
 
536
        }
 
537
        
 
538
        /* Container spec. */
 
539
        writeInt(st.ContainerSpec == null ? 0 : 1);
 
540
        if (st.ContainerSpec != null) {
 
541
            writeStr(st.ContainerSpec.name());
 
542
            st.ContainerSpec.serialize(tc, st, this);
 
543
        }
 
544
        
 
545
        /* Invocation spec. */
 
546
        writeInt(st.InvocationSpec == null ? 0 : 1);
 
547
        if (st.InvocationSpec != null) {
 
548
            writeRef(st.InvocationSpec.ClassHandle);
 
549
            writeStr(st.InvocationSpec.AttrName);
 
550
            writeInt(st.InvocationSpec.Hint);
 
551
            writeRef(st.InvocationSpec.InvocationHandler);
 
552
        }
 
553
        
 
554
        /* HLL info. */
 
555
        writeStr(st.hllOwner == null ? "" : st.hllOwner.name);
 
556
        writeInt(st.hllRole);
 
557
        
 
558
        /* Location of REPR data. */
 
559
        outputs[STABLES].putInt(outputs[STABLE_DATA].position());
 
560
        
 
561
        /* If the REPR has a function to serialize representation data, call it. */
 
562
        st.REPR.serialize_repr_data(tc, st, this);
 
563
    }
 
564
    
 
565
    private SixModelObject closureToStaticCodeRef(CodeRef closure, boolean fatal) {
 
566
        SixModelObject staticCode = ((CodeRef)closure).staticInfo.staticCode;
 
567
        if (staticCode == null)
 
568
        {
 
569
            if (fatal)
 
570
                throw ExceptionHandling.dieInternal(tc,
 
571
                    "Serialization Error: missing static code ref for closure " +
 
572
                    ((CodeRef)staticCode).name);
 
573
            else
 
574
                return null;
 
575
        }
 
576
        if (staticCode.sc == null) {
 
577
            if (fatal)
 
578
                throw ExceptionHandling.dieInternal(tc,
 
579
                    "Serialization Error: could not locate static code ref for closure " +
 
580
                    ((CodeRef)staticCode).name);
 
581
            else
 
582
                return null;
 
583
        }
 
584
        return staticCode;
 
585
    }
 
586
 
 
587
    private void serializeClosure(CodeRef closure) {
 
588
        /* Locate the static code object. */
 
589
        SixModelObject staticCodeRef = closureToStaticCodeRef(closure, true);
 
590
        SerializationContext staticCodeSC = staticCodeRef.sc;
 
591
 
 
592
        /* Ensure there's space in the closures table; grow if not. */
 
593
        growToHold(CLOSURES, CLOSURES_TABLE_ENTRY_SIZE);
 
594
        
 
595
        /* Get the index of the context (which will add it to the todo list if
 
596
         * needed). */
 
597
        int contextIdx = getSerializedOuterContextIdx(closure);
 
598
        
 
599
        /* Add an entry to the closures table. */
 
600
        int staticSCId = getSCId(staticCodeSC);
 
601
        int staticIdx = staticCodeSC.root_codes.indexOf(staticCodeRef);
 
602
        outputs[CLOSURES].putInt(staticSCId);
 
603
        outputs[CLOSURES].putInt(staticIdx);
 
604
        outputs[CLOSURES].putInt(contextIdx);
 
605
        
 
606
        /* Check if it has a static code object. */
 
607
        if (closure.codeObject != null) {
 
608
            outputs[CLOSURES].putInt(1);
 
609
            if (closure.codeObject.sc == null) {
 
610
                closure.codeObject.sc = this.sc;
 
611
                this.sc.root_objects.add(closure.codeObject);
 
612
            }
 
613
            outputs[CLOSURES].putInt(getSCId(closure.codeObject.sc));
 
614
            outputs[CLOSURES].putInt(closure.codeObject.sc.root_objects.indexOf(closure.codeObject));
 
615
        }
 
616
        else {
 
617
            outputs[CLOSURES].putInt(0);
 
618
            outputs[CLOSURES].putInt(0); // pad
 
619
            outputs[CLOSURES].putInt(0); // pad
 
620
        }
 
621
        
 
622
        /* Increment count of closures in the table. */
 
623
        numClosures++;
 
624
 
 
625
        /* Add the closure to this SC, and mark it as as being in it. */
 
626
        this.sc.root_codes.add((CodeRef)closure);
 
627
        closure.sc = this.sc;
 
628
    }
 
629
 
 
630
    private int getSerializedOuterContextIdx(CodeRef closure) {
 
631
        if (closure.isCompilerStub)
 
632
            return 0;
 
633
        if (closure.outer == null)
 
634
            return 0;
 
635
        return getSerializedContextIdx(closure.outer);
 
636
    }
 
637
 
 
638
    private int getSerializedContextIdx(CallFrame cf) {
 
639
        if (cf.sc == null) {
 
640
            /* Make sure we should chase a level down. */
 
641
            if (closureToStaticCodeRef(cf.codeRef, false) == null) {
 
642
                return 0;
 
643
            }
 
644
            else {
 
645
                contexts.add(cf);
 
646
                cf.sc = this.sc;
 
647
                return contexts.size();
 
648
            }
 
649
        }
 
650
        else {
 
651
            if (cf.sc != this.sc)
 
652
                ExceptionHandling.dieInternal(tc,
 
653
                    "Serialization Error: reference to context outside of SC");
 
654
            int idx = contexts.indexOf(cf);
 
655
            if (idx < 0)
 
656
                ExceptionHandling.dieInternal(tc, 
 
657
                    "Serialization Error: could not locate outer context in current SC");
 
658
            return idx + 1;
 
659
        }
 
660
    }
 
661
 
 
662
    private void serializeContext(CallFrame cf) {
 
663
        /* Locate the static code ref this context points to. */
 
664
        SixModelObject staticCodeRef = closureToStaticCodeRef(cf.codeRef, true);
 
665
        SerializationContext staticCodeSC = staticCodeRef.sc;
 
666
        if (staticCodeSC == null)
 
667
            ExceptionHandling.dieInternal(tc,
 
668
                "Serialization Error: closure outer is a code object not in an SC");
 
669
        int staticSCId = getSCId(staticCodeSC);
 
670
        int staticIdx = staticCodeSC.root_codes.indexOf(staticCodeRef);
 
671
 
 
672
        /* Ensure there's space in the contexts table; grow if not. */
 
673
        growToHold(CONTEXTS, CONTEXTS_TABLE_ENTRY_SIZE);
 
674
        
 
675
        /* Make contexts table entry. */
 
676
        outputs[CONTEXTS].putInt(staticSCId);
 
677
        outputs[CONTEXTS].putInt(staticIdx);
 
678
        outputs[CONTEXTS].putInt(outputs[CONTEXT_DATA].position());
 
679
        
 
680
        /* See if there's any relevant outer context, and if so set it up to
 
681
         * be serialized. */
 
682
        if (cf.outer != null)
 
683
            outputs[CONTEXTS].putInt(getSerializedContextIdx(cf.outer));
 
684
        else
 
685
            outputs[CONTEXTS].putInt(0);
 
686
        
 
687
        /* Set up writer. */
 
688
        currentBuffer = CONTEXT_DATA;
 
689
 
 
690
        /* Serialize lexicals. */
 
691
        int numLexicals = 0;
 
692
        numLexicals += cf.oLex == null ? 0 : cf.oLex.length;
 
693
        numLexicals += cf.iLex == null ? 0 : cf.iLex.length;
 
694
        numLexicals += cf.nLex == null ? 0 : cf.nLex.length;
 
695
        numLexicals += cf.sLex == null ? 0 : cf.sLex.length;
 
696
        writeInt(numLexicals);
 
697
        if (cf.oLex != null) {
 
698
            String[] names = cf.codeRef.staticInfo.oLexicalNames;
 
699
            for (int i = 0; i < cf.oLex.length; i++) {
 
700
                writeStr(names[i]);
 
701
                writeRef(cf.oLex[i]);
 
702
            }
 
703
        }
 
704
        if (cf.iLex != null) {
 
705
            String[] names = cf.codeRef.staticInfo.iLexicalNames;
 
706
            for (int i = 0; i < cf.iLex.length; i++) {
 
707
                writeStr(names[i]);
 
708
                writeInt(cf.iLex[i]);
 
709
            }
 
710
        }
 
711
        if (cf.nLex != null) {
 
712
            String[] names = cf.codeRef.staticInfo.nLexicalNames;
 
713
            for (int i = 0; i < cf.nLex.length; i++) {
 
714
                writeStr(names[i]);
 
715
                writeNum(cf.nLex[i]);
 
716
            }
 
717
        }
 
718
        if (cf.sLex != null) {
 
719
            String[] names = cf.codeRef.staticInfo.sLexicalNames;
 
720
            for (int i = 0; i < cf.sLex.length; i++) {
 
721
                writeStr(names[i]);
 
722
                writeStr(cf.sLex[i]);
 
723
            }
 
724
        }
 
725
    }
 
726
 
 
727
    /* Grows a buffer as needed to hold more data. */
 
728
    private void growToHold(int idx, int required) {
 
729
        ByteBuffer check = this.outputs[idx];
 
730
        int position = check.position();
 
731
        if (position + required >= check.capacity()) {
 
732
            ByteBuffer replacement = ByteBuffer.allocate(
 
733
                Math.max(check.capacity() * 2, position + required));
 
734
            replacement.order(ByteOrder.LITTLE_ENDIAN);
 
735
            check.position(0);
 
736
            replacement.put(check);
 
737
            replacement.position(position);
 
738
            this.outputs[idx] = replacement;
 
739
        }
 
740
    }
 
741
    
 
742
    /* Goes through the list of repossessions and serializes them all. */
 
743
    private void serializeRepossessions() {
 
744
        /* Allocate table space, provided we've actually something to do. */
 
745
        int numRepos = sc.rep_indexes.size();
 
746
        if (numRepos == 0)
 
747
            return;
 
748
        growToHold(REPOS, numRepos * REPOS_TABLE_ENTRY_SIZE);
 
749
        
 
750
        /* Make entries. */
 
751
        for (int i = 0; i < numRepos; i++) {
 
752
            int objIdx = sc.rep_indexes.get(i) >> 1;
 
753
            int isST = sc.rep_indexes.get(i) & 1;
 
754
            SerializationContext origSC = sc.rep_scs.get(i);
 
755
 
 
756
            /* Work out original object's SC location. */
 
757
            int origSCIdx = getSCId(origSC);
 
758
            int origIdx = isST != 0
 
759
                ? origSC.root_stables.indexOf(sc.root_stables.get(objIdx))
 
760
                : origSC.root_objects.indexOf(sc.root_objects.get(objIdx));
 
761
            if (origIdx < 0)
 
762
                throw new RuntimeException(
 
763
                    "Could not find object when writing repossessions; " +
 
764
                    (isST != 0
 
765
                        ? "STable"
 
766
                        : "REPR = " + sc.root_objects.get(objIdx).st.REPR.name));
 
767
            
 
768
            /* Write table row. */
 
769
            outputs[REPOS].putInt(isST);
 
770
            outputs[REPOS].putInt(objIdx);
 
771
            outputs[REPOS].putInt(origSCIdx);
 
772
            outputs[REPOS].putInt(origIdx);
 
773
        }
 
774
    }
 
775
    
 
776
    /* This is the overall serialization loop. It keeps an index into the list of
 
777
     * STables and objects in the SC. As we discover new ones, they get added. We
 
778
     * finished when we've serialized everything. */
 
779
    private void serializationLoop() {
 
780
        boolean workTodo = true;
 
781
        while (workTodo) {
 
782
            /* Current work list sizes. */
 
783
            int sTablesTodo = sc.root_stables.size();
 
784
            int objectsTodo = sc.root_objects.size();
 
785
            int contextsTodo = contexts.size();
 
786
            
 
787
            /* Reset todo flag - if we do some work we'll go round again as it
 
788
             * may have generated more. */
 
789
            workTodo = false;
 
790
            
 
791
            /* Serialize any STables on the todo list. */
 
792
            while (sTablesListPos < sTablesTodo) {
 
793
                serializeStable(sc.root_stables.get(sTablesListPos));
 
794
                sTablesListPos++;
 
795
                workTodo = true;
 
796
            }
 
797
            
 
798
            /* Serialize any objects on the todo list. */
 
799
            while (objectsListPos < objectsTodo) {
 
800
                serializeObject(sc.root_objects.get(objectsListPos));
 
801
                objectsListPos++;
 
802
                workTodo = true;
 
803
            }
 
804
            
 
805
            /* Serialize any contexts on the todo list. */
 
806
             while (contextsListPos < contextsTodo) {
 
807
                serializeContext(contexts.get(contextsListPos));
 
808
                contextsListPos++;
 
809
                workTodo = true;
 
810
            }
 
811
        }
 
812
        
 
813
        /* Finally, serialize repossessions table (this can't make any more
 
814
         * work, so is done as a separate step here at the end). */
 
815
        serializeRepossessions();
 
816
    }
 
817
}