diff --git a/classpath/java/io/EOFException.java b/classpath/java/io/EOFException.java new file mode 100644 index 0000000000..831c38c47c --- /dev/null +++ b/classpath/java/io/EOFException.java @@ -0,0 +1,11 @@ +package java.io; + +public class EOFException extends IOException { + public EOFException(String message) { + super(message); + } + + public EOFException() { + this(null); + } +} diff --git a/classpath/java/io/NotSerializableException.java b/classpath/java/io/NotSerializableException.java new file mode 100644 index 0000000000..67cd75160d --- /dev/null +++ b/classpath/java/io/NotSerializableException.java @@ -0,0 +1,11 @@ +package java.io; + +public class NotSerializableException extends ObjectStreamException { + public NotSerializableException(String message) { + super(message); + } + + public NotSerializableException() { + this(null); + } +} diff --git a/classpath/java/io/ObjectInputStream.java b/classpath/java/io/ObjectInputStream.java index d12fcd127a..531058b943 100644 --- a/classpath/java/io/ObjectInputStream.java +++ b/classpath/java/io/ObjectInputStream.java @@ -1,8 +1,13 @@ package java.io; +import java.util.HashMap; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + public class ObjectInputStream extends InputStream { private final InputStream in; - private final Reader r; + private final PushbackReader r; public ObjectInputStream(InputStream in) { this.in = in; @@ -21,7 +26,7 @@ public class ObjectInputStream extends InputStream { in.close(); } - public Object readObject() throws IOException { + public Object readObject() throws IOException, ClassNotFoundException { return readObject(new HashMap()); } @@ -67,7 +72,7 @@ public class ObjectInputStream extends InputStream { private void skipSpace() throws IOException { int c; - while ((c = r.read()) != -1 && Character.isSpace((char) c)); + while ((c = r.read()) != -1 && Character.isWhitespace((char) c)); if (c != -1) { r.unread(c); } @@ -91,7 +96,7 @@ public class ObjectInputStream extends InputStream { StringBuilder sb = new StringBuilder(); int c; - while ((c = r.read()) != -1 && ! Character.isSpace((char) c)) { + while ((c = r.read()) != -1 && ! Character.isWhitespace((char) c)) { sb.append((char) c); } if (c != -1) { @@ -108,13 +113,15 @@ public class ObjectInputStream extends InputStream { return Double.parseDouble(readStringToken()); } - private Object readObject(HashMap map) throws IOException { + private Object readObject(HashMap map) + throws IOException, ClassNotFoundException + { skipSpace(); switch (r.read()) { case 'a': return deserializeArray(map); case 'l': - return deserialize(map); + return deserializeObject(map); case 'n': return null; case -1: @@ -124,153 +131,85 @@ public class ObjectInputStream extends InputStream { } } + private Object deserialize(HashMap map) + throws IOException, ClassNotFoundException + { + skipSpace(); + + switch (r.read()) { + case 'a': + return deserializeArray(map); + case 'l': + return deserializeObject(map); + case 'r': + return map.get((int) readLongToken()); + case 'n': + return null; + case 'z': + return (readLongToken() == 0); + case 'b': + return (byte) readLongToken(); + case 'c': + return (char) readLongToken(); + case 's': + return (short) readLongToken(); + case 'i': + return (int) readLongToken(); + case 'j': + return readLongToken(); + case 'f': + return (float) readDoubleToken(); + case 'd': + return readDoubleToken(); + case -1: + throw new EOFException(); + default: + throw new StreamCorruptedException(); + } + } + private Object deserializeArray(HashMap map) - throws IOException + throws IOException, ClassNotFoundException { read('('); int id = (int) readLongToken(); Class c = Class.forName(readStringToken()); int length = (int) readLongToken(); - Object o = Array.newInstance(c.getComponentType(), length); + Class t = c.getComponentType(); + Object o = Array.newInstance(t, length); map.put(id, o); for (int i = 0; i < length; ++i) { - skipSpace(); - - switch (r.read()) { - case 'a': - Array.set(o, i, deserializeArray(map)); - break; - - case 'l': - Array.set(o, i, deserialize(map)); - break; - - case 'r': - Array.set(o, i, map.get((int) readLongToken())); - break; - - case 'n': - Array.set(o, i, null); - break; - - case 'z': - f.setBoolean(o, readLongToken() != 0); - break; - - case 'b': - f.setByte(o, (byte) readLongToken()); - break; - - case 'c': - f.setChar(o, (char) readLongToken()); - break; - - case 's': - f.setShort(o, (short) readLongToken()); - break; - - case 'i': - f.setInt(o, (int) readLongToken()); - break; - - case 'j': - f.setLong(o, readLongToken()); - break; - - case 'f': - f.setFloat(o, (float) readDoubleToken()); - break; - - case 'd': - f.setDouble(o, readDoubleToken()); - break; - - case -1: - throw new EOFException(); - - default: - throw new StreamCorruptedException(); - } + Array.set(o, i, deserialize(map)); } read(')'); + + return o; } - private Object deserialize(HashMap map) - throws IOException + private static native Object makeInstance(Class c); + + private Object deserializeObject(HashMap map) + throws IOException, ClassNotFoundException { read('('); int id = (int) readLongToken(); Class c = Class.forName(readStringToken()); - Object o = c.newInstance(); + Object o = makeInstance(c); map.put(id, o); for (Field f: c.getFields()) { int modifiers = f.getModifiers(); if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { - skipSpace(); - - switch (r.read()) { - case 'a': - Array.set(o, i, deserializeArray(map)); - break; - - case 'l': - Array.set(o, i, deserialize(map)); - break; - - case 'r': - Array.set(o, i, map.get((int) readLongToken())); - break; - - case 'n': - Array.set(o, i, null); - break; - - case 'z': - Array.setBoolean(o, i, readLongToken() != 0); - break; - - case 'b': - Array.setByte(o, i, (byte) readLongToken()); - break; - - case 'c': - Array.setChar(o, i, (char) readLongToken()); - break; - - case 's': - Array.setShort(o, i, (short) readLongToken()); - break; - - case 'i': - Array.setInt(o, i, (int) readLongToken()); - break; - - case 'j': - Array.setLong(o, i, readLongToken()); - break; - - case 'f': - Array.setFloat(o, i, (float) readDoubleToken()); - break; - - case 'd': - Array.setDouble(o, i, readDoubleToken()); - break; - - case -1: - throw new EOFException(); - - default: - throw new StreamCorruptedException(); - } + f.set(o, deserialize(map)); } } read(')'); + + return o; } } diff --git a/classpath/java/io/ObjectOutputStream.java b/classpath/java/io/ObjectOutputStream.java index a771b8e000..25b20adeae 100644 --- a/classpath/java/io/ObjectOutputStream.java +++ b/classpath/java/io/ObjectOutputStream.java @@ -1,6 +1,9 @@ package java.io; import java.util.IdentityHashMap; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; public class ObjectOutputStream extends OutputStream { private final PrintStream out; @@ -76,15 +79,15 @@ public class ObjectOutputStream extends OutputStream { if (o == null) { out.print("n"); } else { - Integer id = map.get(new Identity(o)); + Integer id = map.get(o); if (id == null) { - map.put(new Identity(o), nextId); + map.put(o, nextId); Class c = o.getClass(); if (c.isArray()) { serializeArray(o, map, nextId); } else if (Serializable.class.isAssignableFrom(c)) { - serialize(o, map, nextId); + serializeObject(o, map, nextId); } else { throw new NotSerializableException(c.getName()); } @@ -112,32 +115,33 @@ public class ObjectOutputStream extends OutputStream { for (int i = 0; i < length; ++i) { out.print(" "); + Object v = Array.get(o, i); if (t.equals(boolean.class)) { - writeBoolean(Array.getBoolean(o)); + writeBoolean((Boolean) v); } else if (t.equals(byte.class)) { - writeByte(Array.getByte(o)); + writeByte((Byte) v); } else if (t.equals(char.class)) { - writeChar(Array.getChar(o)); + writeChar((Character) v); } else if (t.equals(short.class)) { - writeShort(Array.getShort(o)); + writeShort((Short) v); } else if (t.equals(int.class)) { - writeInt(Array.getInt(o)); + writeInt((Integer) v); } else if (t.equals(long.class)) { - writeLong(Array.getLong(o)); + writeLong((Long) v); } else if (t.equals(float.class)) { - writeFloat(Array.getFloat(o)); + writeFloat((Float) v); } else if (t.equals(double.class)) { - writeDouble(Array.getDouble(o)); + writeDouble((Double) v); } else { - writeObject(Array.get(o), map, nextId); + writeObject(v, map, nextId); } } out.print(")"); } - private void serialize(Object o, IdentityHashMap map, - int nextId) + private void serializeObject(Object o, IdentityHashMap map, + int nextId) throws IOException { Class c = o.getClass(); @@ -151,25 +155,26 @@ public class ObjectOutputStream extends OutputStream { int modifiers = f.getModifiers(); if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { out.print(" "); + Object v = f.get(o); Class t = f.getType(); if (t.equals(boolean.class)) { - writeBoolean(f.getBoolean(o)); + writeBoolean((Boolean) v); } else if (t.equals(byte.class)) { - writeByte(f.getByte(o)); + writeByte((Byte) v); } else if (t.equals(char.class)) { - writeChar(f.getChar(o)); + writeChar((Character) v); } else if (t.equals(short.class)) { - writeShort(f.getShort(o)); + writeShort((Short) v); } else if (t.equals(int.class)) { - writeInt(f.getInt(o)); + writeInt((Integer) v); } else if (t.equals(long.class)) { - writeLong(f.getLong(o)); + writeLong((Long) v); } else if (t.equals(float.class)) { - writeFloat(f.getFloat(o)); + writeFloat((Float) v); } else if (t.equals(double.class)) { - writeDouble(f.getDouble(o)); + writeDouble((Double) v); } else { - writeObject(f.get(o), map, nextId); + writeObject(v, map, nextId); } } } diff --git a/classpath/java/io/PushbackReader.java b/classpath/java/io/PushbackReader.java new file mode 100644 index 0000000000..1df36fbc80 --- /dev/null +++ b/classpath/java/io/PushbackReader.java @@ -0,0 +1,69 @@ +package java.io; + +public class PushbackReader extends Reader { + private final Reader in; + private final char[] buffer; + private int position; + private int limit; + + public PushbackReader(Reader in, int bufferSize) { + this.in = in; + this.buffer = new char[bufferSize]; + } + + public PushbackReader(Reader in) { + this(in, 1); + } + + public int read(char[] b, int offset, int length) throws IOException { + int count = 0; + + if (position < limit) { + int remaining = limit - position; + if (remaining > length) { + remaining = length; + } + + System.arraycopy(buffer, position, b, offset, remaining); + + count += remaining; + position += remaining; + offset += remaining; + length -= remaining; + } + + if (length > 0) { + int c = in.read(b, offset, length); + if (c == -1) { + if (count == 0) { + count = -1; + } + } else { + count += c; + } + } + + return count; + } + + public void unread(char[] b, int offset, int length) throws IOException { + if (position < length) { + throw new IOException(length + " not in [0," + position + "]"); + } else { + System.arraycopy(buffer, position - length, b, offset, length); + position -= length; + } + } + + public void unread(char[] b) throws IOException { + unread(b, 0, b.length); + } + + public void unread(int c) throws IOException { + unread(new char[] { (char) c }); + } + + public void close() throws IOException { + in.close(); + } +} diff --git a/classpath/java/io/StreamCorruptedException.java b/classpath/java/io/StreamCorruptedException.java new file mode 100644 index 0000000000..2ebb87daf1 --- /dev/null +++ b/classpath/java/io/StreamCorruptedException.java @@ -0,0 +1,11 @@ +package java.io; + +public class StreamCorruptedException extends IOException { + public StreamCorruptedException(String message) { + super(message); + } + + public StreamCorruptedException() { + this(null); + } +} diff --git a/classpath/java/lang/Boolean.java b/classpath/java/lang/Boolean.java index 86dcf05b2d..5d68dcf8ed 100644 --- a/classpath/java/lang/Boolean.java +++ b/classpath/java/lang/Boolean.java @@ -12,6 +12,10 @@ public final class Boolean { this.value = value; } + public static Boolean valueOf(boolean value) { + return (value ? Boolean.TRUE : Boolean.FALSE); + } + public boolean equals(Object o) { return o instanceof Boolean && ((Boolean) o).value == value; } diff --git a/classpath/java/lang/Byte.java b/classpath/java/lang/Byte.java index c87f24dca0..3007906695 100644 --- a/classpath/java/lang/Byte.java +++ b/classpath/java/lang/Byte.java @@ -9,6 +9,10 @@ public final class Byte extends Number { this.value = value; } + public static Byte valueOf(byte value) { + return new Byte(value); + } + public boolean equals(Object o) { return o instanceof Byte && ((Byte) o).value == value; } diff --git a/classpath/java/lang/Character.java b/classpath/java/lang/Character.java index aaa7713f0c..765f005042 100644 --- a/classpath/java/lang/Character.java +++ b/classpath/java/lang/Character.java @@ -9,6 +9,10 @@ public final class Character { this.value = value; } + public static Character valueOf(char value) { + return new Character(value); + } + public boolean equals(Object o) { return o instanceof Character && ((Character) o).value == value; } diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index 6516e5a538..da3ce0bf9f 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -47,7 +47,7 @@ public final class Class { private native void initialize(); - static Class forCanonicalName(String name) { + public static Class forCanonicalName(String name) { try { if (name.startsWith("[")) { return forName(name); @@ -65,6 +65,14 @@ public final class Class { } } + public Class getComponentType() { + if (isArray()) { + return forCanonicalName(new String(name, 1, name.length - 2, false)); + } else { + return null; + } + } + public native boolean isAssignableFrom(Class c); private Field findField(String name) { diff --git a/classpath/java/lang/Double.java b/classpath/java/lang/Double.java index 6073fce540..89d681ac29 100644 --- a/classpath/java/lang/Double.java +++ b/classpath/java/lang/Double.java @@ -13,6 +13,10 @@ public final class Double extends Number { this.value = value; } + public static Double valueOf(double value) { + return new Double(value); + } + public boolean equals(Object o) { return o instanceof Double && ((Double) o).value == value; } diff --git a/classpath/java/lang/Float.java b/classpath/java/lang/Float.java index 769d7e6266..5bbad02ad3 100644 --- a/classpath/java/lang/Float.java +++ b/classpath/java/lang/Float.java @@ -9,6 +9,10 @@ public final class Float extends Number { this.value = value; } + public static Float valueOf(float value) { + return new Float(value); + } + public boolean equals(Object o) { return o instanceof Float && ((Float) o).value == value; } diff --git a/classpath/java/lang/Integer.java b/classpath/java/lang/Integer.java index d22777dcd1..8c2f7e030d 100644 --- a/classpath/java/lang/Integer.java +++ b/classpath/java/lang/Integer.java @@ -12,6 +12,10 @@ public final class Integer extends Number { this.value = value; } + public static Integer valueOf(int value) { + return new Integer(value); + } + public boolean equals(Object o) { return o instanceof Integer && ((Integer) o).value == value; } diff --git a/classpath/java/lang/Long.java b/classpath/java/lang/Long.java index 0238d08d16..b289726e1e 100644 --- a/classpath/java/lang/Long.java +++ b/classpath/java/lang/Long.java @@ -9,6 +9,10 @@ public final class Long extends Number { this.value = value; } + public static Long valueOf(long value) { + return new Long(value); + } + public boolean equals(Object o) { return o instanceof Long && ((Long) o).value == value; } diff --git a/classpath/java/lang/Short.java b/classpath/java/lang/Short.java index 5049a0f509..4b014cc207 100644 --- a/classpath/java/lang/Short.java +++ b/classpath/java/lang/Short.java @@ -9,6 +9,10 @@ public final class Short extends Number { this.value = value; } + public static Short valueOf(short value) { + return new Short(value); + } + public boolean equals(Object o) { return o instanceof Short && ((Short) o).value == value; } diff --git a/classpath/java/lang/System.java b/classpath/java/lang/System.java index e786e6ac80..0afee7fd5e 100644 --- a/classpath/java/lang/System.java +++ b/classpath/java/lang/System.java @@ -29,6 +29,8 @@ public abstract class System { public static native long currentTimeMillis(); + public static native int identityHashCode(Object o); + public static void loadLibrary(String name) { Runtime.getRuntime().loadLibrary(name); } diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index e1097ab548..43da614cb9 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -31,6 +31,10 @@ public class Field extends AccessibleObject { return new String(name, 0, name.length - 1, false); } + public Class getType() { + return Class.forCanonicalName(getName()); + } + public native Object get(Object instance); public native void set(Object instance, Object value); diff --git a/classpath/java/util/HashMap.java b/classpath/java/util/HashMap.java index 8c859da55e..676507391c 100644 --- a/classpath/java/util/HashMap.java +++ b/classpath/java/util/HashMap.java @@ -64,9 +64,9 @@ public class HashMap implements Map { private Cell find(K key) { if (array != null) { - int index = hash(key) & (array.length - 1); + int index = helper.hash(key) & (array.length - 1); for (Cell c = array[index]; c != null; c = c.next()) { - if (equal(key, c.getKey())) { + if (helper.equal(key, c.getKey())) { return c; } } @@ -131,10 +131,10 @@ public class HashMap implements Map { public Cell removeCell(K key) { Cell old = null; if (array != null) { - int index = hash(key) & (array.length - 1); + int index = helper.hash(key) & (array.length - 1); Cell p = null; for (Cell c = array[index]; c != null; c = c.next()) { - if (equal(key, c.getKey())) { + if (helper.equal(key, c.getKey())) { old = c; if (p == null) { array[index] = c.next(); @@ -196,15 +196,17 @@ public class HashMap implements Map { public boolean equal(K a, K b); } - private class MyCell implements Cell { + private static class MyCell implements Cell { public final K key; public V value; public Cell next; + public int hashCode; - public MyCell(K key, V value, Cell next) { + public MyCell(K key, V value, Cell next, int hashCode) { this.key = key; this.value = value; this.next = next; + this.hashCode = hashCode; } public K getKey() { @@ -228,13 +230,13 @@ public class HashMap implements Map { } public int hashCode() { - return helper.hash(key); + return hashCode; } } static class MyHelper implements Helper { public Cell make(K key, V value, Cell next) { - return new MyCell(key, value, next); + return new MyCell(key, value, next, hash(key)); } public int hash(K a) { diff --git a/classpath/java/util/WeakHashMap.java b/classpath/java/util/WeakHashMap.java index 5fb18bf615..22edc2c311 100644 --- a/classpath/java/util/WeakHashMap.java +++ b/classpath/java/util/WeakHashMap.java @@ -79,11 +79,11 @@ public class WeakHashMap implements Map { public HashMap.Cell next; public int hashCode; - public MyCell(K key, V value, HashMap.Cell next) { + public MyCell(K key, V value, HashMap.Cell next, int hashCode) { super(key); this.value = value; this.next = next; - this.hashCode = (key == null ? 0 : key.hashCode()); + this.hashCode = hashCode; } public K getKey() { @@ -115,7 +115,7 @@ public class WeakHashMap implements Map { extends HashMap.MyHelper { public HashMap.Cell make(K key, V value, HashMap.Cell next) { - return new MyCell(key, value, next); + return new MyCell(key, value, next, hash(key)); } } } diff --git a/makefile b/makefile index be2562f2fb..2850aef4fa 100644 --- a/makefile +++ b/makefile @@ -16,7 +16,7 @@ src = src classpath = classpath test = test -input = $(cls)/Reflection.class +input = $(cls)/References.class cxx = g++ cc = gcc diff --git a/src/builtin.cpp b/src/builtin.cpp index 3dd77fff30..1d63e19240 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -49,13 +49,19 @@ Object_wait(Thread* t, jobject this_, jlong milliseconds) void Object_notify(Thread* t, jobject this_) { - vm::notify(t, *this_); + notify(t, *this_); } void Object_notifyAll(Thread* t, jobject this_) { - vm::notifyAll(t, *this_); + notifyAll(t, *this_); +} + +jint +Object_hashCode(Thread* t, jobject this_) +{ + return objectHash(t, *this_); } jclass @@ -119,6 +125,12 @@ SystemClassLoader_resourceExists(Thread* t, jclass, jstring name) } } +jobject +ObjectInputStream_makeInstance(Thread* t, jclass, jclass c) +{ + return pushReference(t, make(t, *c)); +} + jclass Class_primitiveClass(Thread* t, jclass, jchar name) { @@ -547,6 +559,17 @@ System_arraycopy(Thread* t, jclass, jobject src, jint srcOffset, jobject dst, t->exception = makeArrayStoreException(t); } +jint +System_identityHashCode(Thread* t, jclass, jobject o) +{ + if (LIKELY(o)) { + return objectHash(t, *o); + } else { + t->exception = makeNullPointerException(t); + return 0; + } +} + void Runtime_loadLibrary(Thread* t, jobject, jstring name) { @@ -791,6 +814,8 @@ populateBuiltinMap(Thread* t, object map) reinterpret_cast(::Object_toString) }, { "Java_java_lang_Object_wait", reinterpret_cast(::Object_wait) }, + { "Java_java_lang_Object_hashCode", + reinterpret_cast(::Object_hashCode) }, { "Java_java_lang_reflect_Array_get", reinterpret_cast(::Array_get) }, @@ -823,6 +848,9 @@ populateBuiltinMap(Thread* t, object map) { "Java_java_net_URL_00024ResourceInputStream_close", reinterpret_cast(::ResourceInputStream_close) }, + { "Java_java_io_ObjectInputStream_makeInstance", + reinterpret_cast(::ObjectInputStream_makeInstance) }, + { 0, 0 } };