diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index 481032209a..acb61554f4 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -14,12 +14,14 @@ #include #include #include +#include #include #include "jni.h" #include "jni-util.h" #ifdef WIN32 +# include # include # define OPEN _open @@ -34,6 +36,7 @@ # define OPEN_MASK O_BINARY #else # include +# include "sys/mman.h" # define OPEN open # define CLOSE close @@ -47,6 +50,8 @@ # define OPEN_MASK 0 #endif +inline void* operator new(size_t, void* p) throw() { return p; } + namespace { inline bool @@ -95,9 +100,118 @@ doWrite(JNIEnv* e, jint fd, const jbyte* data, jint length) int r = WRITE(fd, data, length); if (r != length) { throwNew(e, "java/io/IOException", strerror(errno)); - } + } } +#ifdef WIN32 + +class Mapping { + public: + Mapping(uint8_t* start, size_t length, HANDLE mapping, HANDLE file): + start(start), + length(length), + mapping(mapping), + file(file) + { } + + uint8_t* start; + size_t length; + HANDLE mapping; + HANDLE file; +}; + +inline Mapping* +map(JNIEnv* e, const char* path) +{ + Mapping* result = 0; + HANDLE file = CreateFile(path, FILE_READ_DATA, FILE_SHARE_READ, 0, + OPEN_EXISTING, 0, 0); + if (file != INVALID_HANDLE_VALUE) { + unsigned size = GetFileSize(file, 0); + if (size != INVALID_FILE_SIZE) { + HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, size, 0); + if (mapping) { + void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + if (data) { + void* p = allocate(e, sizeof(Mapping)); + if (not e->ExceptionOccurred()) { + result = new (p) + Mapping(static_cast(data), size, file, mapping); + } + } + + if (result == 0) { + CloseHandle(mapping); + } + } + } + + if (result == 0) { + CloseHandle(file); + } + } + if (result == 0 and not e->ExceptionOccurred()) { + throwNew(e, "java/io/IOException", "%d", GetLastError()); + } + return result; +} + +inline void +unmap(JNIEnv*, Mapping* mapping) +{ + UnmapViewOfFile(mapping->start); + CloseHandle(mapping->mapping); + CloseHandle(mapping->file); + free(mapping); +} + +#else // not WIN32 + +class Mapping { + public: + Mapping(uint8_t* start, size_t length): + start(start), + length(length) + { } + + uint8_t* start; + size_t length; +}; + +inline Mapping* +map(JNIEnv* e, const char* path) +{ + Mapping* result = 0; + int fd = open(path, O_RDONLY); + if (fd != -1) { + struct stat s; + int r = fstat(fd, &s); + if (r != -1) { + void* data = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (data) { + void* p = allocate(e, sizeof(Mapping)); + if (not e->ExceptionOccurred()) { + result = new (p) Mapping(static_cast(data), s.st_size); + } + } + } + close(fd); + } + if (result == 0 and not e->ExceptionOccurred()) { + throwNew(e, "java/io/IOException", strerror(errno)); + } + return result; +} + +inline void +unmap(JNIEnv*, Mapping* mapping) +{ + munmap(mapping->start, mapping->length); + free(mapping); +} + +#endif // not WIN32 + } // namespace extern "C" JNIEXPORT jstring JNICALL @@ -333,3 +447,42 @@ Java_java_io_FileOutputStream_close(JNIEnv* e, jclass, jint fd) { doClose(e, fd); } + +extern "C" JNIEXPORT void JNICALL +Java_java_io_RandomAccessFile_open(JNIEnv* e, jclass, jstring path, + jlongArray result) +{ + const char* chars = e->GetStringUTFChars(path, 0); + if (chars) { + Mapping* mapping = map(e, chars); + + jlong peer = reinterpret_cast(mapping); + e->SetLongArrayRegion(result, 0, 1, &peer); + + jlong length = (mapping ? mapping->length : 0); + e->SetLongArrayRegion(result, 1, 1, &length); + + e->ReleaseStringUTFChars(path, chars); + } +} + +extern "C" JNIEXPORT void JNICALL +Java_java_io_RandomAccessFile_copy(JNIEnv* e, jclass, jlong peer, + jlong position, jbyteArray buffer, + int offset, int length) +{ + uint8_t* dst = reinterpret_cast + (e->GetPrimitiveArrayCritical(buffer, 0)); + + memcpy(dst + offset, + reinterpret_cast(peer)->start + position, + length); + + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); +} + +extern "C" JNIEXPORT void JNICALL +Java_java_io_RandomAccessFile_close(JNIEnv* e, jclass, jlong peer) +{ + unmap(e, reinterpret_cast(peer)); +} diff --git a/classpath/java-nio.cpp b/classpath/java-nio.cpp index e82f34f0dd..86eb9aa6c5 100644 --- a/classpath/java-nio.cpp +++ b/classpath/java-nio.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "jni.h" diff --git a/classpath/java/io/ByteArrayOutputStream.java b/classpath/java/io/ByteArrayOutputStream.java index 78a54ad249..61fb51759e 100644 --- a/classpath/java/io/ByteArrayOutputStream.java +++ b/classpath/java/io/ByteArrayOutputStream.java @@ -24,6 +24,13 @@ public class ByteArrayOutputStream extends OutputStream { this(0); } + public void reset() { + chain = null; + length = 0; + buffer = null; + position = 0; + } + public int size() { return length; } diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java new file mode 100644 index 0000000000..a2f40bce76 --- /dev/null +++ b/classpath/java/io/RandomAccessFile.java @@ -0,0 +1,72 @@ +/* Copyright (c) 2008, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class RandomAccessFile { + private long peer; + private long length; + private long position = 0; + + public RandomAccessFile(String name, String mode) + throws FileNotFoundException + { + if (! mode.equals("r")) throw new IllegalArgumentException(); + + long[] result = new long[2]; + open(name, result); + peer = result[0]; + length = result[1]; + } + + private static native void open(String name, long[] result) + throws FileNotFoundException; + + public long length() throws IOException { + return length; + } + + public long getFilePointer() throws IOException { + return position; + } + + public void seek(long position) throws IOException { + if (position < 0 || position > length) throw new IOException(); + + this.position = position; + } + + public void readFully(byte[] buffer, int offset, int length) + throws IOException + { + if (peer == 0) throw new IOException(); + + if (length == 0) return; + + if (position + length > this.length) throw new EOFException(); + + if (offset < 0 || offset + length > buffer.length) + throw new ArrayIndexOutOfBoundsException(); + + copy(peer, position, buffer, offset, length); + } + + private static native void copy(long peer, long position, byte[] buffer, + int offset, int length); + + public void close() throws IOException { + if (peer != 0) { + close(peer); + peer = 0; + } + } + + private static native void close(long peer); +} diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index 213d8d58c6..2d907f064b 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -219,6 +219,25 @@ public final class Class { } } + public Constructor getDeclaredConstructor(Class ... parameterTypes) + throws NoSuchMethodException + { + Constructor c = null; + Constructor[] constructors = getDeclaredConstructors(); + + for (int i = 0; i < constructors.length; ++i) { + if (match(parameterTypes, constructors[i].getParameterTypes())) { + c = constructors[i]; + } + } + + if (c == null) { + throw new NoSuchMethodException(); + } else { + return c; + } + } + private int countConstructors(boolean publicOnly) { int count = 0; if (methodTable != null) { diff --git a/classpath/java/lang/Integer.java b/classpath/java/lang/Integer.java index 8a79c9babc..e524cc43c4 100644 --- a/classpath/java/lang/Integer.java +++ b/classpath/java/lang/Integer.java @@ -30,6 +30,10 @@ public final class Integer extends Number implements Comparable { return new Integer(value); } + public static Integer valueOf(String value) { + return valueOf(parseInt(value)); + } + public boolean equals(Object o) { return o instanceof Integer && ((Integer) o).value == value; } diff --git a/classpath/java/lang/Runtime.java b/classpath/java/lang/Runtime.java index 51f15006c7..63a029301a 100644 --- a/classpath/java/lang/Runtime.java +++ b/classpath/java/lang/Runtime.java @@ -75,6 +75,8 @@ public class Runtime { public native long totalMemory(); + public static native void dumpHeap(String outputFile); + private static class MyProcess extends Process { private long pid; private final int in; diff --git a/classpath/java/lang/StringBuffer.java b/classpath/java/lang/StringBuffer.java index c3de7b00da..cc494a7496 100644 --- a/classpath/java/lang/StringBuffer.java +++ b/classpath/java/lang/StringBuffer.java @@ -74,7 +74,15 @@ public class StringBuffer implements CharSequence { sb.append(b, 0, b.length); return this; } - + + public synchronized int indexOf(String s) { + return sb.indexOf(s); + } + + public synchronized int indexOf(String s, int fromIndex) { + return sb.indexOf(s, fromIndex); + } + public synchronized StringBuffer insert(int i, String s) { sb.insert(i, s); return this; @@ -85,6 +93,11 @@ public class StringBuffer implements CharSequence { return this; } + public synchronized StringBuffer insert(int i, int v) { + sb.insert(i, v); + return this; + } + public synchronized StringBuffer delete(int start, int end) { sb.delete(start, end); return this; @@ -112,6 +125,10 @@ public class StringBuffer implements CharSequence { sb.setLength(v); } + public synchronized void setCharAt(int index, char ch) { + sb.setCharAt(index, ch); + } + public synchronized void getChars(int srcOffset, int srcLength, char[] dst, int dstOffset) { diff --git a/classpath/java/lang/StringBuilder.java b/classpath/java/lang/StringBuilder.java index 5b4987ec4b..47b58134d1 100644 --- a/classpath/java/lang/StringBuilder.java +++ b/classpath/java/lang/StringBuilder.java @@ -154,6 +154,10 @@ public class StringBuilder implements CharSequence { return insert(i, new String(new char[] { c }, 0, 1, false)); } + public StringBuilder insert(int i, int v) { + return insert(i, String.valueOf(v)); + } + public StringBuilder delete(int start, int end) { if (start >= end) { return this; @@ -322,4 +326,10 @@ public class StringBuilder implements CharSequence { public CharSequence subSequence(int start, int end) { return substring(start, end); } + + public void setCharAt(int index, char ch) { + if(index < 0 || index >= length) throw new IndexOutOfBoundsException(); + deleteCharAt(index); + insert(index, ch); + } } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 39a9328c29..b28b70c045 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -111,8 +111,8 @@ public class Method extends AccessibleObject implements Member { } } - public static native Object invoke(Method method, Object instance, - Object ... arguments) + private static native Object invoke(Method method, Object instance, + Object ... arguments) throws InvocationTargetException, IllegalAccessException; public Class getReturnType() { diff --git a/classpath/java/util/Calendar.java b/classpath/java/util/Calendar.java index 8d81716253..d39b8a1438 100644 --- a/classpath/java/util/Calendar.java +++ b/classpath/java/util/Calendar.java @@ -45,6 +45,12 @@ public abstract class Calendar { fields[field] = value; } + public void set(int year, int month, int date) { + set(YEAR, year); + set(MONTH, month); + set(DAY_OF_MONTH, date); + } + public void setTime(Date date) { time = date.getTime(); } diff --git a/classpath/java/util/Locale.java b/classpath/java/util/Locale.java index b552d8d311..c53b6f4ed1 100644 --- a/classpath/java/util/Locale.java +++ b/classpath/java/util/Locale.java @@ -11,7 +11,7 @@ package java.util; public class Locale { - public static final Locale ENGLISH = new Locale("en"); + public static final Locale ENGLISH = new Locale("en", "us"); private final String language; private final String country; diff --git a/classpath/java/util/PropertyResourceBundle.java b/classpath/java/util/PropertyResourceBundle.java index b299a08026..01dc850537 100644 --- a/classpath/java/util/PropertyResourceBundle.java +++ b/classpath/java/util/PropertyResourceBundle.java @@ -23,4 +23,8 @@ public class PropertyResourceBundle extends ResourceBundle { public Object handleGetObject(String key) { return map.get(key); } + + public Enumeration getKeys() { + return map.keys(); + } } diff --git a/classpath/java/util/Random.java b/classpath/java/util/Random.java index a513fd5522..856f30424a 100644 --- a/classpath/java/util/Random.java +++ b/classpath/java/util/Random.java @@ -58,6 +58,21 @@ public class Random { return next(32); } + public void nextBytes(byte[] bytes) { + final int length = bytes.length; + for (int i = 0; i < length;) { + int r = nextInt(); + for (int j = Math.min(length - i, 4); j > 0; --j) { + bytes[i++] = (byte) r; + r >>= 8; + } + } + } + + public long nextLong() { + return ((long) next(32) << 32) + next(32); + } + public double nextDouble() { return (((long) next(26) << 27) + next(27)) / (double) (1L << 53); } diff --git a/classpath/java/util/ResourceBundle.java b/classpath/java/util/ResourceBundle.java index cc80a2a957..97bde9b80c 100644 --- a/classpath/java/util/ResourceBundle.java +++ b/classpath/java/util/ResourceBundle.java @@ -118,4 +118,6 @@ public abstract class ResourceBundle { } protected abstract Object handleGetObject(String key); + + public abstract Enumeration getKeys(); } diff --git a/classpath/java/util/Stack.java b/classpath/java/util/Stack.java index c44462fba3..862e15f39e 100644 --- a/classpath/java/util/Stack.java +++ b/classpath/java/util/Stack.java @@ -12,7 +12,7 @@ package java.util; public class Stack extends Vector { public boolean empty() { - return size() != 0; + return size() == 0; } public T peek() { diff --git a/classpath/java/util/Vector.java b/classpath/java/util/Vector.java index acba5f7445..21bd896b7c 100644 --- a/classpath/java/util/Vector.java +++ b/classpath/java/util/Vector.java @@ -61,8 +61,8 @@ public class Vector implements List { return list.set(index, value); } - public T setElementAt(T value, int index) { - return set(index, value); + public void setElementAt(T value, int index) { + set(index, value); } public T elementAt(int index) { diff --git a/classpath/java/util/zip/ZipEntry.java b/classpath/java/util/zip/ZipEntry.java new file mode 100644 index 0000000000..f2d944907f --- /dev/null +++ b/classpath/java/util/zip/ZipEntry.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2008, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +public abstract class ZipEntry { + public abstract String getName(); + public abstract int getCompressedSize(); +} diff --git a/classpath/java/util/zip/ZipFile.java b/classpath/java/util/zip/ZipFile.java new file mode 100644 index 0000000000..a11a3c5dd1 --- /dev/null +++ b/classpath/java/util/zip/ZipFile.java @@ -0,0 +1,313 @@ +/* Copyright (c) 2008, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +import java.io.File; +import java.io.RandomAccessFile; +import java.io.InputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; + +public class ZipFile { + private final RandomAccessFile file; + private final Window window; + private final Map index = new HashMap(); + + public ZipFile(String name) throws IOException { + file = new RandomAccessFile(name, "r"); + window = new Window(file, 4096); + + int fileLength = (int) file.length(); + int pointer = fileLength - 22; + byte[] magic = new byte[] { 0x50, 0x4B, 0x05, 0x06 }; + while (pointer > 0) { + if (equal(window.data, window.seek(pointer, magic.length), + magic, 0, magic.length)) + { + pointer = directoryOffset(window, pointer); + + magic = new byte[] { 0x50, 0x4B, 0x01, 0x02 }; + while (pointer < fileLength) { + if (equal(window.data, window.seek(pointer, magic.length), + magic, 0, magic.length)) + { + index.put(entryName(window, pointer), pointer); + pointer = entryEnd(window, pointer); + } else { + pointer = fileLength; + } + } + pointer = 0; + } else { + -- pointer; + } + } + } + + public ZipFile(File file) throws IOException { + this(file.getAbsolutePath()); + } + + public int size() { + return index.size(); + } + + public Enumeration entries() { + return new MyEnumeration(window, index.values().iterator()); + } + + public ZipEntry getEntry(String name) { + Integer pointer = index.get(name); + return (pointer == null ? null : new MyZipEntry(window, pointer)); + } + + public InputStream getInputStream(ZipEntry entry) throws IOException { + int pointer = ((MyZipEntry) entry).pointer; + int method = compressionMethod(window, pointer); + int size = compressedSize(window, pointer); + InputStream in = new MyInputStream(file, fileData(window, pointer), size); + + final int Stored = 0; + final int Deflated = 8; + + switch (method) { + case Stored: + return in; + + case Deflated: + return new InflaterInputStream(in, new Inflater(true)); + + default: + throw new IOException(); + } + } + + private static boolean equal(byte[] a, int aOffset, byte[] b, int bOffset, + int size) + { + for (int i = 0; i < size; ++i) { + if (a[aOffset + i] != b[bOffset + i]) return false; + } + return true; + } + + private static int get2(Window w, int p) throws IOException { + int offset = w.seek(p, 2); + return + ((w.data[offset + 1] & 0xFF) << 8) | + ((w.data[offset ] & 0xFF) ); + } + + private static int get4(Window w, int p) throws IOException { + int offset = w.seek(p, 4); + return + ((w.data[offset + 3] & 0xFF) << 24) | + ((w.data[offset + 2] & 0xFF) << 16) | + ((w.data[offset + 1] & 0xFF) << 8) | + ((w.data[offset ] & 0xFF) ); + } + + private static int directoryOffset(Window w, int p) throws IOException { + return get4(w, p + 16); + } + + private static int entryNameLength(Window w, int p) throws IOException { + return get2(w, p + 28); + } + + private static String entryName(Window w, int p) throws IOException { + int length = entryNameLength(w, p); + return new String(w.data, w.seek(p + 46, length), length); + } + + private static int compressionMethod(Window w, int p) throws IOException { + return get2(w, p + 10); + } + + private static int compressedSize(Window w, int p) throws IOException { + return get4(w, p + 20); + } + + private static int fileNameLength(Window w, int p) throws IOException { + return get2(w, p + 28); + } + + private static int extraFieldLength(Window w, int p) throws IOException { + return get2(w, p + 30); + } + + private static int commentFieldLength(Window w, int p) throws IOException { + return get2(w, p + 32); + } + + private static int entryEnd(Window w, int p) throws IOException { + final int HeaderSize = 46; + return p + HeaderSize + + fileNameLength(w, p) + + extraFieldLength(w, p) + + commentFieldLength(w, p); + } + + private static int fileData(Window w, int p) throws IOException { + int localHeader = localHeader(w, p); + final int LocalHeaderSize = 30; + return localHeader + + LocalHeaderSize + + localFileNameLength(w, localHeader) + + localExtraFieldLength(w, localHeader); + } + + private static int localHeader(Window w, int p) throws IOException { + return get4(w, p + 42); + } + + private static int localFileNameLength(Window w, int p) throws IOException { + return get2(w, p + 26); + } + + private static int localExtraFieldLength(Window w, int p) + throws IOException + { + return get2(w, p + 28); + } + + public void close() throws IOException { + file.close(); + } + + private static class Window { + private final RandomAccessFile file; + public final byte[] data; + public int start; + public int length; + + public Window(RandomAccessFile file, int size) { + this.file = file; + data = new byte[size]; + } + + public int seek(int start, int length) throws IOException { + int fileLength = (int) file.length(); + + if (length > data.length) { + throw new IllegalArgumentException + ("length " + length + " greater than buffer length " + data.length); + } + + if (start < 0) { + throw new IllegalArgumentException("negative start " + start); + } + + if (start + length > fileLength) { + throw new IllegalArgumentException + ("end " + (start + length) + " greater than file length " + + fileLength); + } + + if (start < this.start || start + length > this.start + this.length) { + this.length = Math.min(data.length, fileLength); + this.start = start - ((this.length - length) / 2); + if (this.start < 0) { + this.start = 0; + } else if (this.start + this.length > fileLength) { + this.start = fileLength - this.length; + } + file.seek(this.start); + file.readFully(data, 0, this.length); + } + + return start - this.start; + } + } + + private static class MyZipEntry extends ZipEntry { + public final Window window; + public final int pointer; + + public MyZipEntry(Window window, int pointer) { + this.window = window; + this.pointer = pointer; + } + + public String getName() { + try { + return entryName(window, pointer); + } catch (IOException e) { + return null; + } + } + + public int getCompressedSize() { + try { + return compressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + } + + private static class MyEnumeration implements Enumeration { + private final Window window; + private final Iterator iterator; + + public MyEnumeration(Window window, Iterator iterator) { + this.window = window; + this.iterator = iterator; + } + + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + public ZipEntry nextElement() { + return new MyZipEntry(window, iterator.next()); + } + } + + private static class MyInputStream extends InputStream { + private RandomAccessFile file; + private int offset; + private int length; + + public MyInputStream(RandomAccessFile file, int start, int length) { + this.file = file; + this.offset = start; + this.length = length; + } + + public int read() throws IOException { + byte[] b = new byte[1]; + int c = read(b); + return (c == -1 ? -1 : b[0] & 0xFF); + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (this.length == 0) return -1; + + if (length > this.length) length = this.length; + + file.seek(this.offset); + file.readFully(b, offset, length); + + this.offset += length; + this.length -= length; + + return length; + } + + public void close() throws IOException { + file = null; + } + } +} diff --git a/classpath/jni-util.h b/classpath/jni-util.h index 8d29a12d1b..9e0972225a 100644 --- a/classpath/jni-util.h +++ b/classpath/jni-util.h @@ -11,6 +11,9 @@ #ifndef JNI_UTIL #define JNI_UTIL +#include "stdio.h" +#include "stdlib.h" + #undef JNIEXPORT #ifdef __MINGW32__ # define JNIEXPORT __declspec(dllexport) @@ -23,15 +26,37 @@ namespace { inline void -throwNew(JNIEnv* e, const char* class_, const char* message) +throwNew(JNIEnv* e, const char* class_, const char* message, ...) { jclass c = e->FindClass(class_); if (c) { - e->ThrowNew(c, message); + if (message) { + static const unsigned BufferSize = 256; + char buffer[BufferSize]; + + va_list list; + va_start(list, message); + vsnprintf(buffer, BufferSize - 1, message, list); + va_end(list); + + e->ThrowNew(c, buffer); + } else { + e->ThrowNew(c, 0); + } e->DeleteLocalRef(c); } } +inline void* +allocate(JNIEnv* e, unsigned size) +{ + void* p = malloc(size); + if (p == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + } + return p; +} + } // namespace #endif//JNI_UTIL diff --git a/makefile b/makefile index 3eab115579..51e6bfff00 100644 --- a/makefile +++ b/makefile @@ -3,21 +3,18 @@ MAKEFLAGS = -s name = avian version = 0.1.1 -build-arch = $(shell uname -m) +build-arch = $(shell uname -m | sed 's/^i.86$$/i386/') ifeq ($(build-arch),Power) build-arch = powerpc endif -ifeq ($(build-arch),i586) - build-arch = i386 -endif -ifeq ($(build-arch),i686) - build-arch = i386 -endif -build-platform = $(shell uname -s | tr [:upper:] [:lower:]) +build-platform = \ + $(shell uname -s | tr [:upper:] [:lower:] \ + | sed 's/^mingw32.*$$/mingw32/' \ + | sed 's/^cygwin.*$$/cygwin/') arch = $(build-arch) -platform = $(build-platform) +platform = $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) ifeq ($(platform),windows) arch = i386 @@ -44,31 +41,38 @@ cxx = $(build-cxx) cc = $(build-cc) ar = ar ranlib = ranlib +dlltool = dlltool objcopy = objcopy vg = nice valgrind --num-callers=32 --db-attach=yes --freelist-vol=100000000 vg += --leak-check=full --suppressions=valgrind.supp db = gdb --args -javac = javac -jar = jar +javac = "$(JAVA_HOME)/bin/javac" +jar = "$(JAVA_HOME)/bin/jar" strip = : strip-all = --strip-all rdynamic = -rdynamic -warnings = -Wall -Wextra -Werror -Wunused-parameter -Winit-self +# note that we supress the non-virtual-dtor warning because we never +# use the delete operator, which means we don't need virtual +# destructors: +warnings = -Wall -Wextra -Werror -Wunused-parameter -Winit-self \ + -Wno-non-virtual-dtor common-cflags = $(warnings) -fno-rtti -fno-exceptions \ - -I$(JAVA_HOME)/include -idirafter $(src) -I$(native-build) \ + "-I$(JAVA_HOME)/include" -idirafter $(src) -I$(native-build) \ -D__STDC_LIMIT_MACROS -D_JNI_IMPLEMENTATION_ -DAVIAN_VERSION=\"$(version)\" \ -DBOOT_CLASSPATH=\"[classpathJar]\" build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ - -I$(JAVA_HOME)/include/linux -I$(src) -pthread + "-I$(JAVA_HOME)/include/linux" -I$(src) -pthread cflags = $(build-cflags) common-lflags = -lm -lz +build-lflags = + lflags = $(common-lflags) -lpthread -ldl system = posix @@ -83,6 +87,8 @@ so-suffix = .so shared = -shared +native-path = echo + ifeq ($(arch),i386) object-arch = i386 object-format = elf32-i386 @@ -96,8 +102,7 @@ ifeq ($(arch),powerpc) endif ifeq ($(platform),darwin) - build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ - -I$(JAVA_HOME)/include/linux -I$(src) + build-cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) lflags = $(common-lflags) -ldl -framework CoreFoundation rdynamic = strip-all = -S -x @@ -107,25 +112,37 @@ ifeq ($(platform),darwin) endif ifeq ($(platform),windows) - inc = $(root)/win32/include - lib = $(root)/win32/lib + inc = "$(root)/win32/include" + lib = "$(root)/win32/lib" system = windows object-format = pe-i386 so-prefix = so-suffix = .dll + exe-suffix = .exe - cxx = i586-mingw32msvc-g++ - cc = i586-mingw32msvc-gcc - dlltool = i586-mingw32msvc-dlltool - ar = i586-mingw32msvc-ar - ranlib = i586-mingw32msvc-ranlib - objcopy = i586-mingw32msvc-objcopy - - rdynamic = -Wl,--export-dynamic lflags = -L$(lib) $(common-lflags) -lws2_32 -mwindows -mconsole cflags = $(common-cflags) -I$(inc) + + ifeq (,$(filter mingw32 cygwin,$(build-platform))) + cxx = i586-mingw32msvc-g++ + cc = i586-mingw32msvc-gcc + dlltool = i586-mingw32msvc-dlltool + ar = i586-mingw32msvc-ar + ranlib = i586-mingw32msvc-ranlib + objcopy = i586-mingw32msvc-objcopy + else + build-cflags = $(common-cflags) \ + "-I$(JAVA_HOME)/include/win32" -I$(src) -mthreads + ifeq ($(build-platform),cygwin) + build-lflags += -mno-cygwin + build-cflags += -mno-cygwin + lflags += -mno-cygwin + cflags += -mno-cygwin + native-path = cygpath -m + endif + endif endif ifeq ($(mode),debug) @@ -144,6 +161,10 @@ ifeq ($(mode),fast) cflags += -O3 -g3 -DNDEBUG strip = strip endif +ifeq ($(mode),small) + cflags += -Os -g3 -DNDEBUG + strip = strip +endif cpp-objects = $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(3)/%.o,$(x))) asm-objects = $(foreach x,$(1),$(patsubst $(2)/%.S,$(3)/%-asm.o,$(x))) @@ -190,6 +211,11 @@ vm-sources = \ $(src)/process.cpp \ $(src)/$(asm).cpp +ifeq ($(heapdump),true) + vm-sources += $(src)/heapdump.cpp + cflags += -DAVIAN_HEAPDUMP +endif + vm-asm-sources = $(src)/$(asm).S ifeq ($(process),compile) @@ -220,9 +246,9 @@ generator-objects = \ generator = $(native-build)/generator static-library = $(native-build)/lib$(name).a -executable = $(native-build)/$(name) +executable = $(native-build)/$(name)${exe-suffix} dynamic-library = $(native-build)/$(so-prefix)$(name)$(so-suffix) -executable-dynamic = $(native-build)/$(name)-dynamic +executable-dynamic = $(native-build)/$(name)-dynamic${exe-suffix} classpath-sources = $(shell find $(classpath) -name '*.java') classpath-classes = \ @@ -261,7 +287,7 @@ vg: build .PHONY: test test: build - /bin/bash $(test)/test.sh 2>/dev/null \ + /bin/sh $(test)/test.sh 2>/dev/null \ $(executable) $(mode) "$(flags)" \ $(call class-names,$(test-build),$(test-classes)) @@ -299,7 +325,7 @@ $(classpath-dep): $(classpath-sources) @echo "compiling classpath classes" @mkdir -p $(dir $(@)) $(javac) -d $(dir $(@)) -bootclasspath $(classpath-build) \ - $(shell make -s --no-print-directory $(classpath-classes)) + $(shell $(MAKE) -s --no-print-directory $(classpath-classes)) @touch $(@) $(test-build)/%.class: $(test)/%.java @@ -309,7 +335,7 @@ $(test-dep): $(test-sources) @echo "compiling test classes" @mkdir -p $(dir $(@)) $(javac) -d $(dir $(@)) -bootclasspath $(classpath-build) \ - $(shell make -s --no-print-directory $(test-classes)) + $(shell $(MAKE) -s --no-print-directory $(test-classes)) @touch $(@) define compile-object @@ -318,11 +344,17 @@ define compile-object $(cxx) $(cflags) -c $(<) -o $(@) endef +define compile-asm-object + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cc) -I$(src) -c $(<) -o $(@) +endef + $(vm-cpp-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) $(compile-object) $(vm-asm-objects): $(native-build)/%-asm.o: $(src)/%.S - $(compile-object) + $(compile-asm-object) $(driver-object): $(driver-source) $(compile-object) @@ -339,7 +371,7 @@ $(boot-object): $(boot-source) $(build)/classpath.jar: $(classpath-dep) (wd=$$(pwd); \ cd $(classpath-build); \ - $(jar) c0f $${wd}/$(@) $$(find . -name '*.class')) + $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" $$(find . -name '*.class')) $(binaryToMacho): $(src)/binaryToMacho.cpp $(cxx) $(^) -o $(@) @@ -353,7 +385,7 @@ else (wd=$$(pwd); \ cd $(build); \ $(objcopy) -I binary classpath.jar \ - -O $(object-format) -B $(object-arch) $${wd}/$(@)) + -O $(object-format) -B $(object-arch) "$${wd}/$(@)") endif $(generator-objects): $(native-build)/%.o: $(src)/%.cpp @@ -377,7 +409,7 @@ $(executable): \ @echo "linking $(@)" ifeq ($(platform),windows) $(dlltool) -z $(@).def $(^) - $(dlltool) -k -d $(@).def -e $(@).exp + $(dlltool) -d $(@).def -e $(@).exp $(cc) $(@).exp $(^) $(lflags) -o $(@) else $(cc) $(^) $(rdynamic) $(lflags) -o $(@) @@ -398,5 +430,5 @@ $(executable-dynamic): $(driver-dynamic-object) $(dynamic-library) $(generator): $(generator-objects) @echo "linking $(@)" - $(build-cc) $(^) -o $(@) + $(build-cc) $(^) $(build-lflags) -o $(@) diff --git a/readme.txt b/readme.txt index 2efc5b8c84..c4e80a3cfd 100644 --- a/readme.txt +++ b/readme.txt @@ -10,6 +10,21 @@ on Mac OS X: $ export JAVA_HOME=/Library/Java/Home $ make $ build/darwin-i386-compile-fast/avian -cp build/test Hello + +on Windows (MSYS): + + $ export JAVA_HOME="C:/Program Files/Java/jdk1.6.0_07" + $ make + $ build/windows-i386-compile-fast/avian -cp build/test Hello + +on Windows (Cygwin): + + $ export JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.6.0_07" + $ make + $ build/windows-i386-compile-fast/avian -cp build/test Hello + +Adjust JAVA_HOME according to your system, but be sure to use forward +slashes in the path. Introduction @@ -40,10 +55,6 @@ Avian can currently target the following platforms: Win32 (i386) Mac OS X (i386) -The Win32 port may be built on Linux using a MinGW cross compiler and -build environment. Builds on MSYS or Cygwin are not yet supported, -but patches to enable them are welcome. - Building -------- @@ -60,20 +71,11 @@ Build requirements include: Earlier versions of some of these packages may also work but have not been tested. -If you are cross-compiling for Windows, you may find it useful to use -our win32 repository: (run this from the directory containing the -avian directory) - - $ git clone git://oss.readytalk.com/win32.git - -This gives you the Windows JNI headers, zlib headers and library, and -a few other useful libraries like OpenSSL and libjpeg. - The build is directed by a single makefile and may be influenced via certain flags described below. $ make platform={linux,windows,darwin} arch={i386,x86_64} \ - process={compile,interpret} mode={debug,debug-fast,fast} + process={compile,interpret} mode={debug,debug-fast,fast,small} * platform - the target platform default: output of $(uname -s | tr [:upper:] [:lower:]) @@ -89,6 +91,33 @@ certain flags described below. * process - choice between pure interpreter or JIT compiler default: compile +If you are compiling for Windows, you may either cross-compile using +MinGW or build natively on Windows under MSYS or Cygwin. + +Installing MSYS: + + 1. Download and install the current MinGW and MSYS packages from + mingw.org, selecting the C and C++ compilers when prompted. Use the + post-install script to create the filesystem link to the compiler. + + 2. Download GNU Make 3.81 from the MSYS download page + (make-3.81-MSYS-1.0.11-2.tar.bz2) and extract the tar file into + e.g. c:/msys/1.0. + +Installing Cygwin: + + 1. Download and run setup.exe from cygwin.com, installing the base + system and these packages: make, gcc-mingw-g++, and (optionally) + git. + +You may also find our win32 repository useful: (run this from the +directory containing the avian directory) + + $ git clone git://oss.readytalk.com/win32.git + +This gives you the Windows JNI headers, zlib headers and library, and +a few other useful libraries like OpenSSL and libjpeg. + Installing ---------- @@ -102,6 +131,9 @@ Embedding The following series of commands illustrates how to produce a stand-alone executable out of a Java application using Avian. +Note: if you are building on Cygwin, add -mno-cygwin to each of the +compile and link commands below. + Step 1: Build Avian, create a new directory, and populate it with the VM object files and bootstrap classpath jar. @@ -223,7 +255,17 @@ main(int ac, const char** av) return exitCode; } EOF - $ g++ -I$JAVA_HOME/include -c main.cpp -o main.o + +on Linux: + $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ + -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o + +on Mac OS X: + $ g++ -I$JAVA_HOME/include -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o + +on Windows: + $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 \ + -D_JNI_IMPLEMENTATION_ -c main.cpp -o main.o Step 5: Link the objects produced above to produce the final @@ -237,3 +279,9 @@ on Mac OS X: $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello -framework CoreFoundation $ strip -S -x hello +on Windows: + $ dlltool -z hello.def *.o + $ dlltool -d hello.def -e hello.exp + $ g++ hello.exp *.o -L../../win32/lib -lmingwthrd -lm -lz -lws2_32 \ + -mwindows -mconsole -o hello.exe + $ strip --strip-all hello.exe diff --git a/src/allocator.h b/src/allocator.h index 4951a7be08..c78273ad8e 100644 --- a/src/allocator.h +++ b/src/allocator.h @@ -17,7 +17,6 @@ namespace vm { class Allocator { public: - virtual ~Allocator() { } virtual void* tryAllocate(unsigned size) = 0; virtual void* allocate(unsigned size) = 0; virtual void free(const void* p, unsigned size) = 0; diff --git a/src/assembler.h b/src/assembler.h index df63d4fd99..7992f7c892 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -78,8 +78,6 @@ const int NoRegister = -1; class Promise { public: - virtual ~Promise() { } - virtual int64_t value() = 0; virtual bool resolved() = 0; }; @@ -101,8 +99,6 @@ class ResolvedPromise: public Promise { class TraceHandler { public: - virtual ~TraceHandler() { } - virtual void handleTrace(Promise* address, unsigned padIndex, unsigned padding) = 0; }; @@ -147,8 +143,6 @@ class Assembler { class Client { public: - virtual ~Client() { } - virtual int acquireTemporary (uint32_t mask = ~static_cast(0)) = 0; virtual void releaseTemporary(int r) = 0; @@ -159,15 +153,11 @@ class Assembler { class Block { public: - virtual ~Block() { } - virtual unsigned resolve(unsigned start, Block* next) = 0; }; class Architecture { public: - virtual ~Architecture() { } - virtual unsigned registerCount() = 0; virtual int stack() = 0; @@ -214,8 +204,6 @@ class Assembler { virtual void release() = 0; }; - virtual ~Assembler() { } - virtual void setClient(Client* client) = 0; virtual Architecture* arch() = 0; diff --git a/src/boot.cpp b/src/boot.cpp index e3f91f6c0d..9f4c96a612 100644 --- a/src/boot.cpp +++ b/src/boot.cpp @@ -11,10 +11,9 @@ #include "stdint.h" #include "stdlib.h" -// since we don't link against libstdc++, we must implement some dummy -// functions: +// since we aren't linking against libstdc++, we must implement this +// ourselves: extern "C" void __cxa_pure_virtual(void) { abort(); } -void operator delete(void*) { abort(); } #ifdef __MINGW32__ # define EXPORT __declspec(dllexport) diff --git a/src/builtin.cpp b/src/builtin.cpp index 396088dc56..718b05f5cb 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -456,6 +456,13 @@ Java_java_lang_System_getVMProperty(Thread* t, jclass, jstring name, r = makeLocalReference(t, makeString(t, "%s", t->m->finder->path())); } else if (strcmp(n, "avian.version") == 0) { r = makeLocalReference(t, makeString(t, AVIAN_VERSION)); + } else if (strcmp(n, "file.encoding") == 0) { + r = makeLocalReference(t, makeString(t, "ASCII")); + } else { + const char* v = findProperty(t, n); + if (v) { + r = makeLocalReference(t, makeString(t, v)); + } } if (r) { @@ -536,8 +543,9 @@ Java_java_lang_Runtime_load(Thread* t, jclass, jstring name, jboolean mapName) char n[length + 1]; stringChars(t, *name, n); - if (mapName and t->m->builtins) { - const char* s = t->m->builtins; + const char* builtins = findProperty(t, "avian.builtins"); + if (mapName and builtins) { + const char* s = builtins; while (*s) { if (strncmp(s, n, length) == 0 and (s[length] == ',' or s[length] == 0)) @@ -578,6 +586,30 @@ Java_java_lang_Runtime_gc(Thread* t, jobject) collect(t, Heap::MajorCollection); } +#ifdef AVIAN_HEAPDUMP + +extern "C" JNIEXPORT void JNICALL +Java_java_lang_Runtime_dumpHeap(Thread* t, jclass, jstring outputFile) +{ + ENTER(t, Thread::ActiveState); + + unsigned length = stringLength(t, *outputFile); + char n[length + 1]; + stringChars(t, *outputFile, n); + FILE* out = fopen(n, "wb"); + if (out) { + { ENTER(t, Thread::ExclusiveState); + dumpHeap(t, out); + } + fclose(out); + } else { + object message = makeString(t, "file not found: %s", n); + t->exception = makeRuntimeException(t, message); + } +} + +#endif//AVIAN_HEAPDUMP + extern "C" JNIEXPORT void JNICALL Java_java_lang_Runtime_exit(Thread* t, jobject, jint code) { diff --git a/src/compile.cpp b/src/compile.cpp index 37d31f8ec9..697f134a9c 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -27,7 +27,7 @@ vmCall(); namespace { -const bool Verbose = true; +const bool DebugCompile = true; const bool DebugNatives = false; const bool DebugCallTable = false; const bool DebugMethodTree = false; @@ -2387,7 +2387,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } break; case goto_: { - uint32_t newIp = (ip - 3) + codeReadInt16(t, code, ip); + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); c->jmp(frame->machineIp(newIp)); @@ -2395,7 +2396,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } break; case goto_w: { - uint32_t newIp = (ip - 5) + codeReadInt32(t, code, ip); + uint32_t offset = codeReadInt32(t, code, ip); + uint32_t newIp = (ip - 5) + offset; assert(t, newIp < codeLength(t, code)); c->jmp(frame->machineIp(newIp)); @@ -2480,7 +2482,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case if_acmpeq: case if_acmpne: { - uint32_t newIp = (ip - 3) + codeReadInt16(t, code, ip); + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = frame->popObject(); @@ -2504,7 +2507,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case if_icmpge: case if_icmplt: case if_icmple: { - uint32_t newIp = (ip - 3) + codeReadInt16(t, code, ip); + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = frame->popInt(); @@ -2543,7 +2547,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case ifge: case iflt: case ifle: { - uint32_t newIp = (ip - 3) + codeReadInt16(t, code, ip); + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = frame->popInt(); @@ -2577,7 +2582,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case ifnull: case ifnonnull: { - uint32_t newIp = (ip - 3) + codeReadInt16(t, code, ip); + uint32_t offset = codeReadInt16(t, code, ip); + uint32_t newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = frame->popObject(); @@ -2824,14 +2830,18 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint32_t newIp; if (instruction == jsr) { - newIp = (ip - 3) + codeReadInt16(t, code, ip); + uint32_t offset = codeReadInt16(t, code, ip); + newIp = (ip - 3) + offset; } else { - newIp = (ip - 5) + codeReadInt32(t, code, ip); + uint32_t offset = codeReadInt32(t, code, ip); + newIp = (ip - 5) + offset; } assert(t, newIp < codeLength(t, code)); // todo: flush stack to memory here + abort(t); + Compiler::State* state = c->saveState(); frame->pushAddress(frame->machineIp(ip)); @@ -3447,12 +3457,26 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } void -logCompile(const void* code, unsigned size, const char* class_, +logCompile(MyThread* t, const void* code, unsigned size, const char* class_, const char* name, const char* spec) { - fprintf(stderr, "%s.%s%s: %p %p\n", - class_, name, spec, code, - static_cast(code) + size); + static FILE* log = 0; + static bool open = false; + if (not open) { + open = true; + const char* path = findProperty(t, "avian.jit.log"); + if (name) { + log = fopen(path, "wb"); + } else if (DebugCompile) { + log = stderr; + } + } + + if (log) { + fprintf(log, "%p %p %s.%s%s\n", + code, static_cast(code) + size, + class_, name, spec); + } } void @@ -3717,9 +3741,7 @@ finish(MyThread* t, Assembler* a, const char* name) a->writeTo(start); - if (Verbose) { - logCompile(start, a->length(), 0, name, 0); - } + logCompile(t, start, a->length(), 0, name, 0); return result; } @@ -3861,16 +3883,14 @@ finish(MyThread* t, Context* context) set(t, result, SingletonBody + offset, p->value); } - if (Verbose) { - logCompile - (start, codeSize, - reinterpret_cast - (&byteArrayBody(t, className(t, methodClass(t, context->method)), 0)), - reinterpret_cast - (&byteArrayBody(t, methodName(t, context->method), 0)), - reinterpret_cast - (&byteArrayBody(t, methodSpec(t, context->method), 0))); - } + logCompile + (t, start, codeSize, + reinterpret_cast + (&byteArrayBody(t, className(t, methodClass(t, context->method)), 0)), + reinterpret_cast + (&byteArrayBody(t, methodName(t, context->method), 0)), + reinterpret_cast + (&byteArrayBody(t, methodSpec(t, context->method), 0))); // for debugging: if (false and @@ -5092,10 +5112,7 @@ compileThunks(MyThread* t, MyProcessor* p) uint8_t* start = reinterpret_cast (&singletonValue(t, p->thunkTable, 0)); - if (Verbose) { - logCompile(start, p->thunkSize * ThunkCount, 0, "thunkTable", 0); - fprintf(stderr, "thunk size: %d\n", p->thunkSize); - } + logCompile(t, start, p->thunkSize * ThunkCount, 0, "thunkTable", 0); tableContext.promise.resolved_ = true; diff --git a/src/compiler.cpp b/src/compiler.cpp index 0ecf5df82f..35d7ceaea9 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -86,8 +86,6 @@ class Site { public: Site(): next(0) { } - virtual ~Site() { } - virtual Site* readTarget(Context*, Read*) { return this; } virtual void toString(Context*, char*, unsigned) = 0; @@ -219,8 +217,6 @@ class Read { value(0), event(0), eventNext(0), size(size) { } - virtual ~Read() { } - virtual Site* pickSite(Context* c, Value* v, bool includeBuddies) = 0; virtual Site* allocateSite(Context* c) = 0; @@ -257,8 +253,6 @@ class Value: public Compiler::Operand { local(false) { } - virtual ~Value() { } - virtual void addPredecessor(Context*, Event*) { } Read* reads; @@ -559,8 +553,6 @@ class Event { readCount(0) { } - virtual ~Event() { } - virtual const char* name() = 0; virtual void compile(Context* c) = 0; diff --git a/src/compiler.h b/src/compiler.h index 038fcc63fd..eb4df43bd1 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -21,8 +21,6 @@ class Compiler { public: class Client { public: - virtual ~Client() { } - virtual intptr_t getThunk(UnaryOperation op, unsigned size) = 0; virtual intptr_t getThunk(TernaryOperation op, unsigned size) = 0; }; @@ -34,8 +32,6 @@ class Compiler { class StackElement { }; class State { }; - virtual ~Compiler() { } - virtual State* saveState() = 0; virtual void restoreState(State* state) = 0; diff --git a/src/finder.cpp b/src/finder.cpp index 57863d5f07..0f8fa0d793 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -52,7 +52,7 @@ equal(const void* a, unsigned al, const void* b, unsigned bl) class Element { public: Element(): next(0) { } - virtual ~Element() { } + virtual System::Region* find(const char* name) = 0; virtual bool exists(const char* name) = 0; virtual void dispose() = 0; @@ -372,7 +372,7 @@ class JarIndex { class JarElement: public Element { public: JarElement(System* s, const char* name): - s(s), name(name), index(0) + s(s), name(name), region(0), index(0) { } virtual void init() { diff --git a/src/finder.h b/src/finder.h index 6b3a868f68..64eb4c287b 100644 --- a/src/finder.h +++ b/src/finder.h @@ -19,7 +19,6 @@ namespace vm { class Finder { public: - virtual ~Finder() { } virtual System::Region* find(const char* name) = 0; virtual bool exists(const char* name) = 0; virtual const char* path() = 0; diff --git a/src/heap.h b/src/heap.h index bf4dc5a360..bc8f9a5a1c 100644 --- a/src/heap.h +++ b/src/heap.h @@ -32,19 +32,16 @@ class Heap: public Allocator { class Visitor { public: - virtual ~Visitor() { } virtual void visit(void*) = 0; }; class Walker { public: - virtual ~Walker() { } virtual bool visit(unsigned) = 0; }; class Client { public: - virtual ~Client() { } virtual void collect(void* context, CollectionType type) = 0; virtual void visitRoots(Visitor*) = 0; virtual bool isFixed(void*) = 0; @@ -54,7 +51,6 @@ class Heap: public Allocator { virtual void walk(void*, Walker*) = 0; }; - virtual ~Heap() { } virtual void setClient(Client* client) = 0; virtual void collect(CollectionType type, unsigned footprint) = 0; virtual void* allocateFixed(Allocator* allocator, unsigned sizeInWords, diff --git a/src/heapdump.cpp b/src/heapdump.cpp new file mode 100644 index 0000000000..6d17d82e5a --- /dev/null +++ b/src/heapdump.cpp @@ -0,0 +1,329 @@ +/* Copyright (c) 2008, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "machine.h" + +using namespace vm; + +namespace { + +const uintptr_t PointerShift = log(BytesPerWord); + +class Set { + public: + class Entry { + public: + object value; + uint32_t number; + int next; + }; + + static unsigned footprint(unsigned capacity) { + return sizeof(Set) + + pad(sizeof(int) * capacity) + + pad(sizeof(Set::Entry) * capacity); + } + + Set(unsigned capacity): + size(0), + capacity(capacity), + index(reinterpret_cast + (reinterpret_cast(this) + + sizeof(Set))), + entries(reinterpret_cast + (reinterpret_cast(index) + + pad(sizeof(int) * capacity))) + { } + + unsigned size; + unsigned capacity; + int* index; + Entry* entries; +}; + +class Stack { + public: + class Entry { + public: + object value; + int offset; + }; + + static const unsigned Capacity = 4096; + + Stack(Stack* next): next(next), entryCount(0) { } + + Stack* next; + unsigned entryCount; + Entry entries[Capacity]; +}; + +class Context { + public: + Context(Thread* thread, FILE* out): + thread(thread), out(out), objects(0), stack(0), nextNumber(1) + { } + + ~Context() { + if (objects) { + thread->m->heap->free(objects, Set::footprint(objects->capacity)); + } + while (stack) { + Stack* dead = stack; + stack = dead->next; + thread->m->heap->free(stack, sizeof(Stack)); + } + } + + Thread* thread; + FILE* out; + Set* objects; + Stack* stack; + uint32_t nextNumber; +}; + +void +push(Context* c, object p, int offset) +{ + if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) { + c->stack = new (c->thread->m->heap->allocate(sizeof(Stack))) + Stack(c->stack); + } + Stack::Entry* e = c->stack->entries + (c->stack->entryCount++); + e->value = p; + e->offset = offset; +} + +bool +pop(Context* c, object* p, int* offset) +{ + if (c->stack) { + if (c->stack->entryCount == 0) { + if (c->stack->next) { + Stack* dead = c->stack; + c->stack = dead->next; + c->thread->m->heap->free(dead, sizeof(Stack)); + } else { + return false; + } + } + Stack::Entry* e = c->stack->entries + (--c->stack->entryCount); + *p = e->value; + *offset = e->offset; + return true; + } else { + return false; + } +} + +unsigned +hash(object p, unsigned capacity) +{ + return (reinterpret_cast(p) >> PointerShift) + & (capacity - 1); +} + +Set::Entry* +find(Context* c, object p) +{ + if (c->objects == 0) return false; + + for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) { + Set::Entry* e = c->objects->entries + i; + if (e->value == p) { + return e; + } + i = e->next; + } + + return false; +} + +Set::Entry* +add(Context* c UNUSED, Set* set, object p, uint32_t number) +{ + assert(c->thread, set->size < set->capacity); + + unsigned index = hash(p, set->capacity); + + int offset = set->size++; + Set::Entry* e = set->entries + offset; + e->value = p; + e->number = number; + e->next = set->index[index]; + set->index[index] = offset; + return e; +} + +Set::Entry* +add(Context* c, object p) +{ + if (c->objects == 0 or c->objects->size == c->objects->capacity) { + unsigned capacity; + if (c->objects) { + capacity = c->objects->capacity * 2; + } else { + capacity = 4096; // must be power of two + } + + Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity))) + Set(capacity); + + memset(set->index, 0xFF, sizeof(int) * capacity); + + if (c->objects) { + for (unsigned i = 0; i < c->objects->capacity; ++i) { + for (int j = c->objects->index[i]; j >= 0;) { + Set::Entry* e = c->objects->entries + j; + add(c, set, e->value, e->number); + j = e->next; + } + } + + c->thread->m->heap->free + (c->objects, Set::footprint(c->objects->capacity)); + } + + c->objects = set; + } + + return add(c, c->objects, p, 0); +} + +enum { + Root, + Size, + ClassName, + Push, + Pop +}; + +inline object +get(object o, unsigned offsetInWords) +{ + return static_cast + (mask(cast(o, offsetInWords * BytesPerWord))); +} + +void +write1(Context* c, uint8_t v) +{ + fwrite(&v, 1, 1, c->out); +} + +void +write4(Context* c, uint32_t v) +{ + uint8_t b[] = { v >> 24, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF }; + fwrite(b, 4, 1, c->out); +} + +void +writeString(Context* c, int8_t* p, unsigned size) +{ + write4(c, size); + fwrite(p, size, 1, c->out); +} + +unsigned +objectSize(Thread* t, object o) +{ + unsigned n = baseSize(t, o, objectClass(t, o)); + if (objectExtended(t, o)) { + ++ n; + } + return n; +} + +void +visit(Context* c, object p) +{ + Thread* t = c->thread; + int nextChildOffset; + + write1(c, Root); + + visit: { + Set::Entry* e = find(c, p); + if (e) { + write4(c, e->number); + } else { + e = add(c, p); + e->number = c->nextNumber++; + + write4(c, e->number); + + write1(c, Size); + write4(c, objectSize(t, p)); + + if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) { + object name = className(t, p); + if (name) { + write1(c, ClassName); + writeString(c, &byteArrayBody(t, name, 0), + byteArrayLength(t, name) - 1); + } + } + + nextChildOffset = walkNext(t, p, -1); + if (nextChildOffset != -1) { + goto children; + } + } + } + + goto pop; + + children: { + write1(c, Push); + push(c, p, nextChildOffset); + p = get(p, nextChildOffset); + goto visit; + } + + pop: { + if (pop(c, &p, &nextChildOffset)) { + write1(c, Pop); + nextChildOffset = walkNext(t, p, nextChildOffset); + if (nextChildOffset >= 0) { + goto children; + } else { + goto pop; + } + } + } +} + +} // namespace + +namespace vm { + +void +dumpHeap(Thread* t, FILE* out) +{ + Context context(t, out); + + class Visitor : public Heap::Visitor { + public: + Visitor(Context* c): c(c) { } + + virtual void visit(void* p) { + ::visit(c, static_cast(mask(*static_cast(p)))); + } + + Context* c; + } v(&context); + + add(&context, 0)->number = 0; + + visitRoots(t->m, &v); +} + +} // namespace vm diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 329e948687..8ffb581151 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -2046,12 +2046,13 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) } // namespace vm -#define BUILTINS_PROPERTY "avian.builtins" #define BOOTSTRAP_PROPERTY "avian.bootstrap" +#define CRASHDIR_PROPERTY "avian.crash.dir" #define CLASSPATH_PROPERTY "java.class.path" #define BOOTCLASSPATH_PREPEND_OPTION "bootclasspath/p" #define BOOTCLASSPATH_OPTION "bootclasspath" #define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" +#define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" extern "C" JNIEXPORT jint JNICALL JNI_GetDefaultJavaVMInitArgs(void*) @@ -2065,12 +2066,14 @@ JNI_CreateJavaVM(Machine** m, Thread** t, void* args) JavaVMInitArgs* a = static_cast(args); unsigned heapLimit = 0; - const char* builtins = 0; const char* bootLibrary = 0; const char* classpath = 0; const char* bootClasspathPrepend = ""; const char* bootClasspath = ""; const char* bootClasspathAppend = ""; + const char* crashDumpDirectory = 0; + + unsigned propertyCount = 0; for (int i = 0; i < a->nOptions; ++i) { if (strncmp(a->options[i].optionString, "-X", 2) == 0) { @@ -2092,21 +2095,21 @@ JNI_CreateJavaVM(Machine** m, Thread** t, void* args) } } else if (strncmp(a->options[i].optionString, "-D", 2) == 0) { const char* p = a->options[i].optionString + 2; - if (strncmp(p, BUILTINS_PROPERTY "=", - sizeof(BUILTINS_PROPERTY)) == 0) - { - builtins = p + sizeof(BUILTINS_PROPERTY); - } else if (strncmp(p, BOOTSTRAP_PROPERTY "=", - sizeof(BOOTSTRAP_PROPERTY)) == 0) + if (strncmp(p, BOOTSTRAP_PROPERTY "=", + sizeof(BOOTSTRAP_PROPERTY)) == 0) { bootLibrary = p + sizeof(BOOTSTRAP_PROPERTY); + } else if (strncmp(p, CRASHDIR_PROPERTY "=", + sizeof(CRASHDIR_PROPERTY)) == 0) + { + crashDumpDirectory = p + sizeof(CRASHDIR_PROPERTY); } else if (strncmp(p, CLASSPATH_PROPERTY "=", sizeof(CLASSPATH_PROPERTY)) == 0) { classpath = p + sizeof(CLASSPATH_PROPERTY); } - // todo: add properties to VM + ++ propertyCount; } } @@ -2128,13 +2131,22 @@ JNI_CreateJavaVM(Machine** m, Thread** t, void* args) append(&classpathPointer, bootClasspathAppend, bcpal, PATH_SEPARATOR); append(&classpathPointer, classpath, cpl, 0); - System* s = makeSystem(); + System* s = makeSystem(crashDumpDirectory); Heap* h = makeHeap(s, heapLimit); Finder* f = makeFinder(s, classpathBuffer, bootLibrary); Processor* p = makeProcessor(s, h); + const char** properties = static_cast + (h->allocate(sizeof(const char*) * propertyCount)); + const char** propertyPointer = properties; + for (int i = 0; i < a->nOptions; ++i) { + if (strncmp(a->options[i].optionString, "-D", 2) == 0) { + *(propertyPointer++) = a->options[i].optionString + 2; + } + } + *m = new (h->allocate(sizeof(Machine))) - Machine(s, h, f, p, bootLibrary, builtins); + Machine(s, h, f, p, properties, propertyCount); *t = p->makeThread(*m, 0, 0); diff --git a/src/machine.cpp b/src/machine.cpp index 2bf6674f10..8e53e35d6a 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -198,13 +198,13 @@ visitRoots(Thread* t, Heap::Visitor* v) void walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, - unsigned arrayElementSize, unsigned arrayLength) + unsigned arrayElementSize, unsigned arrayLength, unsigned start) { unsigned fixedSizeInWords = ceiling(fixedSize, BytesPerWord); unsigned arrayElementSizeInWords = ceiling(arrayElementSize, BytesPerWord); - for (unsigned i = 0; i < fixedSizeInWords; ++i) { + for (unsigned i = start; i < fixedSizeInWords; ++i) { if (mask[i / 32] & (static_cast(1) << (i % 32))) { if (not w->visit(i)) { return; @@ -222,8 +222,19 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, } if (arrayObjectElements) { - for (unsigned i = 0; i < arrayLength; ++i) { - for (unsigned j = 0; j < arrayElementSizeInWords; ++j) { + unsigned arrayStart; + unsigned elementStart; + if (start > fixedSizeInWords) { + unsigned s = start - fixedSizeInWords; + arrayStart = s / arrayElementSizeInWords; + elementStart = s % arrayElementSizeInWords; + } else { + arrayStart = 0; + elementStart = 0; + } + + for (unsigned i = arrayStart; i < arrayLength; ++i) { + for (unsigned j = elementStart; j < arrayElementSizeInWords; ++j) { unsigned k = fixedSizeInWords + j; if (mask[k / 32] & (static_cast(1) << (k % 32))) { if (not w->visit @@ -238,7 +249,7 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, } void -walk(Thread* t, Heap::Walker* w, object o) +walk(Thread* t, Heap::Walker* w, object o, unsigned start) { object class_ = static_cast(t->m->heap->follow(objectClass(t, o))); object objectMask = static_cast @@ -255,16 +266,16 @@ walk(Thread* t, Heap::Walker* w, object o) memcpy(mask, &intArrayBody(t, objectMask, 0), intArrayLength(t, objectMask) * 4); - walk(t, w, mask, fixedSize, arrayElementSize, arrayLength); + walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); } else if (classVmFlags(t, class_) & SingletonFlag) { unsigned length = singletonLength(t, o); if (length) { walk(t, w, singletonMask(t, o), - (singletonCount(t, o) + 2) * BytesPerWord, 0, 0); - } else { + (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); + } else if (start == 0) { w->visit(0); } - } else { + } else if (start == 0) { w->visit(0); } } @@ -1534,20 +1545,7 @@ class HeapClient: public Heap::Client { HeapClient(Machine* m): m(m) { } virtual void visitRoots(Heap::Visitor* v) { - v->visit(&(m->loader)); - v->visit(&(m->bootstrapClassMap)); - v->visit(&(m->monitorMap)); - v->visit(&(m->stringMap)); - v->visit(&(m->types)); - v->visit(&(m->jniMethodTable)); - - for (Reference* r = m->jniReferences; r; r = r->next) { - v->visit(&(r->target)); - } - - for (Thread* t = m->rootThread; t; t = t->peer) { - ::visitRoots(t, v); - } + ::visitRoots(m, v); postVisit(m->rootThread, v); } @@ -1616,7 +1614,7 @@ class HeapClient: public Heap::Client { virtual void walk(void* p, Heap::Walker* w) { object o = static_cast(m->heap->follow(mask(p))); - ::walk(m->rootThread, w, o); + ::walk(m->rootThread, w, o, 0); } void dispose() { @@ -1632,8 +1630,8 @@ class HeapClient: public Heap::Client { namespace vm { Machine::Machine(System* system, Heap* heap, Finder* finder, - Processor* processor, const char* bootLibrary, - const char* builtins): + Processor* processor, const char** properties, + unsigned propertyCount): vtable(&javaVMVTable), system(system), heapClient(new (heap->allocate(sizeof(HeapClient))) @@ -1644,7 +1642,8 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, rootThread(0), exclusive(0), jniReferences(0), - builtins(builtins), + properties(properties), + propertyCount(propertyCount), activeCount(0), liveCount(0), fixedFootprint(0), @@ -1677,7 +1676,8 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, not system->success(system->make(&heapLock)) or not system->success(system->make(&classLock)) or not system->success(system->make(&referenceLock)) or - not system->success(system->load(&libraries, bootLibrary, false))) + not system->success + (system->load(&libraries, findProperty(this, "avian.bootstrap"), false))) { system->abort(); } @@ -1706,6 +1706,8 @@ Machine::dispose() heap->free(heapPool[i], Thread::HeapSizeInBytes); } + heap->free(properties, sizeof(const char*) * propertyCount); + static_cast(heapClient)->dispose(); heap->free(this, sizeof(*this)); @@ -2595,12 +2597,14 @@ findInHierarchy(Thread* t, object class_, object name, object spec, PROTECT(t, class_); object o = 0; - if (classFlags(t, class_) & ACC_INTERFACE) { - if (classVirtualTable(t, class_)) { - o = findInTable - (t, classVirtualTable(t, class_), name, spec, methodName, methodSpec); - } - } else { + if ((classFlags(t, class_) & ACC_INTERFACE) + and classVirtualTable(t, class_)) + { + o = findInTable + (t, classVirtualTable(t, class_), name, spec, methodName, methodSpec); + } + + if (o == 0) { for (; o == 0 and class_; class_ = classSuper(t, class_)) { o = find(t, class_, name, spec); } @@ -2765,6 +2769,44 @@ collect(Thread* t, Heap::CollectionType type) #endif } +int +walkNext(Thread* t, object o, int previous) +{ + class Walker: public Heap::Walker { + public: + Walker(): value(-1) { } + + bool visit(unsigned offset) { + value = offset; + return false; + } + + int value; + } walker; + + walk(t, &walker, o, previous + 1); + return walker.value; +} + +void +visitRoots(Machine* m, Heap::Visitor* v) +{ + v->visit(&(m->loader)); + v->visit(&(m->bootstrapClassMap)); + v->visit(&(m->monitorMap)); + v->visit(&(m->stringMap)); + v->visit(&(m->types)); + v->visit(&(m->jniMethodTable)); + + for (Reference* r = m->jniReferences; r; r = r->next) { + v->visit(&(r->target)); + } + + for (Thread* t = m->rootThread; t; t = t->peer) { + ::visitRoots(t, v); + } +} + void printTrace(Thread* t, object exception) { diff --git a/src/machine.h b/src/machine.h index 7e4f235979..2dab96f273 100644 --- a/src/machine.h +++ b/src/machine.h @@ -1138,7 +1138,7 @@ class Machine { }; Machine(System* system, Heap* heap, Finder* finder, Processor* processor, - const char* bootLibrary, const char* builtins); + const char** properties, unsigned propertyCount); ~Machine() { dispose(); @@ -1159,7 +1159,8 @@ class Machine { Thread* rootThread; Thread* exclusive; Reference* jniReferences; - const char* builtins; + const char** properties; + unsigned propertyCount; unsigned activeCount; unsigned liveCount; unsigned fixedFootprint; @@ -1227,7 +1228,7 @@ class Thread { t->protector = this; } - virtual ~Protector() { + ~Protector() { t->protector = next; } @@ -1526,6 +1527,29 @@ setObjectClass(Thread*, object o, object value) | (reinterpret_cast(cast(o, 0)) & (~PointerMask))); } +inline const char* +findProperty(Machine* m, const char* name) +{ + for (unsigned i = 0; i < m->propertyCount; ++i) { + const char* p = m->properties[i]; + const char* n = name; + while (*p and *p != '=' and *n and *p == *n) { + ++ p; + ++ n; + } + if (*p == '=' and *n == 0) { + return p + 1; + } + } + return 0; +} + +inline const char* +findProperty(Thread* t, const char* name) +{ + return findProperty(t->m, name); +} + object& arrayBodyUnsafe(Thread*, object, unsigned); @@ -2200,6 +2224,12 @@ intern(Thread* t, object s); void exit(Thread* t); +int +walkNext(Thread* t, object o, int previous); + +void +visitRoots(Machine* m, Heap::Visitor* v); + inline jobject makeLocalReference(Thread* t, object o) { @@ -2293,6 +2323,9 @@ makeSingleton(Thread* t, unsigned count) return o; } +void +dumpHeap(Thread* t, FILE* out); + } // namespace vm void diff --git a/src/posix.cpp b/src/posix.cpp index 1afece6113..1291809fa1 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -257,9 +257,10 @@ class MySystem: public System { Thread* t = static_cast(context); if (owner_ == t) { - bool interrupted; - bool notified; - unsigned depth; + // Initialized here to make gcc 4.2 a happy compiler + bool interrupted = false; + bool notified = false; + unsigned depth = 0; { ACQUIRE(t->mutex); @@ -820,7 +821,7 @@ handleSignal(int signal, siginfo_t* info, void* context) namespace vm { System* -makeSystem() +makeSystem(const char*) { return new (malloc(sizeof(MySystem))) MySystem(); } diff --git a/src/powerpc.cpp b/src/powerpc.cpp index 3f312c21f0..2c42461726 100644 --- a/src/powerpc.cpp +++ b/src/powerpc.cpp @@ -301,8 +301,6 @@ class Task { public: Task(Task* next): next(next) { } - virtual ~Task() { } - virtual void run(Context* c) = 0; Task* next; diff --git a/src/process.cpp b/src/process.cpp index ecfac1f2d8..c2693d0281 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -167,8 +167,7 @@ resolveNativeMethod2(Thread* t, object method) } #ifdef __MINGW32__ - // on windows, we also try the _%s@%d variant, since the SWT - // libraries use it. + // on windows, we also try the _%s@%d and %s@%d variants unsigned footprint = methodParameterFootprint(t, method) + 1; if (methodFlags(t, method) & ACC_STATIC) { ++ footprint; @@ -186,6 +185,12 @@ resolveNativeMethod2(Thread* t, object method) if (p) { return p; } + + // one more try without the leading underscore + p = ::resolveNativeMethod(t, undecorated + 1, decorated + 1); + if (p) { + return p; + } #endif return 0; diff --git a/src/processor.h b/src/processor.h index 459970143e..a0ef3491b2 100644 --- a/src/processor.h +++ b/src/processor.h @@ -23,15 +23,11 @@ class Processor { class StackVisitor { public: - virtual ~StackVisitor() { } - virtual bool visit(StackWalker* walker) = 0; }; class StackWalker { public: - virtual ~StackWalker() { } - virtual void walk(StackVisitor* v) = 0; virtual object method() = 0; @@ -41,8 +37,6 @@ class Processor { virtual unsigned count() = 0; }; - virtual ~Processor() { } - virtual Thread* makeThread(Machine* m, object javaThread, Thread* parent) = 0; diff --git a/src/stream.h b/src/stream.h index c740d082b1..bf59e41865 100644 --- a/src/stream.h +++ b/src/stream.h @@ -19,7 +19,6 @@ class Stream { public: class Client { public: - virtual ~Client() { } virtual void NO_RETURN handleError() = 0; }; diff --git a/src/system.h b/src/system.h index 4223451756..10addecdd4 100644 --- a/src/system.h +++ b/src/system.h @@ -28,7 +28,6 @@ class System { class Thread { public: - virtual ~Thread() { } virtual void interrupt() = 0; virtual void join() = 0; virtual void dispose() = 0; @@ -36,13 +35,11 @@ class System { class ThreadVisitor { public: - virtual ~ThreadVisitor() { } virtual void visit(void* ip, void* base, void* stack) = 0; }; class Runnable { public: - virtual ~Runnable() { } virtual void attach(Thread*) = 0; virtual void run() = 0; virtual bool interrupted() = 0; @@ -51,7 +48,6 @@ class System { class Mutex { public: - virtual ~Mutex() { } virtual void acquire() = 0; virtual void release() = 0; virtual void dispose() = 0; @@ -59,7 +55,6 @@ class System { class Monitor { public: - virtual ~Monitor() { } virtual bool tryAcquire(Thread* context) = 0; virtual void acquire(Thread* context) = 0; virtual void release(Thread* context) = 0; @@ -72,7 +67,6 @@ class System { class Local { public: - virtual ~Local() { } virtual void* get() = 0; virtual void set(void* p) = 0; virtual void dispose() = 0; @@ -80,7 +74,6 @@ class System { class Region { public: - virtual ~Region() { } virtual const uint8_t* start() = 0; virtual size_t length() = 0; virtual void dispose() = 0; @@ -88,7 +81,6 @@ class System { class Library { public: - virtual ~Library() { } virtual void* resolve(const char* function) = 0; virtual const char* name() = 0; virtual bool mapName() = 0; @@ -99,8 +91,6 @@ class System { class SignalHandler { public: - virtual ~SignalHandler() { } - virtual bool handleSignal(void** ip, void** base, void** stack, void** thread) = 0; }; @@ -120,8 +110,6 @@ class System { System::Monitor* m; }; - virtual ~System() { } - virtual bool success(Status) = 0; virtual void* tryAllocate(unsigned sizeInBytes) = 0; virtual void free(const void* p) = 0; @@ -193,7 +181,7 @@ assert(System* s, bool v) #endif // not NDEBUG System* -makeSystem(); +makeSystem(const char* crashDumpDirectory); } // namespace vm diff --git a/src/type-generator.cpp b/src/type-generator.cpp index 231e31fb56..34a79ee5e6 100644 --- a/src/type-generator.cpp +++ b/src/type-generator.cpp @@ -371,7 +371,6 @@ class Scalar : public Object { unsigned elementSize; bool noassert; bool nogc; - bool hide; static Scalar* make(Object* owner, Object* typeObject, const char* typeName, const char* name, unsigned size) @@ -385,7 +384,6 @@ class Scalar : public Object { o->elementSize = size; o->noassert = false; o->nogc = false; - o->hide = false; return o; } }; @@ -527,19 +525,6 @@ memberGC(Object* o) return not memberNoGC(o) and equal(memberTypeName(o), "object"); } -bool& -memberHide(Object* o) -{ - switch (o->type) { - case Object::Scalar: - case Object::Array: - return static_cast(o)->hide; - - default: - UNREACHABLE; - } -} - class Method : public Object { public: Object* owner; @@ -588,7 +573,6 @@ class Type : public Object { Object* super; List members; List methods; - bool hideConstructor; bool overridesMethods; static Type* make(Object::ObjectType type, const char* name, @@ -601,7 +585,6 @@ class Type : public Object { o->super = 0; o->members.first = o->members.last = 0; o->methods.first = o->methods.last = 0; - o->hideConstructor = false; o->overridesMethods = false; return o; } @@ -718,18 +701,6 @@ typeSuper(Object* o) } } -bool& -typeHideConstructor(Object* o) -{ - switch (o->type) { - case Object::Type: - return static_cast(o)->hideConstructor; - - default: - UNREACHABLE; - } -} - class Number : public Object { public: unsigned value; @@ -1141,15 +1112,7 @@ void parseSubdeclaration(Object* t, Object* p, Object* declarations) { const char* front = string(car(p)); - if (equal(front, "hide")) { - if (equal(string(car(cdr(p))), "constructor")) { - typeHideConstructor(t) = true; - } else { - Object* member = parseMember(t, cdr(p), declarations); - memberHide(member) = true; - addMember(t, member); - } - } else if (equal(front, "extends")) { + if (equal(front, "extends")) { assert(t->type == Object::Type); assert(typeSuper(t) == 0); typeSuper(t) = declaration(string(car(cdr(p))), declarations); @@ -1169,8 +1132,7 @@ memberEqual(Object* a, Object* b) case Object::Scalar: return equal(memberTypeName(a), memberTypeName(b)) and memberNoAssert(a) == memberNoAssert(b) - and memberNoGC(a) == memberNoGC(b) - and memberHide(a) == memberHide(b); + and memberNoGC(a) == memberNoGC(b); // todo: compare array fields @@ -1468,8 +1430,7 @@ parse(Input* in, const char* javaClassDirectory) } void -writeAccessorName(Output* out, Object* member, bool respectHide = false, - bool unsafe = false) +writeAccessorName(Output* out, Object* member, bool unsafe = false) { const char* owner = typeName(memberOwner(member)); out->write(owner); @@ -1477,9 +1438,6 @@ writeAccessorName(Output* out, Object* member, bool respectHide = false, if (unsafe) { out->write("Unsafe"); } - if (respectHide and memberHide(member)) { - out->write("0"); - } } void @@ -1557,7 +1515,7 @@ writeAccessor(Output* out, Object* member, Object* offset, bool unsafe = false) } out->write("\n"); - writeAccessorName(out, member, true, unsafe); + writeAccessorName(out, member, unsafe); if (memberOwner(member)->type == Object::Pod) { out->write("("); out->write(capitalize(::typeName(memberOwner(member)))); @@ -1876,7 +1834,7 @@ writeConstructorInitializations(Output* out, Object* t) switch (m->type) { case Object::Scalar: { out->write(" "); - writeAccessorName(out, m, true); + writeAccessorName(out, m); out->write("(t, o) = "); out->write(obfuscate(memberName(m))); out->write(";\n"); @@ -1887,7 +1845,7 @@ writeConstructorInitializations(Output* out, Object* t) if (memberTypeObject(m) == 0) { out->write("&"); } - writeAccessorName(out, m, true); + writeAccessorName(out, m); out->write("(t, o, 0), 0, length * "); out->write(arrayElementSize(m)); out->write(");\n"); @@ -1914,7 +1872,6 @@ writeInitializerDeclarations(Output* out, Object* declarations) case Object::Type: { out->write("void init"); out->write(capitalize(typeName(o))); - if (typeHideConstructor(o)) out->write("0"); out->write("(Thread* t, object o"); writeConstructorParameters(out, o); @@ -1936,7 +1893,6 @@ writeConstructorDeclarations(Output* out, Object* declarations) case Object::Type: { out->write("object make"); out->write(capitalize(typeName(o))); - if (typeHideConstructor(o)) out->write("0"); out->write("(Thread* t"); writeConstructorParameters(out, o); @@ -1958,7 +1914,6 @@ writeInitializers(Output* out, Object* declarations) case Object::Type: { out->write("void\ninit"); out->write(capitalize(typeName(o))); - if (typeHideConstructor(o)) out->write("0"); out->write("(Thread* t, object o"); writeConstructorParameters(out, o); @@ -1989,7 +1944,6 @@ writeConstructors(Output* out, Object* declarations) case Object::Type: { out->write("object make"); out->write(capitalize(typeName(o))); - if (typeHideConstructor(o)) out->write("0"); out->write("(Thread* t"); writeConstructorParameters(out, o); @@ -2027,7 +1981,6 @@ writeConstructors(Output* out, Object* declarations) out->write(" init"); out->write(capitalize(typeName(o))); - if (typeHideConstructor(o)) out->write("0"); out->write("(t, o"); writeConstructorArguments(out, o); out->write(");\n"); diff --git a/src/windows.cpp b/src/windows.cpp index 9d2cb6b58c..de295d90b0 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -10,6 +10,7 @@ #include "sys/stat.h" #include "windows.h" +#include "sys/timeb.h" #undef max #undef min @@ -40,25 +41,11 @@ class MutexResource { HANDLE m; }; -System::SignalHandler* segFaultHandler = 0; -LPTOP_LEVEL_EXCEPTION_FILTER oldSegFaultHandler = 0; +class MySystem; +MySystem* system; LONG CALLBACK -handleException(LPEXCEPTION_POINTERS e) -{ - if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - bool jump = segFaultHandler->handleSignal - (reinterpret_cast(&(e->ContextRecord->Eip)), - reinterpret_cast(&(e->ContextRecord->Ebp)), - reinterpret_cast(&(e->ContextRecord->Esp)), - reinterpret_cast(&(e->ContextRecord->Ebx))); - - if (jump) { - return EXCEPTION_CONTINUE_EXECUTION; - } - } - return EXCEPTION_CONTINUE_SEARCH; -} +handleException(LPEXCEPTION_POINTERS e); DWORD WINAPI run(void* r) @@ -495,7 +482,14 @@ class MySystem: public System { System::Library* next_; }; - MySystem() { + MySystem(const char* crashDumpDirectory): + segFaultHandler(0), + oldSegFaultHandler(0), + crashDumpDirectory(crashDumpDirectory) + { + expect(this, system == 0); + system = this; + mutex = CreateMutex(0, false, 0); assert(this, mutex); } @@ -721,26 +715,116 @@ class MySystem: public System { } virtual void abort() { - asm("int3"); - ::abort(); + // trigger an EXCEPTION_ACCESS_VIOLATION, which we will catch and + // generate a debug dump for + *static_cast(0) = 0; } virtual void dispose() { + system = 0; CloseHandle(mutex); ::free(this); } HANDLE mutex; + System::SignalHandler* segFaultHandler; + LPTOP_LEVEL_EXCEPTION_FILTER oldSegFaultHandler; + const char* crashDumpDirectory; }; +struct MINIDUMP_EXCEPTION_INFORMATION { + DWORD thread; + LPEXCEPTION_POINTERS exception; + BOOL exceptionInCurrentAddressSpace; +}; + +struct MINIDUMP_USER_STREAM_INFORMATION; +struct MINIDUMP_CALLBACK_INFORMATION; + +enum MINIDUMP_TYPE { + MiniDumpNormal = 0 +}; + +typedef BOOL (*MiniDumpWriteDumpType) +(HANDLE processHandle, + DWORD processId, + HANDLE file, + MINIDUMP_TYPE type, + const MINIDUMP_EXCEPTION_INFORMATION* exception, + const MINIDUMP_USER_STREAM_INFORMATION* userStream, + const MINIDUMP_CALLBACK_INFORMATION* callback); + +void +dump(LPEXCEPTION_POINTERS e, const char* directory) +{ + HINSTANCE dbghelp = LoadLibrary("dbghelp.dll"); + + if (dbghelp) { + MiniDumpWriteDumpType MiniDumpWriteDump = reinterpret_cast + (GetProcAddress(dbghelp, "MiniDumpWriteDump")); + + if (MiniDumpWriteDump) { + char name[MAX_PATH]; + _timeb tb; + _ftime(&tb); + snprintf(name, MAX_PATH, "%s\\crash-%lld.mdmp", directory, + (static_cast(tb.time) * 1000) + + static_cast(tb.millitm)); + + HANDLE file = CreateFile + (name, FILE_WRITE_DATA, 0, 0, CREATE_ALWAYS, 0, 0); + + if (file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION exception + = { GetCurrentThreadId(), e, true }; + + MiniDumpWriteDump + (GetCurrentProcess(), + GetCurrentProcessId(), + file, + MiniDumpNormal, + &exception, + 0, + 0); + + CloseHandle(file); + } + } + + FreeLibrary(dbghelp); + } +} + +LONG CALLBACK +handleException(LPEXCEPTION_POINTERS e) +{ + if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + bool jump = system->segFaultHandler->handleSignal + (reinterpret_cast(&(e->ContextRecord->Eip)), + reinterpret_cast(&(e->ContextRecord->Ebp)), + reinterpret_cast(&(e->ContextRecord->Esp)), + reinterpret_cast(&(e->ContextRecord->Ebx))); + + if (jump) { + return EXCEPTION_CONTINUE_EXECUTION; + } + } + + if (system->crashDumpDirectory) { + dump(e, system->crashDumpDirectory); + } + + return EXCEPTION_CONTINUE_SEARCH; +} + } // namespace namespace vm { System* -makeSystem() +makeSystem(const char* crashDumpDirectory) { - return new (malloc(sizeof(MySystem))) MySystem(); + return new (malloc(sizeof(MySystem))) MySystem(crashDumpDirectory); } } // namespace vm diff --git a/src/x86.cpp b/src/x86.cpp index 3b01573a74..6028d33b1d 100644 --- a/src/x86.cpp +++ b/src/x86.cpp @@ -216,8 +216,6 @@ class Task { public: Task(Task* next): next(next) { } - virtual ~Task() { } - virtual void run(Context* c) = 0; Task* next; diff --git a/test/Zip.java b/test/Zip.java new file mode 100644 index 0000000000..b18d5f1a0b --- /dev/null +++ b/test/Zip.java @@ -0,0 +1,26 @@ +import java.io.InputStream; +import java.util.Enumeration; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; + +public class Zip { + + public static void main(String[] args) throws Exception { + ZipFile file = new ZipFile("build/classpath.jar"); + + byte[] buffer = new byte[4096]; + for (Enumeration e = file.entries(); e.hasMoreElements();) { + ZipEntry entry = e.nextElement(); + InputStream in = file.getInputStream(entry); + try { + int size = 0; + int c; while ((c = in.read(buffer)) != -1) size += c; + System.out.println + (entry.getName() + " " + entry.getCompressedSize() + " " + size); + } finally { + in.read(); + } + } + } + +} diff --git a/test/test.sh b/test/test.sh index cfd03fe2df..fd028e39f0 100644 --- a/test/test.sh +++ b/test/test.sh @@ -17,7 +17,7 @@ for test in ${tests}; do printf "%16s" "${test}: " case ${mode} in - debug|debug-fast|fast ) + debug|debug-fast|fast|small ) ${vm} ${flags} ${test} >>${log} 2>&1;; stress* )