mirror of
https://github.com/corda/corda.git
synced 2025-01-19 11:16:54 +00:00
Merge pull request #94 from dscho/serialization
Implement Java-compatible serialization
This commit is contained in:
commit
d0d4f600dc
234
classpath/avian/LegacyObjectInputStream.java
Normal file
234
classpath/avian/LegacyObjectInputStream.java
Normal file
@ -0,0 +1,234 @@
|
||||
/* 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 java.io;
|
||||
|
||||
import avian.VMClass;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class LegacyObjectInputStream extends InputStream {
|
||||
private final InputStream in;
|
||||
private final PushbackReader r;
|
||||
|
||||
public LegacyObjectInputStream(InputStream in) {
|
||||
this.in = in;
|
||||
this.r = new PushbackReader(new InputStreamReader(in));
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
public int read(byte[] b, int offset, int length) throws IOException {
|
||||
return in.read(b, offset, length);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
public Object readObject() throws IOException, ClassNotFoundException {
|
||||
return readObject(new HashMap());
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
read('z');
|
||||
return readLongToken() != 0;
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
read('b');
|
||||
return (byte) readLongToken();
|
||||
}
|
||||
|
||||
public char readChar() throws IOException {
|
||||
read('c');
|
||||
return (char) readLongToken();
|
||||
}
|
||||
|
||||
public short readShort() throws IOException {
|
||||
read('s');
|
||||
return (short) readLongToken();
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
read('i');
|
||||
return (int) readLongToken();
|
||||
}
|
||||
|
||||
public long readLong() throws IOException {
|
||||
read('j');
|
||||
return readLongToken();
|
||||
}
|
||||
|
||||
public float readFloat() throws IOException {
|
||||
read('f');
|
||||
return (float) readDoubleToken();
|
||||
}
|
||||
|
||||
public double readDouble() throws IOException {
|
||||
read('d');
|
||||
return readDoubleToken();
|
||||
}
|
||||
|
||||
public void defaultReadObject() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void skipSpace() throws IOException {
|
||||
int c;
|
||||
while ((c = r.read()) != -1 && Character.isWhitespace((char) c));
|
||||
if (c != -1) {
|
||||
r.unread(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void read(char v) throws IOException {
|
||||
skipSpace();
|
||||
|
||||
int c = r.read();
|
||||
if (c != v) {
|
||||
if (c == -1) {
|
||||
throw new EOFException();
|
||||
} else {
|
||||
throw new StreamCorruptedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readStringToken() throws IOException {
|
||||
skipSpace();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int c;
|
||||
while ((c = r.read()) != -1 && ! Character.isWhitespace((char) c) && c != ')') {
|
||||
sb.append((char) c);
|
||||
}
|
||||
if (c != -1) {
|
||||
r.unread(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private long readLongToken() throws IOException {
|
||||
return Long.parseLong(readStringToken());
|
||||
}
|
||||
|
||||
private double readDoubleToken() throws IOException {
|
||||
return Double.parseDouble(readStringToken());
|
||||
}
|
||||
|
||||
private Object readObject(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
skipSpace();
|
||||
switch (r.read()) {
|
||||
case 'a':
|
||||
return deserializeArray(map);
|
||||
case 'l':
|
||||
return deserializeObject(map);
|
||||
case 'n':
|
||||
return null;
|
||||
case -1:
|
||||
throw new EOFException();
|
||||
default:
|
||||
throw new StreamCorruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
private Object deserialize(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
skipSpace();
|
||||
switch (r.read()) {
|
||||
case 'a':
|
||||
return deserializeArray(map);
|
||||
case 'l':
|
||||
return deserializeObject(map);
|
||||
case 'r':
|
||||
return map.get((int) readLongToken());
|
||||
case 'n':
|
||||
return null;
|
||||
case 'z':
|
||||
return (readLongToken() != 0);
|
||||
case 'b':
|
||||
return (byte) readLongToken();
|
||||
case 'c':
|
||||
return (char) readLongToken();
|
||||
case 's':
|
||||
return (short) readLongToken();
|
||||
case 'i':
|
||||
return (int) readLongToken();
|
||||
case 'j':
|
||||
return readLongToken();
|
||||
case 'f':
|
||||
return (float) readDoubleToken();
|
||||
case 'd':
|
||||
return readDoubleToken();
|
||||
case -1:
|
||||
throw new EOFException();
|
||||
default:
|
||||
throw new StreamCorruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
private Object deserializeArray(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
read('(');
|
||||
int id = (int) readLongToken();
|
||||
Class c = Class.forName(readStringToken());
|
||||
int length = (int) readLongToken();
|
||||
Class t = c.getComponentType();
|
||||
Object o = Array.newInstance(t, length);
|
||||
|
||||
map.put(id, o);
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
Array.set(o, i, deserialize(map));
|
||||
}
|
||||
|
||||
read(')');
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
private static native Object makeInstance(VMClass c);
|
||||
|
||||
private Object deserializeObject(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
read('(');
|
||||
int id = (int) readLongToken();
|
||||
Class c = Class.forName(readStringToken());
|
||||
Object o = makeInstance(c.vmClass);
|
||||
|
||||
map.put(id, o);
|
||||
|
||||
for (Field f: c.getAllFields()) {
|
||||
int modifiers = f.getModifiers();
|
||||
if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) {
|
||||
try {
|
||||
f.set(o, deserialize(map));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read(')');
|
||||
|
||||
return o;
|
||||
}
|
||||
}
|
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(")");
|
||||
}
|
||||
|
||||
}
|
51
classpath/java/io/FilterReader.java
Normal file
51
classpath/java/io/FilterReader.java
Normal file
@ -0,0 +1,51 @@
|
||||
/* 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 java.io;
|
||||
|
||||
public abstract class FilterReader extends Reader {
|
||||
protected Reader in;
|
||||
|
||||
protected FilterReader(Reader in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
public int read(char[] buffer, int offset, int length) throws IOException {
|
||||
return in.read(buffer, offset, length);
|
||||
}
|
||||
|
||||
public boolean ready() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
return in.markSupported();
|
||||
}
|
||||
|
||||
public void mark(int readAheadLimit) throws IOException {
|
||||
in.mark(readAheadLimit);
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
in.reset();
|
||||
}
|
||||
}
|
@ -10,225 +10,434 @@
|
||||
|
||||
package java.io;
|
||||
|
||||
import static java.io.ObjectOutputStream.STREAM_MAGIC;
|
||||
import static java.io.ObjectOutputStream.STREAM_VERSION;
|
||||
import static java.io.ObjectOutputStream.TC_NULL;
|
||||
import static java.io.ObjectOutputStream.TC_REFERENCE;
|
||||
import static java.io.ObjectOutputStream.TC_CLASSDESC;
|
||||
import static java.io.ObjectOutputStream.TC_OBJECT;
|
||||
import static java.io.ObjectOutputStream.TC_STRING;
|
||||
import static java.io.ObjectOutputStream.TC_ARRAY;
|
||||
import static java.io.ObjectOutputStream.TC_CLASS;
|
||||
import static java.io.ObjectOutputStream.TC_BLOCKDATA;
|
||||
import static java.io.ObjectOutputStream.TC_ENDBLOCKDATA;
|
||||
import static java.io.ObjectOutputStream.TC_RESET;
|
||||
import static java.io.ObjectOutputStream.TC_BLOCKDATALONG;
|
||||
import static java.io.ObjectOutputStream.TC_EXCEPTION;
|
||||
import static java.io.ObjectOutputStream.TC_LONGSTRING;
|
||||
import static java.io.ObjectOutputStream.TC_PROXYCLASSDESC;
|
||||
import static java.io.ObjectOutputStream.TC_ENUM;
|
||||
import static java.io.ObjectOutputStream.SC_WRITE_METHOD;
|
||||
import static java.io.ObjectOutputStream.SC_BLOCK_DATA;
|
||||
import static java.io.ObjectOutputStream.SC_SERIALIZABLE;
|
||||
import static java.io.ObjectOutputStream.SC_EXTERNALIZABLE;
|
||||
import static java.io.ObjectOutputStream.SC_ENUM;
|
||||
import static java.io.ObjectOutputStream.getReadOrWriteMethod;
|
||||
|
||||
import avian.VMClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class ObjectInputStream extends InputStream {
|
||||
private final InputStream in;
|
||||
private final PushbackReader r;
|
||||
public class ObjectInputStream extends InputStream implements DataInput {
|
||||
private final static int HANDLE_OFFSET = 0x7e0000;
|
||||
|
||||
public ObjectInputStream(InputStream in) {
|
||||
private final InputStream in;
|
||||
private final ArrayList references;
|
||||
|
||||
public ObjectInputStream(InputStream in) throws IOException {
|
||||
this.in = in;
|
||||
this.r = new PushbackReader(new InputStreamReader(in));
|
||||
short signature = (short)rawShort();
|
||||
if (signature != STREAM_MAGIC) {
|
||||
throw new IOException("Unrecognized signature: 0x"
|
||||
+ Integer.toHexString(signature));
|
||||
}
|
||||
int version = rawShort();
|
||||
if (version != STREAM_VERSION) {
|
||||
throw new IOException("Unsupported version: " + version);
|
||||
}
|
||||
references = new ArrayList();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
private int rawByte() throws IOException {
|
||||
int c = read();
|
||||
if (c < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private int rawShort() throws IOException {
|
||||
return (rawByte() << 8) | rawByte();
|
||||
}
|
||||
|
||||
private int rawInt() throws IOException {
|
||||
return (rawShort() << 16) | rawShort();
|
||||
}
|
||||
|
||||
private long rawLong() throws IOException {
|
||||
return ((rawInt() & 0xffffffffl) << 32) | rawInt();
|
||||
}
|
||||
|
||||
private String rawString() throws IOException {
|
||||
int length = rawShort();
|
||||
byte[] array = new byte[length];
|
||||
readFully(array);
|
||||
return new String(array);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int offset, int length) throws IOException {
|
||||
return in.read(b, offset, length);
|
||||
}
|
||||
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void readFully(byte[] b, int offset, int length) throws IOException {
|
||||
while (length > 0) {
|
||||
int count = read(b, offset, length);
|
||||
if (count < 0) {
|
||||
throw new EOFException("Reached EOF " + length + " bytes too early");
|
||||
}
|
||||
offset += count;
|
||||
length -= count;
|
||||
}
|
||||
}
|
||||
|
||||
public String readLine() throws IOException {
|
||||
int c = read();
|
||||
if (c < 0) {
|
||||
return null;
|
||||
} else if (c == '\n') {
|
||||
return "";
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (;;) {
|
||||
builder.append((char)c);
|
||||
c = read();
|
||||
if (c < 0 || c == '\n') {
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
public Object readObject() throws IOException, ClassNotFoundException {
|
||||
return readObject(new HashMap());
|
||||
private int remainingBlockData;
|
||||
|
||||
private int rawBlockDataByte() throws IOException {
|
||||
while (remainingBlockData <= 0) {
|
||||
int b = rawByte();
|
||||
if (b == TC_BLOCKDATA) {
|
||||
remainingBlockData = rawByte();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unknown token: 0x"
|
||||
+ Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
--remainingBlockData;
|
||||
return rawByte();
|
||||
}
|
||||
|
||||
private int rawBlockDataShort() throws IOException {
|
||||
return (rawBlockDataByte() << 8) | rawBlockDataByte();
|
||||
}
|
||||
|
||||
private int rawBlockDataInt() throws IOException {
|
||||
return (rawBlockDataShort() << 16) | rawBlockDataShort();
|
||||
}
|
||||
|
||||
private long rawBlockDataLong() throws IOException {
|
||||
return ((rawBlockDataInt() & 0xffffffffl) << 32) | rawBlockDataInt();
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
read('z');
|
||||
return readLongToken() != 0;
|
||||
return rawBlockDataByte() != 0;
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
read('b');
|
||||
return (byte) readLongToken();
|
||||
return (byte)rawBlockDataByte();
|
||||
}
|
||||
|
||||
public char readChar() throws IOException {
|
||||
read('c');
|
||||
return (char) readLongToken();
|
||||
return (char)rawBlockDataShort();
|
||||
}
|
||||
|
||||
public short readShort() throws IOException {
|
||||
read('s');
|
||||
return (short) readLongToken();
|
||||
return (short)rawBlockDataShort();
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
read('i');
|
||||
return (int) readLongToken();
|
||||
return rawBlockDataInt();
|
||||
}
|
||||
|
||||
public long readLong() throws IOException {
|
||||
read('j');
|
||||
return readLongToken();
|
||||
return rawBlockDataLong();
|
||||
}
|
||||
|
||||
public float readFloat() throws IOException {
|
||||
read('f');
|
||||
return (float) readDoubleToken();
|
||||
return Float.intBitsToFloat(rawBlockDataInt());
|
||||
}
|
||||
|
||||
public double readDouble() throws IOException {
|
||||
read('d');
|
||||
return readDoubleToken();
|
||||
}
|
||||
|
||||
public void defaultReadObject() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
return Double.longBitsToDouble(rawBlockDataLong());
|
||||
}
|
||||
|
||||
private void skipSpace() throws IOException {
|
||||
int c;
|
||||
while ((c = r.read()) != -1 && Character.isWhitespace((char) c));
|
||||
if (c != -1) {
|
||||
r.unread(c);
|
||||
public int readUnsignedByte() throws IOException {
|
||||
return rawBlockDataByte();
|
||||
}
|
||||
|
||||
public int readUnsignedShort() throws IOException {
|
||||
return rawBlockDataShort();
|
||||
}
|
||||
|
||||
public String readUTF() throws IOException {
|
||||
int length = rawBlockDataShort();
|
||||
if (remainingBlockData < length) {
|
||||
throw new IOException("Short block data: "
|
||||
+ remainingBlockData + " < " + length);
|
||||
}
|
||||
byte[] bytes = new byte[length];
|
||||
readFully(bytes);
|
||||
remainingBlockData -= length;
|
||||
return new String(bytes, "UTF-8");
|
||||
}
|
||||
|
||||
public int skipBytes(int count) throws IOException {
|
||||
int i = 0;
|
||||
while (i < count) {
|
||||
if (read() < 0) {
|
||||
return i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static Class charToPrimitiveType(int c) {
|
||||
if (c == 'B') {
|
||||
return Byte.TYPE;
|
||||
} else if (c == 'C') {
|
||||
return Character.TYPE;
|
||||
} else if (c == 'D') {
|
||||
return Double.TYPE;
|
||||
} else if (c == 'F') {
|
||||
return Float.TYPE;
|
||||
} else if (c == 'I') {
|
||||
return Integer.TYPE;
|
||||
} else if (c == 'J') {
|
||||
return Long.TYPE;
|
||||
} else if (c == 'S') {
|
||||
return Short.TYPE;
|
||||
} else if (c == 'Z') {
|
||||
return Boolean.TYPE;
|
||||
}
|
||||
throw new RuntimeException("Unhandled char: " + (char)c);
|
||||
}
|
||||
|
||||
private void expectToken(int token) throws IOException {
|
||||
int c = rawByte();
|
||||
if (c != token) {
|
||||
throw new UnsupportedOperationException("Unexpected token: 0x"
|
||||
+ Integer.toHexString(c));
|
||||
}
|
||||
}
|
||||
|
||||
private void read(char v) throws IOException {
|
||||
skipSpace();
|
||||
|
||||
int c = r.read();
|
||||
if (c != v) {
|
||||
if (c == -1) {
|
||||
throw new EOFException();
|
||||
private void field(Field field, Object o)
|
||||
throws IOException, IllegalArgumentException, IllegalAccessException,
|
||||
ClassNotFoundException
|
||||
{
|
||||
Class type = field.getType();
|
||||
if (!type.isPrimitive()) {
|
||||
field.set(o, readObject());
|
||||
} else {
|
||||
if (type == Byte.TYPE) {
|
||||
field.setByte(o, (byte)rawByte());
|
||||
} else if (type == Character.TYPE) {
|
||||
field.setChar(o, (char)rawShort());
|
||||
} else if (type == Double.TYPE) {
|
||||
field.setDouble(o, Double.longBitsToDouble(rawLong()));
|
||||
} else if (type == Float.TYPE) {
|
||||
field.setFloat(o, Float.intBitsToFloat(rawInt()));
|
||||
} else if (type == Integer.TYPE) {
|
||||
field.setInt(o, rawInt());
|
||||
} else if (type == Long.TYPE) {
|
||||
field.setLong(o, rawLong());
|
||||
} else if (type == Short.TYPE) {
|
||||
field.setShort(o, (short)rawShort());
|
||||
} else if (type == Boolean.TYPE) {
|
||||
field.setBoolean(o, rawByte() != 0);
|
||||
} else {
|
||||
throw new StreamCorruptedException();
|
||||
throw new IOException("Unhandled type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readStringToken() throws IOException {
|
||||
skipSpace();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int c;
|
||||
while ((c = r.read()) != -1 && ! Character.isWhitespace((char) c) && c != ')') {
|
||||
sb.append((char) c);
|
||||
}
|
||||
if (c != -1) {
|
||||
r.unread(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private long readLongToken() throws IOException {
|
||||
return Long.parseLong(readStringToken());
|
||||
}
|
||||
|
||||
private double readDoubleToken() throws IOException {
|
||||
return Double.parseDouble(readStringToken());
|
||||
}
|
||||
|
||||
private Object readObject(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
skipSpace();
|
||||
switch (r.read()) {
|
||||
case 'a':
|
||||
return deserializeArray(map);
|
||||
case 'l':
|
||||
return deserializeObject(map);
|
||||
case 'n':
|
||||
public Object readObject() throws IOException, ClassNotFoundException {
|
||||
int c = rawByte();
|
||||
if (c == TC_NULL) {
|
||||
return null;
|
||||
case -1:
|
||||
throw new EOFException();
|
||||
default:
|
||||
throw new StreamCorruptedException();
|
||||
}
|
||||
if (c == TC_STRING) {
|
||||
int length = rawShort();
|
||||
byte[] bytes = new byte[length];
|
||||
readFully(bytes);
|
||||
String s = new String(bytes, "UTF-8");
|
||||
references.add(s);
|
||||
return s;
|
||||
}
|
||||
if (c == TC_REFERENCE) {
|
||||
int handle = rawInt();
|
||||
return references.get(handle - HANDLE_OFFSET);
|
||||
}
|
||||
if (c != TC_OBJECT) {
|
||||
throw new IOException("Unexpected token: 0x"
|
||||
+ Integer.toHexString(c));
|
||||
}
|
||||
|
||||
// class desc
|
||||
c = rawByte();
|
||||
ClassDesc classDesc;
|
||||
if (c == TC_REFERENCE) {
|
||||
int handle = rawInt() - HANDLE_OFFSET;
|
||||
classDesc = (ClassDesc)references.get(handle);
|
||||
} else if (c == TC_CLASSDESC) {
|
||||
classDesc = classDesc();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unexpected token: 0x"
|
||||
+ Integer.toHexString(c));
|
||||
}
|
||||
|
||||
try {
|
||||
Object o = makeInstance(classDesc.clazz.vmClass);
|
||||
references.add(o);
|
||||
|
||||
do {
|
||||
Object o1 = classDesc.clazz.cast(o);
|
||||
boolean customized = (classDesc.flags & SC_WRITE_METHOD) != 0;
|
||||
Method readMethod = customized ?
|
||||
getReadOrWriteMethod(o, "readObject") : null;
|
||||
if (readMethod == null) {
|
||||
if (customized) {
|
||||
throw new IOException("Could not find required readObject method "
|
||||
+ "in " + classDesc.clazz);
|
||||
}
|
||||
defaultReadObject(o, classDesc.fields);
|
||||
} else {
|
||||
current = o1;
|
||||
currentFields = classDesc.fields;
|
||||
readMethod.invoke(o, this);
|
||||
current = null;
|
||||
currentFields = null;
|
||||
expectToken(TC_ENDBLOCKDATA);
|
||||
}
|
||||
} while ((classDesc = classDesc.superClassDesc) != null);
|
||||
|
||||
return o;
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Object deserialize(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
skipSpace();
|
||||
switch (r.read()) {
|
||||
case 'a':
|
||||
return deserializeArray(map);
|
||||
case 'l':
|
||||
return deserializeObject(map);
|
||||
case 'r':
|
||||
return map.get((int) readLongToken());
|
||||
case 'n':
|
||||
return null;
|
||||
case 'z':
|
||||
return (readLongToken() != 0);
|
||||
case 'b':
|
||||
return (byte) readLongToken();
|
||||
case 'c':
|
||||
return (char) readLongToken();
|
||||
case 's':
|
||||
return (short) readLongToken();
|
||||
case 'i':
|
||||
return (int) readLongToken();
|
||||
case 'j':
|
||||
return readLongToken();
|
||||
case 'f':
|
||||
return (float) readDoubleToken();
|
||||
case 'd':
|
||||
return readDoubleToken();
|
||||
case -1:
|
||||
throw new EOFException();
|
||||
default:
|
||||
throw new StreamCorruptedException();
|
||||
}
|
||||
private static class ClassDesc {
|
||||
Class clazz;
|
||||
int flags;
|
||||
Field[] fields;
|
||||
ClassDesc superClassDesc;
|
||||
}
|
||||
|
||||
private Object deserializeArray(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
read('(');
|
||||
int id = (int) readLongToken();
|
||||
Class c = Class.forName(readStringToken());
|
||||
int length = (int) readLongToken();
|
||||
Class t = c.getComponentType();
|
||||
Object o = Array.newInstance(t, length);
|
||||
private ClassDesc classDesc() throws ClassNotFoundException, IOException {
|
||||
ClassDesc result = new ClassDesc();
|
||||
String className = rawString();
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
result.clazz = loader.loadClass(className);
|
||||
long serialVersionUID = rawLong();
|
||||
try {
|
||||
Field field = result.clazz.getField("serialVersionUID");
|
||||
long expected = field.getLong(null);
|
||||
if (expected != serialVersionUID) {
|
||||
throw new IOException("Incompatible serial version UID: 0x"
|
||||
+ Long.toHexString(serialVersionUID) + " != 0x"
|
||||
+ Long.toHexString(expected));
|
||||
}
|
||||
} catch (Exception ignored) { }
|
||||
references.add(result);
|
||||
|
||||
map.put(id, o);
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
Array.set(o, i, deserialize(map));
|
||||
result.flags = rawByte();
|
||||
if ((result.flags & ~(SC_SERIALIZABLE | SC_WRITE_METHOD)) != 0) {
|
||||
throw new UnsupportedOperationException("Cannot handle flags: 0x"
|
||||
+ Integer.toHexString(result.flags));
|
||||
}
|
||||
|
||||
read(')');
|
||||
int fieldCount = rawShort();
|
||||
result.fields = new Field[fieldCount];
|
||||
for (int i = 0; i < result.fields.length; i++) {
|
||||
int typeChar = rawByte();
|
||||
String fieldName = rawString();
|
||||
try {
|
||||
result.fields[i] = result.clazz.getDeclaredField(fieldName);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
Class type;
|
||||
if (typeChar == '[' || typeChar == 'L') {
|
||||
String typeName = (String)readObject();
|
||||
if (typeName.startsWith("L") && typeName.endsWith(";")) {
|
||||
typeName = typeName.substring(1, typeName.length() - 1)
|
||||
.replace('/', '.');
|
||||
}
|
||||
type = loader.loadClass(typeName);
|
||||
} else {
|
||||
type = charToPrimitiveType(typeChar);
|
||||
}
|
||||
if (result.fields[i].getType() != type) {
|
||||
throw new IOException("Unexpected type of field " + fieldName
|
||||
+ ": expected " + result.fields[i].getType() + " but got " + type);
|
||||
}
|
||||
}
|
||||
expectToken(TC_ENDBLOCKDATA);
|
||||
int c = rawByte();
|
||||
if (c == TC_CLASSDESC) {
|
||||
result.superClassDesc = classDesc();
|
||||
} else if (c != TC_NULL) {
|
||||
throw new UnsupportedOperationException("Unexpected token: 0x"
|
||||
+ Integer.toHexString(c));
|
||||
}
|
||||
|
||||
return o;
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object current;
|
||||
private Field[] currentFields;
|
||||
|
||||
public void defaultReadObject() throws IOException {
|
||||
defaultReadObject(current, currentFields);
|
||||
}
|
||||
|
||||
private void defaultReadObject(Object o, Field[] fields) throws IOException {
|
||||
try {
|
||||
for (Field field : fields) {
|
||||
field(field, o);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static native Object makeInstance(VMClass c);
|
||||
|
||||
private Object deserializeObject(HashMap<Integer, Object> map)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
read('(');
|
||||
int id = (int) readLongToken();
|
||||
Class c = Class.forName(readStringToken());
|
||||
Object o = makeInstance(c.vmClass);
|
||||
|
||||
map.put(id, o);
|
||||
|
||||
for (Field f: c.getAllFields()) {
|
||||
int modifiers = f.getModifiers();
|
||||
if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) {
|
||||
try {
|
||||
f.set(o, deserialize(map));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read(')');
|
||||
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
@ -10,18 +10,44 @@
|
||||
|
||||
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.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class ObjectOutputStream extends OutputStream {
|
||||
private final PrintStream out;
|
||||
public class ObjectOutputStream extends OutputStream implements DataOutput {
|
||||
final static short STREAM_MAGIC = (short)0xaced;
|
||||
final static short STREAM_VERSION = 5;
|
||||
final static byte TC_NULL = (byte)0x70;
|
||||
final static byte TC_REFERENCE = (byte)0x71;
|
||||
final static byte TC_CLASSDESC = (byte)0x72;
|
||||
final static byte TC_OBJECT = (byte)0x73;
|
||||
final static byte TC_STRING = (byte)0x74;
|
||||
final static byte TC_ARRAY = (byte)0x75;
|
||||
final static byte TC_CLASS = (byte)0x76;
|
||||
final static byte TC_BLOCKDATA = (byte)0x77;
|
||||
final static byte TC_ENDBLOCKDATA = (byte)0x78;
|
||||
final static byte TC_RESET = (byte)0x79;
|
||||
final static byte TC_BLOCKDATALONG = (byte)0x7a;
|
||||
final static byte TC_EXCEPTION = (byte)0x7b;
|
||||
final static byte TC_LONGSTRING = (byte)0x7c;
|
||||
final static byte TC_PROXYCLASSDESC = (byte)0x7d;
|
||||
final static byte TC_ENUM = (byte)0x7e;
|
||||
final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE
|
||||
final static byte SC_BLOCK_DATA = 0x08; //if SC_EXTERNALIZABLE
|
||||
final static byte SC_SERIALIZABLE = 0x02;
|
||||
final static byte SC_EXTERNALIZABLE = 0x04;
|
||||
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 {
|
||||
out.write(c);
|
||||
}
|
||||
@ -38,169 +64,274 @@ 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, int scFlags) 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 | scFlags);
|
||||
|
||||
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;
|
||||
}
|
||||
if (o instanceof String) {
|
||||
byte[] bytes = ((String)o).getBytes("UTF-8");
|
||||
rawByte(TC_STRING);
|
||||
rawShort(bytes.length);
|
||||
write(bytes);
|
||||
return;
|
||||
}
|
||||
rawByte(TC_OBJECT);
|
||||
Method writeObject = getReadOrWriteMethod(o, "writeObject");
|
||||
if (writeObject == null) {
|
||||
classDesc(o.getClass(), 0);
|
||||
defaultWriteObject(o);
|
||||
} else try {
|
||||
classDesc(o.getClass(), SC_WRITE_METHOD);
|
||||
current = o;
|
||||
writeObject.invoke(o, this);
|
||||
current = null;
|
||||
rawByte(TC_ENDBLOCKDATA);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBoolean(boolean v) {
|
||||
out.print("z");
|
||||
out.print((v ? 1 : 0));
|
||||
static Method getReadOrWriteMethod(Object o, String methodName) {
|
||||
try {
|
||||
Method method = o.getClass().getDeclaredMethod(methodName,
|
||||
new Class[] { methodName.startsWith("write") ?
|
||||
ObjectOutputStream.class : ObjectInputStream.class });
|
||||
method.setAccessible(true);
|
||||
int modifiers = method.getModifiers();
|
||||
if ((modifiers & Modifier.STATIC) == 0 ||
|
||||
(modifiers & Modifier.PRIVATE) != 0) {
|
||||
return method;
|
||||
}
|
||||
} catch (NoSuchMethodException ignored) { }
|
||||
return null;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
private Object current;
|
||||
|
||||
public void defaultWriteObject() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
defaultWriteObject(current);
|
||||
}
|
||||
|
||||
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 defaultWriteObject(Object o) throws IOException {
|
||||
for (Field field : getFields(o.getClass())) {
|
||||
field(o, field);
|
||||
}
|
||||
}
|
||||
|
||||
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(")");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ public class Field<T> extends AccessibleObject {
|
||||
|
||||
case LongField:
|
||||
return Long.valueOf
|
||||
((int) getPrimitive(target, vmField.code, vmField.offset));
|
||||
(getPrimitive(target, vmField.code, vmField.offset));
|
||||
|
||||
case FloatField:
|
||||
return Float.valueOf
|
||||
@ -215,6 +215,85 @@ public class Field<T> extends AccessibleObject {
|
||||
}
|
||||
}
|
||||
|
||||
private void set(Object instance, long value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
Object target;
|
||||
if ((vmField.flags & Modifier.STATIC) != 0) {
|
||||
target = vmField.class_.staticTable;
|
||||
} else if (Class.isInstance(vmField.class_, instance)) {
|
||||
target = instance;
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
switch (vmField.code) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
case CharField:
|
||||
case ShortField:
|
||||
case IntField:
|
||||
case LongField:
|
||||
case FloatField:
|
||||
case DoubleField:
|
||||
setPrimitive(target, vmField.code, vmField.offset, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException
|
||||
("needed " + getType() + ", got primitive type when setting "
|
||||
+ Class.getName(vmField.class_) + "." + getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void setByte(Object instance, byte value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, value & 0xff);
|
||||
}
|
||||
|
||||
public void setBoolean(Object instance, boolean value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setChar(Object instance, char value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, value & 0xffff);
|
||||
}
|
||||
|
||||
public void setShort(Object instance, short value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, value & 0xffff);
|
||||
}
|
||||
|
||||
public void setInt(Object instance, int value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, value & 0xffffffffl);
|
||||
}
|
||||
|
||||
public void setLong(Object instance, long value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, value);
|
||||
}
|
||||
|
||||
public void setFloat(Object instance, float value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, Float.floatToIntBits(value));
|
||||
}
|
||||
|
||||
public void setDouble(Object instance, double value)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
set(instance, Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
private Annotation getAnnotation(Object[] a) {
|
||||
if (a[0] == null) {
|
||||
a[0] = Proxy.newProxyInstance
|
||||
|
@ -307,6 +307,13 @@ Avian_java_io_ObjectInputStream_makeInstance
|
||||
return reinterpret_cast<int64_t>(make(t, c));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT int64_t JNICALL
|
||||
Avian_avian_LegacyObjectInputStream_makeInstance
|
||||
(Thread* t, object, uintptr_t* arguments)
|
||||
{
|
||||
return Avian_java_io_ObjectInputStream_makeInstance(t, NULL, arguments);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT int64_t JNICALL
|
||||
Avian_java_lang_reflect_Field_getPrimitive
|
||||
(Thread* t, object, uintptr_t* arguments)
|
||||
|
116
test/Serialize.java
Normal file
116
test/Serialize.java
Normal file
@ -0,0 +1,116 @@
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Serialize implements Serializable {
|
||||
public static final long serialVersionUID = 1l;
|
||||
public int dummy = 0x12345678;
|
||||
private static void expect(boolean v) {
|
||||
if (! v) throw new RuntimeException();
|
||||
}
|
||||
|
||||
private static void expectEqual(boolean a, boolean b) {
|
||||
expect(a == b);
|
||||
}
|
||||
|
||||
private static void expectEqual(int a, int b) {
|
||||
expect(a == b);
|
||||
}
|
||||
|
||||
private static void expectEqual(String a, String b) {
|
||||
expect(a.equals(b));
|
||||
}
|
||||
|
||||
protected static void hexdump(byte[] a) {
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if ((i & 0xf) == 0) {
|
||||
System.err.println();
|
||||
}
|
||||
String hex = Integer.toHexString(a[i] & 0xff);
|
||||
System.err.print(" " + (hex.length() == 1 ? "0" : "") + hex);
|
||||
}
|
||||
System.err.println();
|
||||
}
|
||||
|
||||
private static void expectEqual(byte[] a, int[] b) {
|
||||
expect(a.length == b.length);
|
||||
|
||||
for (int i = 0; i < a.length; ++i) {
|
||||
expect(a[i] == (byte)b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ObjectOutputStream out2 = new ObjectOutputStream(out);
|
||||
out2.writeBoolean(true);
|
||||
out2.flush();
|
||||
out2.writeByte(17);
|
||||
out2.flush();
|
||||
out2.writeInt(0xcafebabe);
|
||||
out2.flush();
|
||||
out2.writeUTF("Max & Möritz");
|
||||
out2.flush();
|
||||
out2.writeChar('ɛ');
|
||||
out2.flush();
|
||||
out2.writeObject(new Serialize());
|
||||
out2.close();
|
||||
byte[] array = out.toByteArray();
|
||||
expectEqual(array, new int[] {
|
||||
// magic
|
||||
0xac, 0xed,
|
||||
// version
|
||||
0x00, 0x05,
|
||||
// blockdata, length
|
||||
0x77, 0x1,
|
||||
// true
|
||||
1,
|
||||
// blockdata, length
|
||||
0x77, 0x1,
|
||||
// (byte)17
|
||||
17,
|
||||
// blockdata, length
|
||||
0x77, 0x4,
|
||||
// 0xcafebabe
|
||||
0xca, 0xfe, 0xba, 0xbe,
|
||||
// blockdata, length
|
||||
0x77, 0xf,
|
||||
// "Max & Möritz"
|
||||
0x0, 0xd, 'M', 'a', 'x', ' ', '&', ' ', 'M', 0xc3, 0xb6, 'r', 'i', 't', 'z',
|
||||
// blockdata, length
|
||||
0x77, 0x2,
|
||||
// 'ö'
|
||||
0x02, 0x5b,
|
||||
// object
|
||||
0x73,
|
||||
// class desc, "Serialize"
|
||||
0x72, 0, 9, 'S', 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'e',
|
||||
// serialVersionUID
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
// flags (SC_SERIALIZABLE)
|
||||
2,
|
||||
// field count
|
||||
0x0, 0x1,
|
||||
// int dummy
|
||||
'I', 0x0, 0x5, 'd', 'u', 'm', 'm', 'y',
|
||||
// class annotation
|
||||
0x78,
|
||||
// super class desc
|
||||
0x70,
|
||||
// classdata[]
|
||||
0x12, 0x34, 0x56, 0x78
|
||||
});
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(array);
|
||||
ObjectInputStream in2 = new ObjectInputStream(in);
|
||||
expectEqual(true, in2.readBoolean());
|
||||
expectEqual(17, in2.readByte());
|
||||
expectEqual(0xcafebabe, in2.readInt());
|
||||
expectEqual("Max & Möritz", in2.readUTF());
|
||||
expectEqual('ɛ', in2.readChar());
|
||||
Serialize unserialized = (Serialize) in2.readObject();
|
||||
expectEqual(0x12345678, unserialized.dummy);
|
||||
in2.close();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user