mirror of
https://github.com/corda/corda.git
synced 2025-01-19 11:16:54 +00:00
Implement a Java-compatible ObjectOutputStream
The Java Language Specification documents the serialization protocol implemented by this change set: http://docs.oracle.com/javase/7/docs/platform/serialization/spec/protocol.html#10258 This change is intended to make it easier to use Avian VM as a drop-in replacement for the Oracle JVM when serializing objects. The previous serialization code is still available as avian.LegacyObjectOutputStream. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
parent
f2dd4add26
commit
c2a6f4a726
211
classpath/avian/LegacyObjectOutputStream.java
Normal file
211
classpath/avian/LegacyObjectOutputStream.java
Normal file
@ -0,0 +1,211 @@
|
||||
/* Copyright (c) 2008-2013, Avian Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
There is NO WARRANTY for this software. See license.txt for
|
||||
details. */
|
||||
|
||||
package avian;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.NotSerializableException;
|
||||
|
||||
public class LegacyObjectOutputStream extends OutputStream {
|
||||
private final PrintStream out;
|
||||
|
||||
public LegacyObjectOutputStream(OutputStream out) {
|
||||
this.out = new PrintStream(out);
|
||||
}
|
||||
|
||||
public void write(int c) throws IOException {
|
||||
out.write(c);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int offset, int length) throws IOException {
|
||||
out.write(b, offset, length);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
public void writeObject(Object o) throws IOException {
|
||||
writeObject(o, new IdentityHashMap(), new int[] {0});
|
||||
}
|
||||
|
||||
public void writeBoolean(boolean v) {
|
||||
out.print("z");
|
||||
out.print((v ? 1 : 0));
|
||||
}
|
||||
|
||||
public void writeByte(byte v) {
|
||||
out.print("b");
|
||||
out.print((int) v);
|
||||
}
|
||||
|
||||
public void writeChar(char v) {
|
||||
out.print("c");
|
||||
out.print((int) v);
|
||||
}
|
||||
|
||||
public void writeShort(short v) {
|
||||
out.print("s");
|
||||
out.print((int) v);
|
||||
}
|
||||
|
||||
public void writeInt(int v) {
|
||||
out.print("i");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void writeLong(long v) {
|
||||
out.print("j");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void writeFloat(float v) {
|
||||
out.print("f");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void writeDouble(double v) {
|
||||
out.print("d");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void defaultWriteObject() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void writeObject(Object o, IdentityHashMap<Object, Integer> map,
|
||||
int[] nextId)
|
||||
throws IOException
|
||||
{
|
||||
if (o == null) {
|
||||
out.print("n");
|
||||
} else {
|
||||
Integer id = map.get(o);
|
||||
if (id == null) {
|
||||
map.put(o, nextId[0]);
|
||||
|
||||
Class c = o.getClass();
|
||||
if (c.isArray()) {
|
||||
serializeArray(o, map, nextId);
|
||||
} else if (Serializable.class.isAssignableFrom(c)) {
|
||||
serializeObject(o, map, nextId);
|
||||
} else {
|
||||
throw new NotSerializableException(c.getName());
|
||||
}
|
||||
} else {
|
||||
out.print("r");
|
||||
out.print(id.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void serializeArray(Object o, IdentityHashMap<Object, Integer> map,
|
||||
int[] nextId)
|
||||
throws IOException
|
||||
{
|
||||
Class c = o.getClass();
|
||||
Class t = c.getComponentType();
|
||||
int length = Array.getLength(o);
|
||||
|
||||
out.print("a(");
|
||||
out.print(nextId[0]++);
|
||||
out.print(" ");
|
||||
out.print(c.getName());
|
||||
out.print(" ");
|
||||
out.print(length);
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
out.print(" ");
|
||||
Object v = Array.get(o, i);
|
||||
if (t.equals(boolean.class)) {
|
||||
writeBoolean((Boolean) v);
|
||||
} else if (t.equals(byte.class)) {
|
||||
writeByte((Byte) v);
|
||||
} else if (t.equals(char.class)) {
|
||||
writeChar((Character) v);
|
||||
} else if (t.equals(short.class)) {
|
||||
writeShort((Short) v);
|
||||
} else if (t.equals(int.class)) {
|
||||
writeInt((Integer) v);
|
||||
} else if (t.equals(long.class)) {
|
||||
writeLong((Long) v);
|
||||
} else if (t.equals(float.class)) {
|
||||
writeFloat((Float) v);
|
||||
} else if (t.equals(double.class)) {
|
||||
writeDouble((Double) v);
|
||||
} else {
|
||||
writeObject(v, map, nextId);
|
||||
}
|
||||
}
|
||||
|
||||
out.print(")");
|
||||
}
|
||||
|
||||
private void serializeObject(Object o, IdentityHashMap<Object, Integer> map,
|
||||
int[] nextId)
|
||||
throws IOException
|
||||
{
|
||||
Class c = o.getClass();
|
||||
|
||||
out.print("l(");
|
||||
out.print(nextId[0]++);
|
||||
out.print(" ");
|
||||
out.print(c.getName());
|
||||
|
||||
for (Field f: c.getAllFields()) {
|
||||
int modifiers = f.getModifiers();
|
||||
if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) {
|
||||
out.print(" ");
|
||||
Object v;
|
||||
|
||||
try {
|
||||
v = f.get(o);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Class t = f.getType();
|
||||
if (t.equals(boolean.class)) {
|
||||
writeBoolean((Boolean) v);
|
||||
} else if (t.equals(byte.class)) {
|
||||
writeByte((Byte) v);
|
||||
} else if (t.equals(char.class)) {
|
||||
writeChar((Character) v);
|
||||
} else if (t.equals(short.class)) {
|
||||
writeShort((Short) v);
|
||||
} else if (t.equals(int.class)) {
|
||||
writeInt((Integer) v);
|
||||
} else if (t.equals(long.class)) {
|
||||
writeLong((Long) v);
|
||||
} else if (t.equals(float.class)) {
|
||||
writeFloat((Float) v);
|
||||
} else if (t.equals(double.class)) {
|
||||
writeDouble((Double) v);
|
||||
} else {
|
||||
writeObject(v, map, nextId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.print(")");
|
||||
}
|
||||
|
||||
}
|
@ -10,16 +10,41 @@
|
||||
|
||||
package java.io;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class ObjectOutputStream extends OutputStream {
|
||||
private final PrintStream out;
|
||||
public class ObjectOutputStream extends OutputStream implements DataOutput {
|
||||
private final static short STREAM_MAGIC = (short)0xaced;
|
||||
private final static short STREAM_VERSION = 5;
|
||||
private final static byte TC_NULL = (byte)0x70;
|
||||
private final static byte TC_REFERENCE = (byte)0x71;
|
||||
private final static byte TC_CLASSDESC = (byte)0x72;
|
||||
private final static byte TC_OBJECT = (byte)0x73;
|
||||
private final static byte TC_STRING = (byte)0x74;
|
||||
private final static byte TC_ARRAY = (byte)0x75;
|
||||
private final static byte TC_CLASS = (byte)0x76;
|
||||
private final static byte TC_BLOCKDATA = (byte)0x77;
|
||||
private final static byte TC_ENDBLOCKDATA = (byte)0x78;
|
||||
private final static byte TC_RESET = (byte)0x79;
|
||||
private final static byte TC_BLOCKDATALONG = (byte)0x7a;
|
||||
private final static byte TC_EXCEPTION = (byte)0x7b;
|
||||
private final static byte TC_LONGSTRING = (byte)0x7c;
|
||||
private final static byte TC_PROXYCLASSDESC = (byte)0x7d;
|
||||
private final static byte TC_ENUM = (byte)0x7e;
|
||||
private final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE
|
||||
private final static byte SC_BLOCK_DATA = 0x08; //if SC_EXTERNALIZABLE
|
||||
private final static byte SC_SERIALIZABLE = 0x02;
|
||||
private final static byte SC_EXTERNALIZABLE = 0x04;
|
||||
private final static byte SC_ENUM = 0x10;
|
||||
|
||||
public ObjectOutputStream(OutputStream out) {
|
||||
this.out = new PrintStream(out);
|
||||
private final OutputStream out;
|
||||
|
||||
public ObjectOutputStream(OutputStream out) throws IOException {
|
||||
this.out = out;
|
||||
rawShort(STREAM_MAGIC);
|
||||
rawShort(STREAM_VERSION);
|
||||
}
|
||||
|
||||
public void write(int c) throws IOException {
|
||||
@ -38,169 +63,235 @@ public class ObjectOutputStream extends OutputStream {
|
||||
out.close();
|
||||
}
|
||||
|
||||
private void rawByte(int v) throws IOException {
|
||||
out.write((byte)(v & 0xff));
|
||||
}
|
||||
|
||||
private void rawShort(int v) throws IOException {
|
||||
rawByte(v >> 8);
|
||||
rawByte(v);
|
||||
}
|
||||
|
||||
private void rawInt(int v) throws IOException {
|
||||
rawShort(v >> 16);
|
||||
rawShort(v);
|
||||
}
|
||||
|
||||
private void rawLong(long v) throws IOException {
|
||||
rawInt((int)(v >> 32));
|
||||
rawInt((int)(v & 0xffffffffl));
|
||||
}
|
||||
|
||||
private void blockData(int... bytes) throws IOException {
|
||||
blockData(bytes, null, null);
|
||||
}
|
||||
|
||||
private void blockData(int[] bytes, byte[] bytes2, char[] chars) throws IOException {
|
||||
int count = (bytes == null ? 0 : bytes.length)
|
||||
+ (bytes2 == null ? 0 : bytes2.length)
|
||||
+ (chars == null ? 0 : chars.length * 2);
|
||||
if (count < 0x100) {
|
||||
rawByte(TC_BLOCKDATA);
|
||||
rawByte(count);
|
||||
} else {
|
||||
rawByte(TC_BLOCKDATALONG);
|
||||
rawInt(count);
|
||||
}
|
||||
if (bytes != null) {
|
||||
for (int b : bytes) {
|
||||
rawByte(b);
|
||||
}
|
||||
}
|
||||
if (bytes2 != null) {
|
||||
for (byte b : bytes2) {
|
||||
rawByte(b & 0xff);
|
||||
}
|
||||
}
|
||||
if (chars != null) {
|
||||
for (char c : chars) {
|
||||
rawShort((short)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBoolean(boolean v) throws IOException {
|
||||
blockData(v ? 1 : 0);
|
||||
}
|
||||
|
||||
public void writeByte(int v) throws IOException {
|
||||
blockData(v);
|
||||
}
|
||||
|
||||
public void writeShort(int v) throws IOException {
|
||||
blockData(v >> 8, v);
|
||||
}
|
||||
|
||||
public void writeChar(int v) throws IOException {
|
||||
blockData(v >> 8, v);
|
||||
}
|
||||
|
||||
public void writeInt(int v) throws IOException {
|
||||
blockData(v >> 24, v >> 16, v >> 8, v);
|
||||
}
|
||||
|
||||
public void writeLong(long v) throws IOException {
|
||||
int u = (int)(v >> 32), l = (int)(v & 0xffffffff);
|
||||
blockData(u >> 24, u >> 16, u >> 8, u, l >> 24, l >> 16, l >> 8, l);
|
||||
}
|
||||
|
||||
public void writeFloat(float v) throws IOException {
|
||||
writeInt(Float.floatToIntBits(v));
|
||||
}
|
||||
|
||||
public void writeDouble(double v) throws IOException {
|
||||
writeLong(Double.doubleToLongBits(v));
|
||||
}
|
||||
|
||||
public void writeBytes(String s) throws IOException {
|
||||
blockData(null, s.getBytes(), null);
|
||||
}
|
||||
|
||||
public void writeChars(String s) throws IOException {
|
||||
blockData(null, null, s.toCharArray());
|
||||
}
|
||||
|
||||
public void writeUTF(String s) throws IOException {
|
||||
byte[] bytes = s.getBytes();
|
||||
int length = bytes.length;
|
||||
blockData(new int[] { length >> 8, length }, bytes, null);
|
||||
}
|
||||
|
||||
private int classHandle;
|
||||
|
||||
private void string(String s) throws IOException {
|
||||
int length = s.length();
|
||||
rawShort(length);
|
||||
for (byte b : s.getBytes()) {
|
||||
rawByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
private static char primitiveTypeChar(Class type) {
|
||||
if (type == Byte.TYPE) {
|
||||
return 'B';
|
||||
} else if (type == Character.TYPE) {
|
||||
return 'C';
|
||||
} else if (type == Double.TYPE) {
|
||||
return 'D';
|
||||
} else if (type == Float.TYPE) {
|
||||
return 'F';
|
||||
} else if (type == Integer.TYPE) {
|
||||
return 'I';
|
||||
} else if (type == Long.TYPE) {
|
||||
return 'J';
|
||||
} else if (type == Short.TYPE) {
|
||||
return 'S';
|
||||
} else if (type == Boolean.TYPE) {
|
||||
return 'Z';
|
||||
}
|
||||
throw new RuntimeException("Unhandled primitive type: " + type);
|
||||
}
|
||||
|
||||
private void classDesc(Class clazz) throws IOException {
|
||||
rawByte(TC_CLASSDESC);
|
||||
|
||||
// class name
|
||||
string(clazz.getName());
|
||||
|
||||
// serial version UID
|
||||
long serialVersionUID = 1l;
|
||||
try {
|
||||
Field field = clazz.getField("serialVersionUID");
|
||||
serialVersionUID = field.getLong(null);
|
||||
} catch (Exception ignored) {}
|
||||
rawLong(serialVersionUID);
|
||||
|
||||
// handle
|
||||
rawByte(SC_SERIALIZABLE);
|
||||
|
||||
Field[] fields = getFields(clazz);
|
||||
rawShort(fields.length);
|
||||
for (Field field : fields) {
|
||||
Class fieldType = field.getType();
|
||||
if (fieldType.isPrimitive()) {
|
||||
rawByte(primitiveTypeChar(fieldType));
|
||||
string(field.getName());
|
||||
} else {
|
||||
rawByte(fieldType.isArray() ? '[' : 'L');
|
||||
string(field.getName());
|
||||
rawByte(TC_STRING);
|
||||
string("L" + fieldType.getName().replace('.', '/') + ";");
|
||||
}
|
||||
}
|
||||
rawByte(TC_ENDBLOCKDATA); // TODO: write annotation
|
||||
rawByte(TC_NULL); // super class desc
|
||||
}
|
||||
|
||||
private void field(Object o, Field field) throws IOException {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
Class type = field.getType();
|
||||
if (!type.isPrimitive()) {
|
||||
writeObject(field.get(o));
|
||||
} else if (type == Byte.TYPE) {
|
||||
rawByte(field.getByte(o));
|
||||
} else if (type == Character.TYPE) {
|
||||
char c = field.getChar(o);
|
||||
rawShort((short)c);
|
||||
} else if (type == Double.TYPE) {
|
||||
double d = field.getDouble(o);
|
||||
rawLong(Double.doubleToLongBits(d));
|
||||
} else if (type == Float.TYPE) {
|
||||
float f = field.getFloat(o);
|
||||
rawInt(Float.floatToIntBits(f));
|
||||
} else if (type == Integer.TYPE) {
|
||||
int i = field.getInt(o);
|
||||
rawInt(i);
|
||||
} else if (type == Long.TYPE) {
|
||||
long l = field.getLong(o);
|
||||
rawLong(l);
|
||||
} else if (type == Short.TYPE) {
|
||||
short s = field.getShort(o);
|
||||
rawShort(s);
|
||||
} else if (type == Boolean.TYPE) {
|
||||
boolean b = field.getBoolean(o);
|
||||
rawByte(b ? 1 : 0);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Field '" + field.getName()
|
||||
+ "' has unsupported type: " + type);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Field[] getFields(Class clazz) {
|
||||
ArrayList<Field> list = new ArrayList<Field>();
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (0 == (field.getModifiers() &
|
||||
(Modifier.STATIC | Modifier.TRANSIENT))) {
|
||||
list.add(field);
|
||||
}
|
||||
}
|
||||
return list.toArray(new Field[list.size()]);
|
||||
}
|
||||
|
||||
public void writeObject(Object o) throws IOException {
|
||||
writeObject(o, new IdentityHashMap(), new int[] {0});
|
||||
if (o == null) {
|
||||
rawByte(TC_NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
public void writeBoolean(boolean v) {
|
||||
out.print("z");
|
||||
out.print((v ? 1 : 0));
|
||||
rawByte(TC_OBJECT);
|
||||
classDesc(o.getClass());
|
||||
for (Field field : getFields(o.getClass())) {
|
||||
field(o, field);
|
||||
}
|
||||
|
||||
public void writeByte(byte v) {
|
||||
out.print("b");
|
||||
out.print((int) v);
|
||||
}
|
||||
|
||||
public void writeChar(char v) {
|
||||
out.print("c");
|
||||
out.print((int) v);
|
||||
}
|
||||
|
||||
public void writeShort(short v) {
|
||||
out.print("s");
|
||||
out.print((int) v);
|
||||
}
|
||||
|
||||
public void writeInt(int v) {
|
||||
out.print("i");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void writeLong(long v) {
|
||||
out.print("j");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void writeFloat(float v) {
|
||||
out.print("f");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void writeDouble(double v) {
|
||||
out.print("d");
|
||||
out.print(v);
|
||||
}
|
||||
|
||||
public void defaultWriteObject() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void writeObject(Object o, IdentityHashMap<Object, Integer> map,
|
||||
int[] nextId)
|
||||
throws IOException
|
||||
{
|
||||
if (o == null) {
|
||||
out.print("n");
|
||||
} else {
|
||||
Integer id = map.get(o);
|
||||
if (id == null) {
|
||||
map.put(o, nextId[0]);
|
||||
|
||||
Class c = o.getClass();
|
||||
if (c.isArray()) {
|
||||
serializeArray(o, map, nextId);
|
||||
} else if (Serializable.class.isAssignableFrom(c)) {
|
||||
serializeObject(o, map, nextId);
|
||||
} else {
|
||||
throw new NotSerializableException(c.getName());
|
||||
}
|
||||
} else {
|
||||
out.print("r");
|
||||
out.print(id.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void serializeArray(Object o, IdentityHashMap<Object, Integer> map,
|
||||
int[] nextId)
|
||||
throws IOException
|
||||
{
|
||||
Class c = o.getClass();
|
||||
Class t = c.getComponentType();
|
||||
int length = Array.getLength(o);
|
||||
|
||||
out.print("a(");
|
||||
out.print(nextId[0]++);
|
||||
out.print(" ");
|
||||
out.print(c.getName());
|
||||
out.print(" ");
|
||||
out.print(length);
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
out.print(" ");
|
||||
Object v = Array.get(o, i);
|
||||
if (t.equals(boolean.class)) {
|
||||
writeBoolean((Boolean) v);
|
||||
} else if (t.equals(byte.class)) {
|
||||
writeByte((Byte) v);
|
||||
} else if (t.equals(char.class)) {
|
||||
writeChar((Character) v);
|
||||
} else if (t.equals(short.class)) {
|
||||
writeShort((Short) v);
|
||||
} else if (t.equals(int.class)) {
|
||||
writeInt((Integer) v);
|
||||
} else if (t.equals(long.class)) {
|
||||
writeLong((Long) v);
|
||||
} else if (t.equals(float.class)) {
|
||||
writeFloat((Float) v);
|
||||
} else if (t.equals(double.class)) {
|
||||
writeDouble((Double) v);
|
||||
} else {
|
||||
writeObject(v, map, nextId);
|
||||
}
|
||||
}
|
||||
|
||||
out.print(")");
|
||||
}
|
||||
|
||||
private void serializeObject(Object o, IdentityHashMap<Object, Integer> map,
|
||||
int[] nextId)
|
||||
throws IOException
|
||||
{
|
||||
Class c = o.getClass();
|
||||
|
||||
out.print("l(");
|
||||
out.print(nextId[0]++);
|
||||
out.print(" ");
|
||||
out.print(c.getName());
|
||||
|
||||
for (Field f: c.getAllFields()) {
|
||||
int modifiers = f.getModifiers();
|
||||
if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) {
|
||||
out.print(" ");
|
||||
Object v;
|
||||
|
||||
try {
|
||||
v = f.get(o);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Class t = f.getType();
|
||||
if (t.equals(boolean.class)) {
|
||||
writeBoolean((Boolean) v);
|
||||
} else if (t.equals(byte.class)) {
|
||||
writeByte((Byte) v);
|
||||
} else if (t.equals(char.class)) {
|
||||
writeChar((Character) v);
|
||||
} else if (t.equals(short.class)) {
|
||||
writeShort((Short) v);
|
||||
} else if (t.equals(int.class)) {
|
||||
writeInt((Integer) v);
|
||||
} else if (t.equals(long.class)) {
|
||||
writeLong((Long) v);
|
||||
} else if (t.equals(float.class)) {
|
||||
writeFloat((Float) v);
|
||||
} else if (t.equals(double.class)) {
|
||||
writeDouble((Double) v);
|
||||
} else {
|
||||
writeObject(v, map, nextId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.print(")");
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user