зеркало из https://github.com/mono/ikvm-fork.git
Automagic .NET serialization support (for classes that are Java serializable).
This commit is contained in:
Родитель
4460da2db0
Коммит
2cb0073b40
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче