2
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
3
* Copyright 2009 Jeroen Frijters
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6
* This code is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 2 only, as
8
* published by the Free Software Foundation. Oracle designates this
9
* particular file as subject to the "Classpath" exception as provided
10
* by Oracle in the LICENSE file that accompanied this code.
12
* This code is distributed in the hope that it will be useful, but WITHOUT
13
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15
* version 2 for more details (a copy is included in the LICENSE file that
16
* accompanied this code).
18
* You should have received a copy of the GNU General Public License version
19
* 2 along with this work; if not, write to the Free Software Foundation,
20
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23
* or visit www.oracle.com if you need additional information or have any
28
import cli.System.Runtime.Serialization.IObjectReference;
29
import cli.System.Runtime.Serialization.SerializationException;
30
import cli.System.Runtime.Serialization.SerializationInfo;
31
import cli.System.Runtime.Serialization.StreamingContext;
32
import cli.System.SerializableAttribute;
33
import java.util.ArrayList;
36
public final class InteropObjectOutputStream extends ObjectOutputStream
38
private final ObjectDataOutputStream dos;
39
private Object curObj;
40
private ObjectStreamClass curDesc;
41
private PutFieldImpl curPut;
43
@SerializableAttribute.Annotation
44
private static final class ReplaceProxy implements IObjectReference
48
@cli.System.Security.SecurityCriticalAttribute.Annotation
49
public Object GetRealObject(StreamingContext context)
55
static final class DynamicProxy implements Serializable
59
private Object readResolve()
65
public static void writeObject(Object obj, SerializationInfo info)
69
boolean replaced = false;
70
Class cl = obj.getClass();
71
ObjectStreamClass desc;
75
desc = ObjectStreamClass.lookup(cl, true);
76
if (!desc.hasWriteReplaceMethod() ||
77
(obj = desc.invokeWriteReplace(obj)) == null ||
78
(repCl = obj.getClass()) == cl)
87
info.AddValue("obj", obj);
88
info.SetType(ikvm.runtime.Util.getInstanceTypeFromClass(ReplaceProxy.class));
92
new InteropObjectOutputStream(info, obj, cl, desc);
97
ikvm.runtime.Util.throwException(new SerializationException(x.getMessage(), x));
101
private InteropObjectOutputStream(SerializationInfo info, Object obj, Class cl, ObjectStreamClass desc) throws IOException
103
dos = new ObjectDataOutputStream(info);
104
if (obj instanceof ObjectStreamClass)
106
ObjectStreamClass osc = (ObjectStreamClass)obj;
113
writeNonProxyDesc(osc);
116
else if (obj instanceof Serializable)
118
if (desc.isDynamicClass())
120
info.SetType(ikvm.runtime.Util.getInstanceTypeFromClass(DynamicProxy.class));
122
writeOrdinaryObject(obj, desc);
126
throw new NotSerializableException(cl.getName());
131
private void writeProxyDesc(ObjectStreamClass desc) throws IOException
133
writeByte(TC_PROXYCLASSDESC);
134
Class cl = desc.forClass();
135
Class[] ifaces = cl.getInterfaces();
136
writeInt(ifaces.length);
137
for (int i = 0; i < ifaces.length; i++)
139
writeObject(ifaces[i]);
141
writeObject(desc.getSuperDesc());
144
private void writeNonProxyDesc(ObjectStreamClass desc) throws IOException
146
writeByte(TC_CLASSDESC);
147
writeObject(desc.forClass());
148
desc.writeNonProxy(this);
149
writeObject(desc.getSuperDesc());
152
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc) throws IOException
154
desc.checkSerialize();
156
if (desc.isExternalizable() && !desc.isProxy())
158
writeExternalData((Externalizable)obj);
162
writeSerialData(obj, desc);
166
private void writeExternalData(Externalizable obj) throws IOException
168
Object oldObj = curObj;
169
ObjectStreamClass oldDesc = curDesc;
170
PutFieldImpl oldPut = curPut;
175
obj.writeExternal(this);
183
private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException
185
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
186
for (int i = 0; i < slots.length; i++)
188
ObjectStreamClass slotDesc = slots[i].desc;
189
if (slotDesc.hasWriteObjectMethod())
191
Object oldObj = curObj;
192
ObjectStreamClass oldDesc = curDesc;
193
PutFieldImpl oldPut = curPut;
197
slotDesc.invokeWriteObject(obj, this);
205
defaultWriteFields(obj, slotDesc);
210
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException
212
desc.checkDefaultSerialize();
214
byte[] primVals = new byte[desc.getPrimDataSize()];
215
desc.getPrimFieldValues(obj, primVals);
218
Object[] objVals = new Object[desc.getNumObjFields()];
219
desc.getObjFieldValues(obj, objVals);
220
for (int i = 0; i < objVals.length; i++)
222
writeObject(objVals[i]);
227
public void defaultWriteObject() throws IOException
229
defaultWriteFields(curObj, curDesc);
235
throw new UnsupportedOperationException();
244
public ObjectOutputStream.PutField putFields() throws IOException
248
if (curObj == null || curDesc == null)
250
throw new NotActiveException("not in call to writeObject");
252
curPut = new PutFieldImpl(curDesc);
258
public void reset() throws IOException
260
throw new IOException("stream active");
264
protected void writeObjectOverride(Object obj)
266
// TODO consider tagging ghost arrays
267
dos.writeObject(obj);
271
public void writeFields() throws IOException
275
throw new NotActiveException("no current PutField object");
277
curPut.writeFields();
281
public void writeUnshared(Object obj)
283
throw new UnsupportedOperationException();
287
public void useProtocolVersion(int version)
289
throw new IllegalStateException("stream non-empty");
293
public void write(byte[] buf) throws IOException
295
write(buf, 0, buf.length);
299
public void write(int val) throws IOException
305
public void write(byte[] buf, int off, int len) throws IOException
307
dos.write(buf, off, len);
311
public void writeBoolean(boolean val) throws IOException
313
dos.writeBoolean(val);
317
public void writeByte(int val) throws IOException
323
public void writeBytes(String str) throws IOException
329
public void writeChar(int val) throws IOException
335
public void writeChars(String str) throws IOException
341
public void writeDouble(double val) throws IOException
343
dos.writeDouble(val);
347
public void writeFloat(float val) throws IOException
353
public void writeInt(int val) throws IOException
359
public void writeLong(long val) throws IOException
365
public void writeShort(int val) throws IOException
371
public void writeUTF(String str) throws IOException
376
// private API used by ObjectStreamClass
378
void writeTypeString(String str) throws IOException
383
private final class PutFieldImpl extends PutField {
385
/** class descriptor describing serializable fields */
386
private final ObjectStreamClass desc;
387
/** primitive field values */
388
private final byte[] primVals;
389
/** object field values */
390
private final Object[] objVals;
393
* Creates PutFieldImpl object for writing fields defined in given
396
PutFieldImpl(ObjectStreamClass desc) {
398
primVals = new byte[desc.getPrimDataSize()];
399
objVals = new Object[desc.getNumObjFields()];
402
public void put(String name, boolean val) {
403
Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
406
public void put(String name, byte val) {
407
primVals[getFieldOffset(name, Byte.TYPE)] = val;
410
public void put(String name, char val) {
411
Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
414
public void put(String name, short val) {
415
Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
418
public void put(String name, int val) {
419
Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
422
public void put(String name, float val) {
423
Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
426
public void put(String name, long val) {
427
Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
430
public void put(String name, double val) {
431
Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
434
public void put(String name, Object val) {
435
objVals[getFieldOffset(name, Object.class)] = val;
438
// deprecated in ObjectOutputStream.PutField
439
public void write(ObjectOutput out) throws IOException {
441
* Applications should *not* use this method to write PutField
442
* data, as it will lead to stream corruption if the PutField
443
* object writes any primitive data (since block data mode is not
444
* unset/set properly, as is done in OOS.writeFields()). This
445
* broken implementation is being retained solely for behavioral
446
* compatibility, in order to support applications which use
447
* OOS.PutField.write() for writing only non-primitive data.
449
* Serialization of unshared objects is not implemented here since
450
* it is not necessary for backwards compatibility; also, unshared
451
* semantics may not be supported by the given ObjectOutput
452
* instance. Applications which write unshared objects using the
453
* PutField API must use OOS.writeFields().
455
if (InteropObjectOutputStream.this != out) {
456
throw new IllegalArgumentException("wrong stream");
458
out.write(primVals, 0, primVals.length);
460
ObjectStreamField[] fields = desc.getFields(false);
461
int numPrimFields = fields.length - objVals.length;
462
// REMIND: warn if numPrimFields > 0?
463
for (int i = 0; i < objVals.length; i++) {
464
if (fields[numPrimFields + i].isUnshared()) {
465
throw new IOException("cannot write unshared object");
467
out.writeObject(objVals[i]);
472
* Writes buffered primitive data and object fields to stream.
474
void writeFields() throws IOException {
475
InteropObjectOutputStream.this.write(primVals, 0, primVals.length);
477
ObjectStreamField[] fields = desc.getFields(false);
478
int numPrimFields = fields.length - objVals.length;
479
for (int i = 0; i < objVals.length; i++) {
480
writeObject(objVals[i]);
485
* Returns offset of field with given name and type. A specified type
486
* of null matches all types, Object.class matches all non-primitive
487
* types, and any other non-null type matches assignable types only.
488
* Throws IllegalArgumentException if no matching field found.
490
private int getFieldOffset(String name, Class type) {
491
ObjectStreamField field = desc.getField(name, type);
493
throw new IllegalArgumentException("no such field " + name +
494
" with type " + type);
496
return field.getOffset();
500
private static final class ObjectDataOutputStream extends DataOutputStream
504
* blockStart objCount
505
* -1 0 Previous byte was a marker (or we're at the start of the stream), next byte will be MARKER, OBJECTS or BYTES
506
* >=0 0 We're currently writing a byte stream (that starts at blockStart + 1)
507
* -1 >0 We're currently writing objects (just a count of objects really)
510
private static final byte MARKER = 0;
511
private static final byte OBJECTS = 10;
512
private static final byte BYTES = 20;
513
private final SerializationInfo info;
514
private byte[] buf = new byte[16];
516
private int blockStart = -1;
517
private int objCount;
520
ObjectDataOutputStream(SerializationInfo info)
527
private void grow(int minGrow)
529
int newSize = buf.length + Math.max(buf.length, minGrow);
530
byte[] newBuf = new byte[newSize];
531
System.arraycopy(buf, 0, newBuf, 0, pos);
535
private void switchToData()
539
if (pos == buf.length)
547
int len = packedLength(objCount);
548
if (pos + len >= buf.length)
552
writePacked(pos, objCount);
557
// reserve space for the number of bytes
558
// (we assume that one byte will suffice, but if it won't the endData code will copy the data to make room)
559
if (pos == buf.length)
566
private void endData()
568
if (blockStart == -1)
570
if (pos == buf.length)
574
buf[pos++] = OBJECTS;
578
int len = (pos - blockStart) - 1;
579
int lenlen = packedLength(len);
583
if (buf.length - pos <= lenlen)
587
System.arraycopy(buf, blockStart + 1, buf, blockStart + 1 + lenlen, pos - (blockStart + 1));
590
writePacked(blockStart, len);
595
private int packedLength(int val)
599
// we only use packed integers for lengths or counts, so they can never be negative
606
else if (val < 16129)
616
private void writePacked(int pos, int v)
624
buf[pos + 0] = (byte)(128 | (v >> 7));
625
buf[pos + 1] = (byte)(v & 127);
629
buf[pos + 0] = (byte)128;
630
buf[pos + 1] = (byte)(v >>> 24);
631
buf[pos + 2] = (byte)(v >>> 16);
632
buf[pos + 3] = (byte)(v >>> 8);
633
buf[pos + 4] = (byte)(v >>> 0);
638
public void write(int b)
640
if (blockStart == -1)
644
if (pos == buf.length)
648
buf[pos++] = (byte)b;
652
public void write(byte[] b, int off, int len)
658
if (blockStart == -1)
662
if (pos + len >= buf.length)
666
System.arraycopy(b, off, buf, pos, len);
670
public void writeObject(Object obj)
677
info.AddValue("$" + (objId++), obj);
685
if (blockStart == -1)
687
// we've just written a marker, so we don't need to do anything else
699
info.AddValue("$data", buf);
704
if (buf.length != pos)
706
byte[] newBuf = new byte[pos];
707
System.arraycopy(buf, 0, newBuf, 0, pos);
717
public void writeMarker()
721
if (blockStart != -1)
725
if (pos == buf.length)