Automagic .NET serialization support (for classes that are Java serializable).

This commit is contained in:
jfrijters 2009-08-11 05:58:44 +00:00
Родитель 4460da2db0
Коммит 2cb0073b40
6 изменённых файлов: 1444 добавлений и 240 удалений

Просмотреть файл

@ -23,7 +23,10 @@
*/
package ikvm.internal;
import cli.System.Runtime.Serialization.SerializationException;
import cli.System.Runtime.Serialization.SerializationInfo;
import cli.System.Security.Permissions.SecurityAction;
import cli.System.Security.Permissions.SecurityPermissionAttribute;
import java.io.InteropObjectInputStream;
import java.io.InteropObjectOutputStream;
import java.io.IOException;
@ -34,29 +37,15 @@ public final class Serialization
{
private Serialization() { }
public static ObjectOutputStream newOutputAdapter(Object obj, SerializationInfo info, Class clazz)
@SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.Demand, SerializationFormatter = true)
public static void writeObject(Object obj, SerializationInfo info)
{
try
{
return new InteropObjectOutputStream(obj, info, clazz);
}
catch (IOException x)
{
// this exception is impossible
throw (InternalError)new InternalError().initCause(x);
}
InteropObjectOutputStream.writeObject(obj, info);
}
public static ObjectInputStream newInputAdapter(Object obj, SerializationInfo info, Class clazz)
@SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.Demand, SerializationFormatter = true)
public static void readObject(Object obj, SerializationInfo info)
{
try
{
return new InteropObjectInputStream(obj, info, clazz);
}
catch (IOException x)
{
// this exception is impossible
throw (InternalError)new InternalError().initCause(x);
}
InteropObjectInputStream.readObject(obj, info);
}
}

Просмотреть файл

@ -1,57 +1,216 @@
/*
Copyright (C) 2009 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
* Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2009 Jeroen Frijters
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.io;
import cli.System.Runtime.Serialization.SerializationException;
import cli.System.Runtime.Serialization.SerializationInfo;
import java.util.concurrent.atomic.AtomicBoolean;
@ikvm.lang.Internal
public final class InteropObjectInputStream extends ObjectInputStream
{
private final Object obj;
private final SerializationInfo info;
private final ObjectStreamClass desc;
private ObjectDataInputStream dis;
private CallbackContext curContext;
public InteropObjectInputStream(Object obj, SerializationInfo info, Class clazz) throws IOException
public static void readObject(Object obj, SerializationInfo info)
{
this.obj = obj;
this.info = info;
this.desc = ObjectStreamClass.lookup(clazz);
try
{
new InteropObjectInputStream(obj, info);
}
catch (Exception x)
{
ikvm.runtime.Util.throwException(new SerializationException(x.getMessage(), x));
}
}
private InteropObjectInputStream(Object obj, SerializationInfo info) throws IOException, ClassNotFoundException
{
dis = new ObjectDataInputStream(info);
if (obj instanceof ObjectStreamClass)
{
switch (readByte())
{
case TC_PROXYCLASSDESC:
readProxyDesc((ObjectStreamClass)obj);
break;
case TC_CLASSDESC:
readNonProxyDesc((ObjectStreamClass)obj);
break;
default:
throw new StreamCorruptedException();
}
}
else
{
readOrdinaryObject(obj);
}
}
private ObjectStreamClass readClassDesc() throws IOException, ClassNotFoundException
{
return (ObjectStreamClass)readObject();
}
private void readProxyDesc(ObjectStreamClass desc) throws IOException, ClassNotFoundException
{
int numIfaces = readInt();
Class[] ifaces = new Class[numIfaces];
for (int i = 0; i < numIfaces; i++)
{
ifaces[i] = (Class)readObject();
}
Class cl = java.lang.reflect.Proxy.getProxyClass(Thread.currentThread().getContextClassLoader(), ifaces);
desc.initProxy(cl, null, readClassDesc());
}
private void readNonProxyDesc(ObjectStreamClass desc) throws IOException, ClassNotFoundException
{
Class cl = (Class)readObject();
ObjectStreamClass readDesc = new ObjectStreamClass();
readDesc.readNonProxy(this);
desc.initNonProxy(readDesc, cl, null, readClassDesc());
}
private void readOrdinaryObject(Object obj) throws IOException, ClassNotFoundException
{
ObjectStreamClass desc = readClassDesc();
desc.checkDeserialize();
if (obj instanceof InteropObjectOutputStream.DynamicProxy)
{
InteropObjectOutputStream.DynamicProxy pp = (InteropObjectOutputStream.DynamicProxy)obj;
try
{
obj = desc.newInstance();
pp.obj = obj;
}
catch (Exception ex)
{
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
}
if (desc.isExternalizable())
{
readExternalData((Externalizable)obj, desc);
}
else
{
readSerialData(obj, desc);
}
}
private void readExternalData(Externalizable obj, ObjectStreamClass desc) throws IOException, ClassNotFoundException
{
CallbackContext oldContext = curContext;
curContext = null;
try {
obj.readExternal(this);
skipCustomData();
} finally {
curContext = oldContext;
}
}
private void readSerialData(Object obj, ObjectStreamClass desc) throws IOException, ClassNotFoundException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++)
{
ObjectStreamClass slotDesc = slots[i].desc;
if (slots[i].hasData)
{
if (slotDesc.hasReadObjectMethod())
{
CallbackContext oldContext = curContext;
try
{
curContext = new CallbackContext(obj, slotDesc);
slotDesc.invokeReadObject(obj, this);
}
finally
{
curContext.setUsed();
curContext = oldContext;
}
}
else
{
defaultReadFields(obj, slotDesc);
}
if (slotDesc.hasWriteObjectData())
{
skipCustomData();
}
}
else if (slotDesc.hasReadObjectNoDataMethod())
{
slotDesc.invokeReadObjectNoData(obj);
}
}
}
private void skipCustomData() throws IOException
{
dis.skipMarker();
}
private void defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException, ClassNotFoundException
{
byte[] primVals = new byte[desc.getPrimDataSize()];
readFully(primVals);
desc.setPrimFieldValues(obj, primVals);
Object[] objVals = new Object[desc.getNumObjFields()];
for (int i = 0; i < objVals.length; i++)
{
objVals[i] = readObject();
}
desc.setObjFieldValues(obj, objVals);
}
@Override
public void defaultReadObject()
public void defaultReadObject() throws IOException, ClassNotFoundException
{
byte[] primVals = (byte[])info.GetValue(desc.getName() + ":p", ikvm.runtime.Util.getInstanceTypeFromClass(cli.System.Object.class));
Object[] objVals = (Object[])info.GetValue(desc.getName() + ":o", ikvm.runtime.Util.getInstanceTypeFromClass(cli.System.Object.class));
desc.setPrimFieldValues(obj, primVals);
desc.setObjFieldValues(obj, objVals);
if (curContext == null) {
throw new NotActiveException("not in call to readObject");
}
Object curObj = curContext.getObj();
ObjectStreamClass curDesc = curContext.getDesc();
defaultReadFields(curObj, curDesc);
}
@Override
protected Object readObjectOverride()
protected Object readObjectOverride() throws IOException
{
throw new UnsupportedOperationException();
return dis.readObject();
}
@Override
@ -61,9 +220,16 @@ public final class InteropObjectInputStream extends ObjectInputStream
}
@Override
public ObjectInputStream.GetField readFields()
public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException
{
throw new UnsupportedOperationException();
if (curContext == null) {
throw new NotActiveException("not in call to readObject");
}
Object curObj = curContext.getObj();
ObjectStreamClass curDesc = curContext.getDesc();
GetFieldImpl getField = new GetFieldImpl(curDesc);
getField.readFields();
return getField;
}
@Override
@ -71,119 +237,504 @@ public final class InteropObjectInputStream extends ObjectInputStream
{
throw new UnsupportedOperationException();
}
@Override
public int read() throws IOException
{
return dis.read();
}
@Override
public int read()
public int read(byte[] buf, int off, int len) throws IOException
{
return dis.read(buf, off, len);
}
@Override
public int available() throws IOException
{
return dis.available();
}
@Override
public void close() throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public int read(byte[] buf, int off, int len)
public boolean readBoolean() throws IOException
{
throw new UnsupportedOperationException();
return dis.readBoolean();
}
@Override
public int available()
public byte readByte() throws IOException
{
throw new UnsupportedOperationException();
return dis.readByte();
}
@Override
public void close()
public int readUnsignedByte() throws IOException
{
throw new UnsupportedOperationException();
return dis.readUnsignedByte();
}
@Override
public boolean readBoolean()
public char readChar() throws IOException
{
throw new UnsupportedOperationException();
return dis.readChar();
}
@Override
public byte readByte()
public short readShort() throws IOException
{
throw new UnsupportedOperationException();
return dis.readShort();
}
@Override
public int readUnsignedByte()
public int readUnsignedShort() throws IOException
{
throw new UnsupportedOperationException();
return dis.readUnsignedShort();
}
@Override
public char readChar()
public int readInt() throws IOException
{
throw new UnsupportedOperationException();
return dis.readInt();
}
@Override
public short readShort()
public long readLong() throws IOException
{
throw new UnsupportedOperationException();
return dis.readLong();
}
@Override
public int readUnsignedShort()
public float readFloat() throws IOException
{
throw new UnsupportedOperationException();
return dis.readFloat();
}
@Override
public int readInt()
public double readDouble() throws IOException
{
throw new UnsupportedOperationException();
return dis.readDouble();
}
@Override
public long readLong()
public void readFully(byte[] buf) throws IOException
{
throw new UnsupportedOperationException();
readFully(buf, 0, buf.length);
}
@Override
public float readFloat()
public void readFully(byte[] buf, int off, int len) throws IOException
{
throw new UnsupportedOperationException();
dis.readFully(buf, off, len);
}
@Override
public double readDouble()
public int skipBytes(int len) throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public void readFully(byte[] buf)
{
throw new UnsupportedOperationException();
}
@Override
public void readFully(byte[] buf, int off, int len)
{
throw new UnsupportedOperationException();
}
@Override
public int skipBytes(int len)
{
throw new UnsupportedOperationException();
return dis.skipBytes(len);
}
@Deprecated
@Override
public String readLine()
public String readLine() throws IOException
{
throw new UnsupportedOperationException();
return dis.readLine();
}
@Override
public String readUTF()
public String readUTF() throws IOException
{
throw new UnsupportedOperationException();
return dis.readUTF();
}
// private API used by ObjectStreamClass
@Override
String readTypeString() throws IOException
{
return (String)readObjectOverride();
}
private final class GetFieldImpl extends GetField {
/** class descriptor describing serializable fields */
private final ObjectStreamClass desc;
/** primitive field values */
private final byte[] primVals;
/** object field values */
private final Object[] objVals;
/** object field value handles */
private final int[] objHandles;
/**
* Creates GetFieldImpl object for reading fields defined in given
* class descriptor.
*/
GetFieldImpl(ObjectStreamClass desc) {
this.desc = desc;
primVals = new byte[desc.getPrimDataSize()];
objVals = new Object[desc.getNumObjFields()];
objHandles = new int[objVals.length];
}
public ObjectStreamClass getObjectStreamClass() {
return desc;
}
public boolean defaulted(String name) throws IOException {
return (getFieldOffset(name, null) < 0);
}
public boolean get(String name, boolean val) throws IOException {
int off = getFieldOffset(name, Boolean.TYPE);
return (off >= 0) ? Bits.getBoolean(primVals, off) : val;
}
public byte get(String name, byte val) throws IOException {
int off = getFieldOffset(name, Byte.TYPE);
return (off >= 0) ? primVals[off] : val;
}
public char get(String name, char val) throws IOException {
int off = getFieldOffset(name, Character.TYPE);
return (off >= 0) ? Bits.getChar(primVals, off) : val;
}
public short get(String name, short val) throws IOException {
int off = getFieldOffset(name, Short.TYPE);
return (off >= 0) ? Bits.getShort(primVals, off) : val;
}
public int get(String name, int val) throws IOException {
int off = getFieldOffset(name, Integer.TYPE);
return (off >= 0) ? Bits.getInt(primVals, off) : val;
}
public float get(String name, float val) throws IOException {
int off = getFieldOffset(name, Float.TYPE);
return (off >= 0) ? Bits.getFloat(primVals, off) : val;
}
public long get(String name, long val) throws IOException {
int off = getFieldOffset(name, Long.TYPE);
return (off >= 0) ? Bits.getLong(primVals, off) : val;
}
public double get(String name, double val) throws IOException {
int off = getFieldOffset(name, Double.TYPE);
return (off >= 0) ? Bits.getDouble(primVals, off) : val;
}
public Object get(String name, Object val) throws IOException {
int off = getFieldOffset(name, Object.class);
if (off >= 0) {
return objVals[off];
} else {
return val;
}
}
/**
* Reads primitive and object field values from stream.
*/
void readFields() throws IOException {
readFully(primVals, 0, primVals.length);
for (int i = 0; i < objVals.length; i++) {
objVals[i] = readObjectOverride();
}
}
/**
* Returns offset of field with given name and type. A specified type
* of null matches all types, Object.class matches all non-primitive
* types, and any other non-null type matches assignable types only.
* If no matching field is found in the (incoming) class
* descriptor but a matching field is present in the associated local
* class descriptor, returns -1. Throws IllegalArgumentException if
* neither incoming nor local class descriptor contains a match.
*/
private int getFieldOffset(String name, Class type) {
ObjectStreamField field = desc.getField(name, type);
if (field != null) {
return field.getOffset();
} else if (desc.getLocalDesc().getField(name, type) != null) {
return -1;
} else {
throw new IllegalArgumentException("no such field " + name +
" with type " + type);
}
}
}
private final static class CallbackContext {
private final Object obj;
private final ObjectStreamClass desc;
private final AtomicBoolean used = new AtomicBoolean();
public CallbackContext(Object obj, ObjectStreamClass desc) {
this.obj = obj;
this.desc = desc;
}
public Object getObj() throws NotActiveException {
checkAndSetUsed();
return obj;
}
public ObjectStreamClass getDesc() {
return desc;
}
private void checkAndSetUsed() throws NotActiveException {
if (!used.compareAndSet(false, true)) {
throw new NotActiveException(
"not in readObject invocation or fields already read");
}
}
public void setUsed() {
used.set(true);
}
}
private static final class ObjectDataInputStream extends DataInputStream
{
private final static class Inp extends FilterInputStream
{
private static final byte MARKER = 0;
private static final byte OBJECTS = 10;
private static final byte BYTES = 20;
private final SerializationInfo info;
private byte[] buf;
private int pos;
private int blockEnd = -1;
private int objCount;
private int objId;
Inp(SerializationInfo info) throws IOException
{
super(null);
this.info = info;
buf = (byte[])info.GetValue("$data", ikvm.runtime.Util.getInstanceTypeFromClass(cli.System.Object.class));
modeSwitch();
}
private void modeSwitch() throws IOException
{
if (pos == buf.length)
{
// next read will report error
blockEnd = -1;
objCount = -1;
}
else if (buf[pos] == OBJECTS)
{
pos++;
objCount = readPacked();
blockEnd = -1;
}
else if (buf[pos] == BYTES)
{
pos++;
switchToData();
}
else if (buf[pos] == MARKER)
{
}
else
{
throw new StreamCorruptedException();
}
}
private int packedLength(int val)
{
if (val < 0)
{
// we only use packed integers for lengths or counts, so they can never be negative
throw new Error();
}
else if (val < 128)
{
return 1;
}
else if (val < 16129)
{
return 2;
}
else
{
return 5;
}
}
private int readPacked()
{
int b1 = buf[pos++] & 0xFF;
if (b1 < 128)
{
return b1;
}
else if (b1 != 128)
{
return ((b1 & 127) << 7) + (buf[pos++] & 0xFF);
}
else
{
int v = 0;
v |= (buf[pos++] & 0xFF) << 24;
v |= (buf[pos++] & 0xFF) << 16;
v |= (buf[pos++] & 0xFF) << 8;
v |= (buf[pos++] & 0xFF) << 0;
return v;
}
}
private void switchToData() throws IOException
{
if (blockEnd != -1 || objCount != 0 || buf[pos] == MARKER)
{
throw new StreamCorruptedException();
}
int len = readPacked();
blockEnd = pos + len;
if (blockEnd > buf.length)
{
throw new StreamCorruptedException();
}
}
public int read() throws IOException
{
if (blockEnd - pos < 1)
{
switchToData();
}
return buf[pos++] & 0xFF;
}
public int read(byte b[], int off, int len) throws IOException
{
if (len == 0)
{
return 0;
}
if (blockEnd - pos < len)
{
switchToData();
}
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
public long skip(long n) throws IOException
{
if (n == 0)
{
return 0;
}
if (blockEnd - pos < n)
{
switchToData();
}
pos += (int)n;
return n;
}
public int available() throws IOException
{
return 0;
}
public void close() throws IOException
{
}
public void mark(int readlimit)
{
}
public void reset() throws IOException
{
}
public boolean markSupported()
{
return false;
}
Object readObject() throws IOException
{
if (objCount <= 0)
{
if (pos != blockEnd || buf[pos] == MARKER)
{
throw new StreamCorruptedException();
}
blockEnd = -1;
objCount = readPacked();
}
objCount--;
return info.GetValue("$" + (objId++), ikvm.runtime.Util.getInstanceTypeFromClass(cli.System.Object.class));
}
void skipMarker() throws IOException
{
for (;;)
{
if (objCount > 0)
{
objId += objCount;
objCount = 0;
if (buf[pos] == MARKER)
{
pos++;
modeSwitch();
break;
}
switchToData();
}
if (blockEnd != -1)
{
pos = blockEnd;
blockEnd = -1;
}
if (pos == buf.length)
{
throw new StreamCorruptedException();
}
if (buf[pos] == MARKER)
{
pos++;
modeSwitch();
break;
}
blockEnd = -1;
objCount = readPacked();
}
}
}
ObjectDataInputStream(SerializationInfo info) throws IOException
{
super(new Inp(info));
}
public Object readObject() throws IOException
{
return ((Inp)in).readObject();
}
public void skipMarker() throws IOException
{
((Inp)in).skipMarker();
}
}
}

Просмотреть файл

@ -1,58 +1,231 @@
/*
Copyright (C) 2009 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
* Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2009 Jeroen Frijters
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.io;
import cli.System.Runtime.Serialization.IObjectReference;
import cli.System.Runtime.Serialization.SerializationException;
import cli.System.Runtime.Serialization.SerializationInfo;
import cli.System.Runtime.Serialization.StreamingContext;
import cli.System.SerializableAttribute;
import java.util.ArrayList;
@ikvm.lang.Internal
public final class InteropObjectOutputStream extends ObjectOutputStream
{
private final Object obj;
private final SerializationInfo info;
private final ObjectStreamClass desc;
private final ObjectDataOutputStream dos;
private Object curObj;
private ObjectStreamClass curDesc;
private PutFieldImpl curPut;
public InteropObjectOutputStream(Object obj, SerializationInfo info, Class clazz) throws IOException
@SerializableAttribute.Annotation
private static final class ReplaceProxy implements IObjectReference
{
this.obj = obj;
this.info = info;
this.desc = ObjectStreamClass.lookup(clazz);
private Object obj;
public Object GetRealObject(StreamingContext context)
{
return obj;
}
}
static final class DynamicProxy implements Serializable
{
Object obj;
private Object readResolve()
{
return obj;
}
}
public static void writeObject(Object obj, SerializationInfo info)
{
try
{
boolean replaced = false;
Class cl = obj.getClass();
ObjectStreamClass desc;
for (;;)
{
Class repCl;
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
replaced = true;
}
if (replaced)
{
info.AddValue("obj", obj);
info.SetType(ikvm.runtime.Util.getInstanceTypeFromClass(ReplaceProxy.class));
}
else
{
new InteropObjectOutputStream(info, obj, cl, desc);
}
}
catch (IOException x)
{
ikvm.runtime.Util.throwException(new SerializationException(x.getMessage(), x));
}
}
private InteropObjectOutputStream(SerializationInfo info, Object obj, Class cl, ObjectStreamClass desc) throws IOException
{
dos = new ObjectDataOutputStream(info);
if (obj instanceof ObjectStreamClass)
{
ObjectStreamClass osc = (ObjectStreamClass)obj;
if (osc.isProxy())
{
writeProxyDesc(osc);
}
else
{
writeNonProxyDesc(osc);
}
}
else if (obj instanceof Serializable)
{
if (desc.isDynamicClass())
{
info.SetType(ikvm.runtime.Util.getInstanceTypeFromClass(DynamicProxy.class));
}
writeOrdinaryObject(obj, desc);
}
else
{
throw new NotSerializableException(cl.getName());
}
dos.close();
}
private void writeProxyDesc(ObjectStreamClass desc) throws IOException
{
writeByte(TC_PROXYCLASSDESC);
Class cl = desc.forClass();
Class[] ifaces = cl.getInterfaces();
writeInt(ifaces.length);
for (int i = 0; i < ifaces.length; i++)
{
writeObject(ifaces[i]);
}
writeObject(desc.getSuperDesc());
}
private void writeNonProxyDesc(ObjectStreamClass desc) throws IOException
{
writeByte(TC_CLASSDESC);
writeObject(desc.forClass());
desc.writeNonProxy(this);
writeObject(desc.getSuperDesc());
}
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc) throws IOException
{
desc.checkSerialize();
writeObject(desc);
if (desc.isExternalizable() && !desc.isProxy())
{
writeExternalData((Externalizable)obj);
}
else
{
writeSerialData(obj, desc);
}
}
private void writeExternalData(Externalizable obj) throws IOException
{
Object oldObj = curObj;
ObjectStreamClass oldDesc = curDesc;
PutFieldImpl oldPut = curPut;
curObj = obj;
curDesc = null;
curPut = null;
obj.writeExternal(this);
dos.writeMarker();
curObj = oldObj;
curDesc = oldDesc;
curPut = oldPut;
}
private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++)
{
ObjectStreamClass slotDesc = slots[i].desc;
if (slotDesc.hasWriteObjectMethod())
{
Object oldObj = curObj;
ObjectStreamClass oldDesc = curDesc;
PutFieldImpl oldPut = curPut;
curObj = obj;
curDesc = slotDesc;
curPut = null;
slotDesc.invokeWriteObject(obj, this);
dos.writeMarker();
curObj = oldObj;
curDesc = oldDesc;
curPut = oldPut;
}
else
{
defaultWriteFields(obj, slotDesc);
}
}
}
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException
{
desc.checkDefaultSerialize();
byte[] primVals = new byte[desc.getPrimDataSize()];
desc.getPrimFieldValues(obj, primVals);
write(primVals);
Object[] objVals = new Object[desc.getNumObjFields()];
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++)
{
writeObject(objVals[i]);
}
}
@Override
public void defaultWriteObject() throws IOException
{
desc.checkDefaultSerialize();
byte[] primVals = new byte[desc.getPrimDataSize()];
desc.getPrimFieldValues(obj, primVals);
Object[] objVals = new Object[desc.getNumObjFields()];
desc.getObjFieldValues(obj, objVals);
// TODO consider if we should loop thru objVals to look for ghost arrays
// TODO consider writing desc
info.AddValue(desc.getName() + ":p", primVals);
info.AddValue(desc.getName() + ":o", objVals);
defaultWriteFields(curObj, curDesc);
}
@Override
@ -64,31 +237,43 @@ public final class InteropObjectOutputStream extends ObjectOutputStream
@Override
public void flush()
{
throw new UnsupportedOperationException();
}
@Override
public PutField putFields()
public ObjectOutputStream.PutField putFields() throws IOException
{
throw new UnsupportedOperationException();
if (curPut == null)
{
if (curObj == null || curDesc == null)
{
throw new NotActiveException("not in call to writeObject");
}
curPut = new PutFieldImpl(curDesc);
}
return curPut;
}
@Override
public void reset()
public void reset() throws IOException
{
throw new UnsupportedOperationException();
throw new IOException("stream active");
}
@Override
protected void writeObjectOverride(Object obj)
{
throw new UnsupportedOperationException();
// TODO consider tagging ghost arrays
dos.writeObject(obj);
}
@Override
public void writeFields()
public void writeFields() throws IOException
{
throw new UnsupportedOperationException();
if (curPut == null)
{
throw new NotActiveException("no current PutField object");
}
curPut.writeFields();
}
@Override
@ -100,90 +285,447 @@ public final class InteropObjectOutputStream extends ObjectOutputStream
@Override
public void useProtocolVersion(int version)
{
throw new UnsupportedOperationException();
throw new IllegalStateException("stream non-empty");
}
@Override
public void write(byte[] buf)
public void write(byte[] buf) throws IOException
{
throw new UnsupportedOperationException();
write(buf, 0, buf.length);
}
@Override
public void write(int val)
public void write(int val) throws IOException
{
throw new UnsupportedOperationException();
dos.write(val);
}
@Override
public void write(byte[] buf, int off, int len)
public void write(byte[] buf, int off, int len) throws IOException
{
throw new UnsupportedOperationException();
dos.write(buf, off, len);
}
@Override
public void writeBoolean(boolean val)
public void writeBoolean(boolean val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeBoolean(val);
}
@Override
public void writeByte(int val)
public void writeByte(int val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeByte(val);
}
@Override
public void writeBytes(String str)
public void writeBytes(String str) throws IOException
{
throw new UnsupportedOperationException();
dos.writeBytes(str);
}
@Override
public void writeChar(int val)
public void writeChar(int val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeChar(val);
}
@Override
public void writeChars(String str)
public void writeChars(String str) throws IOException
{
throw new UnsupportedOperationException();
dos.writeChars(str);
}
@Override
public void writeDouble(double val)
public void writeDouble(double val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeDouble(val);
}
@Override
public void writeFloat(float val)
public void writeFloat(float val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeFloat(val);
}
@Override
public void writeInt(int val)
public void writeInt(int val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeInt(val);
}
@Override
public void writeLong(long val)
public void writeLong(long val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeLong(val);
}
@Override
public void writeShort(int val)
public void writeShort(int val) throws IOException
{
throw new UnsupportedOperationException();
dos.writeShort(val);
}
@Override
public void writeUTF(String str)
public void writeUTF(String str) throws IOException
{
throw new UnsupportedOperationException();
dos.writeUTF(str);
}
// private API used by ObjectStreamClass
@Override
void writeTypeString(String str) throws IOException
{
writeObject(str);
}
private final class PutFieldImpl extends PutField {
/** class descriptor describing serializable fields */
private final ObjectStreamClass desc;
/** primitive field values */
private final byte[] primVals;
/** object field values */
private final Object[] objVals;
/**
* Creates PutFieldImpl object for writing fields defined in given
* class descriptor.
*/
PutFieldImpl(ObjectStreamClass desc) {
this.desc = desc;
primVals = new byte[desc.getPrimDataSize()];
objVals = new Object[desc.getNumObjFields()];
}
public void put(String name, boolean val) {
Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
}
public void put(String name, byte val) {
primVals[getFieldOffset(name, Byte.TYPE)] = val;
}
public void put(String name, char val) {
Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
}
public void put(String name, short val) {
Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
}
public void put(String name, int val) {
Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
}
public void put(String name, float val) {
Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
}
public void put(String name, long val) {
Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
}
public void put(String name, double val) {
Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
}
public void put(String name, Object val) {
objVals[getFieldOffset(name, Object.class)] = val;
}
// deprecated in ObjectOutputStream.PutField
public void write(ObjectOutput out) throws IOException {
/*
* Applications should *not* use this method to write PutField
* data, as it will lead to stream corruption if the PutField
* object writes any primitive data (since block data mode is not
* unset/set properly, as is done in OOS.writeFields()). This
* broken implementation is being retained solely for behavioral
* compatibility, in order to support applications which use
* OOS.PutField.write() for writing only non-primitive data.
*
* Serialization of unshared objects is not implemented here since
* it is not necessary for backwards compatibility; also, unshared
* semantics may not be supported by the given ObjectOutput
* instance. Applications which write unshared objects using the
* PutField API must use OOS.writeFields().
*/
if (InteropObjectOutputStream.this != out) {
throw new IllegalArgumentException("wrong stream");
}
out.write(primVals, 0, primVals.length);
ObjectStreamField[] fields = desc.getFields(false);
int numPrimFields = fields.length - objVals.length;
// REMIND: warn if numPrimFields > 0?
for (int i = 0; i < objVals.length; i++) {
if (fields[numPrimFields + i].isUnshared()) {
throw new IOException("cannot write unshared object");
}
out.writeObject(objVals[i]);
}
}
/**
* Writes buffered primitive data and object fields to stream.
*/
void writeFields() throws IOException {
InteropObjectOutputStream.this.write(primVals, 0, primVals.length);
ObjectStreamField[] fields = desc.getFields(false);
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
writeObject(objVals[i]);
}
}
/**
* Returns offset of field with given name and type. A specified type
* of null matches all types, Object.class matches all non-primitive
* types, and any other non-null type matches assignable types only.
* Throws IllegalArgumentException if no matching field found.
*/
private int getFieldOffset(String name, Class type) {
ObjectStreamField field = desc.getField(name, type);
if (field == null) {
throw new IllegalArgumentException("no such field " + name +
" with type " + type);
}
return field.getOffset();
}
}
private static final class ObjectDataOutputStream extends DataOutputStream
{
/*
* States:
* blockStart objCount
* -1 0 Previous byte was a marker (or we're at the start of the stream), next byte will be MARKER, OBJECTS or BYTES
* >=0 0 We're currently writing a byte stream (that starts at blockStart + 1)
* -1 >0 We're currently writing objects (just a count of objects really)
*
*/
private static final byte MARKER = 0;
private static final byte OBJECTS = 10;
private static final byte BYTES = 20;
private final SerializationInfo info;
private byte[] buf = new byte[16];
private int pos;
private int blockStart = -1;
private int objCount;
private int objId;
ObjectDataOutputStream(SerializationInfo info)
{
super(null);
out = this;
this.info = info;
}
private void grow(int minGrow)
{
int newSize = buf.length + Math.max(buf.length, minGrow);
byte[] newBuf = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, pos);
buf = newBuf;
}
private void switchToData()
{
if (objCount == 0)
{
if (pos == buf.length)
{
grow(1);
}
buf[pos++] = BYTES;
}
else
{
int len = packedLength(objCount);
if (pos + len >= buf.length)
{
grow(len);
}
writePacked(pos, objCount);
objCount = 0;
pos += len;
}
blockStart = pos;
pos++;
}
private void endData()
{
if (blockStart == -1)
{
if (pos == buf.length)
{
grow(1);
}
buf[pos++] = OBJECTS;
}
else
{
int len = (pos - blockStart) - 1;
int lenlen = packedLength(len);
if (lenlen > 1)
{
lenlen--;
if (buf.length - pos <= lenlen)
{
grow(lenlen);
}
System.arraycopy(buf, blockStart + 1, buf, blockStart + 1 + lenlen, pos - (blockStart + 1));
pos += lenlen;
}
writePacked(blockStart, len);
blockStart = -1;
}
}
private int packedLength(int val)
{
if (val < 0)
{
// we only use packed integers for lengths or counts, so they can never be negative
throw new Error();
}
else if (val < 128)
{
return 1;
}
else if (val < 16129)
{
return 2;
}
else
{
return 5;
}
}
private void writePacked(int pos, int v)
{
if (v < 128)
{
buf[pos] = (byte)v;
}
else if (v < 16129)
{
buf[pos + 0] = (byte)(128 | (v >> 7));
buf[pos + 1] = (byte)(v & 127);
}
else
{
buf[pos + 0] = (byte)128;
buf[pos + 1] = (byte)(v >>> 24);
buf[pos + 2] = (byte)(v >>> 16);
buf[pos + 3] = (byte)(v >>> 8);
buf[pos + 4] = (byte)(v >>> 0);
}
}
@Override
public void write(int b)
{
if (blockStart == -1)
{
switchToData();
}
if (pos == buf.length)
{
grow(1);
}
buf[pos++] = (byte)b;
}
@Override
public void write(byte[] b, int off, int len)
{
if (len == 0)
{
return;
}
if (blockStart == -1)
{
switchToData();
}
if (pos + len >= buf.length)
{
grow(len);
}
System.arraycopy(b, off, buf, pos, len);
pos += len;
}
public void writeObject(Object obj)
{
if (objCount == 0)
{
endData();
}
objCount++;
info.AddValue("$" + (objId++), obj);
}
@Override
public void close()
{
if (objCount == 0)
{
if (blockStart == -1)
{
// we've just written a marker, so we don't need to do anything else
}
else
{
endData();
}
}
else
{
switchToData();
}
trim();
info.AddValue("$data", buf);
}
private void trim()
{
if (buf.length != pos)
{
byte[] newBuf = new byte[pos];
System.arraycopy(buf, 0, newBuf, 0, pos);
buf = newBuf;
}
}
@Override
public void flush()
{
}
public void writeMarker()
{
if (objCount == 0)
{
if (blockStart != -1)
{
endData();
}
if (pos == buf.length)
{
grow(1);
}
buf[pos++] = MARKER;
}
else
{
switchToData();
}
blockStart = -1;
}
}
}

Просмотреть файл

@ -161,6 +161,9 @@ public class ObjectStreamClass implements Serializable {
private ObjectStreamClass localDesc;
/** superclass descriptor appearing in stream */
private ObjectStreamClass superDesc;
/** [IKVM] true if the the class was dynamically loaded from Java bytecode */
private boolean dynamicClass;
/**
* Initializes native code.
@ -422,6 +425,7 @@ public class ObjectStreamClass implements Serializable {
isEnum = Enum.class.isAssignableFrom(cl);
serializable = Serializable.class.isAssignableFrom(cl);
externalizable = Externalizable.class.isAssignableFrom(cl);
dynamicClass = isDynamicTypeWrapper(cl);
Class superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
@ -2307,4 +2311,17 @@ public class ObjectStreamClass implements Serializable {
}
}
}
// [IKVM] interop serialization support
boolean isDynamicClass() {
return dynamicClass;
}
private static native boolean isDynamicTypeWrapper(Class cl);
// to force us to be resolved early (by the .NET deserialization engine)
// we implement readResolve() (which results in us implementing IObjectReference)
private Object readResolve() {
return this;
}
}

Просмотреть файл

@ -22,6 +22,10 @@
*/
using System;
using System.Reflection;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;
#if IKVM_REF_EMIT
using IKVM.Reflection.Emit;
#else
@ -33,24 +37,22 @@ namespace IKVM.Internal
// This class deals with .NET serialization. When a class is Java serializable it will attempt to automagically make it .NET serializable.
public static class Serialization
{
private static CustomAttributeBuilder serializableAttribute = new CustomAttributeBuilder(typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
private static TypeWrapper iserializable = ClassLoaderWrapper.GetWrapperFromType(typeof(System.Runtime.Serialization.ISerializable));
private static TypeWrapper serializable = ClassLoaderWrapper.LoadClassCritical("java.io.Serializable");
private static TypeWrapper externalizable = ClassLoaderWrapper.LoadClassCritical("java.io.Externalizable");
private static TypeWrapper objectStreamClass = ClassLoaderWrapper.LoadClassCritical("java.io.ObjectStreamClass");
private static TypeWrapper javaLangEnum = ClassLoaderWrapper.LoadClassCritical("java.lang.Enum");
private static readonly CustomAttributeBuilder serializableAttribute = new CustomAttributeBuilder(typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
private static readonly TypeWrapper iserializable = ClassLoaderWrapper.GetWrapperFromType(typeof(ISerializable));
private static readonly TypeWrapper iobjectreference = ClassLoaderWrapper.GetWrapperFromType(typeof(IObjectReference));
private static readonly TypeWrapper serializable = ClassLoaderWrapper.LoadClassCritical("java.io.Serializable");
private static readonly TypeWrapper externalizable = ClassLoaderWrapper.LoadClassCritical("java.io.Externalizable");
private static readonly PermissionSet psetSerializationFormatter;
private static bool IsTriviallySerializable(TypeWrapper wrapper)
static Serialization()
{
if ((wrapper.Modifiers & IKVM.Attributes.Modifiers.Enum) != 0)
{
return true;
}
if (wrapper == CoreClasses.java.lang.Object.Wrapper)
{
return true;
}
if (!IsTriviallySerializable(wrapper.BaseTypeWrapper))
psetSerializationFormatter = new PermissionSet(PermissionState.None);
psetSerializationFormatter.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
}
private static bool IsSafeForAutomagicSerialization(TypeWrapper wrapper)
{
if (wrapper.TypeAsBaseType.IsSerializable)
{
return false;
}
@ -58,59 +60,156 @@ namespace IKVM.Internal
{
return false;
}
if (wrapper.IsSubTypeOf(serializable))
if (wrapper.IsSubTypeOf(iobjectreference))
{
return false;
}
if (wrapper.GetMethodWrapper("GetObjectData", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null)
{
return false;
}
if (wrapper.GetMethodWrapper("<init>", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null)
{
return false;
}
return true;
}
internal static void AddAutomagicSerialization(TypeWrapper wrapper)
{
if ((wrapper.Modifiers & IKVM.Attributes.Modifiers.Enum) != 0)
{
MarkSerializable(wrapper);
}
else if (wrapper.IsSubTypeOf(serializable) && IsSafeForAutomagicSerialization(wrapper))
{
if (wrapper.IsSubTypeOf(externalizable))
{
return false;
MethodWrapper ctor = wrapper.GetMethodWrapper("<init>", "()V", false);
if (ctor != null && ctor.IsPublic)
{
MarkSerializable(wrapper);
AddConstructor(wrapper, ctor, null, true);
if (!wrapper.BaseTypeWrapper.IsSubTypeOf(serializable))
{
AddGetObjectData(wrapper);
}
if (wrapper.BaseTypeWrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", true) != null)
{
RemoveReadResolve(wrapper);
}
}
}
if (wrapper == objectStreamClass || wrapper == CoreClasses.java.lang.Class.Wrapper || wrapper == javaLangEnum)
else if (wrapper.BaseTypeWrapper.IsSubTypeOf(serializable))
{
return false;
ConstructorInfo baseCtor = wrapper.BaseTypeWrapper.TypeAsBaseType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
if (baseCtor != null && (baseCtor.IsFamily || baseCtor.IsFamilyOrAssembly))
{
MarkSerializable(wrapper);
AddConstructor(wrapper, null, baseCtor, false);
AddReadResolve(wrapper);
}
}
if (wrapper.GetMethodWrapper("readObject", "(Ljava.io.ObjectInputStream;)V", false) != null)
else
{
return false;
MethodWrapper baseCtor = wrapper.BaseTypeWrapper.GetMethodWrapper("<init>", "()V", false);
if (baseCtor != null && baseCtor.IsAccessibleFrom(wrapper.BaseTypeWrapper, wrapper, wrapper))
{
MarkSerializable(wrapper);
AddGetObjectData(wrapper);
AddConstructor(wrapper, baseCtor, null, true);
AddReadResolve(wrapper);
}
}
if (wrapper.GetMethodWrapper("writeObject", "(Ljava.io.ObjectOutputStream;)V", false) != null)
{
return false;
}
if (wrapper.GetMethodWrapper("readObjectNoData", "()V", false) != null)
{
return false;
}
if (wrapper.GetMethodWrapper("writeReplace", "()Ljava.lang.Object;", false) != null)
{
return false;
}
if (wrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", false) != null)
{
return false;
}
if (wrapper.GetFieldWrapper("serialPersistentFields", "[Ljava.io.ObjectStreamField;") != null)
{
return false;
}
if (wrapper.GetMethodWrapper("GetObjectData", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null)
{
return false;
}
if (wrapper.GetMethodWrapper("<init>", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null)
{
return false;
}
return true;
}
return false;
}
internal static void AddAutomagicSerialization(DynamicTypeWrapper wrapper)
private static void MarkSerializable(TypeWrapper wrapper)
{
if (IsTriviallySerializable(wrapper))
TypeBuilder tb = wrapper.TypeAsBuilder;
tb.SetCustomAttribute(serializableAttribute);
}
private static void AddGetObjectData(TypeWrapper wrapper)
{
TypeBuilder tb = wrapper.TypeAsBuilder;
tb.AddInterfaceImplementation(typeof(ISerializable));
MethodBuilder getObjectData = tb.DefineMethod("GetObjectData", MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.NewSlot, null,
new Type[] { typeof(SerializationInfo), typeof(StreamingContext) });
AttributeHelper.HideFromJava(getObjectData);
getObjectData.AddDeclarativeSecurity(SecurityAction.Demand, psetSerializationFormatter);
tb.DefineMethodOverride(getObjectData, typeof(ISerializable).GetMethod("GetObjectData"));
CodeEmitter ilgen = CodeEmitter.Create(getObjectData);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
TypeWrapper serializationHelper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.Serialization");
MethodWrapper mw = serializationHelper.GetMethodWrapper("writeObject", "(Ljava.lang.Object;Lcli.System.Runtime.Serialization.SerializationInfo;)V", false);
mw.Link();
mw.EmitCall(ilgen);
ilgen.Emit(OpCodes.Ret);
}
private static void AddConstructor(TypeWrapper wrapper, MethodWrapper defaultConstructor, ConstructorInfo serializationConstructor, bool callReadObject)
{
TypeBuilder tb = wrapper.TypeAsBuilder;
ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Family, CallingConventions.Standard, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) });
AttributeHelper.HideFromJava(ctor);
ctor.AddDeclarativeSecurity(SecurityAction.Demand, psetSerializationFormatter);
CodeEmitter ilgen = CodeEmitter.Create(ctor);
ilgen.Emit(OpCodes.Ldarg_0);
if (defaultConstructor != null)
{
wrapper.TypeAsBuilder.SetCustomAttribute(serializableAttribute);
defaultConstructor.Link();
defaultConstructor.EmitCall(ilgen);
}
else
{
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Ldarg_2);
ilgen.Emit(OpCodes.Call, serializationConstructor);
}
if (callReadObject)
{
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
TypeWrapper serializationHelper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.Serialization");
MethodWrapper mw = serializationHelper.GetMethodWrapper("readObject", "(Ljava.lang.Object;Lcli.System.Runtime.Serialization.SerializationInfo;)V", false);
mw.Link();
mw.EmitCall(ilgen);
}
ilgen.Emit(OpCodes.Ret);
}
private static void AddReadResolve(TypeWrapper wrapper)
{
MethodWrapper mw = wrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", false);
if (mw != null && !wrapper.IsSubTypeOf(iobjectreference))
{
TypeBuilder tb = wrapper.TypeAsBuilder;
tb.AddInterfaceImplementation(typeof(IObjectReference));
MethodBuilder getRealObject = tb.DefineMethod("IObjectReference.GetRealObject", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final,
typeof(object), new Type[] { typeof(StreamingContext) });
AttributeHelper.HideFromJava(getRealObject);
tb.DefineMethodOverride(getRealObject, typeof(IObjectReference).GetMethod("GetRealObject"));
CodeEmitter ilgen = CodeEmitter.Create(getRealObject);
mw.Link();
ilgen.Emit(OpCodes.Ldarg_0);
mw.EmitCall(ilgen);
ilgen.Emit(OpCodes.Ret);
}
}
private static void RemoveReadResolve(TypeWrapper wrapper)
{
TypeBuilder tb = wrapper.TypeAsBuilder;
tb.AddInterfaceImplementation(typeof(IObjectReference));
MethodBuilder getRealObject = tb.DefineMethod("IObjectReference.GetRealObject", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final,
typeof(object), new Type[] { typeof(StreamingContext) });
AttributeHelper.HideFromJava(getRealObject);
tb.DefineMethodOverride(getRealObject, typeof(IObjectReference).GetMethod("GetRealObject"));
CodeEmitter ilgen = CodeEmitter.Create(getRealObject);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ret);
}
}
}

Просмотреть файл

@ -657,6 +657,12 @@ namespace IKVM.NativeCode.java
#endif
}
public static bool isDynamicTypeWrapper(object cl)
{
TypeWrapper wrapper = TypeWrapper.FromClass(cl);
return !wrapper.IsFastClassLiteralSafe;
}
public static bool hasStaticInitializer(object cl)
{
TypeWrapper wrapper = TypeWrapper.FromClass(cl);