diff --git a/classpath/avian/Atomic.java b/classpath/avian/Atomic.java new file mode 100644 index 0000000000..1d6ee7569e --- /dev/null +++ b/classpath/avian/Atomic.java @@ -0,0 +1,10 @@ +package avian; + +import java.lang.reflect.Field; + +public class Atomic { + public static native long getOffset(Field field); + + public static native boolean compareAndSwapObject + (Object o, long offset, Object old, Object new_); +} diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index 0f3b838d9b..bb087560fe 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -51,7 +51,9 @@ # endif # include "unistd.h" # include "limits.h" +# include "signal.h" # include "sys/time.h" +# include "sys/types.h" # include "sys/sysctl.h" # include "sys/utsname.h" # include "sys/wait.h" @@ -281,6 +283,11 @@ Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid, jlong tid) return exitCode; } +extern "C" JNIEXPORT void JNICALL +Java_java_lang_Runtime_kill(JNIEnv*, jclass, jlong pid) { + TerminateProcess(reinterpret_cast(pid), 1); +} + Locale getLocale() { const char* lang = ""; const char* reg = ""; @@ -468,6 +475,11 @@ Java_java_lang_Runtime_waitFor(JNIEnv*, jclass, jlong pid, jlong) return exitCode; } +extern "C" JNIEXPORT void JNICALL +Java_java_lang_Runtime_kill(JNIEnv*, jclass, jlong pid) { + kill((pid_t)pid, SIGTERM); +} + Locale getLocale() { Locale fallback; diff --git a/classpath/java/io/BufferedReader.java b/classpath/java/io/BufferedReader.java index 236a086b80..2618dfdbd6 100644 --- a/classpath/java/io/BufferedReader.java +++ b/classpath/java/io/BufferedReader.java @@ -46,8 +46,7 @@ public class BufferedReader extends Reader { sb.append(buffer, position, i - position); position = i + 1; if(i+1 < limit && buffer[i+1] == '\n') { - position = i + 1; - return sb.toString(); + position = i + 2; } return sb.toString(); } else if (buffer[i] == '\n') { diff --git a/classpath/java/io/File.java b/classpath/java/io/File.java index 51e006e53f..9342627bd5 100644 --- a/classpath/java/io/File.java +++ b/classpath/java/io/File.java @@ -61,6 +61,10 @@ public class File implements Serializable { return isFile(path); } + public boolean isAbsolute() { + return path.equals(toAbsolutePath(path)); + } + private static native boolean canRead(String path); public boolean canRead() { diff --git a/classpath/java/io/InputStream.java b/classpath/java/io/InputStream.java index a225432044..8e74c2db8d 100644 --- a/classpath/java/io/InputStream.java +++ b/classpath/java/io/InputStream.java @@ -56,7 +56,7 @@ public abstract class InputStream { } public void reset() throws IOException { - // ignore + throw new IOException("mark/reset not supported"); } public boolean markSupported() { diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index a2f40bce76..cc3c41e757 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -12,16 +12,21 @@ package java.io; public class RandomAccessFile { private long peer; - private long length; + private File file; private long position = 0; - + private long length; + public RandomAccessFile(String name, String mode) throws FileNotFoundException { if (! mode.equals("r")) throw new IllegalArgumentException(); + file = new File(name); + open(); + } + private void open() throws FileNotFoundException { long[] result = new long[2]; - open(name, result); + open(file.getPath(), result); peer = result[0]; length = result[1]; } @@ -29,7 +34,15 @@ public class RandomAccessFile { private static native void open(String name, long[] result) throws FileNotFoundException; + private void refresh() throws IOException { + if (file.length() != length) { + close(); + open(); + } + } + public long length() throws IOException { + refresh(); return length; } @@ -38,7 +51,7 @@ public class RandomAccessFile { } public void seek(long position) throws IOException { - if (position < 0 || position > length) throw new IOException(); + if (position < 0 || position > length()) throw new IOException(); this.position = position; } @@ -50,12 +63,16 @@ public class RandomAccessFile { if (length == 0) return; - if (position + length > this.length) throw new EOFException(); + if (position + length > this.length) { + if (position + length > length()) throw new EOFException(); + } if (offset < 0 || offset + length > buffer.length) throw new ArrayIndexOutOfBoundsException(); copy(peer, position, buffer, offset, length); + + position += length; } private static native void copy(long peer, long position, byte[] buffer, diff --git a/classpath/java/lang/Enum.java b/classpath/java/lang/Enum.java index e09bd73c2c..cf1d033cc3 100644 --- a/classpath/java/lang/Enum.java +++ b/classpath/java/lang/Enum.java @@ -14,7 +14,7 @@ import java.lang.reflect.Method; public abstract class Enum> implements Comparable { private final String name; - private final int ordinal; + protected final int ordinal; public Enum(String name, int ordinal) { this.name = name; diff --git a/classpath/java/lang/Float.java b/classpath/java/lang/Float.java index 1e2c62972b..d466cca2a3 100644 --- a/classpath/java/lang/Float.java +++ b/classpath/java/lang/Float.java @@ -12,8 +12,10 @@ package java.lang; public final class Float extends Number { public static final Class TYPE = Class.forCanonicalName("F"); - - private final float value; + private static final int EXP_BIT_MASK = 0x7F800000; + private static final int SIGNIF_BIT_MASK = 0x007FFFFF; + + private final float value; public Float(String value) { this.value = parseFloat(value); @@ -88,6 +90,17 @@ public final class Float extends Number { throw new NumberFormatException(s); } } + + public static int floatToIntBits(float value) { + int result = floatToRawIntBits(value); + + // Check for NaN based on values of bit fields, maximum + // exponent and nonzero significand. + if (((result & EXP_BIT_MASK) == EXP_BIT_MASK) && (result & SIGNIF_BIT_MASK) != 0) { + result = 0x7fc00000; + } + return result; + } public static native int floatToRawIntBits(float value); diff --git a/classpath/java/lang/Runtime.java b/classpath/java/lang/Runtime.java index a24f850bf3..88c8ba71e3 100644 --- a/classpath/java/lang/Runtime.java +++ b/classpath/java/lang/Runtime.java @@ -122,6 +122,8 @@ public class Runtime { private static native void load(String name, boolean mapName); + private static native void kill(long pid); + public native void gc(); public native void exit(int code); @@ -147,7 +149,9 @@ public class Runtime { } public void destroy() { - throw new RuntimeException("not implemented"); + if (pid != 0) { + kill(pid); + } } public InputStream getInputStream() { diff --git a/classpath/java/net/URL.java b/classpath/java/net/URL.java index 874eec964b..eb46c61860 100644 --- a/classpath/java/net/URL.java +++ b/classpath/java/net/URL.java @@ -19,6 +19,8 @@ public final class URL { private String host; private int port; private String file; + private String path; + private String query; private String ref; public URL(String s) throws MalformedURLException { @@ -55,6 +57,14 @@ public final class URL { public String getRef() { return ref; } + + public String getPath() { + return path; + } + + public String getQuery() { + return query; + } public URLConnection openConnection() throws IOException { return handler.openConnection(this); @@ -90,5 +100,13 @@ public final class URL { this.port = port; this.file = file; this.ref = ref; + + int q = file.lastIndexOf('?'); + if (q != -1) { + this.query = file.substring(q + 1); + this.path = file.substring(0, q); + } else { + this.path = file; + } } } diff --git a/classpath/java/nio/Buffer.java b/classpath/java/nio/Buffer.java index 3de26da72f..c93c11f347 100644 --- a/classpath/java/nio/Buffer.java +++ b/classpath/java/nio/Buffer.java @@ -15,45 +15,50 @@ public abstract class Buffer { protected int position; protected int limit; - public int limit() { + public final int limit() { return limit; } - public int remaining() { + public final int remaining() { return limit-position; } - public int position() { + public final int position() { return position; } - public int capacity() { + public final int capacity() { return capacity; } - public Buffer limit(int newLimit) { + public final Buffer limit(int newLimit) { limit = newLimit; return this; } - public Buffer position(int newPosition) { + public final Buffer position(int newPosition) { position = newPosition; return this; } - public boolean hasRemaining() { + public final boolean hasRemaining() { return remaining() > 0; } - public Buffer clear() { + public final Buffer clear() { position = 0; limit = capacity; return this; } - public Buffer flip() { + public final Buffer flip() { limit = position; position = 0; return this; } + + public final Buffer rewind() { + position = 0; + return this; + } } diff --git a/classpath/java/nio/ByteBuffer.java b/classpath/java/nio/ByteBuffer.java index e8191f90a3..2dee03f7d9 100644 --- a/classpath/java/nio/ByteBuffer.java +++ b/classpath/java/nio/ByteBuffer.java @@ -36,6 +36,13 @@ public class ByteBuffer extends Buffer implements Comparable { position = 0; } + public ByteBuffer asReadOnlyBuffer() { + ByteBuffer b = new ByteBuffer(array, arrayOffset, capacity, true); + b.position(position()); + b.limit(limit()); + return b; + } + public int compareTo(ByteBuffer o) { int end = (remaining() < o.remaining() ? remaining() : o.remaining()); @@ -159,6 +166,13 @@ public class ByteBuffer extends Buffer implements Comparable { | ((array[p + 3] & 0xFF)); } + public short getShort(int position) { + checkGet(position, 2); + + int p = arrayOffset + position; + return (short) (((array[p] & 0xFF) << 8) | ((array[p + 1] & 0xFF))); + } + public int getInt() { checkGet(4); int i = get() << 24; diff --git a/classpath/java/util/AbstractCollection.java b/classpath/java/util/AbstractCollection.java index 58a034765f..54cd7288df 100644 --- a/classpath/java/util/AbstractCollection.java +++ b/classpath/java/util/AbstractCollection.java @@ -72,4 +72,7 @@ public abstract class AbstractCollection implements Collection { public abstract Iterator iterator(); + public String toString() { + return Collections.toString(this); + } } diff --git a/classpath/java/util/AbstractList.java b/classpath/java/util/AbstractList.java index e1d4f3c307..fb9ccdccfd 100644 --- a/classpath/java/util/AbstractList.java +++ b/classpath/java/util/AbstractList.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, Avian Contributors +/* Copyright (c) 2009-2011, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -22,4 +22,20 @@ public abstract class AbstractList extends AbstractCollection public ListIterator listIterator() { return new Collections.ArrayListIterator(this); } + + public int indexOf(Object o) { + int i = 0; + for (T v: this) { + if (o == null) { + if (v == null) { + return i; + } + } else if (o.equals(v)) { + return i; + } + + ++ i; + } + return -1; + } } diff --git a/classpath/java/util/TreeSet.java b/classpath/java/util/TreeSet.java index 16f291a134..01e76496a2 100644 --- a/classpath/java/util/TreeSet.java +++ b/classpath/java/util/TreeSet.java @@ -41,6 +41,18 @@ public class TreeSet extends AbstractSet implements Collection { add(item); } } + + public T first() { + if (isEmpty()) throw new NoSuchElementException(); + + return set.first().value().value; + } + + public T last() { + if (isEmpty()) throw new NoSuchElementException(); + + return set.last().value().value; + } public Iterator iterator() { return new MyIterator(set.first()); diff --git a/classpath/java/util/UUID.java b/classpath/java/util/UUID.java new file mode 100644 index 0000000000..54f5e6295e --- /dev/null +++ b/classpath/java/util/UUID.java @@ -0,0 +1,45 @@ +package java.util; + +public class UUID { + private final byte[] data; + + private UUID(byte[] data) { + this.data = data; + } + + public static UUID randomUUID() { + byte[] array = new byte[16]; + + new Random().nextBytes(array); + + array[6] &= 0x0f; + array[6] |= 0x40; + array[8] &= 0x3f; + array[8] |= 0x80; + + return new UUID(array); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + toHex(sb, data, 0, 4); sb.append('-'); + toHex(sb, data, 4, 2); sb.append('-'); + toHex(sb, data, 6, 2); sb.append('-'); + toHex(sb, data, 8, 2); sb.append('-'); + toHex(sb, data, 10, 6); + return sb.toString(); + } + + private static char toHex(int i) { + return (char) (i < 10 ? i + '0' : (i - 10) + 'A'); + } + + private static void toHex(StringBuilder sb, byte[] array, int offset, + int length) + { + for (int i = offset; i < offset + length; ++i) { + sb.append(toHex((array[i] >> 4) & 0xf)); + sb.append(toHex((array[i] ) & 0xf)); + } + } +} diff --git a/classpath/java/util/concurrent/ConcurrentLinkedQueue.java b/classpath/java/util/concurrent/ConcurrentLinkedQueue.java new file mode 100644 index 0000000000..251adadbcc --- /dev/null +++ b/classpath/java/util/concurrent/ConcurrentLinkedQueue.java @@ -0,0 +1,93 @@ +package java.util.concurrent; + +import avian.Atomic; + +public class ConcurrentLinkedQueue { + private static final long QueueHead; + private static final long QueueTail; + private static final long NodeNext; + + static { + try { + QueueHead = Atomic.getOffset + (ConcurrentLinkedQueue.class.getField("head")); + + QueueTail = Atomic.getOffset + (ConcurrentLinkedQueue.class.getField("tail")); + + NodeNext = Atomic.getOffset + (Node.class.getField("next")); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private volatile Node head = new Node(null, null); + private volatile Node tail = head; + + public void clear() { + // todo: can we safely make this O(1)? + while (poll() != null) { } + } + + public boolean add(T value) { + Node n = new Node(value, null); + while (true) { + Node t = tail; + Node next = tail.next; + if (t == tail) { + if (next != null) { + Atomic.compareAndSwapObject(this, QueueTail, t, next); + } else if (Atomic.compareAndSwapObject(tail, NodeNext, null, n)) { + Atomic.compareAndSwapObject(this, QueueTail, t, n); + break; + } + } + } + + return true; + } + + public T peek() { + return poll(false); + } + + public T poll() { + return poll(true); + } + + public T poll(boolean remove) { + while (true) { + Node h = head; + Node t = tail; + Node next = head.next; + + if (h == head) { + if (h == t) { + if (next != null) { + Atomic.compareAndSwapObject(this, QueueTail, t, next); + } else { + return null; + } + } else { + T value = next.value; + if ((! remove) + || Atomic.compareAndSwapObject(this, QueueHead, h, next)) + { + return value; + } + } + } + } + } + + private static class Node { + public volatile T value; + public volatile Node next; + + public Node(T value, Node next) { + this.value = value; + this.next = next; + } + } +} diff --git a/makefile b/makefile index 44cb1fed48..218216f537 100755 --- a/makefile +++ b/makefile @@ -54,7 +54,7 @@ test = test classpath = avian -test-executable = $(executable) +test-executable = $(shell pwd)/$(executable) boot-classpath = $(classpath-build) embed-prefix = /avian-embedded @@ -110,7 +110,7 @@ ifneq ($(openjdk),) boot-javahome-object = $(build)/boot-javahome.o else options := $(options)-openjdk - test-executable = $(executable-dynamic) + test-executable = $(shell pwd)/$(executable-dynamic) library-path = \ $(library-path-variable)=$(build):$(openjdk)/jre/lib/$(openjdk-arch) javahome = "$$($(native-path) "$(openjdk)/jre")" @@ -246,7 +246,8 @@ ifeq ($(arch),arm) ifeq ($(build-platform),darwin) ios = true else - cflags += -marm -Wno-psabi + no-psabi = -Wno-psabi + cflags += -marm $(no-psabi) endif ifneq ($(arch),$(build-arch)) @@ -319,7 +320,7 @@ ifeq ($(platform),darwin) converter-cflags += -DOPPOSITE_ENDIAN endif flags = -arch armv6 -isysroot \ - /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/ + /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/ openjdk-extra-cflags += $(flags) cflags += $(flags) asmflags += $(flags) @@ -372,7 +373,8 @@ ifeq ($(platform),windows) exe-suffix = .exe lflags = -L$(lib) $(common-lflags) -lws2_32 -mwindows -mconsole - cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 + cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 -DTARGET_PLATFORM_WINDOWS + ifeq (,$(filter mingw32 cygwin,$(build-platform))) openjdk-extra-cflags += -I$(src)/openjdk/caseSensitive @@ -446,6 +448,7 @@ ifeq ($(use-lto),true) ifeq ($(shell expr 4 \< $(gcc-major) \ \| \( 4 \<= $(gcc-major) \& 6 \<= $(gcc-minor) \)),1) optimization-cflags += -flto + no-lto = -fno-lto lflags += $(optimization-cflags) endif endif @@ -584,12 +587,12 @@ endif cflags += $(extra-cflags) lflags += $(extra-lflags) +openjdk-cflags += $(extra-cflags) + driver-source = $(src)/main.cpp driver-object = $(build)/main.o driver-dynamic-objects = \ - $(build)/main-dynamic.o \ - $(build)/$(system).o \ - $(build)/finder.o + $(build)/main-dynamic.o boot-source = $(src)/boot.cpp boot-object = $(build)/boot.o @@ -957,6 +960,8 @@ else endif $(strip) $(strip-all) $(@) +# todo: the $(no-lto) flag below is due to odd undefined reference errors on +# Ubuntu 11.10 which may be fixable without disabling LTO. $(executable-dynamic): $(driver-dynamic-objects) $(dynamic-library) @echo "linking $(@)" ifdef msvc @@ -965,7 +970,7 @@ ifdef msvc -MANIFESTFILE:$(@).manifest $(mt) -manifest $(@).manifest -outputresource:"$(@);1" else - $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) -o $(@) + $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) $(no-lto) -o $(@) endif $(strip) $(strip-all) $(@) diff --git a/src/arm.cpp b/src/arm.cpp index e822035bfa..7d6a3dac91 100644 --- a/src/arm.cpp +++ b/src/arm.cpp @@ -2626,12 +2626,13 @@ class MyAssembler: public Assembler { poolSize += TargetBytesPerWord; } - if (needJump(b)) { + bool jump = needJump(b); + if (jump) { write4 (dst + dstOffset, ::b((poolSize + TargetBytesPerWord - 8) >> 2)); } - dstOffset += poolSize + TargetBytesPerWord; + dstOffset += poolSize + (jump ? TargetBytesPerWord : 0); } unsigned size = b->size - blockOffset; diff --git a/src/arm.h b/src/arm.h index a5285f86c3..42fc44e8bf 100644 --- a/src/arm.h +++ b/src/arm.h @@ -146,18 +146,24 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, switch (argumentTypes[ati]) { case DOUBLE_TYPE: case INT64_TYPE: { - if (gprIndex + Alignment <= GprCount) { // pass argument on registers - if (gprIndex % Alignment) { // 8-byte alignment - memset(gprTable + gprIndex, 0, 4); // probably not necessary, but for good luck - ++gprIndex; + if (gprIndex + Alignment <= GprCount) { // pass argument in register(s) + if (Alignment == 1 + and BytesPerWord < 8 + and gprIndex + Alignment == GprCount) + { + gprTable[gprIndex++] = arguments[ai]; + stack[stackIndex++] = arguments[ai + 1]; + } else { + if (gprIndex % Alignment) { + ++gprIndex; + } + + memcpy(gprTable + gprIndex, arguments + ai, 8); + gprIndex += 8 / BytesPerWord; } - - memcpy(gprTable + gprIndex, arguments + ai, 8); - gprIndex += 8 / BytesPerWord; } else { // pass argument on stack gprIndex = GprCount; - if (stackIndex % Alignment) { // 8-byte alignment - memset(stack + stackIndex, 0, 4); // probably not necessary, but for good luck + if (stackIndex % Alignment) { ++stackIndex; } diff --git a/src/bootimage.cpp b/src/bootimage.cpp index 05f99c605e..28f45dd382 100644 --- a/src/bootimage.cpp +++ b/src/bootimage.cpp @@ -211,10 +211,12 @@ allFields(Thread* t, object typeMaps, object c, unsigned* count, object* array) includeMembers = false; *count += reinterpret_cast(&byteArrayBody(t, *array, 0)) ->fixedFieldCount; - } else if (classSuper(t, c)) { + } else { includeMembers = true; - fields = getNonStaticFields - (t, typeMaps, classSuper(t, c), fields, count, array); + if (classSuper(t, c)) { + fields = getNonStaticFields + (t, typeMaps, classSuper(t, c), fields, count, array); + } } if (classFieldTable(t, c)) { @@ -1314,9 +1316,9 @@ writeBootImage2(Thread* t, FILE* bootimageOutput, FILE* codeOutput, unsigned buildOffset = BytesPerWord; unsigned targetOffset = TargetBytesPerWord; bool sawArray = false; - Type type; - unsigned buildSize; - unsigned targetSize; + Type type = Type_none; + unsigned buildSize = 0; + unsigned targetSize = 0; for (unsigned j = 1; j < count; ++j) { switch (source[j - 1]) { case Type_object: diff --git a/src/classpath-avian.cpp b/src/classpath-avian.cpp index 7d806ce87e..39cde4ff83 100644 --- a/src/classpath-avian.cpp +++ b/src/classpath-avian.cpp @@ -605,6 +605,33 @@ Avian_java_lang_Thread_yield t->m->system->yield(); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Atomic_getOffset +(Thread* t, object, uintptr_t* arguments) +{ + return fieldOffset + (t, jfieldVmField(t, reinterpret_cast(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Atomic_compareAndSwapObject +(Thread* t, object, uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[0]); + int64_t offset; memcpy(&offset, arguments + 1, 8); + uintptr_t expect = arguments[3]; + uintptr_t update = arguments[4]; + + bool success = atomicCompareAndSwap + (&cast(target, offset), expect, update); + + if (success) { + mark(t, target, offset); + } + + return success; +} + extern "C" JNIEXPORT int64_t JNICALL Avian_avian_Classes_primitiveClass (Thread* t, object, uintptr_t* arguments) diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 8d70b860dd..2b12362a6c 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -2594,15 +2594,25 @@ Avian_sun_misc_Unsafe_compareAndSwapObject extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_compareAndSwapLong -(Thread*, object, uintptr_t* arguments) +(Thread* t UNUSED, object, uintptr_t* arguments) { object target = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); uint64_t expect; memcpy(&expect, arguments + 4, 8); uint64_t update; memcpy(&update, arguments + 6, 8); +#ifdef AVIAN_HAS_CAS64 return atomicCompareAndSwap64 (&cast(target, offset), expect, update); +#else + ACQUIRE_FIELD_FOR_WRITE(t, local::fieldForOffset(t, target, offset)); + if (cast(target, offset) == expect) { + cast(target, offset) = update; + return true; + } else { + return false; + } +#endif } extern "C" JNIEXPORT int64_t JNICALL @@ -2747,17 +2757,27 @@ Avian_sun_misc_Unsafe_park bool absolute = arguments[1]; int64_t time; memcpy(&time, arguments + 2, 8); + int64_t then = t->m->system->now(); + if (absolute) { - time -= t->m->system->now(); + time -= then; if (time <= 0) { return; } + } else { + time /= 1000 * 1000; } monitorAcquire(t, local::interruptLock(t, t->javaThread)); - while (not (threadUnparked(t, t->javaThread) - or monitorWait(t, local::interruptLock(t, t->javaThread), time))) - { } + while (time > 0 + and (not (threadUnparked(t, t->javaThread) + or monitorWait + (t, local::interruptLock(t, t->javaThread), time)))) + { + int64_t now = t->m->system->now(); + time -= now - then; + then = now; + } threadUnparked(t, t->javaThread) = false; monitorRelease(t, local::interruptLock(t, t->javaThread)); } diff --git a/src/compile.cpp b/src/compile.cpp index c993e82f6e..25fa2c5ab5 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -2011,7 +2011,7 @@ findExceptionHandler(Thread* t, object method, void* ip) if (key >= start and key < end) { object catchType = arrayBody(t, table, i + 1); - if (catchType == 0 or instanceOf(t, catchType, t->exception)) { + if (exceptionMatch(t, catchType, t->exception)) { return compiled + intArrayBody(t, index, (i * 3) + 2); } } @@ -2274,7 +2274,7 @@ uintptr_t methodAddress(Thread* t, object method) { if (methodFlags(t, method) & ACC_NATIVE) { - return nativeThunk(static_cast(t)); + return bootNativeThunk(static_cast(t)); } else { return methodCompiled(t, method); } @@ -8249,7 +8249,7 @@ class SignalHandler: public System::SignalHandler { t->exception = vm::root(t, root); } - //printTrace(t, t->exception); + // printTrace(t, t->exception); object continuation; findUnwindTarget(t, ip, frame, stack, &continuation); @@ -8395,6 +8395,12 @@ class MyProcessor: public Processor { difference(&(t->virtualCallTarget), t)); fprintf(stderr, "virtualCallIndex %d\n", difference(&(t->virtualCallIndex), t)); + fprintf(stderr, "heapImage %d\n", + difference(&(t->heapImage), t)); + fprintf(stderr, "codeImage %d\n", + difference(&(t->codeImage), t)); + fprintf(stderr, "thunkTable %d\n", + difference(&(t->thunkTable), t)); exit(0); } @@ -8820,6 +8826,10 @@ class MyProcessor: public Processor { local::compileThunks(static_cast(t), &codeAllocator); + if (not (image and code)) { + bootThunks = thunks; + } + segFaultHandler.m = t->m; expect(t, t->m->system->success (t->m->system->handleSegFault(&segFaultHandler))); @@ -8909,20 +8919,22 @@ compileMethod2(MyThread* t, void* ip) compile(t, codeAllocator(t), 0, target); - uintptr_t address; - if ((methodFlags(t, target) & ACC_NATIVE) - and useLongJump(t, reinterpret_cast(ip))) - { - address = bootNativeThunk(t); - } else { - address = methodAddress(t, target); - } - uint8_t* updateIp = static_cast(ip); MyProcessor* p = processor(t); - if (updateIp < p->codeImage or updateIp >= p->codeImage + p->codeImageSize) { + bool updateCaller = updateIp < p->codeImage + or updateIp >= p->codeImage + p->codeImageSize; + + uintptr_t address; + if (methodFlags(t, target) & ACC_NATIVE) { + address = useLongJump(t, reinterpret_cast(ip)) + or (not updateCaller) ? bootNativeThunk(t) : nativeThunk(t); + } else { + address = methodAddress(t, target); + } + + if (updateCaller) { UnaryOperation op; if (callNodeFlags(t, node) & TraceElement::LongCall) { if (callNodeFlags(t, node) & TraceElement::TailCall) { @@ -9225,6 +9237,52 @@ fixupHeap(MyThread* t UNUSED, uintptr_t* map, unsigned size, uintptr_t* heap) } } +void +resetClassRuntimeState(Thread* t, object c, uintptr_t* heap, unsigned heapSize) +{ + classRuntimeDataIndex(t, c) = 0; + + if (classArrayElementSize(t, c) == 0) { + object staticTable = classStaticTable(t, c); + if (staticTable) { + for (unsigned i = 0; i < singletonCount(t, staticTable); ++i) { + if (singletonIsObject(t, staticTable, i) + and (reinterpret_cast + (singletonObject(t, staticTable, i)) < heap or + reinterpret_cast + (singletonObject(t, staticTable, i)) > heap + heapSize)) + { + singletonObject(t, staticTable, i) = 0; + } + } + } + } + + if (classMethodTable(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object m = arrayBody(t, classMethodTable(t, c), i); + + methodNativeID(t, m) = 0; + methodRuntimeDataIndex(t, m) = 0; + + if (methodVmFlags(t, m) & ClassInitFlag) { + classVmFlags(t, c) |= NeedInitFlag; + classVmFlags(t, c) &= ~InitErrorFlag; + } + } + } + + t->m->processor->initVtable(t, c); +} + +void +resetRuntimeState(Thread* t, object map, uintptr_t* heap, unsigned heapSize) +{ + for (HashMapIterator it(t, map); it.hasMore();) { + resetClassRuntimeState(t, tripleSecond(t, it.next()), heap, heapSize); + } +} + void fixupMethods(Thread* t, object map, BootImage* image UNUSED, uint8_t* code) { @@ -9327,7 +9385,11 @@ boot(MyThread* t, BootImage* image, uint8_t* code) // fprintf(stderr, "code from %p to %p\n", // code, code + image->codeSize); - fixupHeap(t, heapMap, heapMapSizeInWords, heap); + static bool fixed = false; + + if (not fixed) { + fixupHeap(t, heapMap, heapMapSizeInWords, heap); + } t->m->heap->setImmortalHeap(heap, image->heapSize / BytesPerWord); @@ -9372,11 +9434,30 @@ boot(MyThread* t, BootImage* image, uint8_t* code) findThunks(t, image, code); - fixupVirtualThunks(t, code); + if (fixed) { + resetRuntimeState + (t, classLoaderMap(t, root(t, Machine::BootLoader)), heap, + image->heapSize); - fixupMethods - (t, classLoaderMap(t, root(t, Machine::BootLoader)), image, code); - fixupMethods(t, classLoaderMap(t, root(t, Machine::AppLoader)), image, code); + resetRuntimeState + (t, classLoaderMap(t, root(t, Machine::AppLoader)), heap, + image->heapSize); + + for (unsigned i = 0; i < arrayLength(t, t->m->types); ++i) { + resetClassRuntimeState + (t, type(t, static_cast(i)), heap, image->heapSize); + } + } else { + fixupVirtualThunks(t, code); + + fixupMethods + (t, classLoaderMap(t, root(t, Machine::BootLoader)), image, code); + + fixupMethods + (t, classLoaderMap(t, root(t, Machine::AppLoader)), image, code); + } + + fixed = true; setRoot(t, Machine::BootstrapClassMap, makeHashMap(t, 0, 0)); } diff --git a/src/finder.cpp b/src/finder.cpp index 7257fa2603..f53738e9f4 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -920,7 +920,7 @@ class MyFinder: public Finder { namespace vm { -Finder* +JNIEXPORT Finder* makeFinder(System* s, Allocator* a, const char* path, const char* bootLibrary) { return new (a->allocate(sizeof(MyFinder))) MyFinder(s, a, path, bootLibrary); diff --git a/src/heap.cpp b/src/heap.cpp index c8f30c0369..b99de70de7 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -544,9 +544,9 @@ class Fixie { // you add/remove/change fields in this class: uint8_t age; - bool hasMask; - bool marked; - bool dirty; + uint8_t hasMask; + uint8_t marked; + uint8_t dirty; uint32_t size; Fixie* next; Fixie** handle; @@ -560,7 +560,7 @@ fixie(void* body) } void -free(Context* c, Fixie** fixies); +free(Context* c, Fixie** fixies, bool resetImmortal = false); class Context { public: @@ -630,9 +630,9 @@ class Context { } void disposeFixies() { - free(this, &tenuredFixies); - free(this, &dirtyTenuredFixies); - free(this, &fixies); + free(this, &tenuredFixies, true); + free(this, &dirtyTenuredFixies, true); + free(this, &fixies, true); } System* system; @@ -846,13 +846,25 @@ bitset(Context* c UNUSED, void* o) } void -free(Context* c, Fixie** fixies) +free(Context* c, Fixie** fixies, bool resetImmortal) { for (Fixie** p = fixies; *p;) { Fixie* f = *p; if (f->immortal()) { - p = &(f->next); + if (resetImmortal) { + if (DebugFixies) { + fprintf(stderr, "reset immortal fixie %p\n", f); + } + *p = f->next; + memset(f->mask(), 0, Fixie::maskSize(f->size, f->hasMask)); + f->next = 0; + f->handle = 0; + f->marked = false; + f->dirty = false; + } else { + p = &(f->next); + } } else { *p = f->next; if (DebugFixies) { diff --git a/src/interpret.cpp b/src/interpret.cpp index 65293463f0..6e04cd4942 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -698,7 +698,7 @@ findExceptionHandler(Thread* t, object method, unsigned ip) } } - if (catchType == 0 or instanceOf(t, catchType, t->exception)) { + if (exceptionMatch(t, catchType, t->exception)) { return eh; } } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 4d4518febd..34f82efc65 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3024,6 +3024,8 @@ boot(Thread* t, uintptr_t*) setRoot(t, Machine::OutOfMemoryError, makeThrowable(t, Machine::OutOfMemoryErrorType)); + setRoot(t, Machine::Shutdown, makeThrowable(t, Machine::ThrowableType)); + setRoot(t, Machine::FinalizerThread, t->m->classpath->makeThread(t, t)); threadDaemon(t, root(t, Machine::FinalizerThread)) = true; diff --git a/src/machine.cpp b/src/machine.cpp index 838564b5ef..fbf8ca2511 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -45,6 +45,15 @@ void join(Thread* t, Thread* o) { if (t != o) { + // todo: There's potentially a leak here on systems where we must + // call join on a thread in order to clean up all resources + // associated with it. If a thread has already been zombified by + // the time we get here, acquireSystem will return false, which + // means we can't safely join it because the System::Thread may + // already have been disposed. In that case, the thread has + // already exited (or will soon), but the OS will never free all + // its resources because it doesn't know we're completely done + // with it. if (acquireSystem(t, o)) { o->systemThread->join(); releaseSystem(t, o); @@ -138,35 +147,37 @@ dispose(Thread* t, Thread* o, bool remove) } void -joinAll(Thread* m, Thread* o) +visitAll(Thread* m, Thread* o, void (*visit)(Thread*, Thread*)) { for (Thread* p = o->child; p;) { Thread* child = p; p = p->peer; - joinAll(m, child); + visitAll(m, child, visit); } - join(m, o); + visit(m, o); } void -disposeAll(Thread* m, Thread* o) +disposeNoRemove(Thread* m, Thread* o) { - for (Thread* p = o->child; p;) { - Thread* child = p; - p = p->peer; - disposeAll(m, child); - } - dispose(m, o, false); } +void +interruptDaemon(Thread* m, Thread* o) +{ + if (o->flags & Thread::DaemonFlag) { + interrupt(m, o); + } +} + void turnOffTheLights(Thread* t) { expect(t, t->m->liveCount == 1); - joinAll(t, t->m->rootThread); + visitAll(t, t->m->rootThread, join); enter(t, Thread::ExitState); @@ -215,7 +226,7 @@ turnOffTheLights(Thread* t) Machine* m = t->m; - disposeAll(t, t->m->rootThread); + visitAll(t, t->m->rootThread, disposeNoRemove); System* s = m->system; Heap* h = m->heap; @@ -1662,13 +1673,17 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) set(t, addendum, ClassAddendumMethodTable, classMethodTable(t, class_)); - unsigned oldLength = arrayLength(t, classMethodTable(t, class_)); + unsigned oldLength = classMethodTable(t, class_) ? + arrayLength(t, classMethodTable(t, class_)) : 0; + object newMethodTable = makeArray (t, oldLength + listSize(t, abstractVirtuals)); - memcpy(&arrayBody(t, newMethodTable, 0), - &arrayBody(t, classMethodTable(t, class_), 0), - oldLength * sizeof(object)); + if (oldLength) { + memcpy(&arrayBody(t, newMethodTable, 0), + &arrayBody(t, classMethodTable(t, class_), 0), + oldLength * sizeof(object)); + } mark(t, newMethodTable, ArrayBody, oldLength); @@ -2443,6 +2458,7 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, collecting(false), triedBuiltinOnLoad(false), dumpedHeapOnOOM(false), + alive(true), heapPoolIndex(0) { heap->setClient(heapClient); @@ -2566,7 +2582,7 @@ Thread::init() enter(this, ActiveState); - if (image and image) { + if (image and code) { m->processor->boot(this, image, code); } else { boot(this); @@ -2694,6 +2710,17 @@ shutDown(Thread* t) } } } + + // interrupt daemon threads and tell them to die + + // todo: be more aggressive about killing daemon threads, e.g. at + // any GC point, not just at waits/sleeps + { ACQUIRE(t, t->m->stateLock); + + t->m->alive = false; + + visitAll(t, t->m->rootThread, interruptDaemon); + } } void @@ -3247,9 +3274,7 @@ classInitializer(Thread* t, object class_) { object o = arrayBody(t, classMethodTable(t, class_), i); - if (vm::strcmp(reinterpret_cast(""), - &byteArrayBody(t, methodName(t, o), 0)) == 0) - { + if (methodVmFlags(t, o) & ClassInitFlag) { return o; } } @@ -3798,6 +3823,9 @@ initClass(Thread* t, object c) object resolveObjectArrayClass(Thread* t, object loader, object elementClass) { + PROTECT(t, loader); + PROTECT(t, elementClass); + { object arrayClass = classRuntimeDataArrayClass (t, getClassRuntimeData(t, elementClass)); if (arrayClass) { @@ -3805,9 +3833,6 @@ resolveObjectArrayClass(Thread* t, object loader, object elementClass) } } - PROTECT(t, loader); - PROTECT(t, elementClass); - object elementSpec = className(t, elementClass); PROTECT(t, elementSpec); diff --git a/src/machine.h b/src/machine.h index 70f3d7295e..b11ca98667 100644 --- a/src/machine.h +++ b/src/machine.h @@ -157,8 +157,7 @@ const unsigned ContinuationFlag = 1 << 11; // method vmFlags: const unsigned ClassInitFlag = 1 << 0; -const unsigned CompiledFlag = 1 << 1; -const unsigned ConstructorFlag = 1 << 2; +const unsigned ConstructorFlag = 1 << 1; #ifndef JNI_VERSION_1_6 #define JNI_VERSION_1_6 0x00010006 @@ -1266,6 +1265,7 @@ class Machine { ArithmeticException, ArrayIndexOutOfBoundsException, OutOfMemoryError, + Shutdown, VirtualFileFinders, VirtualFiles }; @@ -1322,6 +1322,7 @@ class Machine { bool collecting; bool triedBuiltinOnLoad; bool dumpedHeapOnOOM; + bool alive; JavaVMVTable javaVMVTable; JNIEnvVTable jniEnvVTable; uintptr_t* heapPool[ThreadHeapPoolSize]; @@ -1357,6 +1358,9 @@ run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*), void checkDaemon(Thread* t); +object& +root(Thread* t, Machine::Root root); + extern "C" uint64_t vmRun(uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments, void* checkpoint); @@ -1507,7 +1511,7 @@ class Thread { vm::run(t, runThread, 0); - if (t->exception) { + if (t->exception and t->exception != root(t, Machine::Shutdown)) { printTrace(t, t->exception); } @@ -3182,7 +3186,11 @@ wait(Thread* t, object o, int64_t milliseconds) bool interrupted = monitorWait(t, m, milliseconds); if (interrupted) { - throwNew(t, Machine::InterruptedExceptionType); + if (t->m->alive or (t->flags & Thread::DaemonFlag) == 0) { + throwNew(t, Machine::InterruptedExceptionType); + } else { + throw_(t, root(t, Machine::Shutdown)); + } } } else { throwNew(t, Machine::IllegalMonitorStateExceptionType); @@ -3256,6 +3264,14 @@ getAndClearInterrupted(Thread* t, Thread* target) } } +inline bool +exceptionMatch(Thread* t, object type, object exception) +{ + return type == 0 + or (exception != root(t, Machine::Shutdown) + and instanceOf(t, type, t->exception)); +} + object intern(Thread* t, object s); diff --git a/src/posix.cpp b/src/posix.cpp index f1a8ca0065..074c6b379c 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -169,7 +169,7 @@ class MySystem: public System { } virtual void dispose() { - s->free(this); + ::free(this); } pthread_t thread; @@ -197,7 +197,7 @@ class MySystem: public System { virtual void dispose() { pthread_mutex_destroy(&mutex); - s->free(this); + ::free(this); } System* s; @@ -410,7 +410,7 @@ class MySystem: public System { virtual void dispose() { expect(s, owner_ == 0); pthread_mutex_destroy(&mutex); - s->free(this); + ::free(this); } System* s; @@ -441,7 +441,7 @@ class MySystem: public System { int r UNUSED = pthread_key_delete(key); expect(s, r == 0); - s->free(this); + ::free(this); } System* s; @@ -468,7 +468,7 @@ class MySystem: public System { if (start_) { munmap(start_, length_); } - s->free(this); + ::free(this); } System* s; @@ -494,7 +494,7 @@ class MySystem: public System { if (directory) { closedir(directory); } - s->free(this); + ::free(this); } System* s; @@ -541,10 +541,10 @@ class MySystem: public System { } if (name_) { - s->free(name_); + ::free(const_cast(name_)); } - s->free(this); + ::free(this); } System* s; @@ -772,13 +772,22 @@ class MySystem: public System { } virtual FileType stat(const char* name, unsigned* length) { - struct stat s; - int r = ::stat(name, &s); + // Ugly Hack Alert: It seems that the Apple iOS Simulator's stat + // implementation writes beyond the end of the struct stat we pass + // it, which can clobber unrelated parts of the stack. Perhaps + // this is due to some kind of header/library mismatch, but I've + // been unable to track it down so far. The workaround is to give + // it 8 words more than it should need, where 8 is a number I just + // made up and seems to work. + void* array[ceiling(sizeof(struct stat), sizeof(void*)) + 8]; + struct stat* s = reinterpret_cast(array); + + int r = ::stat(name, s); if (r == 0) { - if (S_ISREG(s.st_mode)) { - *length = s.st_size; + if (S_ISREG(s->st_mode)) { + *length = s->st_size; return TypeFile; - } else if (S_ISDIR(s.st_mode)) { + } else if (S_ISDIR(s->st_mode)) { *length = 0; return TypeDirectory; } else { @@ -990,7 +999,7 @@ handleSignal(int signal, siginfo_t*, void* context) namespace vm { -System* +JNIEXPORT System* makeSystem(const char*) { return new (malloc(sizeof(MySystem))) MySystem(); diff --git a/src/powerpc.cpp b/src/powerpc.cpp index 59e0c77146..7e315b1f35 100644 --- a/src/powerpc.cpp +++ b/src/powerpc.cpp @@ -2733,11 +2733,12 @@ class MyAssembler: public Assembler { assert(&c, jumpTableSize); - if (needJump(b)) { + bool jump = needJump(b); + if (jump) { write4(dst + dstOffset, ::b(jumpTableSize + TargetBytesPerWord)); } - dstOffset += jumpTableSize + TargetBytesPerWord; + dstOffset += jumpTableSize + (jump ? TargetBytesPerWord : 0); } unsigned size = b->size - blockOffset; @@ -2749,7 +2750,7 @@ class MyAssembler: public Assembler { dstOffset += size; } - unsigned index = c.code.length(); + unsigned index = dstOffset; assert(&c, index % TargetBytesPerWord == 0); for (ConstantPoolEntry* e = c.constantPool; e; e = e->next) { e->address = dst + index; diff --git a/src/target.h b/src/target.h index bbd39238d1..509a574996 100644 --- a/src/target.h +++ b/src/target.h @@ -147,7 +147,7 @@ const unsigned TargetFieldOffset = 8; const unsigned TargetBitsPerWord = TargetBytesPerWord * 8; -const uintptr_t TargetPointerMask +const target_uintptr_t TargetPointerMask = ((~static_cast(0)) / TargetBytesPerWord) * TargetBytesPerWord; diff --git a/src/windows.cpp b/src/windows.cpp index 969dabda3a..42a79d3024 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -119,7 +119,7 @@ class MySystem: public System { CloseHandle(event); CloseHandle(mutex); CloseHandle(thread); - s->free(this); + ::free(this); } HANDLE thread; @@ -150,7 +150,7 @@ class MySystem: public System { virtual void dispose() { CloseHandle(mutex); - s->free(this); + ::free(this); } System* s; @@ -377,7 +377,7 @@ class MySystem: public System { virtual void dispose() { assert(s, owner_ == 0); CloseHandle(mutex); - s->free(this); + ::free(this); } System* s; @@ -408,7 +408,7 @@ class MySystem: public System { bool r UNUSED = TlsFree(key); assert(s, r); - s->free(this); + ::free(this); } System* s; @@ -472,7 +472,7 @@ class MySystem: public System { if (handle and handle != INVALID_HANDLE_VALUE) { FindClose(handle); } - s->free(this); + ::free(this); } System* s; @@ -523,10 +523,10 @@ class MySystem: public System { } if (name_) { - s->free(name_); + ::free(const_cast(name_)); } - s->free(this); + ::free(this); } System* s; @@ -986,7 +986,7 @@ handleException(LPEXCEPTION_POINTERS e) namespace vm { -System* +JNIEXPORT System* makeSystem(const char* crashDumpDirectory) { return new (malloc(sizeof(MySystem))) MySystem(crashDumpDirectory); diff --git a/src/x86.h b/src/x86.h index 72570e03a8..72cc891ac1 100644 --- a/src/x86.h +++ b/src/x86.h @@ -255,6 +255,8 @@ atomicCompareAndSwap32(uint32_t* p, uint32_t old, uint32_t new_) #endif } +#define AVIAN_HAS_CAS64 + inline bool atomicCompareAndSwap64(uint64_t* p, uint64_t old, uint64_t new_) { diff --git a/test/Files.java b/test/Files.java new file mode 100644 index 0000000000..6bf01dadaa --- /dev/null +++ b/test/Files.java @@ -0,0 +1,29 @@ +import java.io.File; + +public class Files { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void isAbsoluteTest(boolean absolutePath) { + File file = new File("test.txt"); + if (absolutePath) { + file = file.getAbsoluteFile(); + } + + boolean isAbsolute = file.isAbsolute(); + + if (absolutePath) { + expect(isAbsolute); + } else { + expect(!isAbsolute); + } + + } + + public static void main(String[] args) { + isAbsoluteTest(true); + isAbsoluteTest(false); + } + +} diff --git a/test/Floats.java b/test/Floats.java index 0c513244b9..2e869a9a87 100644 --- a/test/Floats.java +++ b/test/Floats.java @@ -229,5 +229,36 @@ public class Floats { double d = (double) z; expect(d == 12345.0); } + + // Test floatToIntBits + { + int orig = 0x7f800001; + float NaN = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(NaN); + int expected = 0x7fc00000; + expect(result == expected); + } + + { + int orig = 0x7f801001; + float NaN = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(NaN); + int expected = 0x7fc00000; + expect(result == expected); + } + + { + int orig = 0x00800001; + float number = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(number); + expect(result == orig); + } + + { + int orig = 0x80800003; + float number = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(number); + expect(result == orig); + } } } diff --git a/test/Processes.java b/test/Processes.java new file mode 100644 index 0000000000..372513f0c1 --- /dev/null +++ b/test/Processes.java @@ -0,0 +1,31 @@ +import java.io.IOException; + +public class Processes { + public static void main(String[] args) { + long start = System.currentTimeMillis(); + try { + final Process p = Runtime.getRuntime().exec("sleep 10"); + new Thread() { + public void run() { + try { + Thread.sleep(100); + } catch(InterruptedException e) { + // ignore + } + p.destroy(); + } + }.start(); + try { + p.waitFor(); + } catch(InterruptedException e) { + // ignore + } + long stop = System.currentTimeMillis(); + if(stop - start > 5000) { + throw new RuntimeException("test failed; we didn't kill the process..."); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/UrlTest.java b/test/UrlTest.java new file mode 100644 index 0000000000..35281b367a --- /dev/null +++ b/test/UrlTest.java @@ -0,0 +1,42 @@ +import java.net.MalformedURLException; +import java.net.URL; + +public class UrlTest { + private static String query="var1=val1&var2=val2"; + private static String path="testpath"; + private static String domain="file://www.readytalk.com"; + private static String file=path + "?" + query; + private static URL url; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void setupURL() throws MalformedURLException { + StringBuilder builder = new StringBuilder(); + builder.append(domain); + builder.append("/"); + builder.append(file); + url = new URL(builder.toString()); + } + + private static void testGetPath() { + expect(url.getPath().equals(path)); + } + + private static void testGetFile() { + expect(url.getFile().equals(file)); + } + + private static void testGetQuery() { + expect(url.getQuery().equals(query)); + } + + public static void main(String[] args) throws MalformedURLException { + setupURL(); + testGetPath(); + testGetFile(); + testGetQuery(); + } + +}