diff --git a/classpath/java/util/Cell.java b/classpath/avian/Cell.java similarity index 92% rename from classpath/java/util/Cell.java rename to classpath/avian/Cell.java index 4903a80a8b..fe902feebe 100644 --- a/classpath/java/util/Cell.java +++ b/classpath/avian/Cell.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -package java.util; +package avian; public class Cell { public T value; diff --git a/classpath/java/util/PersistentSet.java b/classpath/avian/PersistentSet.java similarity index 99% rename from classpath/java/util/PersistentSet.java rename to classpath/avian/PersistentSet.java index 3cbe4431f4..0441b38238 100644 --- a/classpath/java/util/PersistentSet.java +++ b/classpath/avian/PersistentSet.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -8,7 +8,9 @@ There is NO WARRANTY for this software. See license.txt for details. */ -package java.util; +package avian; + +import java.util.Comparator; public class PersistentSet implements Iterable { private static final Node NullNode = new Node(null); diff --git a/classpath/avian/resource/Handler.java b/classpath/avian/resource/Handler.java new file mode 100644 index 0000000000..a10e575a85 --- /dev/null +++ b/classpath/avian/resource/Handler.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2008-2009, 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 avian.resource; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new ResourceConnection(url); + } + + private static class ResourceConnection extends URLConnection { + public ResourceConnection(URL url) { + super(url); + } + + public int getContentLength() { + return ResourceInputStream.getContentLength(url.getFile()); + } + + public InputStream getInputStream() throws IOException { + return new ResourceInputStream(url.getFile()); + } + } + + private static class ResourceInputStream extends InputStream { + private long peer; + private int position; + + public ResourceInputStream(String path) throws IOException { + peer = open(path); + if (peer == 0) { + throw new FileNotFoundException(path); + } + } + + private static native int getContentLength(String path); + + private static native long open(String path) throws IOException; + + private static native int read(long peer, int position) throws IOException; + + private static native int read(long peer, int position, + byte[] b, int offset, int length) + throws IOException; + + public static native void close(long peer) throws IOException; + + public int read() throws IOException { + if (peer != 0) { + int c = read(peer, position); + if (c >= 0) { + ++ position; + } + return c; + } else { + throw new IOException(); + } + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (peer != 0) { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset + length > b.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + int c = read(peer, position, b, offset, length); + if (c >= 0) { + position += c; + } + return c; + } else { + throw new IOException(); + } + } + + public void close() throws IOException { + if (peer != 0) { + close(peer); + peer = 0; + } + } + } +} diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index acb61554f4..1bf1a04fec 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -23,6 +23,7 @@ #ifdef WIN32 # include # include +# include # define OPEN _open # define CLOSE _close diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index ad9b2c2c69..1225ca9e69 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -82,7 +82,7 @@ namespace { int descriptor(JNIEnv* e, HANDLE h) { - int fd = _open_osfhandle(reinterpret_cast(h), 0); + int fd = _open_osfhandle(reinterpret_cast(h), 0); if (fd == -1) { throwNew(e, "java/io/IOException", strerror(errno)); } diff --git a/classpath/java/lang/Appendable.java b/classpath/java/lang/Appendable.java new file mode 100644 index 0000000000..4c3b10bf22 --- /dev/null +++ b/classpath/java/lang/Appendable.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2009, 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.lang; + +import java.io.IOException; + +public interface Appendable { + public Appendable append(char c) throws IOException; + + public Appendable append(CharSequence sequence) throws IOException; + + public Appendable append(CharSequence sequence, int start, int end) + throws IOException; +} diff --git a/classpath/java/lang/ArrayIndexOutOfBoundsException.java b/classpath/java/lang/ArrayIndexOutOfBoundsException.java index a2e8e4c634..7545661bb7 100644 --- a/classpath/java/lang/ArrayIndexOutOfBoundsException.java +++ b/classpath/java/lang/ArrayIndexOutOfBoundsException.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -10,7 +10,7 @@ package java.lang; -public class ArrayIndexOutOfBoundsException extends RuntimeException { +public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { public ArrayIndexOutOfBoundsException(String message, Throwable cause) { super(message, cause); } diff --git a/classpath/java/lang/Character.java b/classpath/java/lang/Character.java index 7d097366a8..61c3b3bedb 100644 --- a/classpath/java/lang/Character.java +++ b/classpath/java/lang/Character.java @@ -187,4 +187,40 @@ public final class Character implements Comparable { return isHighSurrogate(high) && isLowSurrogate(low); } + public static int codePointAt(CharSequence sequence, int offset) { + int length = sequence.length(); + if (offset < 0 || offset >= length) { + throw new IndexOutOfBoundsException(); + } + + char high = sequence.charAt(offset); + if (! isHighSurrogate(high) || offset >= length) { + return high; + } + char low = sequence.charAt(offset + 1); + if (! isLowSurrogate(low)) { + return high; + } + + return toCodePoint(high, low); + } + + public static int codePointCount(CharSequence sequence, int start, int end) { + int length = sequence.length(); + if (start < 0 || start > end || end >= length) { + throw new IndexOutOfBoundsException(); + } + + int count = 0; + for (int i = start; i < end; ++i) { + if (isHighSurrogate(sequence.charAt(i)) + && (i + 1) < end + && isLowSurrogate(sequence.charAt(i + 1))) + { + ++ i; + } + ++ count; + } + return count; + } } diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index 2d907f064b..551bd710d8 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -14,12 +14,19 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.GenericDeclaration; +import java.lang.annotation.Annotation; import java.io.InputStream; import java.io.IOException; import java.net.URL; +import java.security.ProtectionDomain; +import java.security.Permissions; +import java.security.AllPermission; -public final class Class { +public final class Class implements Type, GenericDeclaration { private static final int PrimitiveFlag = 1 << 4; private short flags; @@ -84,6 +91,9 @@ public final class Class { ClassLoader loader) throws ClassNotFoundException { + if (loader == null) { + loader = Class.class.loader; + } Class c = loader.loadClass(name); if (initialize) { c.initialize(); @@ -168,6 +178,8 @@ public final class Class { private Method findMethod(String name, Class[] parameterTypes) { if (methodTable != null) { + if (parameterTypes == null) + parameterTypes = new Class[0]; for (int i = 0; i < methodTable.length; ++i) { if (methodTable[i].getName().equals(name) && match(parameterTypes, methodTable[i].getParameterTypes())) @@ -434,8 +446,63 @@ public final class Class { return null; } } - + public boolean desiredAssertionStatus() { return false; } + + public T cast(Object o) { + return (T) o; + } + + public Object[] getSigners() { + throw new UnsupportedOperationException(); + } + + public Annotation[] getDeclaredAnnotations() { + throw new UnsupportedOperationException(); + } + + public boolean isEnum() { + throw new UnsupportedOperationException(); + } + + public TypeVariable>[] getTypeParameters() { + throw new UnsupportedOperationException(); + } + + public String getSimpleName() { + throw new UnsupportedOperationException(); + } + + public Method getEnclosingMethod() { + throw new UnsupportedOperationException(); + } + + public Constructor getEnclosingConstructor() { + throw new UnsupportedOperationException(); + } + + public Class getEnclosingClass() { + throw new UnsupportedOperationException(); + } + + public Class[] getDeclaredClasses() { + throw new UnsupportedOperationException(); + } + + public A getAnnotation(Class c) { + throw new UnsupportedOperationException(); + } + + public ProtectionDomain getProtectionDomain() { + Permissions p = new Permissions(); + p.add(new AllPermission()); + return new ProtectionDomain(null, p); + } + + // for GNU Classpath compatibility: + void setSigners(Object[] signers) { + throw new UnsupportedOperationException(); + } } diff --git a/classpath/java/lang/ClassNotFoundException.java b/classpath/java/lang/ClassNotFoundException.java index e2752a538a..1dfc694b06 100644 --- a/classpath/java/lang/ClassNotFoundException.java +++ b/classpath/java/lang/ClassNotFoundException.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -11,8 +11,11 @@ package java.lang; public class ClassNotFoundException extends Exception { + private final Throwable cause2; + public ClassNotFoundException(String message, Throwable cause) { super(message, cause); + cause2 = cause; } public ClassNotFoundException(String message) { diff --git a/classpath/java/lang/Enum.java b/classpath/java/lang/Enum.java index 5edcbf6cd5..db1c7fa496 100644 --- a/classpath/java/lang/Enum.java +++ b/classpath/java/lang/Enum.java @@ -53,4 +53,12 @@ public abstract class Enum> implements Comparable { public String toString() { return name; } + + public Class getDeclaringClass() { + Class c = getClass(); + while (c.getSuperclass() != Enum.class) { + c = c.getSuperclass(); + } + return c; + } } diff --git a/classpath/java/lang/ExceptionInInitializerError.java b/classpath/java/lang/ExceptionInInitializerError.java index 912e43ba8d..9deea39355 100644 --- a/classpath/java/lang/ExceptionInInitializerError.java +++ b/classpath/java/lang/ExceptionInInitializerError.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -11,11 +11,14 @@ package java.lang; public class ExceptionInInitializerError extends Error { + private final Throwable cause2; + public ExceptionInInitializerError(String message) { super(message); + cause2 = null; } public ExceptionInInitializerError() { - super(); + this(null); } } diff --git a/classpath/java/lang/Object.java b/classpath/java/lang/Object.java index b902b38060..a1fb1a0521 100644 --- a/classpath/java/lang/Object.java +++ b/classpath/java/lang/Object.java @@ -25,7 +25,7 @@ public class Object { return this == o; } - protected void finalize() { } + protected void finalize() throws Throwable { } public native final Class getClass(); @@ -41,5 +41,14 @@ public class Object { wait(0); } - public native final void wait(long timeout) throws InterruptedException; + public native final void wait(long milliseconds) throws InterruptedException; + + public final void wait(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds != 0) { + ++ milliseconds; + } + wait(milliseconds); + } } diff --git a/classpath/java/lang/StackOverflowError.java b/classpath/java/lang/StackOverflowError.java index c5acc3fb6a..d8144b6666 100644 --- a/classpath/java/lang/StackOverflowError.java +++ b/classpath/java/lang/StackOverflowError.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -10,9 +10,9 @@ package java.lang; -public class StackOverflowError extends Error { +public class StackOverflowError extends VirtualMachineError { public StackOverflowError(String message) { - super(message, null); + super(message); } public StackOverflowError() { diff --git a/classpath/java/lang/StackTraceElement.java b/classpath/java/lang/StackTraceElement.java index f20dc27803..cd63e42292 100644 --- a/classpath/java/lang/StackTraceElement.java +++ b/classpath/java/lang/StackTraceElement.java @@ -18,8 +18,8 @@ public class StackTraceElement { private String file; private int line; - private StackTraceElement(String class_, String method, String file, - int line) + public StackTraceElement(String class_, String method, String file, + int line) { this.class_ = class_; this.method = method; @@ -56,7 +56,7 @@ public class StackTraceElement { } public String getClassName() { - return class_; + return class_.replace('/', '.'); } public String getMethodName() { diff --git a/classpath/java/lang/String.java b/classpath/java/lang/String.java index e7a6aabfff..79dcc8ee75 100644 --- a/classpath/java/lang/String.java +++ b/classpath/java/lang/String.java @@ -12,14 +12,30 @@ package java.lang; import java.io.UnsupportedEncodingException; import java.util.regex.Pattern; +import java.util.Comparator; +import java.util.Locale; import java.io.ByteArrayOutputStream; +import java.io.Serializable; + +public final class String + implements Comparable, CharSequence, Serializable +{ + public static Comparator CASE_INSENSITIVE_ORDER + = new Comparator() { + public int compare(String a, String b) { + return a.compareToIgnoreCase(b); + } + }; -public final class String implements Comparable, CharSequence { private final Object data; private final int offset; private final int length; private int hashCode; + public String() { + this(new char[0], 0, 0); + } + public String(char[] data, int offset, int length, boolean copy) { this((Object) data, offset, length, copy); } @@ -32,9 +48,11 @@ public final class String implements Comparable, CharSequence { this(data, 0, data.length); } - public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException { + public String(byte bytes[], int offset, int length, String charsetName) + throws UnsupportedEncodingException + { this(bytes, offset, length); - if (!charsetName.equals("UTF-8")) { + if (! charsetName.equalsIgnoreCase("UTF-8")) { throw new UnsupportedEncodingException(charsetName); } } @@ -57,12 +75,29 @@ public final class String implements Comparable, CharSequence { public String(byte[] data, String charset) throws UnsupportedEncodingException - { - this(data); - if (! charset.equals("UTF-8")) { - throw new UnsupportedEncodingException(charset); - } + { + this(data); + if (! charset.equals("UTF-8")) { + throw new UnsupportedEncodingException(charset); } + } + + public String(byte bytes[], int highByte, int offset, int length) { + if (offset < 0 || offset + length > bytes.length) { + throw new IndexOutOfBoundsException + (offset + " < 0 or " + offset + " + " + length + " > " + bytes.length); + } + + char[] c = new char[length]; + int mask = highByte << 8; + for (int i = 0; i < length; ++i) { + c[i] = (char) ((bytes[offset + i] & 0xFF) | mask); + } + + this.data = c; + this.offset = 0; + this.length = length; + } private String(Object data, int offset, int length, boolean copy) { int l; @@ -200,7 +235,7 @@ public final class String implements Comparable, CharSequence { } public boolean equalsIgnoreCase(String s) { - return this == s || compareToIgnoreCase(s) == 0; + return this == s || (s != null && compareToIgnoreCase(s) == 0); } public int compareTo(String s) { @@ -381,7 +416,7 @@ public final class String implements Comparable, CharSequence { } } else { throw new IndexOutOfBoundsException - (start + " not in (0, " + end + ") or " + end + " > " + length); + (start + " not in [0, " + end + ") or " + end + " > " + length); } } @@ -534,6 +569,10 @@ public final class String implements Comparable, CharSequence { return Pattern.matches(regex, this); } + public String replaceAll(String regex, String replacement) { + return Pattern.compile(regex).matcher(this).replaceAll(replacement); + } + public native String intern(); public static String valueOf(Object s) { @@ -557,7 +596,9 @@ public final class String implements Comparable, CharSequence { } public static String valueOf(int v) { - return Integer.toString(v); + // use Integer.toString(int, int), because GNU Classpath's + // Integer.toString(int) just calls String.valueOf(int): + return Integer.toString(v, 10); } public static String valueOf(long v) { @@ -572,6 +613,14 @@ public final class String implements Comparable, CharSequence { return Double.toString(v); } + public static String valueOf(char[] data, int offset, int length) { + return new String(data, offset, length); + } + + public static String valueOf(char[] data) { + return valueOf(data, 0, data.length); + } + public int lastIndexOf(int ch, int lastIndex) { for (int i = lastIndex ; i >= 0; --i) { if (charAt(i) == ch) { @@ -581,4 +630,63 @@ public final class String implements Comparable, CharSequence { return -1; } + + public boolean regionMatches(int thisOffset, String match, int matchOffset, + int length) + { + return regionMatches(false, thisOffset, match, matchOffset, length); + } + + public boolean regionMatches(boolean ignoreCase, int thisOffset, + String match, int matchOffset, int length) + { + String a = substring(thisOffset, thisOffset + length); + String b = match.substring(matchOffset, matchOffset + length); + if (ignoreCase) { + return a.equalsIgnoreCase(b); + } else { + return a.equals(b); + } + } + + public boolean isEmpty() { + return length == 0; + } + + public boolean contains(String match) { + return indexOf(match) != -1; + } + + public int codePointAt(int offset) { + return Character.codePointAt(this, offset); + } + + public int codePointCount(int start, int end) { + return Character.codePointCount(this, start, end); + } + + public String replace(CharSequence match, CharSequence replacement) { + throw new UnsupportedOperationException(); + } + + public String toUpperCase(Locale locale) { + throw new UnsupportedOperationException(); + } + + public String toLowerCase(Locale locale) { + throw new UnsupportedOperationException(); + } + + // for GNU Classpath compatibility: + static char[] zeroBasedStringValue(String s) { + if (s.offset == 0) { + if (s.data instanceof char[]) { + char[] data = (char[]) s.data; + if (data.length == s.length) { + return data; + } + } + } + return s.toCharArray(); + } } diff --git a/classpath/java/lang/StringBuilder.java b/classpath/java/lang/StringBuilder.java index 47b58134d1..77332204a5 100644 --- a/classpath/java/lang/StringBuilder.java +++ b/classpath/java/lang/StringBuilder.java @@ -10,7 +10,7 @@ package java.lang; -public class StringBuilder implements CharSequence { +public class StringBuilder implements CharSequence, Appendable { private static final int BufferSize = 32; private Cell chain; @@ -54,6 +54,14 @@ public class StringBuilder implements CharSequence { } } + public StringBuilder append(CharSequence sequence) { + return append(sequence.toString()); + } + + public Appendable append(CharSequence sequence, int start, int end) { + return append(sequence.subSequence(start, end)); + } + public StringBuilder append(char[] b, int offset, int length) { return append(new String(b, offset, length)); } @@ -150,6 +158,10 @@ public class StringBuilder implements CharSequence { return this; } + public StringBuilder insert(int i, CharSequence s) { + return insert(i, s.toString()); + } + public StringBuilder insert(int i, char c) { return insert(i, new String(new char[] { c }, 0, 1, false)); } @@ -332,4 +344,8 @@ public class StringBuilder implements CharSequence { deleteCharAt(index); insert(index, ch); } + + public void ensureCapacity(int capacity) { + // ignore + } } diff --git a/classpath/java/lang/Thread.java b/classpath/java/lang/Thread.java index 731331051d..4279e5bfbf 100644 --- a/classpath/java/lang/Thread.java +++ b/classpath/java/lang/Thread.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -15,14 +15,30 @@ import java.util.WeakHashMap; public class Thread implements Runnable { private long peer; + private boolean interrupted; + private boolean daemon; + private byte state; + private byte priority; private final Runnable task; private Map locals; - private boolean interrupted; private Object sleepLock; private ClassLoader classLoader; - private String name; + private UncaughtExceptionHandler exceptionHandler; - public Thread(Runnable task, String name) { + // package private for GNU Classpath, which inexplicably bypasses + // the accessor methods: + String name; + ThreadGroup group; + + private static UncaughtExceptionHandler defaultExceptionHandler; + + public static final int MIN_PRIORITY = 1; + public static final int NORM_PRIORITY = 5; + public static final int MAX_PRIORITY = 10; + + public Thread(ThreadGroup group, Runnable task, String name, long stackSize) + { + this.group = group; this.task = task; this.name = name; @@ -41,8 +57,28 @@ public class Thread implements Runnable { classLoader = current.classLoader; } + public Thread(ThreadGroup group, Runnable task, String name) { + this(group, task, name, 0); + } + + public Thread(ThreadGroup group, String name) { + this(null, null, name); + } + + public Thread(Runnable task, String name) { + this(null, task, name); + } + public Thread(Runnable task) { - this(task, "Thread["+task+"]"); + this(null, task, "Thread["+task+"]"); + } + + public Thread(String name) { + this(null, null, name); + } + + public Thread() { + this((Runnable) null); } public synchronized void start() { @@ -58,6 +94,28 @@ public class Thread implements Runnable { private native long doStart(); + private static void run(Thread t) throws Throwable { + t.state = (byte) State.RUNNABLE.ordinal(); + try { + t.run(); + } catch (Throwable e) { + UncaughtExceptionHandler eh = t.exceptionHandler; + UncaughtExceptionHandler deh = defaultExceptionHandler; + if (eh != null) { + eh.uncaughtException(t, e); + } else if (deh != null) { + deh.uncaughtException(t, e); + } else { + throw e; + } + } finally { + synchronized (t) { + t.state = (byte) State.TERMINATED.ordinal(); + t.notifyAll(); + } + } + } + public void run() { if (task != null) { task.run(); @@ -101,6 +159,10 @@ public class Thread implements Runnable { } } + public static boolean isInterrupted() { + return currentThread().interrupted; + } + public static void sleep(long milliseconds) throws InterruptedException { Thread t = currentThread(); if (t.sleepLock == null) { @@ -111,6 +173,16 @@ public class Thread implements Runnable { } } + public static void sleep(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds > 0) { + ++ milliseconds; + } + + sleep(milliseconds); + } + public StackTraceElement[] getStackTrace() { return Throwable.resolveTrace(getStackTrace(peer)); } @@ -124,5 +196,125 @@ public class Thread implements Runnable { public String getName() { return name; } + + public void setName(String name) { + this.name = name; + } + + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + UncaughtExceptionHandler eh = exceptionHandler; + return (eh == null ? group : eh); + } + + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return defaultExceptionHandler; + } + + public void setUncaughtExceptionHandler(UncaughtExceptionHandler h) { + exceptionHandler = h; + } + + public static void setDefaultUncaughtExceptionHandler + (UncaughtExceptionHandler h) + { + defaultExceptionHandler = h; + } + + public State getState() { + return State.values()[state]; + } + + public boolean isAlive() { + switch (getState()) { + case NEW: + case TERMINATED: + return false; + + default: + return true; + } + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + if (priority < MIN_PRIORITY || priority > MAX_PRIORITY) { + throw new IllegalArgumentException(); + } + this.priority = (byte) priority; + } + + public boolean isDaemon() { + return daemon; + } + + public void setDaemon(boolean v) { + daemon = v; + } + + public static native void yield(); + + public synchronized void join() throws InterruptedException { + while (getState() != State.TERMINATED) { + wait(); + } + } + + public synchronized void join(long milliseconds) throws InterruptedException + { + long then = System.currentTimeMillis(); + long remaining = milliseconds; + while (remaining > 0 && getState() != State.TERMINATED) { + wait(remaining); + + remaining = milliseconds - (System.currentTimeMillis() - then); + } + } + + public void join(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds > 0) { + ++ milliseconds; + } + + join(milliseconds); + } + + public ThreadGroup getThreadGroup() { + return group; + } + + public static native boolean holdsLock(Object o); + + public long getId() { + return peer; + } + + public void stop() { + throw new UnsupportedOperationException(); + } + + public void stop(Throwable t) { + throw new UnsupportedOperationException(); + } + + public void suspend() { + throw new UnsupportedOperationException(); + } + + public void resume() { + throw new UnsupportedOperationException(); + } + + public interface UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e); + } + + public enum State { + NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED + } } diff --git a/classpath/java/lang/ThreadDeath.java b/classpath/java/lang/ThreadDeath.java new file mode 100644 index 0000000000..167f6633dc --- /dev/null +++ b/classpath/java/lang/ThreadDeath.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, 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.lang; + +public class ThreadDeath extends Error { + public ThreadDeath() { } +} diff --git a/classpath/java/lang/ThreadGroup.java b/classpath/java/lang/ThreadGroup.java new file mode 100644 index 0000000000..9636b6a53a --- /dev/null +++ b/classpath/java/lang/ThreadGroup.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2009, 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.lang; + +public class ThreadGroup implements Thread.UncaughtExceptionHandler { + private final ThreadGroup parent; + private final String name; + + public ThreadGroup(ThreadGroup parent, String name) { + this.parent = parent; + this.name = name; + } + + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else { + Thread.UncaughtExceptionHandler deh + = Thread.getDefaultUncaughtExceptionHandler(); + if (deh != null) { + deh.uncaughtException(t, e); + } else if (! (e instanceof ThreadDeath)) { + e.printStackTrace(); + } + } + } +} diff --git a/classpath/java/lang/Throwable.java b/classpath/java/lang/Throwable.java index 333a156166..09861fe9b4 100644 --- a/classpath/java/lang/Throwable.java +++ b/classpath/java/lang/Throwable.java @@ -13,8 +13,9 @@ package java.lang; import java.io.PrintStream; import java.io.PrintWriter; import java.io.IOException; +import java.io.Serializable; -public class Throwable { +public class Throwable implements Serializable { private String message; private Object trace; private Throwable cause; @@ -109,4 +110,9 @@ public class Throwable { cause.printStackTrace(sb, nl); } } + + public Throwable fillInStackTrace() { + trace = trace(0); + return this; + } } diff --git a/classpath/java/lang/VirtualMachineError.java b/classpath/java/lang/VirtualMachineError.java new file mode 100644 index 0000000000..b59ebd41be --- /dev/null +++ b/classpath/java/lang/VirtualMachineError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2009, 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.lang; + +public class VirtualMachineError extends Error { + public VirtualMachineError(String message) { + super(message); + } + + public VirtualMachineError() { + this(null); + } +} diff --git a/classpath/java/lang/ref/Reference.java b/classpath/java/lang/ref/Reference.java index cdfacb191a..ed322a8c42 100644 --- a/classpath/java/lang/ref/Reference.java +++ b/classpath/java/lang/ref/Reference.java @@ -22,6 +22,10 @@ public abstract class Reference { this.queue = queue; } + protected Reference(T target) { + this(target, null); + } + public T get() { return target; } diff --git a/classpath/java/lang/reflect/Constructor.java b/classpath/java/lang/reflect/Constructor.java index b4254e7c82..11df2f1fae 100644 --- a/classpath/java/lang/reflect/Constructor.java +++ b/classpath/java/lang/reflect/Constructor.java @@ -10,7 +10,9 @@ package java.lang.reflect; -public class Constructor extends AccessibleObject implements Member { +public class Constructor extends AccessibleObject + implements Member, GenericDeclaration +{ private Method method; public Constructor(Method method) { @@ -46,6 +48,18 @@ public class Constructor extends AccessibleObject implements Member { return method.getName(); } + public boolean isSynthetic() { + return method.isSynthetic(); + } + + public TypeVariable>[] getTypeParameters() { + throw new UnsupportedOperationException(); + } + + public Type[] getGenericParameterTypes() { + return method.getGenericParameterTypes(); + } + private static native T make(Class c); public T newInstance(Object ... arguments) diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 978859ce05..19df851027 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -101,6 +101,38 @@ public class Field extends AccessibleObject { } } + public boolean getBoolean(Object instance) throws IllegalAccessException { + return ((Boolean) get(instance)).booleanValue(); + } + + public byte getByte(Object instance) throws IllegalAccessException { + return ((Byte) get(instance)).byteValue(); + } + + public short getShort(Object instance) throws IllegalAccessException { + return ((Short) get(instance)).shortValue(); + } + + public char getChar(Object instance) throws IllegalAccessException { + return ((Character) get(instance)).charValue(); + } + + public int getInt(Object instance) throws IllegalAccessException { + return ((Integer) get(instance)).intValue(); + } + + public float getFloat(Object instance) throws IllegalAccessException { + return ((Float) get(instance)).floatValue(); + } + + public long getLong(Object instance) throws IllegalAccessException { + return ((Long) get(instance)).longValue(); + } + + public double getDouble(Object instance) throws IllegalAccessException { + return ((Double) get(instance)).doubleValue(); + } + public void set(Object instance, Object value) throws IllegalAccessException { @@ -162,6 +194,10 @@ public class Field extends AccessibleObject { } } + public boolean isEnumConstant() { + throw new UnsupportedOperationException(); + } + private static native long getPrimitive (Object instance, int code, int offset); diff --git a/classpath/java/lang/reflect/GenericDeclaration.java b/classpath/java/lang/reflect/GenericDeclaration.java new file mode 100644 index 0000000000..61914661bb --- /dev/null +++ b/classpath/java/lang/reflect/GenericDeclaration.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, 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.lang.reflect; + +public interface GenericDeclaration { + public TypeVariable[] getTypeParameters(); +} diff --git a/classpath/java/lang/reflect/Member.java b/classpath/java/lang/reflect/Member.java index 4654d38875..6af945750f 100644 --- a/classpath/java/lang/reflect/Member.java +++ b/classpath/java/lang/reflect/Member.java @@ -19,4 +19,6 @@ public interface Member { public int getModifiers(); public String getName(); + + public boolean isSynthetic(); } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 68058aedce..f6ac1bd6cd 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -10,7 +10,9 @@ package java.lang.reflect; -public class Method extends AccessibleObject implements Member { +public class Method extends AccessibleObject + implements Member, GenericDeclaration +{ private byte vmFlags; private byte returnCode; private byte parameterCount; @@ -124,4 +126,28 @@ public class Method extends AccessibleObject implements Member { } throw new RuntimeException(); } + + public boolean isSynthetic() { + throw new UnsupportedOperationException(); + } + + public Object getDefaultValue() { + throw new UnsupportedOperationException(); + } + + public Type[] getGenericParameterTypes() { + throw new UnsupportedOperationException(); + } + + public Type getGenericReturnType() { + throw new UnsupportedOperationException(); + } + + public Class[] getExceptionTypes() { + throw new UnsupportedOperationException(); + } + + public TypeVariable>[] getTypeParameters() { + throw new UnsupportedOperationException(); + } } diff --git a/classpath/java/lang/reflect/Type.java b/classpath/java/lang/reflect/Type.java new file mode 100644 index 0000000000..b1a04ec765 --- /dev/null +++ b/classpath/java/lang/reflect/Type.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2009, 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.lang.reflect; + +public interface Type { } diff --git a/classpath/java/lang/reflect/TypeVariable.java b/classpath/java/lang/reflect/TypeVariable.java new file mode 100644 index 0000000000..65d531d6bf --- /dev/null +++ b/classpath/java/lang/reflect/TypeVariable.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2009, 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.lang.reflect; + +public interface TypeVariable + extends Type +{ + public Type[] getBounds(); + public T getGenericDeclaration(); + public String getName(); +} diff --git a/classpath/java/net/URL.java b/classpath/java/net/URL.java index bf2174c447..2b405b307e 100644 --- a/classpath/java/net/URL.java +++ b/classpath/java/net/URL.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -11,7 +11,6 @@ package java.net; import java.io.IOException; -import java.io.FileNotFoundException; import java.io.InputStream; public final class URL { @@ -73,7 +72,7 @@ public final class URL { throws MalformedURLException { if ("resource".equals(protocol)) { - return new ResourceHandler(); + return new avian.resource.Handler(); } else { throw new MalformedURLException("unknown protocol: " + protocol); } @@ -88,87 +87,4 @@ public final class URL { this.file = file; this.ref = ref; } - - private static class ResourceHandler extends URLStreamHandler { - protected URLConnection openConnection(URL url) { - return new ResourceConnection(url); - } - } - - private static class ResourceConnection extends URLConnection { - public ResourceConnection(URL url) { - super(url); - } - - public int getContentLength() { - return ResourceInputStream.getContentLength(url.getFile()); - } - - public InputStream getInputStream() throws IOException { - return new ResourceInputStream(url.getFile()); - } - } - - private static class ResourceInputStream extends InputStream { - private long peer; - private int position; - - public ResourceInputStream(String path) throws IOException { - peer = open(path); - if (peer == 0) { - throw new FileNotFoundException(path); - } - } - - private static native int getContentLength(String path); - - private static native long open(String path) throws IOException; - - private static native int read(long peer, int position) throws IOException; - - private static native int read(long peer, int position, - byte[] b, int offset, int length) - throws IOException; - - public static native void close(long peer) throws IOException; - - public int read() throws IOException { - if (peer != 0) { - int c = read(peer, position); - if (c >= 0) { - ++ position; - } - return c; - } else { - throw new IOException(); - } - } - - public int read(byte[] b, int offset, int length) throws IOException { - if (peer != 0) { - if (b == null) { - throw new NullPointerException(); - } - - if (offset < 0 || offset + length > b.length) { - throw new ArrayIndexOutOfBoundsException(); - } - - int c = read(peer, position, b, offset, length); - if (c >= 0) { - position += c; - } - return c; - } else { - throw new IOException(); - } - } - - public void close() throws IOException { - if (peer != 0) { - close(peer); - peer = 0; - } - } - } } diff --git a/classpath/java/security/AllPermission.java b/classpath/java/security/AllPermission.java new file mode 100644 index 0000000000..6bc99d53e8 --- /dev/null +++ b/classpath/java/security/AllPermission.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2009, 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.security; + +public class AllPermission extends Permission { } diff --git a/classpath/java/security/CodeSource.java b/classpath/java/security/CodeSource.java new file mode 100644 index 0000000000..030597fa22 --- /dev/null +++ b/classpath/java/security/CodeSource.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2009, 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.security; + +public class CodeSource { } diff --git a/classpath/java/security/Permission.java b/classpath/java/security/Permission.java new file mode 100644 index 0000000000..bc6c438e18 --- /dev/null +++ b/classpath/java/security/Permission.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2009, 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.security; + +public abstract class Permission { + public PermissionCollection newPermissionCollection() { + return null; + } +} diff --git a/classpath/java/security/PermissionCollection.java b/classpath/java/security/PermissionCollection.java new file mode 100644 index 0000000000..6e2de457e9 --- /dev/null +++ b/classpath/java/security/PermissionCollection.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, 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.security; + +public abstract class PermissionCollection { + public abstract void add(Permission p); +} diff --git a/classpath/java/security/Permissions.java b/classpath/java/security/Permissions.java new file mode 100644 index 0000000000..2c2b688972 --- /dev/null +++ b/classpath/java/security/Permissions.java @@ -0,0 +1,41 @@ +/* Copyright (c) 2009, 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.security; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; + +public class Permissions extends PermissionCollection { + private final Map collections = new HashMap(); + + public void add(Permission p) { + Class c = p.getClass(); + PermissionCollection pc = collections.get(c); + if (pc == null) { + pc = p.newPermissionCollection(); + if (pc == null) { + pc = new MyPermissionCollection(); + } + collections.put(c, pc); + } + pc.add(p); + } + + private static class MyPermissionCollection extends PermissionCollection { + private final Set permissions = new HashSet(); + + public void add(Permission p) { + permissions.add(p); + } + } +} diff --git a/classpath/java/security/ProtectionDomain.java b/classpath/java/security/ProtectionDomain.java new file mode 100644 index 0000000000..f7670e28a4 --- /dev/null +++ b/classpath/java/security/ProtectionDomain.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2009, 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.security; + +public class ProtectionDomain { + private final CodeSource codeSource; + private final PermissionCollection permissions; + + public ProtectionDomain(CodeSource codeSource, + PermissionCollection permissions) + { + this.codeSource = codeSource; + this.permissions = permissions; + } +} diff --git a/classpath/java/util/TreeSet.java b/classpath/java/util/TreeSet.java index 3c63160b99..5a1d4a8c47 100644 --- a/classpath/java/util/TreeSet.java +++ b/classpath/java/util/TreeSet.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -10,6 +10,9 @@ package java.util; +import avian.PersistentSet; +import avian.Cell; + public class TreeSet extends AbstractSet implements Collection { private PersistentSet> set; private int size; diff --git a/classpath/java/util/zip/ZipFile.java b/classpath/java/util/zip/ZipFile.java index a11a3c5dd1..d59885496f 100644 --- a/classpath/java/util/zip/ZipFile.java +++ b/classpath/java/util/zip/ZipFile.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -63,7 +63,7 @@ public class ZipFile { return index.size(); } - public Enumeration entries() { + public Enumeration entries() { return new MyEnumeration(window, index.values().iterator()); } diff --git a/makefile b/makefile index 583d0fdb90..21b007dd13 100644 --- a/makefile +++ b/makefile @@ -18,10 +18,6 @@ bootimage-platform = \ $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) platform = $(bootimage-platform) -ifeq ($(platform),windows) - arch = i386 -endif - mode = fast process = compile @@ -43,6 +39,23 @@ endif ifeq ($(continuations),true) options := $(options)-continuations endif +ifdef gnu + options := $(options)-gnu + gnu-sources = $(src)/gnu.cpp + gnu-jar = $(gnu)/share/classpath/glibj.zip + gnu-libraries = \ + $(gnu)/lib/classpath/libjavaio.a \ + $(gnu)/lib/classpath/libjavalang.a \ + $(gnu)/lib/classpath/libjavalangreflect.a \ + $(gnu)/lib/classpath/libjavamath.a \ + $(gnu)/lib/classpath/libjavanet.a \ + $(gnu)/lib/classpath/libjavanio.a \ + $(gnu)/lib/classpath/libjavautil.a + gnu-object-dep = $(build)/gnu-object.dep + gnu-cflags = -DBOOT_BUILTINS=\"javaio,javalang,javalangreflect,javamath,javanet,javanio,javautil\" -DAVIAN_GNU + gnu-lflags = -lgmp + gnu-objects = $(shell find $(build)/gnu-objects -name "*.o") +endif root = $(shell (cd .. && pwd)) build = build @@ -53,6 +66,12 @@ src = src classpath = classpath test = test +ifdef gnu + avian-classpath-build = $(build)/avian-classpath +else + avian-classpath-build = $(classpath-build) +endif + input = List build-cxx = g++ @@ -83,13 +102,14 @@ warnings = -Wall -Wextra -Werror -Wunused-parameter -Winit-self \ common-cflags = $(warnings) -fno-rtti -fno-exceptions -fno-omit-frame-pointer \ "-I$(JAVA_HOME)/include" -idirafter $(src) -I$(native-build) \ -D__STDC_LIMIT_MACROS -D_JNI_IMPLEMENTATION_ -DAVIAN_VERSION=\"$(version)\" \ + $(gnu-cflags) build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ "-I$(JAVA_HOME)/include/linux" -I$(src) -pthread cflags = $(build-cflags) -common-lflags = -lm -lz +common-lflags = -lm -lz $(gnu-lflags) build-lflags = @@ -167,6 +187,21 @@ ifeq ($(platform),windows) native-path = cygpath -m endif endif + + ifeq ($(arch),x86_64) + cxx = x86_64-pc-mingw32-g++ + cc = x86_64-pc-mingw32-gcc + dlltool = x86_64-pc-mingw32-dlltool + ar = x86_64-pc-mingw32-ar + ranlib = x86_64-pc-mingw32-ranlib + objcopy = x86_64-pc-mingw32-objcopy + strip = : + inc = "$(root)/win64/include" + lib = "$(root)/win64/lib" + pointer-size = 8 + object-format = pe-x86-64 + endif + endif ifeq ($(mode),debug) @@ -232,7 +267,8 @@ vm-sources = \ $(src)/$(process).cpp \ $(src)/builtin.cpp \ $(src)/jnienv.cpp \ - $(src)/process.cpp + $(src)/process.cpp \ + $(gnu-sources) vm-asm-sources = $(src)/$(asm).S @@ -314,16 +350,43 @@ classpath-sources = $(shell find $(classpath) -name '*.java') classpath-classes = \ $(call java-classes,$(classpath-sources),$(classpath),$(classpath-build)) classpath-object = $(native-build)/classpath-jar.o -classpath-dep = $(classpath-build)/dep +classpath-dep = $(classpath-build).dep + +gnu-blacklist = \ + java/lang/AbstractStringBuffer.class \ + java/lang/reflect/Proxy.class + +gnu-overrides = \ + avian/*.class \ + avian/resource/*.class \ + java/lang/Class.class \ + java/lang/Enum.class \ + java/lang/InheritableThreadLocal.class \ + java/lang/Object.class \ + java/lang/StackTraceElement.class \ + java/lang/String*.class \ + java/lang/StringBuffer.class \ + java/lang/StringBuilder.class \ + java/lang/Thread.class \ + java/lang/ThreadLocal.class \ + java/lang/Throwable.class \ + java/lang/ref/PhantomReference.class \ + java/lang/ref/Reference.class \ + java/lang/ref/ReferenceQueue.class \ + java/lang/ref/WeakReference.class \ + java/lang/reflect/AccessibleObject.class \ + java/lang/reflect/Constructor.class \ + java/lang/reflect/Field.class \ + java/lang/reflect/Method.class test-sources = $(wildcard $(test)/*.java) test-classes = $(call java-classes,$(test-sources),$(test),$(test-build)) -test-dep = $(test-build)/dep +test-dep = $(test-build).dep test-extra-sources = $(wildcard $(test)/extra/*.java) test-extra-classes = \ $(call java-classes,$(test-extra-sources),$(test),$(test-build)) -test-extra-dep = $(test-build)/extra/dep +test-extra-dep = $(test-build)-extra.dep class-name = $(patsubst $(1)/%.class,%,$(2)) class-names = $(foreach x,$(2),$(call class-name,$(1),$(x))) @@ -393,11 +456,24 @@ $(native-build)/type-generator.o: \ $(classpath-build)/%.class: $(classpath)/%.java @echo $(<) -$(classpath-dep): $(classpath-sources) +$(classpath-dep): $(classpath-sources) $(gnu-jar) @echo "compiling classpath classes" - @mkdir -p $(dir $(@)) - $(javac) -d $(dir $(@)) -bootclasspath $(classpath-build) \ + @mkdir -p $(avian-classpath-build) + $(javac) -d $(avian-classpath-build) \ + -bootclasspath $(avian-classpath-build) \ $(shell $(MAKE) -s --no-print-directory $(classpath-classes)) +ifdef gnu + (wd=$$(pwd) && \ + cd $(avian-classpath-build) && \ + $(jar) c0f "$$($(native-path) "$${wd}/$(build)/overrides.jar")" \ + $(gnu-overrides)) + @mkdir -p $(classpath-build) + (wd=$$(pwd) && \ + cd $(classpath-build) && \ + $(jar) xf $(gnu-jar) && \ + rm $(gnu-blacklist) && \ + jar xf "$$($(native-path) "$${wd}/$(build)/overrides.jar")") +endif @touch $(@) $(test-build)/%.class: $(test)/%.java @@ -405,16 +481,16 @@ $(test-build)/%.class: $(test)/%.java $(test-dep): $(test-sources) @echo "compiling test classes" - @mkdir -p $(dir $(@)) - $(javac) -d $(dir $(@)) -bootclasspath $(classpath-build) \ + @mkdir -p $(test-build) + $(javac) -d $(test-build) -bootclasspath $(classpath-build) \ $(shell $(MAKE) -s --no-print-directory $(test-classes)) - $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(dir $(@)) \ + $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ test/Subroutine.java @touch $(@) $(test-extra-dep): $(test-extra-sources) @echo "compiling extra test classes" - @mkdir -p $(dir $(@)) + @mkdir -p $(test-build) $(javac) -d $(test-build) -bootclasspath $(classpath-build) \ $(shell $(MAKE) -s --no-print-directory $(test-extra-classes)) @touch $(@) @@ -456,9 +532,9 @@ $(boot-object): $(boot-source) $(compile-object) $(build)/classpath.jar: $(classpath-dep) - (wd=$$(pwd); \ - cd $(classpath-build); \ - $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" $$(find . -name '*.class')) + (wd=$$(pwd) && \ + cd $(classpath-build) && \ + $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" .) $(binaryToMacho): $(src)/binaryToMacho.cpp $(cxx) $(^) -o $(@) @@ -469,8 +545,8 @@ ifeq ($(platform),darwin) $(binaryToMacho) $(asm) $(build)/classpath.jar __TEXT __text \ __binary_classpath_jar_start __binary_classpath_jar_end > $(@) else - (wd=$$(pwd); \ - cd $(build); \ + (wd=$$(pwd) && \ + cd $(build) && \ $(objcopy) -I binary classpath.jar \ -O $(object-format) -B $(object-arch) "$${wd}/$(@)") endif @@ -484,10 +560,11 @@ $(generator-objects): $(native-build)/%.o: $(src)/%.cpp $(jni-objects): $(native-build)/%.o: $(classpath)/%.cpp $(compile-object) +$(static-library): $(gnu-object-dep) $(static-library): $(vm-objects) $(jni-objects) $(vm-heapwalk-objects) @echo "creating $(@)" rm -rf $(@) - $(ar) cru $(@) $(^) + $(ar) cru $(@) $(^) $(call gnu-objects) $(ranlib) $(@) $(bootimage-bin): $(bootimage-generator) @@ -499,24 +576,32 @@ ifeq ($(platform),darwin) $(binaryToMacho) $(asm) $(<) __BOOT __boot \ __binary_bootimage_bin_start __binary_bootimage_bin_end > $(@) else - (wd=$$(pwd); \ - cd $(native-build); \ + (wd=$$(pwd) && \ + cd $(native-build) && \ $(objcopy) --rename-section=.data=.boot -I binary bootimage.bin \ - -O $(object-format) -B $(object-arch) "$${wd}/$(@).tmp"; \ + -O $(object-format) -B $(object-arch) "$${wd}/$(@).tmp" && \ $(objcopy) --set-section-flags .boot=alloc,load,code "$${wd}/$(@).tmp" \ "$${wd}/$(@)") endif +$(gnu-object-dep): $(gnu-libraries) + @mkdir -p $(build)/gnu-objects + (cd $(build)/gnu-objects && \ + for x in $(gnu-libraries); do ar x $${x}; done) + @touch $(@) + +$(executable): $(gnu-object-dep) $(executable): \ $(vm-objects) $(jni-objects) $(driver-object) $(vm-heapwalk-objects) \ $(boot-object) $(vm-classpath-object) @echo "linking $(@)" ifeq ($(platform),windows) - $(dlltool) -z $(@).def $(^) + $(dlltool) -z $(@).def $(^) $(call gnu-objects) $(dlltool) -d $(@).def -e $(@).exp - $(cc) $(@).exp $(^) $(lflags) -o $(@) + $(cc) $(@).exp $(^) $(call gnu-objects) $(lflags) -o $(@) else - $(cc) $(^) $(rdynamic) $(lflags) $(bootimage-lflags) -o $(@) + $(cc) $(^) $(call gnu-objects) $(rdynamic) $(lflags) $(bootimage-lflags) \ + -o $(@) endif $(strip) $(strip-all) $(@) @@ -545,11 +630,13 @@ else $(cc) $(^) $(rdynamic) $(lflags) -o $(@) endif +$(dynamic-library): $(gnu-object-dep) $(dynamic-library): \ $(vm-objects) $(dynamic-object) $(jni-objects) $(vm-heapwalk-objects) \ - $(boot-object) $(vm-classpath-object) + $(boot-object) $(vm-classpath-object) $(gnu-libraries) @echo "linking $(@)" - $(cc) $(^) $(shared) $(lflags) $(bootimage-lflags) -o $(@) + $(cc) $(^) $(call gnu-objects) $(shared) $(lflags) $(bootimage-lflags) \ + -o $(@) $(strip) $(strip-all) $(@) $(executable-dynamic): $(driver-dynamic-object) $(dynamic-library) @@ -560,4 +647,3 @@ $(executable-dynamic): $(driver-dynamic-object) $(dynamic-library) $(generator): $(generator-objects) @echo "linking $(@)" $(build-cc) $(^) $(build-lflags) -o $(@) - diff --git a/src/bootimage.cpp b/src/bootimage.cpp index 2e11905ef9..6df24a6ff3 100644 --- a/src/bootimage.cpp +++ b/src/bootimage.cpp @@ -303,6 +303,10 @@ writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code, (t->m->heap->allocate(heapMapSize(HeapCapacity))); memset(heapMap, 0, heapMapSize(HeapCapacity)); + // this map will not be used when the bootimage is loaded, so + // there's no need to preserve it: + t->m->byteArrayMap = makeWeakHashMap(t, 0, 0); + collect(t, Heap::MajorCollection); HeapWalker* heapWalker = makeHeapImage diff --git a/src/builtin.cpp b/src/builtin.cpp index f4541036d4..1da1b7ee62 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -63,6 +63,25 @@ enumerateThreads(Thread* t, Thread* x, object array, unsigned* index, } } +bool +compatibleArrayTypes(Thread* t, object a, object b) +{ + return classArrayElementSize(t, a) + and classArrayElementSize(t, b) + and (a == b + or (not ((classVmFlags(t, a) & PrimitiveFlag) + or (classVmFlags(t, b) & PrimitiveFlag)))); +} + +void +runOnLoadIfFound(Thread* t, System::Library* library) +{ + void* p = library->resolve("JNI_OnLoad"); + if (p) { + reinterpret_cast(p)(t->m, 0); + } +} + } // namespace extern "C" JNIEXPORT int64_t JNICALL @@ -530,7 +549,9 @@ Avian_java_lang_System_arraycopy int32_t length = arguments[4]; if (LIKELY(src and dst)) { - if (LIKELY(objectClass(t, src) == objectClass(t, dst))) { + if (LIKELY(compatibleArrayTypes + (t, objectClass(t, src), objectClass(t, dst)))) + { unsigned elementSize = classArrayElementSize(t, objectClass(t, src)); if (LIKELY(elementSize)) { @@ -602,6 +623,10 @@ Avian_java_lang_Runtime_load and (s[length] == ',' or s[length] == 0)) { // library is built in to this executable + if (not t->m->triedBuiltinOnLoad) { + t->m->triedBuiltinOnLoad = true; + runOnLoadIfFound(t, t->m->libraries); + } return; } else { while (*s and *s != ',') ++ s; @@ -625,6 +650,7 @@ Avian_java_lang_Runtime_load System::Library* lib; if (LIKELY(t->m->system->success(t->m->system->load(&lib, n, mapName)))) { last->setNext(lib); + runOnLoadIfFound(t, lib); } else { object message = makeString(t, "library not found: %s", n); t->exception = makeUnsatisfiedLinkError(t, message); @@ -836,7 +862,7 @@ Avian_java_lang_Thread_enumerate } extern "C" JNIEXPORT int64_t JNICALL -Avian_java_net_URL_00024ResourceInputStream_getContentLength +Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength (Thread* t, object, uintptr_t* arguments) { object path = reinterpret_cast(*arguments); @@ -856,7 +882,7 @@ Avian_java_net_URL_00024ResourceInputStream_getContentLength } extern "C" JNIEXPORT int64_t JNICALL -Avian_java_net_URL_00024ResourceInputStream_open +Avian_avian_resource_Handler_00024ResourceInputStream_open (Thread* t, object, uintptr_t* arguments) { object path = reinterpret_cast(*arguments); @@ -873,7 +899,7 @@ Avian_java_net_URL_00024ResourceInputStream_open } extern "C" JNIEXPORT int64_t JNICALL -Avian_java_net_URL_00024ResourceInputStream_read__JI +Avian_avian_resource_Handler_00024ResourceInputStream_read__JI (Thread*, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); @@ -888,7 +914,7 @@ Avian_java_net_URL_00024ResourceInputStream_read__JI } extern "C" JNIEXPORT int64_t JNICALL -Avian_java_net_URL_00024ResourceInputStream_read__JI_3BII +Avian_avian_resource_Handler_00024ResourceInputStream_read__JI_3BII (Thread* t, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); @@ -913,7 +939,7 @@ Avian_java_net_URL_00024ResourceInputStream_read__JI_3BII } extern "C" JNIEXPORT void JNICALL -Avian_java_net_URL_00024ResourceInputStream_close +Avian_avian_resource_Handler_00024ResourceInputStream_close (Thread*, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); diff --git a/src/common.h b/src/common.h index c3c03e89c4..c9964b7b7d 100644 --- a/src/common.h +++ b/src/common.h @@ -40,10 +40,17 @@ # define ULD "u" #endif #elif defined __x86_64__ -# define LD "ld" -# define LX "lx" -# define LLD "ld" -# define ULD "lu" +# ifdef __MINGW32__ +# define LD "I64d" +# define LX "I64x" +# define LLD "I64d" +# define ULD "I64x" +# else +# define LD "ld" +# define LX "lx" +# define LLD "ld" +# define ULD "lu" +# endif #else # error "Unsupported architecture" #endif @@ -115,7 +122,6 @@ pad(unsigned n) return pad(n, BytesPerWord); } - inline unsigned ceiling(unsigned n, unsigned d) { diff --git a/src/compile-x86.S b/src/compile-x86.S index cab2e20089..5c73e407dc 100644 --- a/src/compile-x86.S +++ b/src/compile-x86.S @@ -21,19 +21,140 @@ .text #ifdef __x86_64__ - -#define THREAD_CONTINUATION 168 -#define THREAD_EXCEPTION 64 -#define THREAD_EXCEPTION_STACK_ADJUSTMENT 176 -#define THREAD_EXCEPTION_OFFSET 184 -#define THREAD_EXCEPTION_HANDLER 192 -#define CONTINUATION_NEXT 8 -#define CONTINUATION_ADDRESS 32 -#define CONTINUATION_RETURN_ADDRESS_OFFSET 40 -#define CONTINUATION_FRAME_POINTER_OFFSET 48 -#define CONTINUATION_LENGTH 56 -#define CONTINUATION_BODY 64 +#ifdef __MINGW32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 64 + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): + pushq %rbp + movq %rsp,%rbp + + // %rcx: thread + // %rdx: function + // %r8 : arguments + // %r9 : argumentsFootprint + // 48(%rbp) : frameSize + // 56(%rbp) : returnType (ignored) + + // allocate stack space, adding room for callee-saved registers + movl 48(%rbp),%eax + subq %rax,%rsp + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // save callee-saved registers + movq %rsp,%r11 + addq %rax,%r11 + + movq %rbx,0(%r11) + movq %r12,8(%r11) + movq %r13,16(%r11) + movq %r14,24(%r11) + movq %r15,32(%r11) + movq %rsi,40(%r11) + movq %rdi,48(%r11) + + // we use rbx to hold the thread pointer, by convention + mov %rcx,%rbx + + // copy arguments into place + movq $0,%r11 + jmp LOCAL(vmInvoke_argumentTest) + +LOCAL(vmInvoke_argumentLoop): + movq (%r8,%r11,1),%rsi + movq %rsi,(%rsp,%r11,1) + addq $8,%r11 + +LOCAL(vmInvoke_argumentTest): + cmpq %r9,%r11 + jb LOCAL(vmInvoke_argumentLoop) + + // call function + call *%rdx + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): + // restore stack pointer + movq %rbp,%rsp + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers (below the stack pointer, but in + // the red zone) + movq %rsp,%r11 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r11 + + movq 0(%r11),%rbx + movq 8(%r11),%r12 + movq 16(%r11),%r13 + movq 24(%r11),%r14 + movq 32(%r11),%r15 + movq 40(%r11),%rsi + movq 48(%r11),%rdi + + // return + popq %rbp + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rcx: thread + // %rdx: address + // %r8 : base + // %r9 : (unused) + // 8(%rsp): argumentFootprint + // 16(%rsp): arguments + // 24(%rsp): frameSize + + movq %r8,%rbp + + // restore (pseudo)-stack pointer (we don't want to touch the real + // stack pointer, since we haven't copied the arguments yet) + movq %rbp,%r9 + + // allocate new frame, adding room for callee-saved registers + movl 24(%rsp),%eax + subq %rax,%r9 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r9 + + movq %rcx,%rbx + + // set return address + movq vmInvoke_returnAddress@GOTPCREL(%rip),%r10 + movq %r10,(%r9) + + // copy arguments into place + movq $0,%r11 + movq 16(%rsp),%r8 + movq 8(%rsp),%rax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%r8,%r11,1),%r10 + movq %r10,8(%r9,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %rax,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %r9,%rsp + + jmp *%rdx +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#else // not __MINGW32__ #define CALLEE_SAVED_REGISTER_FOOTPRINT 48 @@ -88,65 +209,7 @@ GLOBAL(vmInvoke_returnAddress): movq %rbp,%rsp #ifdef AVIAN_CONTINUATIONS - // call the next continuation, if any - movq THREAD_CONTINUATION(%rbx),%rcx - cmpq $0,%rcx - je LOCAL(vmInvoke_exit) - - // allocate a frame of size (continuation.length * BYTES_PER_WORD) - // + CALLEE_SAVED_REGISTER_FOOTPRINT - movq CONTINUATION_LENGTH(%rcx),%rsi - shlq $3,%rsi - subq %rsi,%rsp - subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp - - // copy the continuation body into the frame - leaq CONTINUATION_BODY(%rcx),%rdi - - movq $0,%r9 - jmp LOCAL(vmInvoke_continuationTest) - -LOCAL(vmInvoke_continuationLoop): - movq (%rdi,%r9,1),%r8 - movq %r8,(%rsp,%r9,1) - addq $8,%r9 - -LOCAL(vmInvoke_continuationTest): - cmpq %rsi,%r9 - jb LOCAL(vmInvoke_continuationLoop) - - // set the return address to vmInvoke_returnAddress - movq CONTINUATION_RETURN_ADDRESS_OFFSET(%rcx),%rdi - movq vmInvoke_returnAddress@GOTPCREL(%rip),%r10 - movq %r10,(%rsp,%rdi,1) - - // save the current base pointer in the frame and update it - movq CONTINUATION_FRAME_POINTER_OFFSET(%rcx),%rdi - movq %rbp,(%rsp,%rdi,1) - addq %rsp,%rdi - movq %rdi,%rbp - - // consume the continuation - movq CONTINUATION_NEXT(%rcx),%rdi - movq %rdi,THREAD_CONTINUATION(%rbx) - - // call the continuation unless we're handling an exception - movq THREAD_EXCEPTION(%rbx),%rsi - cmpq $0,%rsi - jne LOCAL(vmInvoke_handleException) - jmp *CONTINUATION_ADDRESS(%rcx) - -LOCAL(vmInvoke_handleException): - // we're handling an exception - call the exception handler instead - movq $0,THREAD_EXCEPTION(%rbx) - movq THREAD_EXCEPTION_STACK_ADJUSTMENT(%rbx),%rdi - subq %rdi,%rsp - movq THREAD_EXCEPTION_OFFSET(%rbx),%rdi - movq %rsi,(%rsp,%rdi,1) - - jmp *THREAD_EXCEPTION_HANDLER(%rbx) - -LOCAL(vmInvoke_exit): +# include "continuations-x86.S" #endif // AVIAN_CONTINUATIONS // restore callee-saved registers (below the stack pointer, but in @@ -215,21 +278,10 @@ LOCAL(vmJumpAndInvoke_argumentTest): // enabled int3 #endif // not AVIAN_CONTINUATIONS + +#endif // not __MINGW32__ #elif defined __i386__ - -#define THREAD_CONTINUATION 96 -#define THREAD_EXCEPTION 36 -#define THREAD_EXCEPTION_STACK_ADJUSTMENT 100 -#define THREAD_EXCEPTION_OFFSET 104 -#define THREAD_EXCEPTION_HANDLER 108 - -#define CONTINUATION_NEXT 4 -#define CONTINUATION_ADDRESS 16 -#define CONTINUATION_RETURN_ADDRESS_OFFSET 20 -#define CONTINUATION_FRAME_POINTER_OFFSET 24 -#define CONTINUATION_LENGTH 28 -#define CONTINUATION_BODY 32 #define CALLEE_SAVED_REGISTER_FOOTPRINT 16 @@ -286,72 +338,7 @@ vmInvoke_returnAddress: movl %ecx,%esp #ifdef AVIAN_CONTINUATIONS - // call the next continuation, if any - movl THREAD_CONTINUATION(%ebx),%ecx - cmpl $0,%ecx - je LOCAL(vmInvoke_exit) - - // allocate a frame of size (continuation.length * BYTES_PER_WORD) - movl CONTINUATION_LENGTH(%ecx),%esi - shll $2,%esi - subl %esi,%esp - - // copy the continuation body into the frame - leal CONTINUATION_BODY(%ecx),%edi - - push %eax - push %edx - - movl $0,%edx - jmp LOCAL(vmInvoke_continuationTest) - -LOCAL(vmInvoke_continuationLoop): - movl (%edi,%edx,1),%eax - movl %eax,8(%esp,%edx,1) - addl $4,%edx - -LOCAL(vmInvoke_continuationTest): - cmpl %esi,%edx - jb LOCAL(vmInvoke_continuationLoop) - - pop %edx - pop %eax - - // set the return address to vmInvoke_returnAddress - movl CONTINUATION_RETURN_ADDRESS_OFFSET(%ecx),%edi - call LOCAL(getPC) - addl $_GLOBAL_OFFSET_TABLE_,%esi - movl vmInvoke_returnAddress@GOT(%esi),%esi - movl %esi,(%esp,%edi,1) - - // save the current base pointer in the frame and update it - movl CONTINUATION_FRAME_POINTER_OFFSET(%ecx),%edi - movl %ebp,(%esp,%edi,1) - addl %esp,%edi - movl %edi,%ebp - - // consume the continuation - movl CONTINUATION_NEXT(%ecx),%edi - movl %edi,THREAD_CONTINUATION(%ebx) - - // call the continuation unless we're handling an exception - movl THREAD_EXCEPTION(%ebx),%esi - cmpl $0,%esi - jne LOCAL(vmInvoke_handleException) - - jmp *CONTINUATION_ADDRESS(%ecx) - -LOCAL(vmInvoke_handleException): - // we're handling an exception - call the exception handler instead - movl $0,THREAD_EXCEPTION(%ebx) - movl THREAD_EXCEPTION_STACK_ADJUSTMENT(%ebx),%edi - subl %edi,%esp - movl THREAD_EXCEPTION_OFFSET(%ebx),%edi - movl %esi,(%esp,%edi,1) - - jmp *THREAD_EXCEPTION_HANDLER(%ebx) - -LOCAL(vmInvoke_exit): +# include "continuations-x86.S" #endif // AVIAN_CONTINUATIONS // restore callee-saved registers @@ -443,5 +430,5 @@ LOCAL(vmJumpAndInvoke_argumentTest): #endif // AVIAN_CONTINUATIONS #else -# error unsupported platform -#endif +#error unsupported architecture +#endif //def __x86_64__ diff --git a/src/compile.cpp b/src/compile.cpp index b1f5e0111a..ff6f15a5a6 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -493,17 +493,117 @@ class PoolElement: public Promise { }; class Context; +class SubroutineCall; + +class Subroutine { + public: + Subroutine(unsigned ip, unsigned logIndex, Subroutine* listNext, + Subroutine* stackNext): + listNext(listNext), + stackNext(stackNext), + calls(0), + handle(0), + ip(ip), + logIndex(logIndex), + stackIndex(0), + callCount(0), + tableIndex(0), + visited(false) + { } + + Subroutine* listNext; + Subroutine* stackNext; + SubroutineCall* calls; + Compiler::Subroutine* handle; + unsigned ip; + unsigned logIndex; + unsigned stackIndex; + unsigned callCount; + unsigned tableIndex; + bool visited; +}; + +class SubroutinePath; + +class SubroutineCall { + public: + SubroutineCall(Subroutine* subroutine, Promise* returnAddress): + subroutine(subroutine), + returnAddress(returnAddress), + paths(0), + next(subroutine->calls) + { + subroutine->calls = this; + ++ subroutine->callCount; + } + + Subroutine* subroutine; + Promise* returnAddress; + SubroutinePath* paths; + SubroutineCall* next; +}; + +class SubroutinePath { + public: + SubroutinePath(SubroutineCall* call, SubroutinePath* stackNext, + uintptr_t* rootTable): + call(call), + stackNext(stackNext), + listNext(call->paths), + rootTable(rootTable) + { + call->paths = this; + } + + SubroutineCall* call; + SubroutinePath* stackNext; + SubroutinePath* listNext; + uintptr_t* rootTable; +}; + +void +print(SubroutinePath* path) +{ + if (path) { + fprintf(stderr, " ("); + while (true) { + fprintf(stderr, "%p", reinterpret_cast + (path->call->returnAddress->value())); + path = path->stackNext; + if (path) { + fprintf(stderr, ", "); + } else { + break; + } + } + fprintf(stderr, ")"); + } +} + +class SubroutineTrace { + public: + SubroutineTrace(SubroutinePath* path, SubroutineTrace* next): + path(path), + next(next) + { } + + SubroutinePath* path; + SubroutineTrace* next; + uintptr_t map[0]; +}; class TraceElement: public TraceHandler { public: static const unsigned VirtualCall = 1 << 0; static const unsigned TailCall = 1 << 1; - TraceElement(Context* context, object target, - unsigned flags, TraceElement* next): + TraceElement(Context* context, object target, unsigned flags, + TraceElement* next): context(context), address(0), next(next), + subroutineTrace(0), + subroutineTraceCount(0), target(target), argumentIndex(0), flags(flags) @@ -519,6 +619,8 @@ class TraceElement: public TraceHandler { Context* context; Promise* address; TraceElement* next; + SubroutineTrace* subroutineTrace; + unsigned subroutineTraceCount; object target; unsigned argumentIndex; unsigned flags; @@ -548,8 +650,10 @@ enum Event { IpEvent, MarkEvent, ClearEvent, - InitEvent, - TraceEvent + PushExceptionHandlerEvent, + TraceEvent, + PushSubroutineEvent, + PopSubroutineEvent }; unsigned @@ -561,7 +665,7 @@ frameMapSizeInBits(MyThread* t, object method) unsigned frameMapSizeInWords(MyThread* t, object method) { - return ceiling(frameMapSizeInBits(t, method), BitsPerWord) * BytesPerWord; + return ceiling(frameMapSizeInBits(t, method), BitsPerWord); } uint16_t* @@ -686,11 +790,14 @@ class Context { method(method), bootContext(bootContext), objectPool(0), - objectPoolCount(0), + subroutines(0), traceLog(0), - traceLogCount(0), visitTable(makeVisitTable(t, &zone, method)), rootTable(makeRootTable(t, &zone, method)), + subroutineTable(0), + objectPoolCount(0), + traceLogCount(0), + dirtyRoots(false), eventLog(t->m->system, t->m->heap, 1024), protector(this) { } @@ -704,11 +811,14 @@ class Context { method(0), bootContext(0), objectPool(0), - objectPoolCount(0), + subroutines(0), traceLog(0), - traceLogCount(0), visitTable(0), rootTable(0), + subroutineTable(0), + objectPoolCount(0), + traceLogCount(0), + dirtyRoots(false), eventLog(t->m->system, t->m->heap, 0), protector(this) { } @@ -726,11 +836,13 @@ class Context { object method; BootContext* bootContext; PoolElement* objectPool; - unsigned objectPoolCount; + Subroutine* subroutines; TraceElement* traceLog; - unsigned traceLogCount; uint16_t* visitTable; uintptr_t* rootTable; + Subroutine** subroutineTable; + unsigned objectPoolCount; + unsigned traceLogCount; bool dirtyRoots; Vector eventLog; MyProtector protector; @@ -1055,6 +1167,10 @@ class Frame { } void startLogicalIp(unsigned ip) { + if (subroutine) { + context->subroutineTable[ip] = subroutine; + } + c->startLogicalIp(ip); context->eventLog.append(IpEvent); @@ -1309,11 +1425,76 @@ class Frame { return e; } + + unsigned startSubroutine(unsigned ip, Promise* returnAddress) { + pushAddress(addressOperand(returnAddress)); + + Subroutine* subroutine = 0; + for (Subroutine* s = context->subroutines; s; s = s->listNext) { + if (s->ip == ip) { + subroutine = s; + break; + } + } + + if (subroutine == 0) { + context->subroutines = subroutine = new + (context->zone.allocate(sizeof(Subroutine))) + Subroutine(ip, context->eventLog.length() + 1 + BytesPerWord + 2, + context->subroutines, this->subroutine); + + if (context->subroutineTable == 0) { + unsigned size = codeLength(t, methodCode(t, context->method)) + * sizeof(Subroutine*); + + context->subroutineTable = static_cast + (context->zone.allocate(size)); + + memset(context->subroutineTable, 0, size); + } + } + + subroutine->handle = c->startSubroutine(); + this->subroutine = subroutine; + + SubroutineCall* call = new + (context->zone.allocate(sizeof(SubroutineCall))) + SubroutineCall(subroutine, returnAddress); + + context->eventLog.append(PushSubroutineEvent); + context->eventLog.appendAddress(call); + + unsigned nextIndexIndex = context->eventLog.length(); + context->eventLog.append2(0); + + c->saveLocals(); + + return nextIndexIndex; + } + + void returnFromSubroutine(unsigned returnAddressLocal) { + c->endSubroutine(subroutine->handle); + subroutine->stackIndex = localOffsetFromStack + (t, translateLocalIndex(context, 1, returnAddressLocal), + context->method); + } + + void endSubroutine(unsigned nextIndexIndex) { + c->linkSubroutine(subroutine->handle); + + poppedInt(); + + context->eventLog.append(PopSubroutineEvent); + + context->eventLog.set2(nextIndexIndex, context->eventLog.length()); + + subroutine = subroutine->stackNext; + } Context* context; MyThread* t; Compiler* c; - Compiler::Subroutine* subroutine; + Subroutine* subroutine; uint8_t* stackMap; unsigned ip; unsigned sp; @@ -2066,9 +2247,9 @@ instanceOf64(Thread* t, object class_, object o) } uint64_t -makeNewWeakReference64(Thread* t, object class_) +makeNewGeneral64(Thread* t, object class_) { - return reinterpret_cast(makeNewWeakReference(t, class_)); + return reinterpret_cast(makeNewGeneral(t, class_)); } uint64_t @@ -2137,14 +2318,6 @@ pushReturnValue(MyThread* t, Frame* frame, unsigned code, } } -bool -emptyMethod(MyThread* t, object method) -{ - return ((methodFlags(t, method) & ACC_NATIVE) == 0) - and (codeLength(t, methodCode(t, method)) == 1) - and (codeBody(t, methodCode(t, method), 0) == return_); -} - Compiler::Operand* compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall, bool useThunk, unsigned rSize, Promise* addressPromise) @@ -2297,67 +2470,6 @@ handleExit(MyThread* t, Frame* frame) (t, frame, getThunk(t, releaseMonitorForObjectThunk)); } -int -exceptionIndex(MyThread* t, object code, unsigned jsrIp, unsigned dstIp) -{ - object table = codeExceptionHandlerTable(t, code); - unsigned length = exceptionHandlerTableLength(t, table); - for (unsigned i = 0; i < length; ++i) { - ExceptionHandler* eh = exceptionHandlerTableBody(t, table, i); - if (exceptionHandlerCatchType(eh) == 0) { - unsigned ip = exceptionHandlerIp(eh); - unsigned index; - switch (codeBody(t, code, ip++)) { - case astore: - index = codeBody(t, code, ip++); - break; - - case astore_0: - index = 0; - break; - - case astore_1: - index = 1; - break; - - case astore_2: - index = 2; - break; - - case astore_3: - index = 3; - break; - - default: abort(t); - } - - if (ip == jsrIp) { - return -1; - } - - switch (codeBody(t, code, ip++)) { - case jsr: { - uint32_t offset = codeReadInt16(t, code, ip); - if ((ip - 3) + offset == dstIp) { - return index; - } - } break; - - case jsr_w: { - uint32_t offset = codeReadInt32(t, code, ip); - if ((ip - 5) + offset == dstIp) { - return index; - } - } break; - - default: break; - } - } - } - - abort(t); -} - bool inTryBlock(MyThread* t, object code, unsigned ip) { @@ -3541,30 +3653,14 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, assert(t, newIp < codeLength(t, code)); - int index = exceptionIndex(t, code, thisIp, newIp); - if (index >= 0) { - // store a null pointer at the same index the exception would - // be stored in the finally block so we can safely treat that - // location as a GC root. Of course, this assumes there - // wasn't already a live value there, which is something we - // should verify once we have complete data flow information - // (todo). - storeLocal(context, 1, c->constant(0), index); - frame->storedObject(translateLocalIndex(context, 1, index)); - } - - frame->pushAddress(frame->addressOperand(c->machineIp(ip))); + unsigned start = frame->startSubroutine(newIp, c->machineIp(ip)); c->jmp(frame->machineIp(newIp)); - frame->subroutine = c->startSubroutine(); - - compile(t, frame, newIp); + saveStateAndCompile(t, frame, newIp); if (UNLIKELY(t->exception)) return; - frame->poppedInt(); - - c->restoreFromSubroutine(frame->subroutine); + frame->endSubroutine(start); } break; case l2d: { @@ -3863,10 +3959,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, object class_ = resolveClassInPool(t, codePool(t, code), index - 1); if (UNLIKELY(t->exception)) return; - if (classVmFlags(t, class_) & WeakReferenceFlag) { + if (classVmFlags(t, class_) & (WeakReferenceFlag | HasFinalizerFlag)) { frame->pushObject (c->call - (c->constant(getThunk(t, makeNewWeakReference64Thunk)), + (c->constant(getThunk(t, makeNewGeneral64Thunk)), 0, frame->trace(0, 0), BytesPerWord, @@ -4048,10 +4144,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } } break; - case ret: - c->jmp(loadLocal(context, 1, codeBody(t, code, ip))); - c->endSubroutine(frame->subroutine); - return; + case ret: { + unsigned index = codeBody(t, code, ip); + c->saveLocals(); + c->jmp(loadLocal(context, 1, index)); + frame->returnFromSubroutine(index); + } return; case return_: if (needsReturnBarrier(t, context->method)) { @@ -4170,10 +4268,11 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->storeLong(codeReadInt16(t, code, ip)); } break; - case ret: - c->jmp(loadLocal(context, 1, codeReadInt16(t, code, ip))); - c->endSubroutine(frame->subroutine); - return; + case ret: { + unsigned index = codeReadInt16(t, code, ip); + c->jmp(loadLocal(context, 1, index)); + frame->returnFromSubroutine(index); + } return; default: abort(t); } @@ -4296,7 +4395,7 @@ printSet(uintptr_t m, unsigned limit) unsigned calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, - unsigned eventIndex) + unsigned eventIndex, SubroutinePath* subroutinePath = 0) { // for each instruction with more than one predecessor, and for each // stack position, determine if there exists a path to that @@ -4328,7 +4427,8 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, Event e = static_cast(context->eventLog.get(eventIndex++)); switch (e) { case PushContextEvent: { - eventIndex = calculateFrameMaps(t, context, roots, eventIndex); + eventIndex = calculateFrameMaps + (t, context, roots, eventIndex, subroutinePath); } break; case PopContextEvent: @@ -4344,7 +4444,9 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, fprintf(stderr, "\n"); } - uintptr_t* tableRoots = context->rootTable + (ip * mapSize); + uintptr_t* tableRoots + = (subroutinePath ? subroutinePath->rootTable : context->rootTable) + + (ip * mapSize); if (context->visitTable[ip] > 1) { for (unsigned wi = 0; wi < mapSize; ++wi) { @@ -4389,12 +4491,29 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, clearBit(roots, i); } break; - case InitEvent: { + case PushExceptionHandlerEvent: { unsigned reference = context->eventLog.get2(eventIndex); eventIndex += 2; - uintptr_t* tableRoots = context->rootTable + (reference * mapSize); - memcpy(roots, tableRoots, mapSize * BytesPerWord); + if (context->subroutineTable and context->subroutineTable[reference]) { + Subroutine* s = context->subroutineTable[reference]; + unsigned originalEventIndex = eventIndex; + + for (SubroutineCall* c = s->calls; c; c = c->next) { + for (SubroutinePath* p = c->paths; p; p = p->listNext) { + memcpy(roots, p->rootTable + (reference * mapSize), + mapSize * BytesPerWord); + + eventIndex = calculateFrameMaps + (t, context, roots, originalEventIndex, p); + } + } + } else { + memcpy(roots, context->rootTable + (reference * mapSize), + mapSize * BytesPerWord); + + eventIndex = calculateFrameMaps(t, context, roots, eventIndex, 0); + } } break; case TraceEvent: { @@ -4402,14 +4521,67 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, if (DebugFrameMaps) { fprintf(stderr, "trace roots at ip %3d: ", ip); printSet(*roots, mapSize); + if (subroutinePath) { + fprintf(stderr, " "); + print(subroutinePath); + } fprintf(stderr, "\n"); } - memcpy(te->map, roots, mapSize * BytesPerWord); + if (subroutinePath == 0) { + memcpy(te->map, roots, mapSize * BytesPerWord); + } else { + SubroutineTrace* trace = 0; + for (SubroutineTrace* t = te->subroutineTrace; t; t = t->next) { + if (t->path == subroutinePath) { + trace = t; + } + } + + if (trace == 0) { + te->subroutineTrace = trace = new + (context->zone.allocate + (sizeof(SubroutineTrace) + (mapSize * BytesPerWord))) + SubroutineTrace(subroutinePath, te->subroutineTrace); + + ++ te->subroutineTraceCount; + } + + memcpy(trace->map, roots, mapSize * BytesPerWord); + } eventIndex += BytesPerWord; } break; + case PushSubroutineEvent: { + SubroutineCall* call; + context->eventLog.get(eventIndex, &call, BytesPerWord); + eventIndex += BytesPerWord; + + unsigned nextIndex = context->eventLog.get2(eventIndex); + + eventIndex = nextIndex; + + SubroutinePath* path = 0; + for (SubroutinePath* p = call->paths; p; p = p->listNext) { + if (p->stackNext == subroutinePath) { + path = p; + break; + } + } + + if (path == 0) { + path = new (context->zone.allocate(sizeof(SubroutinePath))) + SubroutinePath(call, subroutinePath, + makeRootTable(t, &(context->zone), context->method)); + } + + calculateFrameMaps(t, context, roots, call->subroutine->logIndex, path); + } break; + + case PopSubroutineEvent: + return static_cast(-1); + default: abort(t); } } @@ -4432,7 +4604,7 @@ compareTraceElementPointers(const void* va, const void* vb) } unsigned -frameObjectMapSize(MyThread* t, object method, object map) +simpleFrameMapTableSize(MyThread* t, object method, object map) { int size = frameMapSizeInBits(t, method); return ceiling(intArrayLength(t, map) * size, 32 + size); @@ -4460,21 +4632,266 @@ finish(MyThread* t, Allocator* allocator, Assembler* a, const char* name) } void -setBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i, - unsigned j) +setBit(int32_t* dst, unsigned index) { - unsigned index = ((i * size) + j); - intArrayBody(t, map, count + (index / 32)) - |= static_cast(1) << (index % 32); + dst[index / 32] |= static_cast(1) << (index % 32); } void -clearBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i, - unsigned j) +clearBit(int32_t* dst, unsigned index) { - unsigned index = ((i * size) + j); - intArrayBody(t, map, count + (index / 32)) - &= ~(static_cast(1) << (index % 32)); + dst[index / 32] &= ~(static_cast(1) << (index % 32)); +} + +void +copyFrameMap(int32_t* dst, uintptr_t* src, unsigned mapSizeInBits, + unsigned offset, TraceElement* p, + SubroutinePath* subroutinePath) +{ + if (DebugFrameMaps) { + fprintf(stderr, " orig roots at ip %p: ", reinterpret_cast + (p->address->value())); + printSet(src[0], ceiling(mapSizeInBits, BitsPerWord)); + print(subroutinePath); + fprintf(stderr, "\n"); + + fprintf(stderr, "final roots at ip %p: ", reinterpret_cast + (p->address->value())); + } + + for (unsigned j = 0; j < p->argumentIndex; ++j) { + if (getBit(src, j)) { + if (DebugFrameMaps) { + fprintf(stderr, "1"); + } + setBit(dst, offset + j); + } else { + if (DebugFrameMaps) { + fprintf(stderr, "_"); + } + clearBit(dst, offset + j); + } + } + + if (DebugFrameMaps) { + print(subroutinePath); + fprintf(stderr, "\n"); + } +} + +class FrameMapTableHeader { + public: + FrameMapTableHeader(unsigned indexCount): + indexCount(indexCount) + { } + + unsigned indexCount; +}; + +class FrameMapTableIndexElement { + public: + FrameMapTableIndexElement(int offset, unsigned base, unsigned path): + offset(offset), + base(base), + path(path) + { } + + int offset; + unsigned base; + unsigned path; +}; + +class FrameMapTablePath { + public: + FrameMapTablePath(unsigned stackIndex, unsigned elementCount, unsigned next): + stackIndex(stackIndex), + elementCount(elementCount), + next(next) + { } + + unsigned stackIndex; + unsigned elementCount; + unsigned next; + int32_t elements[0]; +}; + +int +compareInt32s(const void* va, const void* vb) +{ + return *static_cast(va) - *static_cast(vb); +} + +int +compare(SubroutinePath* a, SubroutinePath* b) +{ + if (a->stackNext) { + int d = compare(a->stackNext, b->stackNext); + if (d) return d; + } + int64_t av = a->call->returnAddress->value(); + int64_t bv = b->call->returnAddress->value(); + if (av > bv) { + return 1; + } else if (av < bv) { + return -1; + } else { + return 0; + } +} + +int +compareSubroutineTracePointers(const void* va, const void* vb) +{ + return compare((*static_cast(va))->path, + (*static_cast(vb))->path); +} + +object +makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start, + TraceElement** elements, unsigned pathFootprint, + unsigned mapCount) +{ + unsigned mapSize = frameMapSizeInBits(t, context->method); + unsigned indexOffset = sizeof(FrameMapTableHeader); + unsigned mapsOffset = indexOffset + + (context->traceLogCount * sizeof(FrameMapTableIndexElement)); + unsigned pathsOffset = mapsOffset + (ceiling(mapCount * mapSize, 32) * 4); + + object table = makeByteArray(t, pathsOffset + pathFootprint); + + int8_t* body = &byteArrayBody(t, table, 0); + new (body) FrameMapTableHeader(context->traceLogCount); + + unsigned nextTableIndex = pathsOffset; + unsigned nextMapIndex = 0; + for (unsigned i = 0; i < context->traceLogCount; ++i) { + TraceElement* p = elements[i]; + unsigned mapBase = nextMapIndex; + + unsigned pathIndex; + if (p->subroutineTrace) { + FrameMapTablePath* previous = 0; + Subroutine* subroutine = p->subroutineTrace->path->call->subroutine; + for (Subroutine* s = subroutine; s; s = s->stackNext) { + if (s->tableIndex == 0) { + unsigned pathObjectSize = sizeof(FrameMapTablePath) + + (sizeof(int32_t) * s->callCount); + + assert(t, nextTableIndex + pathObjectSize + <= byteArrayLength(t, table)); + + s->tableIndex = nextTableIndex; + + nextTableIndex += pathObjectSize; + + FrameMapTablePath* current = new (body + s->tableIndex) + FrameMapTablePath + (s->stackIndex, s->callCount, + s->stackNext ? s->stackNext->tableIndex : 0); + + unsigned i = 0; + for (SubroutineCall* c = subroutine->calls; c; c = c->next) { + assert(t, i < s->callCount); + + current->elements[i++] + = static_cast(c->returnAddress->value()) + - reinterpret_cast(start); + } + assert(t, i == s->callCount); + + qsort(current->elements, s->callCount, sizeof(int32_t), + compareInt32s); + + if (previous) { + previous->next = s->tableIndex; + } + + previous = current; + } else { + break; + } + } + + pathIndex = subroutine->tableIndex; + + SubroutineTrace* traces[p->subroutineTraceCount]; + unsigned i = 0; + for (SubroutineTrace* trace = p->subroutineTrace; + trace; trace = trace->next) + { + assert(t, i < p->subroutineTraceCount); + traces[i++] = trace; + } + assert(t, i == p->subroutineTraceCount); + + qsort(traces, p->subroutineTraceCount, sizeof(SubroutineTrace*), + compareSubroutineTracePointers); + + for (unsigned i = 0; i < p->subroutineTraceCount; ++i) { + assert(t, mapsOffset + ceiling(nextMapIndex + mapSize, 32) * 4 + <= pathsOffset); + + copyFrameMap(reinterpret_cast(body + mapsOffset), + traces[i]->map, mapSize, nextMapIndex, p, + traces[i]->path); + + nextMapIndex += mapSize; + } + } else { + pathIndex = 0; + + assert(t, mapsOffset + ceiling(nextMapIndex + mapSize, 32) * 4 + <= pathsOffset); + + copyFrameMap(reinterpret_cast(body + mapsOffset), p->map, + mapSize, nextMapIndex, p, 0); + + nextMapIndex += mapSize; + } + + unsigned elementIndex = indexOffset + + (i * sizeof(FrameMapTableIndexElement)); + + assert(t, elementIndex + sizeof(FrameMapTableIndexElement) <= mapsOffset); + + new (body + elementIndex) FrameMapTableIndexElement + (static_cast(p->address->value()) + - reinterpret_cast(start), mapBase, pathIndex); + } + + assert(t, nextMapIndex == mapCount * mapSize); + + return table; +} + +object +makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start, + TraceElement** elements) +{ + unsigned mapSize = frameMapSizeInBits(t, context->method); + object table = makeIntArray + (t, context->traceLogCount + + ceiling(context->traceLogCount * mapSize, 32)); + + assert(t, intArrayLength(t, table) == context->traceLogCount + + simpleFrameMapTableSize(t, context->method, table)); + + for (unsigned i = 0; i < context->traceLogCount; ++i) { + TraceElement* p = elements[i]; + + intArrayBody(t, table, i) = static_cast(p->address->value()) + - reinterpret_cast(start); + + assert(t, context->traceLogCount + ceiling((i + 1) * mapSize, 32) + <= intArrayLength(t, table)); + + if (mapSize) { + copyFrameMap(&intArrayBody(t, table, context->traceLogCount), p->map, + mapSize, i * mapSize, p, 0); + } + } + + return table; } uint8_t* @@ -4545,9 +4962,29 @@ finish(MyThread* t, Allocator* allocator, Context* context) if (context->traceLogCount) { TraceElement* elements[context->traceLogCount]; unsigned index = 0; + unsigned pathFootprint = 0; + unsigned mapCount = 0; for (TraceElement* p = context->traceLog; p; p = p->next) { assert(t, index < context->traceLogCount); + SubroutineTrace* trace = p->subroutineTrace; + unsigned myMapCount = 1; + if (trace) { + for (Subroutine* s = trace->path->call->subroutine; + s; s = s->stackNext) + { + unsigned callCount = s->callCount; + myMapCount *= callCount; + if (not s->visited) { + s->visited = true; + pathFootprint += sizeof(FrameMapTablePath) + + (sizeof(int32_t) * callCount); + } + } + } + + mapCount += myMapCount; + elements[index++] = p; if (p->target) { @@ -4560,47 +4997,12 @@ finish(MyThread* t, Allocator* allocator, Context* context) qsort(elements, context->traceLogCount, sizeof(TraceElement*), compareTraceElementPointers); - unsigned size = frameMapSizeInBits(t, context->method); - object map = makeIntArray - (t, context->traceLogCount - + ceiling(context->traceLogCount * size, 32)); - - assert(t, intArrayLength(t, map) == context->traceLogCount - + frameObjectMapSize(t, context->method, map)); - - for (unsigned i = 0; i < context->traceLogCount; ++i) { - TraceElement* p = elements[i]; - - intArrayBody(t, map, i) = static_cast(p->address->value()) - - reinterpret_cast(start); - - if (DebugFrameMaps) { - fprintf(stderr, " orig roots at ip %p: ", reinterpret_cast - (p->address->value())); - printSet(p->map[0], frameMapSizeInWords(t, context->method)); - fprintf(stderr, "\n"); - - fprintf(stderr, "final roots at ip %p: ", reinterpret_cast - (p->address->value())); - } - - for (unsigned j = 0; j < p->argumentIndex; ++j) { - if (getBit(p->map, j)) { - if (DebugFrameMaps) { - fprintf(stderr, "1"); - } - setBit(t, map, context->traceLogCount, size, i, j); - } else { - if (DebugFrameMaps) { - fprintf(stderr, "_"); - } - clearBit(t, map, context->traceLogCount, size, i, j); - } - } - - if (DebugFrameMaps) { - fprintf(stderr, "\n"); - } + object map; + if (pathFootprint) { + map = makeGeneralFrameMapTable + (t, context, start, elements, pathFootprint, mapCount); + } else { + map = makeSimpleFrameMapTable(t, context, start, elements); } set(t, methodCode(t, context->method), CodePool, map); @@ -4720,7 +5122,7 @@ compile(MyThread* t, Allocator* allocator, Context* context) uint8_t stackMap[codeMaxStack(t, methodCode(t, context->method))]; Frame frame2(&frame, stackMap); - context->eventLog.append(InitEvent); + context->eventLog.append(PushExceptionHandlerEvent); context->eventLog.append2(start); for (unsigned i = 1; @@ -4733,6 +5135,8 @@ compile(MyThread* t, Allocator* allocator, Context* context) compile(t, &frame2, exceptionHandlerIp(eh), start); if (UNLIKELY(t->exception)) return 0; + context->eventLog.append(PopContextEvent); + eventIndex = calculateFrameMaps(t, context, 0, eventIndex); } } @@ -5136,21 +5540,24 @@ invokeNative(MyThread* t) } } -unsigned -frameMapIndex(MyThread* t, object method, int32_t offset) +void +findFrameMapInSimpleTable(MyThread* t, object method, object table, + int32_t offset, int32_t** map, unsigned* start) { - object map = codePool(t, methodCode(t, method)); - unsigned mapSize = frameObjectMapSize(t, method, map); - unsigned indexSize = intArrayLength(t, map) - mapSize; + unsigned tableSize = simpleFrameMapTableSize(t, method, table); + unsigned indexSize = intArrayLength(t, table) - tableSize; + + *map = &intArrayBody(t, table, indexSize); unsigned bottom = 0; unsigned top = indexSize; for (unsigned span = top - bottom; span; span = top - bottom) { unsigned middle = bottom + (span / 2); - int32_t v = intArrayBody(t, map, middle); + int32_t v = intArrayBody(t, table, middle); if (offset == v) { - return (indexSize * 32) + (frameMapSizeInBits(t, method) * middle); + *start = frameMapSizeInBits(t, method) * middle; + return; } else if (offset < v) { top = middle; } else { @@ -5161,6 +5568,77 @@ frameMapIndex(MyThread* t, object method, int32_t offset) abort(t); } +unsigned +findFrameMap(MyThread* t, void* stack, object method, object table, + unsigned pathIndex) +{ + if (pathIndex) { + FrameMapTablePath* path = reinterpret_cast + (&byteArrayBody(t, table, pathIndex)); + + void* address = static_cast(stack)[path->stackIndex]; + uint8_t* base = reinterpret_cast(methodAddress(t, method)); + for (unsigned i = 0; i < path->elementCount; ++i) { + if (address == base + path->elements[i]) { + return i + (path->elementCount * findFrameMap + (t, stack, method, table, path->next)); + } + } + + abort(t); + } else { + return 0; + } +} + +void +findFrameMapInGeneralTable(MyThread* t, void* stack, object method, + object table, int32_t offset, int32_t** map, + unsigned* start) +{ + FrameMapTableHeader* header = reinterpret_cast + (&byteArrayBody(t, table, 0)); + + FrameMapTableIndexElement* index + = reinterpret_cast + (&byteArrayBody(t, table, sizeof(FrameMapTableHeader))); + + *map = reinterpret_cast(index + header->indexCount); + + unsigned bottom = 0; + unsigned top = header->indexCount; + for (unsigned span = top - bottom; span; span = top - bottom) { + unsigned middle = bottom + (span / 2); + FrameMapTableIndexElement* v = index + middle; + + if (offset == v->offset) { + *start = v->base + (findFrameMap(t, stack, method, table, v->path) + * frameMapSizeInBits(t, method)); + return; + } else if (offset < v->offset) { + top = middle; + } else { + bottom = middle + 1; + } + } + + abort(t); +} + +void +findFrameMap(MyThread* t, void* stack, object method, int32_t offset, + int32_t** map, unsigned* start) +{ + object table = codePool(t, methodCode(t, method)); + if (objectClass(t, table) + == arrayBody(t, t->m->types, Machine::IntArrayType)) + { + findFrameMapInSimpleTable(t, method, table, offset, map, start); + } else { + findFrameMapInGeneralTable(t, stack, method, table, offset, map, start); + } +} + void visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* frame, object method, void* ip) @@ -5168,18 +5646,17 @@ visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* frame, object method, unsigned count = frameMapSizeInBits(t, method); if (count) { - object map = codePool(t, methodCode(t, method)); - int index = frameMapIndex - (t, method, difference - (ip, reinterpret_cast(methodAddress(t, method)))); - void* stack = stackForFrame(t, frame, method); + int32_t* map; + unsigned offset; + findFrameMap + (t, stack, method, difference + (ip, reinterpret_cast(methodAddress(t, method))), &map, &offset); + for (unsigned i = 0; i < count; ++i) { - int j = index + i; - if ((intArrayBody(t, map, j / 32) - & (static_cast(1) << (j % 32)))) - { + int j = offset + i; + if (map[j / 32] & (static_cast(1) << (j % 32))) { v->visit(localObject(t, stack, method, i)); } } @@ -5290,17 +5767,16 @@ walkContinuationBody(MyThread* t, Heap::Walker* w, object c, int start) count -= start - first; } - object map = codePool(t, methodCode(t, method)); - int index = frameMapIndex - (t, method, difference + int32_t* map; + unsigned offset; + findFrameMap + (t, reinterpret_cast(c) + stack, method, difference (continuationAddress(t, c), - reinterpret_cast(methodAddress(t, method)))); + reinterpret_cast(methodAddress(t, method))), &map, &offset); for (int i = count - 1; i >= 0; --i) { - int j = index + i; - if ((intArrayBody(t, map, j / 32) - & (static_cast(1) << (j % 32)))) - { + int j = offset + i; + if (map[j / 32] & (static_cast(1) << (j % 32))) { if (not w->visit(stack + localOffsetFromStack(t, i, method))) { return; } @@ -5479,15 +5955,11 @@ callContinuation(MyThread* t, object continuation, object result, if (rewindMethod(t) == 0) { PROTECT(t, nextContinuation); - - const char* const className = "avian/Continuations"; - const char* const methodName = "rewind"; - const char* const methodSpec - = "(Ljava/lang/Runnable;Lavian/Callback;Ljava/lang/Object;" - "Ljava/lang/Throwable;)V"; object method = resolveMethod - (t, className, methodName, methodSpec); + (t, "avian/Continuations", "rewind", + "(Ljava/lang/Runnable;Lavian/Callback;Ljava/lang/Object;" + "Ljava/lang/Throwable;)V"); if (method) { rewindMethod(t) = method; @@ -5498,11 +5970,6 @@ callContinuation(MyThread* t, object continuation, object result, action = Throw; } } else { - object message = makeString - (t, "%s %s not found in %s", - methodName, methodSpec, className); - - t->exception = makeNoSuchMethodError(t, message); action = Throw; } } @@ -5560,21 +6027,13 @@ callWithCurrentContinuation(MyThread* t, object receiver) { PROTECT(t, receiver); if (receiveMethod(t) == 0) { - const char* const className = "avian/CallbackReceiver"; - const char* const methodName = "receive"; - const char* const methodSpec = "(Lavian/Callback;)Ljava/lang/Object;"; - - object m = resolveMethod(t, className, methodName, methodSpec); + object m = resolveMethod + (t, "avian/CallbackReceiver", "receive", + "(Lavian/Callback;)Ljava/lang/Object;"); if (m) { receiveMethod(t) = m; - } else { - object message = makeString - (t, "%s %s not found in %s", methodName, methodSpec, className); - t->exception = makeNoSuchMethodError(t, message); - } - if (LIKELY(t->exception == 0)) { object continuationClass = arrayBody (t, t->m->types, Machine::ContinuationType); @@ -5616,21 +6075,14 @@ dynamicWind(MyThread* t, object before, object thunk, object after) PROTECT(t, after); if (windMethod(t) == 0) { - const char* const className = "avian/Continuations"; - const char* const methodName = "wind"; - const char* const methodSpec - = "(Ljava/lang/Runnable;Ljava/util/concurrent/Callable;" - "Ljava/lang/Runnable;)Lavian/Continuations$UnwindResult;"; - - object method = resolveMethod(t, className, methodName, methodSpec); + object method = resolveMethod + (t, "avian/Continuations", "wind", + "(Ljava/lang/Runnable;Ljava/util/concurrent/Callable;" + "Ljava/lang/Runnable;)Lavian/Continuations$UnwindResult;"); if (method) { windMethod(t) = method; compile(t, ::codeAllocator(t), 0, method); - } else { - object message = makeString - (t, "%s %s not found in %s", methodName, methodSpec, className); - t->exception = makeNoSuchMethodError(t, message); } } @@ -5992,22 +6444,6 @@ class MyProcessor: public Processor { } } - virtual void - initClass(Thread* t, object c) - { - PROTECT(t, c); - - ACQUIRE(t, t->m->classLock); - if (classNeedsInit(t, c)) { - classVmFlags(t, c) |= InitFlag; - invoke(t, classInitializer(t, c), 0); - if (t->exception) { - t->exception = makeExceptionInInitializerError(t, t->exception); - } - classVmFlags(t, c) &= ~(NeedInitFlag | InitFlag); - } - } - virtual void visitObjects(Thread* vmt, Heap::Visitor* v) { diff --git a/src/compiler.cpp b/src/compiler.cpp index 79cf2dacf6..85fbc379d6 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -2052,7 +2052,7 @@ class MultiRead: public Read { } lastRead = cell; - // fprintf(stderr, "append %p to %p for %p\n", r, lastTarget, this); +// fprintf(stderr, "append %p to %p for %p\n", r, lastTarget, this); lastTarget->value = r; } @@ -2064,7 +2064,7 @@ class MultiRead: public Read { void allocateTarget(Context* c) { Cell* cell = cons(c, 0, 0); - // fprintf(stderr, "allocate target for %p: %p\n", this, cell); +// fprintf(stderr, "allocate target for %p: %p\n", this, cell); if (lastTarget) { lastTarget->next = cell; @@ -4016,6 +4016,31 @@ appendSaveLocals(Context* c) SaveLocalsEvent(c)); } +class CleanLocalsEvent: public Event { + public: + CleanLocalsEvent(Context* c): + Event(c) + { } + + virtual const char* name() { + return "CleanLocalsEvent"; + } + + virtual void compile(Context* c) { + for (FrameIterator it(c, 0, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + clean(c, e.value, 0); + } + } +}; + +void +appendCleanLocals(Context* c) +{ + append(c, new (c->zone->allocate(sizeof(CleanLocalsEvent))) + CleanLocalsEvent(c)); +} + class DummyEvent: public Event { public: DummyEvent(Context* c): @@ -4927,11 +4952,12 @@ class MyCompiler: public Compiler { } virtual void endSubroutine(Subroutine* subroutine) { + appendCleanLocals(&c); static_cast(subroutine)->forkState = ::saveState(&c); } - virtual void restoreFromSubroutine(Subroutine* subroutine) { - ::restoreState(&c, static_cast(subroutine)->forkState); + virtual void linkSubroutine(Subroutine* subroutine) { + restoreState(static_cast(subroutine)->forkState); } virtual void init(unsigned logicalCodeLength, unsigned parameterFootprint, @@ -5032,12 +5058,30 @@ class MyCompiler: public Compiler { (c.zone->allocate(sizeof(LogicalInstruction))) LogicalInstruction(logicalIp, c.stack, c.locals); - if (c.subroutine) { + bool startSubroutine = c.subroutine != 0; + if (startSubroutine) { c.logicalCode[logicalIp]->subroutine = c.subroutine; c.subroutine = 0; } c.logicalIp = logicalIp; + + if (startSubroutine) { + // assume all local variables are initialized on entry to a + // subroutine, since other calls to the subroutine may + // initialize them: + unsigned sizeInBytes = sizeof(Local) * c.localFootprint; + Local* newLocals = static_cast(c.zone->allocate(sizeInBytes)); + memcpy(newLocals, c.locals, sizeInBytes); + c.locals = newLocals; + + for (unsigned li = 0; li < c.localFootprint; ++li) { + Local* local = c.locals + li; + if (local->value == 0) { + initLocal(1, li); + } + } + } } virtual Promise* machineIp(unsigned logicalIp) { diff --git a/src/compiler.h b/src/compiler.h index 9e28d57b5a..8c40def7cc 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -43,7 +43,7 @@ class Compiler { virtual Subroutine* startSubroutine() = 0; virtual void endSubroutine(Subroutine* subroutine) = 0; - virtual void restoreFromSubroutine(Subroutine* subroutine) = 0; + virtual void linkSubroutine(Subroutine* subroutine) = 0; virtual void init(unsigned logicalCodeSize, unsigned parameterFootprint, unsigned localFootprint, unsigned alignedFrameSize) = 0; diff --git a/src/continuations-x86.S b/src/continuations-x86.S new file mode 100644 index 0000000000..bc19003758 --- /dev/null +++ b/src/continuations-x86.S @@ -0,0 +1,161 @@ +#ifdef __x86_64__ + +#define THREAD_CONTINUATION 168 +#define THREAD_EXCEPTION 64 +#define THREAD_EXCEPTION_STACK_ADJUSTMENT 176 +#define THREAD_EXCEPTION_OFFSET 184 +#define THREAD_EXCEPTION_HANDLER 192 + +#define CONTINUATION_NEXT 8 +#define CONTINUATION_ADDRESS 32 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 40 +#define CONTINUATION_FRAME_POINTER_OFFSET 48 +#define CONTINUATION_LENGTH 56 +#define CONTINUATION_BODY 64 + + // call the next continuation, if any + movq THREAD_CONTINUATION(%rbx),%rcx + cmpq $0,%rcx + je LOCAL(vmInvoke_exit) + + // allocate a frame of size (continuation.length * BYTES_PER_WORD) + // + CALLEE_SAVED_REGISTER_FOOTPRINT + movq CONTINUATION_LENGTH(%rcx),%rsi + shlq $3,%rsi + subq %rsi,%rsp + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // copy the continuation body into the frame + leaq CONTINUATION_BODY(%rcx),%rdi + + movq $0,%r9 + jmp LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + movq (%rdi,%r9,1),%r8 + movq %r8,(%rsp,%r9,1) + addq $8,%r9 + +LOCAL(vmInvoke_continuationTest): + cmpq %rsi,%r9 + jb LOCAL(vmInvoke_continuationLoop) + + // set the return address to vmInvoke_returnAddress + movq CONTINUATION_RETURN_ADDRESS_OFFSET(%rcx),%rdi + movq vmInvoke_returnAddress@GOTPCREL(%rip),%r10 + movq %r10,(%rsp,%rdi,1) + + // save the current base pointer in the frame and update it + movq CONTINUATION_FRAME_POINTER_OFFSET(%rcx),%rdi + movq %rbp,(%rsp,%rdi,1) + addq %rsp,%rdi + movq %rdi,%rbp + + // consume the continuation + movq CONTINUATION_NEXT(%rcx),%rdi + movq %rdi,THREAD_CONTINUATION(%rbx) + + // call the continuation unless we're handling an exception + movq THREAD_EXCEPTION(%rbx),%rsi + cmpq $0,%rsi + jne LOCAL(vmInvoke_handleException) + jmp *CONTINUATION_ADDRESS(%rcx) + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + movq $0,THREAD_EXCEPTION(%rbx) + movq THREAD_EXCEPTION_STACK_ADJUSTMENT(%rbx),%rdi + subq %rdi,%rsp + movq THREAD_EXCEPTION_OFFSET(%rbx),%rdi + movq %rsi,(%rsp,%rdi,1) + + jmp *THREAD_EXCEPTION_HANDLER(%rbx) + +LOCAL(vmInvoke_exit): + +#elif defined __i386__ + +#define THREAD_CONTINUATION 96 +#define THREAD_EXCEPTION 36 +#define THREAD_EXCEPTION_STACK_ADJUSTMENT 100 +#define THREAD_EXCEPTION_OFFSET 104 +#define THREAD_EXCEPTION_HANDLER 108 + +#define CONTINUATION_NEXT 4 +#define CONTINUATION_ADDRESS 16 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 20 +#define CONTINUATION_FRAME_POINTER_OFFSET 24 +#define CONTINUATION_LENGTH 28 +#define CONTINUATION_BODY 32 + + // call the next continuation, if any + movl THREAD_CONTINUATION(%ebx),%ecx + cmpl $0,%ecx + je LOCAL(vmInvoke_exit) + + // allocate a frame of size (continuation.length * BYTES_PER_WORD) + movl CONTINUATION_LENGTH(%ecx),%esi + shll $2,%esi + subl %esi,%esp + + // copy the continuation body into the frame + leal CONTINUATION_BODY(%ecx),%edi + + push %eax + push %edx + + movl $0,%edx + jmp LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + movl (%edi,%edx,1),%eax + movl %eax,8(%esp,%edx,1) + addl $4,%edx + +LOCAL(vmInvoke_continuationTest): + cmpl %esi,%edx + jb LOCAL(vmInvoke_continuationLoop) + + pop %edx + pop %eax + + // set the return address to vmInvoke_returnAddress + movl CONTINUATION_RETURN_ADDRESS_OFFSET(%ecx),%edi + call LOCAL(getPC) + addl $_GLOBAL_OFFSET_TABLE_,%esi + movl vmInvoke_returnAddress@GOT(%esi),%esi + movl %esi,(%esp,%edi,1) + + // save the current base pointer in the frame and update it + movl CONTINUATION_FRAME_POINTER_OFFSET(%ecx),%edi + movl %ebp,(%esp,%edi,1) + addl %esp,%edi + movl %edi,%ebp + + // consume the continuation + movl CONTINUATION_NEXT(%ecx),%edi + movl %edi,THREAD_CONTINUATION(%ebx) + + // call the continuation unless we're handling an exception + movl THREAD_EXCEPTION(%ebx),%esi + cmpl $0,%esi + jne LOCAL(vmInvoke_handleException) + + jmp *CONTINUATION_ADDRESS(%ecx) + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + movl $0,THREAD_EXCEPTION(%ebx) + movl THREAD_EXCEPTION_STACK_ADJUSTMENT(%ebx),%edi + subl %edi,%esp + movl THREAD_EXCEPTION_OFFSET(%ebx),%edi + movl %esi,(%esp,%edi,1) + + jmp *THREAD_EXCEPTION_HANDLER(%ebx) + +LOCAL(vmInvoke_exit): + +#else +# error unsupported architecture +#endif + diff --git a/src/gnu.cpp b/src/gnu.cpp new file mode 100644 index 0000000000..0b27ffbc72 --- /dev/null +++ b/src/gnu.cpp @@ -0,0 +1,394 @@ +/* Copyright (c) 2009, 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" +#include "constants.h" +#include "processor.h" + +using namespace vm; + +namespace { + +void +setProperty(Thread* t, object method, object properties, + const char* name, const void* value, const char* format = "%s") +{ + PROTECT(t, method); + PROTECT(t, properties); + + object n = makeString(t, "%s", name); + PROTECT(t, n); + + object v = makeString(t, format, value); + + t->m->processor->invoke(t, method, properties, n, v); +} + +} // namespace + +namespace vm { + +jobject JNICALL +NewDirectByteBuffer(Thread* t, void* address, jlong capacity) +{ + const char* pointerClassName; + const char* initSpec; + if (BytesPerWord == 8) { + pointerClassName = "gnu/classpath/Pointer64"; + initSpec = "(J)V"; + } else { + pointerClassName = "gnu/classpath/Pointer32"; + initSpec = "(I)V"; + } + + object pointerClass = resolveClass(t, pointerClassName); + if (UNLIKELY(pointerClass == 0)) return 0; + PROTECT(t, pointerClass); + + object pointerConstructor = resolveMethod + (t, pointerClass, "", initSpec); + if (UNLIKELY(pointerConstructor == 0)) return 0; + + object pointer = make(t, pointerClass); + PROTECT(t, pointer); + + t->m->processor->invoke(t, pointerConstructor, pointer, address); + if (UNLIKELY(t->exception)) return 0; + + object bufferClass = resolveClass + (t, "java/nio/DirectByteBufferImpl$ReadWrite"); + if (UNLIKELY(bufferClass == 0)) return 0; + PROTECT(t, bufferClass); + + object bufferConstructor = resolveMethod + (t, bufferClass, "", "(Lgnu/classpath/Pointer;int)V"); + if (UNLIKELY(bufferConstructor == 0)) return 0; + + object buffer = make(t, bufferClass); + PROTECT(t, buffer); + + t->m->processor->invoke + (t, bufferConstructor, buffer, &pointer, static_cast(capacity)); + if (UNLIKELY(t->exception)) return 0; + + return makeLocalReference(t, buffer); +} + +void* JNICALL +GetDirectBufferAddress(Thread* t, jobject buffer) +{ + object addressField = resolveField + (t, objectClass(t, *buffer), "address", "Lgnu/classpath/Pointer;"); + if (UNLIKELY(addressField == 0)) return 0; + + object address = cast(*buffer, fieldOffset(t, addressField)); + if (address == 0) return 0; + + const char* dataSpec; + if (BytesPerWord == 8) { + dataSpec = "J"; + } else { + dataSpec = "I"; + } + + object dataField = resolveField + (t, objectClass(t, address), "data", dataSpec); + if (UNLIKELY(dataField == 0)) return 0; + + return cast(address, fieldOffset(t, dataField)); +} + +jlong JNICALL +GetDirectBufferCapacity(Thread* t, jobject buffer) +{ + object capField = resolveField(t, objectClass(t, *buffer), "cap", "I"); + if (UNLIKELY(capField == 0)) return 0; + + return cast(*buffer, fieldOffset(t, capField)); +} + +} // namespace vm + +extern "C" JNIEXPORT void JNICALL +Avian_gnu_classpath_VMSystemProperties_preInit +(Thread* t, object, uintptr_t* arguments) +{ + object properties = reinterpret_cast(arguments[0]); + PROTECT(t, properties); + + object method = resolveMethod + (t, "java/util/Properties", "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"); + + if (UNLIKELY(t->exception)) { + return; + } + + PROTECT(t, method); + + setProperty(t, method, properties, "java.vm.name", "Avian"); + + setProperty(t, method, properties, "java.protocol.handler.pkgs", "avian"); + + setProperty(t, method, properties, "file.encoding", "ASCII"); + + // specify a bogus library path so we can do our own search in + // VMRuntime.nativeLoad: +#define LIBRARY_PATH_SENTINAL "*" + setProperty(t, method, properties, "java.library.path", + LIBRARY_PATH_SENTINAL); + +#ifdef WIN32 +# define FILE_SEPARATOR "\\" + + setProperty(t, method, properties, "line.separator", "\r\n"); + setProperty(t, method, properties, "file.separator", FILE_SEPARATOR); + setProperty(t, method, properties, "path.separator", ";"); + setProperty(t, method, properties, "os.name", "Windows"); + + TCHAR buffer[MAX_PATH]; + GetTempPath(MAX_PATH, buffer); + setProperty(t, method, properties, "java.io.tmpdir", buffer); + + setProperty(t, method, properties, "user.home", + _wgetenv(L"USERPROFILE"), "%ls"); + + GetCurrentDirectory(MAX_PATH, buffer); + setProperty(t, method, properties, "user.dir", buffer); +#else +# define FILE_SEPARATOR "/" + + setProperty(t, method, properties, "line.separator", "\n"); + setProperty(t, method, properties, "file.separator", FILE_SEPARATOR); + setProperty(t, method, properties, "path.separator", ":"); +# ifdef __APPLE__ + setProperty(t, method, properties, "os.name", "Mac OS X"); +# else + setProperty(t, method, properties, "os.name", "Linux"); +# endif + setProperty(t, method, properties, "java.io.tmpdir", "/tmp"); + setProperty(t, method, properties, "user.home", getenv("HOME")); + setProperty(t, method, properties, "user.dir", getenv("PWD")); +#endif +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_gnu_classpath_VMStackWalker_getClassContext +(Thread* t, object, uintptr_t*) +{ + class Visitor: public Processor::StackVisitor { + public: + Visitor(Thread* t): + t(t), skipCount(1), trace(0), index(0), protector(t, &trace) + { } + + virtual bool visit(Processor::StackWalker* walker) { + if (skipCount == 0) { + if (trace == 0) { + trace = makeObjectArray + (t, arrayBody(t, t->m->types, Machine::ClassType), + walker->count()); + } + + assert(t, index < objectArrayLength(t, trace)); + + set(t, trace, ArrayBody + (index * BytesPerWord), + methodClass(t, walker->method())); + + ++ index; + return true; + } else { + -- skipCount; + return true; + } + } + + Thread* t; + unsigned skipCount; + object trace; + unsigned index; + Thread::SingleProtector protector; + } v(t); + + t->m->processor->walkStack(t, &v); + + if (v.trace == 0) { + v.trace = makeObjectArray + (t, arrayBody(t, t->m->types, Machine::ClassType), 0); + } + + return reinterpret_cast(v.trace); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_gnu_classpath_VMStackWalker_getClassLoader +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (classLoader(t, reinterpret_cast(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMRuntime_mapLibraryName +(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + PROTECT(t, name); + + const unsigned soPrefixLength = sizeof(SO_PREFIX) - 1; + const unsigned nameLength = stringLength(t, name); + const unsigned soSuffixLength = sizeof(SO_SUFFIX) - 1; + const unsigned total = soPrefixLength + nameLength + soSuffixLength; + + object s = makeByteArray(t, total + 1); + char* p = reinterpret_cast(&byteArrayBody(t, s, 0)); + + memcpy(p, SO_PREFIX, soPrefixLength); + stringChars(t, name, p + soPrefixLength); + memcpy(p + soPrefixLength + nameLength, SO_SUFFIX, soSuffixLength); + p[total] = 0; + + return reinterpret_cast(makeString(t, s, 0, total, 0)); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_System_arraycopy +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMSystem_arraycopy +(Thread* t, object, uintptr_t* arguments) +{ + Avian_java_lang_System_arraycopy(t, 0, arguments); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Runtime_load +(Thread* t, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMRuntime_nativeLoad +(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + + // given that we set java.library.path to LIBRARY_PATH_SENTINAL, we + // can determine which names are filenames and which are library + // names by looking for the prefix LIBRARY_PATH_SENTINAL + // FILE_SEPARATOR + + unsigned length = stringLength(t, name); + char n[length + 1]; + stringChars(t, name, n); + + const unsigned pathPrefixLength + = sizeof(LIBRARY_PATH_SENTINAL) - 1 + + sizeof(FILE_SEPARATOR) - 1; + + bool mapName = (strncmp(n, LIBRARY_PATH_SENTINAL FILE_SEPARATOR, + pathPrefixLength) == 0); + if (mapName) { + // strip the path prefix, SO prefix, and SO suffix before passing + // the name to Runtime.load + + const unsigned soPrefixLength = sizeof(SO_PREFIX) - 1; + const unsigned soSuffixLength = sizeof(SO_SUFFIX) - 1; + const unsigned newOffset + = stringOffset(t, name) + pathPrefixLength + soPrefixLength; + const unsigned newLength + = length - pathPrefixLength - soPrefixLength - soSuffixLength; + + name = makeString(t, stringData(t, name), newOffset, newLength, 0); + } + + uintptr_t args[] = { reinterpret_cast(name), mapName }; + + Avian_java_lang_Runtime_load(t, 0, args); + + if (t->exception) { + t->exception = 0; + return 0; + } else { + return 1; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_primitiveClass +(Thread* t, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_getPrimitiveClass +(Thread* t, object, uintptr_t* arguments) +{ + return Avian_java_lang_Class_primitiveClass(t, 0, arguments); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_identityHashCode +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMSystem_identityHashCode +(Thread* t, object, uintptr_t* arguments) +{ + return Avian_java_lang_System_identityHashCode(t, 0, arguments); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Runtime_gc +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMRuntime_gc +(Thread* t, object, uintptr_t*) +{ + Avian_java_lang_Runtime_gc(t, 0, 0); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMRuntime_runFinalizationForExit +(Thread*, object, uintptr_t*) +{ + // ignore +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMRuntime_exit +(Thread*, object, uintptr_t* arguments) +{ + exit(arguments[0]); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findClass +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_loadClass +(Thread* t, object, uintptr_t* arguments) +{ + uintptr_t args[] = { 0, arguments[0] }; + + return Avian_avian_SystemClassLoader_findClass(t, 0, args); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findLoadedClass +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_findLoadedClass +(Thread* t, object, uintptr_t* arguments) +{ + uintptr_t args[] = { 0, arguments[1] }; + + return Avian_avian_SystemClassLoader_findLoadedClass(t, 0, args); +} diff --git a/src/heapwalk.cpp b/src/heapwalk.cpp index f6e2b746fe..12128a6dd1 100644 --- a/src/heapwalk.cpp +++ b/src/heapwalk.cpp @@ -88,7 +88,7 @@ class Context { while (stack) { Stack* dead = stack; stack = dead->next; - thread->m->heap->free(stack, sizeof(Stack)); + thread->m->heap->free(dead, sizeof(Stack)); } } diff --git a/src/interpret.cpp b/src/interpret.cpp index 1f14762a5f..7a51fc997a 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -26,6 +26,8 @@ const unsigned FrameMethodOffset = 2; const unsigned FrameIpOffset = 3; const unsigned FrameFootprint = 4; +class ClassInitList; + class Thread: public vm::Thread { public: static const unsigned StackSizeInBytes = 64 * 1024; @@ -36,16 +38,39 @@ class Thread: public vm::Thread { ip(0), sp(0), frame(-1), - code(0) + code(0), + classInitList(0) { } unsigned ip; unsigned sp; int frame; object code; + ClassInitList* classInitList; uintptr_t stack[StackSizeInWords]; }; +class ClassInitList { + public: + ClassInitList(Thread* t, object class_, ClassInitList* next): + t(t), class_(class_), next(next) + { } + + static void push(Thread* t, object class_) { + t->classInitList = new (t->m->heap->allocate(sizeof(ClassInitList))) + ClassInitList(t, class_, t->classInitList); + } + + void pop() { + t->classInitList = next; + t->m->heap->free(this, sizeof(ClassInitList)); + } + + Thread* t; + object class_; + ClassInitList* next; +}; + inline void pushObject(Thread* t, object o) { @@ -348,12 +373,14 @@ popFrame(Thread* t) } } - if (UNLIKELY(methodVmFlags(t, method) & ClassInitFlag)) { - if (t->exception) { - t->exception = makeExceptionInInitializerError(t, t->exception); - } - classVmFlags(t, methodClass(t, method)) &= ~(NeedInitFlag | InitFlag); - release(t, t->m->classLock); + if (UNLIKELY(methodVmFlags(t, method) & ClassInitFlag) + and t->classInitList) + { + assert(t, t->classInitList->class_ == methodClass(t, method)); + + t->classInitList->pop(); + + postInitClass(t, methodClass(t, method)); } t->sp = frameBase(t, t->frame); @@ -732,17 +759,21 @@ invokeNative(Thread* t, object method) bool classInit2(Thread* t, object class_, unsigned ipOffset) { + for (ClassInitList* list = t->classInitList; list; list = list->next) { + if (list->class_ == class_) { + return false; + } + } + PROTECT(t, class_); - acquire(t, t->m->classLock); - if (classVmFlags(t, class_) & NeedInitFlag - and (classVmFlags(t, class_) & InitFlag) == 0) - { - classVmFlags(t, class_) |= InitFlag; + + if (preInitClass(t, class_)) { + ClassInitList::push(t, class_); + t->code = classInitializer(t, class_); t->ip -= ipOffset; return true; } else { - release(t, t->m->classLock); return false; } } @@ -3070,22 +3101,6 @@ class MyProcessor: public Processor { // ignore } - virtual void - initClass(vm::Thread* t, object c) - { - PROTECT(t, c); - - acquire(t, t->m->classLock); - if (classVmFlags(t, c) & NeedInitFlag - and (classVmFlags(t, c) & InitFlag) == 0) - { - classVmFlags(t, c) |= InitFlag; - invoke(t, classInitializer(t, c), 0); - } else { - release(t, t->m->classLock); - } - } - virtual void visitObjects(vm::Thread* vmt, Heap::Visitor* v) { @@ -3098,6 +3113,10 @@ class MyProcessor: public Processor { v->visit(reinterpret_cast(t->stack + (i * 2) + 1)); } } + + for (ClassInitList* list = t->classInitList; list; list = list->next) { + v->visit(reinterpret_cast(&(list->class_))); + } } virtual void diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 5139f09e58..3f9d30b4b8 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -62,8 +62,10 @@ AttachCurrentThread(Machine* m, Thread** t, void*) *t = static_cast(m->localThread->get()); if (*t == 0) { *t = m->processor->makeThread(m, 0, m->rootThread); + m->system->attach(&((*t)->runnable)); enter(*t, Thread::ActiveState); + enter(*t, Thread::IdleState); m->localThread->set(*t); } @@ -264,6 +266,26 @@ ExceptionCheck(Thread* t) return t->exception != 0; } +#ifndef AVIAN_GNU +jobject JNICALL +NewDirectByteBuffer(Thread*, void*, jlong) +{ + return 0; +} + +void* JNICALL +GetDirectBufferAddress(Thread*, jobject) +{ + return 0; +} + +jlong JNICALL +GetDirectBufferCapacity(Thread*, jobject) +{ + return -1; +} +#endif// not AVIAN_GNU + jclass JNICALL GetObjectClass(Thread* t, jobject o) { @@ -823,22 +845,12 @@ CallStaticVoidMethod(Thread* t, jclass c, jmethodID m, ...) va_end(a); } -object -findField(Thread* t, jclass c, const char* name, const char* spec) -{ - object n = makeByteArray(t, "%s", name); - PROTECT(t, n); - - object s = makeByteArray(t, "%s", spec); - return vm::findField(t, *c, n, s); -} - jfieldID JNICALL GetFieldID(Thread* t, jclass c, const char* name, const char* spec) { ENTER(t, Thread::ActiveState); - object field = findField(t, c, name, spec); + object field = resolveField(t, *c, name, spec); if (UNLIKELY(t->exception)) return 0; return fieldOffset(t, field); @@ -849,7 +861,7 @@ GetStaticFieldID(Thread* t, jclass c, const char* name, const char* spec) { ENTER(t, Thread::ActiveState); - object field = findField(t, c, name, spec); + object field = resolveField(t, *c, name, spec); if (UNLIKELY(t->exception)) return 0; return fieldOffset(t, field); @@ -1889,6 +1901,17 @@ append(char** p, const char* value, unsigned length, char tail) namespace vm { +#ifdef AVIAN_GNU +jobject JNICALL +NewDirectByteBuffer(Thread*, void*, jlong); + +void* JNICALL +GetDirectBufferAddress(Thread*, jobject); + +jlong JNICALL +GetDirectBufferCapacity(Thread*, jobject); +#endif//AVIAN_GNU + void populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) { @@ -1914,6 +1937,9 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) envTable->FindClass = ::FindClass; envTable->ThrowNew = ::ThrowNew; envTable->ExceptionCheck = ::ExceptionCheck; + envTable->NewDirectByteBuffer = ::NewDirectByteBuffer; + envTable->GetDirectBufferAddress = ::GetDirectBufferAddress; + envTable->GetDirectBufferCapacity = ::GetDirectBufferCapacity; envTable->DeleteLocalRef = ::DeleteLocalRef; envTable->GetObjectClass = ::GetObjectClass; envTable->IsInstanceOf = ::IsInstanceOf; diff --git a/src/machine.cpp b/src/machine.cpp index 574b5ffd99..7b3a0ad0c6 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -481,6 +481,33 @@ postCollect(Thread* t) } } +void +finalizeObject(Thread* t, object o) +{ + if (t->state == Thread::ExitState) { + // don't waste time running Java finalizers if we're exiting the + // VM + return; + } + + for (object c = objectClass(t, o); c; c = classSuper(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object m = arrayBody(t, classMethodTable(t, c), i); + + if (strcmp(reinterpret_cast("finalize"), + &byteArrayBody(t, methodName(t, m), 0)) == 0 + and strcmp(reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, m), 0)) == 0) + { + t->m->processor->invoke(t, m, o); + t->exception = 0; + return; + } + } + } + abort(t); +} + object makeByteArray(Thread* t, const char* format, va_list a) { @@ -497,21 +524,31 @@ makeByteArray(Thread* t, const char* format, va_list a) } object -parseUtf8(Thread* t, Stream& s, unsigned length) +parseUtf8NonAscii(Thread* t, Stream& s, object bytesSoFar, unsigned byteCount, + unsigned sourceIndex, unsigned lastByteRead) { - object value = makeByteArray(t, length + 1); + PROTECT(t, bytesSoFar); + + unsigned length = byteArrayLength(t, bytesSoFar) - 1; + object value = makeCharArray(t, length + 1); + unsigned vi = 0; - for (unsigned si = 0; si < length; ++si) { - unsigned a = s.read1(); + for (; vi < byteCount; ++vi) { + charArrayBody(t, value, vi) = byteArrayBody(t, bytesSoFar, vi); + } + + unsigned a = lastByteRead; + unsigned si = sourceIndex; + while (true) { if (a & 0x80) { - // todo: handle non-ASCII characters properly if (a & 0x20) { // 3 bytes si += 2; assert(t, si < length); - /*unsigned b = */s.read1(); - /*unsigned c = */s.read1(); - byteArrayBody(t, value, vi++) = '_'; + unsigned b = s.read1(); + unsigned c = s.read1(); + charArrayBody(t, value, vi++) + = ((a & 0xf) << 12) | ((b & 0x3f) << 6) | (c & 0x3f); } else { // 2 bytes ++ si; @@ -519,9 +556,55 @@ parseUtf8(Thread* t, Stream& s, unsigned length) unsigned b = s.read1(); if (a == 0xC0 and b == 0x80) { + charArrayBody(t, value, vi++) = 0; + } else { + charArrayBody(t, value, vi++) = ((a & 0x1f) << 6) | (b & 0x3f); + } + } + } else { + charArrayBody(t, value, vi++) = a; + } + + if (++si < length) { + a = s.read1(); + } else { + break; + } + } + + if (vi < length) { + PROTECT(t, value); + + object v = makeCharArray(t, vi + 1); + memcpy(&charArrayBody(t, v, 0), &charArrayBody(t, value, 0), vi * 2); + value = v; + } + + charArrayBody(t, value, vi) = 0; + return value; +} + +object +parseUtf8(Thread* t, Stream& s, unsigned length) +{ + object value = makeByteArray(t, length + 1); + unsigned vi = 0; + for (unsigned si = 0; si < length; ++si) { + unsigned a = s.read1(); + if (a & 0x80) { + if (a & 0x20) { + // 3 bytes + return parseUtf8NonAscii(t, s, value, vi, si, a); + } else { + // 2 bytes + unsigned b = s.read1(); + + if (a == 0xC0 and b == 0x80) { + ++ si; + assert(t, si < length); byteArrayBody(t, value, vi++) = 0; } else { - byteArrayBody(t, value, vi++) = '_'; + return parseUtf8NonAscii(t, s, value, vi, si, a); } } } else { @@ -541,6 +624,28 @@ parseUtf8(Thread* t, Stream& s, unsigned length) return value; } +void +removeByteArray(Thread* t, object o) +{ + hashMapRemove(t, t->m->byteArrayMap, o, byteArrayHash, objectEqual); +} + +object +internByteArray(Thread* t, object array) +{ + PROTECT(t, array); + + object n = hashMapFindNode + (t, t->m->byteArrayMap, array, byteArrayHash, byteArrayEqual); + if (n) { + return jreferenceTarget(t, tripleFirst(t, n)); + } else { + hashMapInsert(t, t->m->byteArrayMap, array, 0, byteArrayHash); + addFinalizer(t, array, removeByteArray); + return array; + } +} + unsigned parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) { @@ -563,6 +668,11 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) case CONSTANT_Utf8: { if (singletonObject(t, pool, i) == 0) { object value = parseUtf8(t, s, s.read2()); + if (objectClass(t, value) + == arrayBody(t, t->m->types, Machine::ByteArrayType)) + { + value = internByteArray(t, value); + } set(t, pool, SingletonBody + (i * BytesPerWord), value); } } return 1; @@ -583,7 +693,8 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) parsePoolEntry(t, s, index, pool, si); object value = singletonObject(t, pool, si); - value = makeString(t, value, 0, byteArrayLength(t, value) - 1, 0); + value = makeString + (t, value, 0, cast(value, BytesPerWord) - 1, 0); value = intern(t, value); set(t, pool, SingletonBody + (i * BytesPerWord), value); } @@ -1163,6 +1274,17 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) hashMapInsert(t, virtualMap, method, method, methodHash); } + + if (UNLIKELY(strcmp + (reinterpret_cast("finalize"), + &byteArrayBody(t, methodName(t, method), 0)) == 0 + and strcmp + (reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, method), 0)) == 0 + and (not emptyMethod(t, method)))) + { + classVmFlags(t, class_) |= HasFinalizerFlag; + } } else { methodOffset(t, method) = i; @@ -1317,6 +1439,8 @@ updateBootstrapClass(Thread* t, object bootstrapClass, object class_) expect(t, bootstrapClass == arrayBody(t, t->m->types, Machine::ClassType) or classFixedSize(t, bootstrapClass) >= classFixedSize(t, class_)); + expect(t, (classVmFlags(t, class_) & HasFinalizerFlag) == 0); + PROTECT(t, bootstrapClass); PROTECT(t, class_); @@ -1513,7 +1637,7 @@ boot(Thread* t) m->unsafe = true; - m->loader = allocate(t, sizeof(void*) * 3, true); + m->loader = allocate(t, FixedSizeOfSystemClassLoader, true); m->types = allocate(t, pad((TypeCount + 2) * BytesPerWord), true); arrayLength(t, m->types) = TypeCount; @@ -1707,6 +1831,7 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, bootstrapClassMap(0), monitorMap(0), stringMap(0), + byteArrayMap(0), types(0), jniMethodTable(0), finalizers(0), @@ -1715,6 +1840,7 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, weakReferences(0), tenuredWeakReferences(0), unsafe(false), + triedBuiltinOnLoad(false), heapPoolIndex(0) { heap->setClient(heapClient); @@ -1777,6 +1903,7 @@ Thread::Thread(Machine* m, object javaThread, Thread* parent): heapIndex(0), heapOffset(0), protector(0), + classInitStack(0), runnable(this), defaultHeap(static_cast (m->heap->allocate(ThreadHeapSizeInBytes))), @@ -1827,6 +1954,7 @@ Thread::init() boot(this); } + m->byteArrayMap = makeWeakHashMap(this, 0, 0); m->monitorMap = makeWeakHashMap(this, 0, 0); m->jniMethodTable = makeVector(this, 0, 0); @@ -1840,8 +1968,12 @@ Thread::init() if (javaThread) { threadPeer(this, javaThread) = reinterpret_cast(this); } else { + const unsigned NewState = 0; + const unsigned NormalPriority = 5; + this->javaThread = makeThread - (this, reinterpret_cast(this), 0, 0, 0, 0, m->loader, 0); + (this, reinterpret_cast(this), 0, 0, NewState, NormalPriority, + 0, 0, 0, m->loader, 0, 0, 0); } } @@ -2140,6 +2272,28 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, } } +object +makeNewGeneral(Thread* t, object class_) +{ + assert(t, t->state == Thread::ActiveState); + + object instance = makeNew(t, class_); + PROTECT(t, instance); + + if (classVmFlags(t, class_) & WeakReferenceFlag) { + ACQUIRE(t, t->m->referenceLock); + + jreferenceVmNext(t, instance) = t->m->weakReferences; + t->m->weakReferences = instance; + } + + if (classVmFlags(t, class_) & HasFinalizerFlag) { + addFinalizer(t, instance, finalizeObject); + } + + return instance; +} + object makeByteArray(Thread* t, const char* format, ...) { @@ -2406,7 +2560,8 @@ parseClass(Thread* t, const uint8_t* data, unsigned size) set(t, class_, ClassSuper, sc); classVmFlags(t, class_) - |= (classVmFlags(t, sc) & (ReferenceFlag | WeakReferenceFlag)); + |= (classVmFlags(t, sc) + & (ReferenceFlag | WeakReferenceFlag | HasFinalizerFlag)); } parseInterfaceTable(t, s, class_, pool); @@ -2519,24 +2674,57 @@ resolveClass(Thread* t, object spec) } object -resolveMethod(Thread* t, const char* className, const char* methodName, +resolveMethod(Thread* t, object class_, const char* methodName, const char* methodSpec) { - object class_ = resolveClass(t, makeByteArray(t, "%s", className)); - if (LIKELY(t->exception == 0)) { - PROTECT(t, class_); + PROTECT(t, class_); - object name = makeByteArray(t, methodName); - PROTECT(t, name); + object name = makeByteArray(t, methodName); + PROTECT(t, name); - object spec = makeByteArray(t, methodSpec); - object reference = makeReference(t, class_, name, spec); + object spec = makeByteArray(t, methodSpec); - return findMethodInClass(t, class_, referenceName(t, reference), - referenceSpec(t, reference)); + object method = findMethodInClass(t, class_, name, spec); + + if (t->exception == 0 and method == 0) { + object message = makeString + (t, "%s %s not found in %s", methodName, methodSpec, + &byteArrayBody(t, className(t, class_), 0)); + + t->exception = makeNoSuchMethodError(t, message); + return 0; + } else { + return method; + } +} + +object +resolveField(Thread* t, object class_, const char* fieldName, + const char* fieldSpec) +{ + PROTECT(t, class_); + + object name = makeByteArray(t, fieldName); + PROTECT(t, name); + + object spec = makeByteArray(t, fieldSpec); + PROTECT(t, spec); + + object field = 0; + for (; class_ != 0 and field == 0; class_ = classSuper(t, class_)) { + field = findFieldInClass(t, class_, name, spec); } - return 0; + if (t->exception == 0 and field == 0) { + object message = makeString + (t, "%s %s not found in %s", fieldName, fieldSpec, + &byteArrayBody(t, className(t, class_), 0)); + + t->exception = makeNoSuchFieldError(t, message); + return 0; + } else { + return field; + } } object @@ -2565,6 +2753,92 @@ resolveObjectArrayClass(Thread* t, object elementSpec) return resolveClass(t, spec); } +bool +classNeedsInit(Thread* t, object c) +{ + if (classVmFlags(t, c) & NeedInitFlag) { + if (classVmFlags(t, c) & InitFlag) { + // the class is currently being initialized. If this the thread + // which is initializing it, we should not try to initialize it + // recursively. Otherwise, we must wait for the responsible + // thread to finish. + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return false; + } + } + } + return true; + } else { + return false; + } +} + +bool +preInitClass(Thread* t, object c) +{ + if (classVmFlags(t, c) & NeedInitFlag) { + PROTECT(t, c); + ACQUIRE(t, t->m->classLock); + if (classVmFlags(t, c) & NeedInitFlag) { + if (classVmFlags(t, c) & InitFlag) { + // the class is currently being initialized. If this the + // thread which is initializing it, we should not try to + // initialize it recursively. + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return false; + } + } + + // some other thread is on the job - wait for it to finish. + while (classVmFlags(t, c) & InitFlag) { + ENTER(t, Thread::IdleState); + t->m->classLock->wait(t->systemThread, 0); + } + } else if (classVmFlags(t, c) & InitErrorFlag) { + object message = makeString + (t, "%s", &byteArrayBody(t, className(t, c), 0)); + t->exception = makeNoClassDefFoundError(t, message); + } else { + classVmFlags(t, c) |= InitFlag; + return true; + } + } + } + return false; +} + +void +postInitClass(Thread* t, object c) +{ + PROTECT(t, c); + + ACQUIRE(t, t->m->classLock); + if (t->exception) { + t->exception = makeExceptionInInitializerError(t, t->exception); + classVmFlags(t, c) |= NeedInitFlag | InitErrorFlag; + classVmFlags(t, c) &= ~InitFlag; + } else { + classVmFlags(t, c) &= ~(NeedInitFlag | InitFlag); + } + t->m->classLock->notifyAll(t->systemThread); +} + +void +initClass(Thread* t, object c) +{ + PROTECT(t, c); + + if (preInitClass(t, c)) { + Thread::ClassInitStack stack(t, c); + + t->m->processor->invoke(t, classInitializer(t, c), 0); + + postInitClass(t, c); + } +} + object makeObjectArray(Thread* t, object elementClass, unsigned count) { @@ -2856,6 +3130,7 @@ visitRoots(Machine* m, Heap::Visitor* v) v->visit(&(m->bootstrapClassMap)); v->visit(&(m->monitorMap)); v->visit(&(m->stringMap)); + v->visit(&(m->byteArrayMap)); v->visit(&(m->types)); v->visit(&(m->jniMethodTable)); @@ -2972,11 +3247,11 @@ makeTrace(Thread* t, Thread* target) void runJavaThread(Thread* t) { - object method = resolveMethod(t, "java/lang/Thread", "run", "()V"); + object method = resolveMethod + (t, "java/lang/Thread", "run", "(Ljava/lang/Thread;)V"); + if (t->exception == 0) { - t->m->processor->invoke - (t, findMethod(t, method, objectClass(t, t->javaThread)), - t->javaThread); + t->m->processor->invoke(t, method, 0, t->javaThread); } } @@ -3025,3 +3300,29 @@ vmPrintTrace(Thread* t) t->m->processor->walkStack(t, &v); } + +// also for debugging +void* +vmAddressFromLine(Thread* t, object m, unsigned line) +{ + object code = methodCode(t, m); + printf("code: %p\n", code); + object lnt = codeLineNumberTable(t, code); + printf("lnt: %p\n", lnt); + + if (lnt) { + unsigned last = 0; + unsigned bottom = 0; + unsigned top = lineNumberTableLength(t, lnt); + for(unsigned i = bottom; i < top; i++) + { + LineNumber* ln = lineNumberTableBody(t, lnt, i); + if(lineNumberLine(ln) == line) + return reinterpret_cast(lineNumberIp(ln)); + else if(lineNumberLine(ln) > line) + return reinterpret_cast(last); + last = lineNumberIp(ln); + } + } + return 0; +} diff --git a/src/machine.h b/src/machine.h index c75626c5e9..3ab32d8fad 100644 --- a/src/machine.h +++ b/src/machine.h @@ -76,6 +76,7 @@ const int UnknownLine = -2; // class flags (note that we must be careful not to overlap the // standard ACC_* flags): +const unsigned HasFinalMemberFlag = 1 << 13; const unsigned SingletonFlag = 1 << 14; const unsigned ContinuationFlag = 1 << 15; @@ -84,9 +85,10 @@ const unsigned ReferenceFlag = 1 << 0; const unsigned WeakReferenceFlag = 1 << 1; const unsigned NeedInitFlag = 1 << 2; const unsigned InitFlag = 1 << 3; -const unsigned PrimitiveFlag = 1 << 4; -const unsigned BootstrapFlag = 1 << 5; -const unsigned HasFinalMemberFlag = 1 << 6; +const unsigned InitErrorFlag = 1 << 4; +const unsigned PrimitiveFlag = 1 << 5; +const unsigned BootstrapFlag = 1 << 6; +const unsigned HasFinalizerFlag = 1 << 7; // method vmFlags: const unsigned ClassInitFlag = 1 << 0; @@ -1190,6 +1192,7 @@ class Machine { object bootstrapClassMap; object monitorMap; object stringMap; + object byteArrayMap; object types; object jniMethodTable; object finalizers; @@ -1198,6 +1201,7 @@ class Machine { object weakReferences; object tenuredWeakReferences; bool unsafe; + bool triedBuiltinOnLoad; JavaVMVTable javaVMVTable; JNIEnvVTable jniEnvVTable; uintptr_t* heapPool[ThreadHeapPoolSize]; @@ -1265,6 +1269,25 @@ class Thread { object* p; }; + class ClassInitStack { + public: + ClassInitStack(Thread* t, object class_): + next(t->classInitStack), + class_(class_), + protector(t, &(this->class_)) + { + t->classInitStack = this; + } + + ~ClassInitStack() { + protector.t->classInitStack = next; + } + + ClassInitStack* next; + object class_; + SingleProtector protector; + }; + class Runnable: public System::Runnable { public: Runnable(Thread* t): t(t) { } @@ -1317,6 +1340,7 @@ class Thread { unsigned heapIndex; unsigned heapOffset; Protector* protector; + ClassInitStack* classInitStack; Runnable runnable; uintptr_t* defaultHeap; uintptr_t* heap; @@ -1710,7 +1734,7 @@ makeClassNotFoundException(Thread* t, object message) { PROTECT(t, message); object trace = makeTrace(t); - return makeClassNotFoundException(t, message, trace, 0); + return makeClassNotFoundException(t, message, trace, 0, 0); } inline object @@ -1761,6 +1785,14 @@ makeNoSuchMethodError(Thread* t, object message) return makeNoSuchMethodError(t, message, trace, 0); } +inline object +makeNoClassDefFoundError(Thread* t, object message) +{ + PROTECT(t, message); + object trace = makeTrace(t); + return makeNoClassDefFoundError(t, message, trace, 0); +} + inline object makeUnsatisfiedLinkError(Thread* t, object message) { @@ -1774,7 +1806,7 @@ makeExceptionInInitializerError(Thread* t, object cause) { PROTECT(t, cause); object trace = makeTrace(t); - return makeExceptionInInitializerError(t, 0, trace, cause); + return makeExceptionInInitializerError(t, 0, trace, cause, cause); } inline object @@ -1790,27 +1822,16 @@ makeNew(Thread* t, object class_) return instance; } -inline object -makeNewWeakReference(Thread* t, object class_) -{ - assert(t, t->state == Thread::ActiveState); - - object instance = makeNew(t, class_); - PROTECT(t, instance); - - ACQUIRE(t, t->m->referenceLock); - - jreferenceVmNext(t, instance) = t->m->weakReferences; - t->m->weakReferences = instance; - - return instance; -} +object +makeNewGeneral(Thread* t, object class_); inline object make(Thread* t, object class_) { - if (UNLIKELY(classVmFlags(t, class_) & WeakReferenceFlag)) { - return makeNewWeakReference(t, class_); + if (UNLIKELY(classVmFlags(t, class_) + & (WeakReferenceFlag | HasFinalizerFlag))) + { + return makeNewGeneral(t, class_); } else { return makeNew(t, class_); } @@ -1933,9 +1954,9 @@ stringCharAt(Thread* t, object s, int i) if (objectClass(t, data) == arrayBody(t, t->m->types, Machine::ByteArrayType)) { - return byteArrayBody(t, data, i); + return byteArrayBody(t, data, stringOffset(t, s) + i); } else { - return charArrayBody(t, data, i); + return charArrayBody(t, data, stringOffset(t, s) + i); } } @@ -2051,33 +2072,72 @@ fieldSize(Thread* t, object field) object findLoadedClass(Thread* t, object spec); +inline bool +emptyMethod(Thread* t, object method) +{ + return ((methodFlags(t, method) & ACC_NATIVE) == 0) + and (codeLength(t, methodCode(t, method)) == 1) + and (codeBody(t, methodCode(t, method), 0) == return_); +} + object parseClass(Thread* t, const uint8_t* data, unsigned length); object -resolveClass(Thread* t, object spec); +resolveClass(Thread* t, object name); + +inline object +resolveClass(Thread* t, const char* name) +{ + return resolveClass(t, makeByteArray(t, "%s", name)); +} object -resolveMethod(Thread* t, const char* className, const char* methodName, +resolveMethod(Thread* t, object class_, const char* methodName, const char* methodSpec); +inline object +resolveMethod(Thread* t, const char* className, const char* methodName, + const char* methodSpec) +{ + object class_ = resolveClass(t, className); + if (LIKELY(t->exception == 0)) { + return resolveMethod(t, class_, methodName, methodSpec); + } else { + return 0; + } +} + +object +resolveField(Thread* t, object class_, const char* fieldName, + const char* fieldSpec); + +inline object +resolveField(Thread* t, const char* className, const char* fieldName, + const char* fieldSpec) +{ + object class_ = resolveClass(t, className); + if (LIKELY(t->exception == 0)) { + return resolveField(t, class_, fieldName, fieldSpec); + } else { + return 0; + } +} + object resolveObjectArrayClass(Thread* t, object elementSpec); -inline bool -classNeedsInit(Thread* t, object c) -{ - return classVmFlags(t, c) & NeedInitFlag - and (classVmFlags(t, c) & InitFlag) == 0; -} +bool +classNeedsInit(Thread* t, object c); -inline void -initClass(Thread* t, object c) -{ - if (classNeedsInit(t, c)) { - t->m->processor->initClass(t, c); - } -} +bool +preInitClass(Thread* t, object c); + +void +postInitClass(Thread* t, object c); + +void +initClass(Thread* t, object c); object makeObjectArray(Thread* t, object elementClass, unsigned count); @@ -2388,4 +2448,7 @@ dumpHeap(Thread* t, FILE* out); void vmPrintTrace(vm::Thread* t); +void* +vmAddressFromLine(vm::Thread* t, vm::object m, unsigned line); + #endif//MACHINE_H diff --git a/src/main.cpp b/src/main.cpp index 5e412c0bae..074297016d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,6 +83,10 @@ main(int ac, const char** av) ++ vmArgs.nOptions; #endif +#ifdef BOOT_BUILTINS + ++ vmArgs.nOptions; +#endif + JavaVMOption options[vmArgs.nOptions]; vmArgs.options = options; @@ -103,6 +107,11 @@ main(int ac, const char** av) = const_cast("-Davian.bootstrap=" BOOT_LIBRARY); #endif +#ifdef BOOT_BUILTINS + options[optionIndex++].optionString + = const_cast("-Davian.builtins=" BOOT_BUILTINS); +#endif + #define CLASSPATH_PROPERTY "-Djava.class.path=" unsigned classpathSize = strlen(classpath); diff --git a/src/posix.cpp b/src/posix.cpp index fd45942ec5..48096dac52 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -124,9 +124,7 @@ class MySystem: public System { r->setInterrupted(true); - if (flags & Waiting) { - pthread_kill(thread, InterruptSignal); - } + pthread_kill(thread, InterruptSignal); } virtual void join() { @@ -852,6 +850,7 @@ handleSignal(int signal, siginfo_t* info, void* context) } else { switch (signal) { case VisitSignal: + case InterruptSignal: break; default: diff --git a/src/processor.h b/src/processor.h index 234c7b8afe..0f2316f6c1 100644 --- a/src/processor.h +++ b/src/processor.h @@ -78,9 +78,6 @@ class Processor { virtual void initVtable(Thread* t, object c) = 0; - virtual void - initClass(Thread* t, object c) = 0; - virtual void visitObjects(Thread* t, Heap::Visitor* v) = 0; diff --git a/src/thunks.cpp b/src/thunks.cpp index cdb37c4cc6..bb1721ffe1 100644 --- a/src/thunks.cpp +++ b/src/thunks.cpp @@ -38,7 +38,7 @@ THUNK(makeMultidimensionalArray) THUNK(throw_) THUNK(checkCast) THUNK(instanceOf64) -THUNK(makeNewWeakReference64) +THUNK(makeNewGeneral64) THUNK(makeNew64) THUNK(set) THUNK(gcIfNecessary) diff --git a/src/types.def b/src/types.def index 98ad156f23..b099fc3397 100644 --- a/src/types.def +++ b/src/types.def @@ -144,8 +144,9 @@ (type illegalMonitorStateException java/lang/IllegalMonitorStateException) -(type arrayIndexOutOfBoundsException - java/lang/ArrayIndexOutOfBoundsException) +(type indexOutOfBoundsException java/lang/IndexOutOfBoundsException) + +(type arrayIndexOutOfBoundsException java/lang/ArrayIndexOutOfBoundsException) (type arrayStoreException java/lang/ArrayStoreException) @@ -161,6 +162,8 @@ (type error java/lang/Error) +(type virtualMachineError java/lang/VirtualMachineError) + (type stackOverflowError java/lang/StackOverflowError) (type linkageError java/lang/LinkageError) @@ -171,6 +174,8 @@ (type noSuchMethodError java/lang/NoSuchMethodError) +(type noClassDefFoundError java/lang/NoClassDefFoundError) + (type unsatisfiedLinkError java/lang/UnsatisfiedLinkError) (type exceptionInInitializerError java/lang/ExceptionInInitializerError) diff --git a/src/vector.h b/src/vector.h index 78a6059eb0..744ebdff45 100644 --- a/src/vector.h +++ b/src/vector.h @@ -110,6 +110,11 @@ class Vector { append(&v, BytesPerWord); } + void set2(unsigned offset, uint16_t v) { + assert(s, offset <= position - 2); + memcpy(data + offset, &v, 2); + } + unsigned get(unsigned offset) { uint8_t v; get(offset, &v, 1); return v; diff --git a/src/windows.cpp b/src/windows.cpp index 300bcefc7b..52e353e82c 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -574,11 +574,20 @@ class MySystem: public System { if (handler) { segFaultHandler = handler; +#ifdef __i386__ oldSegFaultHandler = SetUnhandledExceptionFilter(handleException); +#elif defined __x86_64__ + AddVectoredExceptionHandler(1, handleException); + oldSegFaultHandler = 0; +#endif return 0; } else if (segFaultHandler) { segFaultHandler = 0; +#ifdef __i386__ SetUnhandledExceptionFilter(oldSegFaultHandler); +#elif defined __x86_64__ + //do nothing, handlers are never "unregistered" anyway +#endif return 0; } else { return 1; @@ -600,10 +609,15 @@ class MySystem: public System { CONTEXT context; rv = GetThreadContext(target->thread, &context); expect(this, rv); - +#ifdef __i386__ visitor->visit(reinterpret_cast(context.Eip), reinterpret_cast(context.Ebp), reinterpret_cast(context.Esp)); +#elif defined __x86_64__ + visitor->visit(reinterpret_cast(context.Rip), + reinterpret_cast(context.Rbp), + reinterpret_cast(context.Rsp)); +#endif rv = ResumeThread(target->thread); expect(this, rv != -1); @@ -798,7 +812,7 @@ dump(LPEXCEPTION_POINTERS e, const char* directory) char name[MAX_PATH]; _timeb tb; _ftime(&tb); - snprintf(name, MAX_PATH, "%s\\crash-%lld.mdmp", directory, + snprintf(name, MAX_PATH, "%s\\crash-%"LLD".mdmp", directory, (static_cast(tb.time) * 1000) + static_cast(tb.millitm)); @@ -830,18 +844,31 @@ LONG CALLBACK handleException(LPEXCEPTION_POINTERS e) { if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { +#ifdef __i386__ void* ip = reinterpret_cast(e->ContextRecord->Eip); void* base = reinterpret_cast(e->ContextRecord->Ebp); void* stack = reinterpret_cast(e->ContextRecord->Esp); void* thread = reinterpret_cast(e->ContextRecord->Ebx); +#elif defined __x86_64__ + void* ip = reinterpret_cast(e->ContextRecord->Rip); + void* base = reinterpret_cast(e->ContextRecord->Rbp); + void* stack = reinterpret_cast(e->ContextRecord->Rsp); + void* thread = reinterpret_cast(e->ContextRecord->Rbx); +#endif bool jump = system->segFaultHandler->handleSignal (&ip, &base, &stack, &thread); - +#ifdef __i386__ e->ContextRecord->Eip = reinterpret_cast(ip); e->ContextRecord->Ebp = reinterpret_cast(base); e->ContextRecord->Esp = reinterpret_cast(stack); e->ContextRecord->Ebx = reinterpret_cast(thread); +#elif defined __x86_64__ + e->ContextRecord->Rip = reinterpret_cast(ip); + e->ContextRecord->Rbp = reinterpret_cast(base); + e->ContextRecord->Rsp = reinterpret_cast(stack); + e->ContextRecord->Rbx = reinterpret_cast(thread); +#endif if (jump) { return EXCEPTION_CONTINUE_EXECUTION; diff --git a/src/x86.S b/src/x86.S index 67b31f872e..1d27e700e0 100644 --- a/src/x86.S +++ b/src/x86.S @@ -8,16 +8,145 @@ There is NO WARRANTY for this software. See license.txt for details. */ + #include "types.h" #define LOCAL(x) .L##x + +#if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif .text - + #ifdef __x86_64__ + +#ifdef __MINGW32__ + +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): + pushq %rbp + //save nonvolatile registers + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + movq %rsp, %rbp -.globl vmNativeCall -vmNativeCall: + + // %rcx: function + // %rdx: arguments + // %r8: arguments count + // %r9: return type + + movq %rcx, %r10 + movq %rdx, %r11 + movq %r8, %r12 + movq %r9, %r13 + + // %r10: function + // %r11: arguments + // %r12: arguments count + // %r13: return type + + //allocate initial stack space + subq $32, %rsp + + //first arg + cmp $0, %r12 + je LOCAL(call) + movq 0(%r11),%rcx + movq 0(%r11),%xmm0 + subq $1, %r12 + + //second arg + cmp $0, %r12 + je LOCAL(call) + movq 8(%r11),%rdx + movq 8(%r11),%xmm1 + subq $1, %r12 + + //third arg + cmp $0, %r12 + je LOCAL(call) + movq 16(%r11),%r8 + movq 16(%r11),%xmm2 + subq $1, %r12 + + //fourth arg + cmp $0, %r12 + je LOCAL(call) + movq 24(%r11),%r9 + movq 24(%r11),%xmm3 + subq $1, %r12 + + + //calculate stack space for arguments, aligned + movq $8, %r15 + leaq (%r15, %r12, 8), %r15 + andq $0xFFFFFFFFFFFFFFF0, %r15 + + //reserve stack space for arguments + subq %r15, %rsp + + //reset the counter + addq $3, %r12 + jmp LOCAL(loopend) + +LOCAL(loop): + movq (%r11, %r12, 8), %r14 + movq %r14, (%rsp, %r12, 8); + subq $1, %r12 + +LOCAL(loopend): + //we don't need to move arg 3 and lower + cmpq $3, %r12 + jne LOCAL(loop) + +LOCAL(call): + call *%r10 + +LOCAL(void): + cmpq $VOID_TYPE,%r13 + jne LOCAL(float) + jmp LOCAL(exit) + +LOCAL(float): + cmpq $FLOAT_TYPE,%r13 + je LOCAL(copy) + cmpq $DOUBLE_TYPE,%r13 + jne LOCAL(exit) + +LOCAL(copy): + movq %xmm0,%rax + +LOCAL(exit): + + movq %rbp, %rsp + //return nonvolatile registers to their former state + popq %r15 + popq %r14 + popq %r13 + popq %r12 + + popq %rbp + ret + +.globl GLOBAL(vmJump) +GLOBAL(vmJump): + movq %rdx,%rbp + movq 8(%rsp),%rax + movq 16(%rsp),%rdx + movq %r8,%rsp + movq %r9,%rbx + jmp *%rcx + +#else // not __MINGW32__ + +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): pushq %rbp movq %rsp,%rbp @@ -113,24 +242,21 @@ LOCAL(exit): popq %rbp ret -.globl vmJump -vmJump: +.globl GLOBAL(vmJump) +GLOBAL(vmJump): movq %rsi,%rbp movq %rdx,%rsp movq %rcx,%rbx movq %r8,%rax movq %r9,%rdx jmp *%rdi + +#endif // not __MINGW32__ #elif defined __i386__ -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmNativeCall -_vmNativeCall: -# else -.globl vmNativeCall -vmNativeCall: -# endif +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): pushl %ebp movl %esp,%ebp @@ -201,13 +327,8 @@ LOCAL(exit): popl %ebp ret -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmJump -_vmJump: -# else -.globl vmJump -vmJump: -# endif +.globl GLOBAL(vmJump) +GLOBAL(vmJump): movl 4(%esp),%esi movl 8(%esp),%ebp movl 16(%esp),%ebx @@ -216,6 +337,4 @@ vmJump: movl 12(%esp),%esp jmp *%esi -#else -# error unsupported platform -#endif +#endif //def __x86_64__ diff --git a/src/x86.cpp b/src/x86.cpp index 50c8383862..59959a2ce9 100644 --- a/src/x86.cpp +++ b/src/x86.cpp @@ -7,6 +7,9 @@ There is NO WARRANTY for this software. See license.txt for details. */ + + +#if (defined __i386__) || (defined __x86_64__) #include "assembler.h" #include "vector.h" @@ -399,81 +402,114 @@ padding(AlignmentPadding* p, unsigned start, unsigned offset, return padding; } -void -encode(Context* c, uint8_t* instruction, unsigned length, int a, int b, - int32_t displacement, int index, unsigned scale) -{ - c->code.append(instruction, length); +#define REX_W 0x48 +#define REX_R 0x44 +#define REX_X 0x42 +#define REX_B 0x41 +#define REX_NONE 0x40 - uint8_t width; - if (displacement == 0 and b != rbp) { - width = 0; - } else if (isInt8(displacement)) { - width = 0x40; - } else { - width = 0x80; +void maybeRex(Context* c, unsigned size, int a, int index, int base, bool always) { + if(BytesPerWord == 8) { + uint8_t byte; + if(size == 8) { + byte = REX_W; + } else { + byte = REX_NONE; + } + if(a != NoRegister && (a & 8)) byte |= REX_R; + if(index != NoRegister && (index & 8)) byte |= REX_X; + if(base != NoRegister && (base & 8)) byte |= REX_B; + if(always or byte != REX_NONE) c->code.append(byte); } +} - if (index == -1) { - c->code.append(width | (a << 3) | b); - if (b == rsp) { - c->code.append(0x24); +inline void maybeRex(Context* c, unsigned size, Assembler::Register* a, + Assembler::Register* b) { + maybeRex(c, size, a->low, NoRegister, b->low, false); +} + +inline void alwaysRex(Context* c, unsigned size, Assembler::Register* a, + Assembler::Register* b) { + maybeRex(c, size, a->low, NoRegister, b->low, true); +} + +inline void maybeRex(Context* c, unsigned size, Assembler::Register* a) { + maybeRex(c, size, NoRegister, NoRegister, a->low, false); +} + +inline void maybeRex(Context* c, unsigned size, Assembler::Register* a, + Assembler::Memory* b) { + maybeRex(c, size, a->low, b->index, b->base, false); +} + +inline void maybeRex(Context* c, unsigned size, Assembler::Memory* a) { + maybeRex(c, size, NoRegister, a->index, a->base, false); +} + +inline int regCode(int a) { + return a & 7; +} + +inline int regCode(Assembler::Register* a) { + return regCode(a->low); +} + +inline void modrm(Context* c, uint8_t mod, int a, int b) { + c->code.append(mod | (regCode(b) << 3) | regCode(a)); +} + +inline void modrm(Context* c, uint8_t mod, Assembler::Register* a, + Assembler::Register* b) { + modrm(c, mod, a->low, b->low); +} + +inline void sib(Context* c, unsigned scale, int index, int base) { + c->code.append((log(scale) << 6) | (regCode(index) << 3) | regCode(base)); +} + +inline void modrmSib(Context* c, int width, int a, int scale, int index, int base) { + if(index == NoRegister) { + modrm(c, width, base, a); + if(regCode(base) == rsp) { + sib(c, 0x00, rsp, rsp); } } else { - assert(c, b != rsp); - c->code.append(width | (a << 3) | 4); - c->code.append((log(scale) << 6) | (index << 3) | b); + modrm(c, width, rsp, a); + sib(c, scale, index, base); } +} - if (displacement == 0 and b != rbp) { - // do nothing - } else if (isInt8(displacement)) { - c->code.append(displacement); +inline void modrmSibImm(Context* c, int a, int scale, int index, int base, int offset) { + if(offset == 0 && regCode(base) != rbp) { + modrmSib(c, 0x00, a, scale, index, base); + } else if(isInt8(offset)) { + modrmSib(c, 0x40, a, scale, index, base); + c->code.append(offset); } else { - c->code.append4(displacement); + modrmSib(c, 0x80, a, scale, index, base); + c->code.append4(offset); } } + -void -rex(Context* c, uint8_t mask, int r) -{ - if (BytesPerWord == 8) { - c->code.append(mask | ((r & 8) >> 3)); - } +inline void modrmSibImm(Context* c, Assembler::Register* a, + Assembler::Memory* b) { + modrmSibImm(c, a->low, b->scale, b->index, b->base, b->offset); } -void -rex(Context* c) -{ - rex(c, 0x48, rax); +inline void opcode(Context* c, uint8_t op) { + c->code.append(op); } -void -encode(Context* c, uint8_t instruction, int a, Assembler::Memory* b, bool rex) -{ - if (rex) { - ::rex(c); - } - - encode(c, &instruction, 1, a, b->base, b->offset, b->index, b->scale); -} - -void -encode2(Context* c, uint16_t instruction, int a, Assembler::Memory* b, - bool rex) -{ - if (rex) { - ::rex(c); - } - - uint8_t i[2] = { instruction >> 8, instruction & 0xff }; - encode(c, i, 2, a, b->base, b->offset, b->index, b->scale); +inline void opcode(Context* c, uint8_t op1, uint8_t op2) { + c->code.append(op1); + c->code.append(op2); } void return_(Context* c) { - c->code.append(0xc3); + opcode(c, 0xc3); } void @@ -485,7 +521,7 @@ unconditional(Context* c, unsigned jump, Assembler::Constant* a) { appendOffsetTask(c, a->value, offset(c), 5); - c->code.append(jump); + opcode(c, jump); c->code.append4(0); } @@ -494,8 +530,7 @@ conditional(Context* c, unsigned condition, Assembler::Constant* a) { appendOffsetTask(c, a->value, offset(c), 6); - c->code.append(0x0f); - c->code.append(condition); + opcode(c, 0x0f, condition); c->code.append4(0); } @@ -565,9 +600,8 @@ jumpR(Context* c, unsigned size UNUSED, Assembler::Register* a) { assert(c, size == BytesPerWord); - if (a->low & 8) rex(c, 0x40, a->low); - c->code.append(0xff); - c->code.append(0xe0 | (a->low & 7)); + maybeRex(c, 4, a); + opcode(c, 0xff, 0xe0 + regCode(a)); } void @@ -582,8 +616,10 @@ void jumpM(Context* c, unsigned size UNUSED, Assembler::Memory* a) { assert(c, size == BytesPerWord); - - encode(c, 0xff, 4, a, false); + + maybeRex(c, 4, a); + opcode(c, 0xff); + modrmSibImm(c, rsp, a->scale, a->index, a->base, a->offset); } void @@ -653,17 +689,19 @@ callR(Context* c, unsigned size UNUSED, Assembler::Register* a) { assert(c, size == BytesPerWord); - if (a->low & 8) rex(c, 0x40, a->low); - c->code.append(0xff); - c->code.append(0xd0 | (a->low & 7)); + //maybeRex.W has no meaning here so we disable it + maybeRex(c, 4, a); + opcode(c, 0xff, 0xd0 + regCode(a)); } void callM(Context* c, unsigned size UNUSED, Assembler::Memory* a) { assert(c, size == BytesPerWord); - - encode(c, 0xff, 2, a, false); + + maybeRex(c, 4, a); + opcode(c, 0xff); + modrmSibImm(c, rdx, a->scale, a->index, a->base, a->offset); } void @@ -689,7 +727,8 @@ pushR(Context* c, unsigned size, Assembler::Register* a) pushR(c, 4, &ah); pushR(c, 4, a); } else { - c->code.append(0x50 | a->low); + maybeRex(c, 4, a); + opcode(c, 0x50 + regCode(a)); } } @@ -706,7 +745,8 @@ popR(Context* c, unsigned size, Assembler::Register* a) popR(c, 4, a); popR(c, 4, &ah); } else { - c->code.append(0x58 | a->low); + maybeRex(c, 4, a); + opcode(c, 0x58 + regCode(a)); if (BytesPerWord == 8 and size == 4) { moveRR(c, 4, a, 8, a); } @@ -724,7 +764,8 @@ popM(Context* c, unsigned size, Assembler::Memory* a) } else { assert(c, BytesPerWord == 4 or size == 8); - encode(c, 0x8f, 0, a, false); + opcode(c, 0x8f); + modrmSibImm(c, 0, a->scale, a->index, a->base, a->offset); } } @@ -747,9 +788,8 @@ negateR(Context* c, unsigned size, Assembler::Register* a) addCarryCR(c, 4, &zero, &ah); negateR(c, 4, &ah); } else { - if (size == 8) rex(c); - c->code.append(0xf7); - c->code.append(0xd8 | a->low); + maybeRex(c, size, a); + opcode(c, 0xf7, 0xd8 + regCode(a)); } } @@ -763,8 +803,8 @@ negateRR(Context* c, unsigned aSize, Assembler::Register* a, } void -moveCR2(Context* c, unsigned, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b, unsigned promiseOffset) +moveCR2(Context* c, UNUSED unsigned aSize, Assembler::Constant* a, + UNUSED unsigned bSize, Assembler::Register* b, unsigned promiseOffset) { if (BytesPerWord == 4 and bSize == 8) { int64_t v = a->value->value(); @@ -780,8 +820,8 @@ moveCR2(Context* c, unsigned, Assembler::Constant* a, moveCR(c, 4, &al, 4, b); moveCR(c, 4, &ah, 4, &bh); } else { - rex(c, 0x48, b->low); - c->code.append(0xb8 | b->low); + maybeRex(c, BytesPerWord, b); + opcode(c, 0xb8 + regCode(b)); if (a->value->resolved()) { c->code.appendAddress(a->value->value()); } else { @@ -806,15 +846,16 @@ swapRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, assert(c, aSize == bSize); assert(c, aSize == BytesPerWord); - rex(c); - c->code.append(0x87); - c->code.append(0xc0 | (b->low << 3) | a->low); + alwaysRex(c, aSize, a, b); + opcode(c, 0x87); + modrm(c, 0xc0, b, a); } void moveRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) + UNUSED unsigned bSize, Assembler::Register* b) { + if (BytesPerWord == 4 and aSize == 8 and bSize == 8) { Assembler::Register ah(a->high); Assembler::Register bh(b->high); @@ -839,31 +880,28 @@ moveRR(Context* c, unsigned aSize, Assembler::Register* a, moveRR(c, BytesPerWord, a, BytesPerWord, b); moveRR(c, 1, b, BytesPerWord, b); } else { - rex(c); - c->code.append(0x0f); - c->code.append(0xbe); - c->code.append(0xc0 | (b->low << 3) | a->low); + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xbe); + modrm(c, 0xc0, a, b); } break; case 2: - rex(c); - c->code.append(0x0f); - c->code.append(0xbf); - c->code.append(0xc0 | (b->low << 3) | a->low); + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xbf); + modrm(c, 0xc0, a, b); break; - case 8: case 4: - if (aSize == 4 and bSize == 8) { - if (BytesPerWord == 8) { - rex(c); - c->code.append(0x63); - c->code.append(0xc0 | (b->low << 3) | a->low); - } else { - if (a->low == rax and b->low == rax and b->high == rdx) { - c->code.append(0x99); // cdq - } else { + if (bSize == 8) { + if (BytesPerWord == 8) { + alwaysRex(c, aSize, b, a); + opcode(c, 0x63); + modrm(c, 0xc0, a, b); + } else { + if (a->low == rax and b->low == rax and b->high == rdx) { + opcode(c, 0x99); //cdq + } else { assert(c, b->low == rax and b->high == rdx); moveRR(c, 4, a, 4, b); @@ -872,14 +910,22 @@ moveRR(Context* c, unsigned aSize, Assembler::Register* a, } } else { if (a->low != b->low) { - rex(c); - c->code.append(0x89); - c->code.append(0xc0 | (a->low << 3) | b->low); + alwaysRex(c, aSize, a, b); + opcode(c, 0x89); + modrm(c, 0xc0, b, a); } } + break; + + case 8: + if (a->low != b->low){ + maybeRex(c, aSize, a, b); + opcode(c, 0x89); + modrm(c, 0xc0, b, a); + } break; } - } + } } void @@ -888,38 +934,49 @@ moveMR(Context* c, unsigned aSize, Assembler::Memory* a, { switch (aSize) { case 1: - encode2(c, 0x0fbe, b->low, a, true); + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xbe); + modrmSibImm(c, b, a); break; case 2: - encode2(c, 0x0fbf, b->low, a, true); + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xbf); + modrmSibImm(c, b, a); break; case 4: - case 8: - if (aSize == 4 and bSize == 8) { - if (BytesPerWord == 8) { - encode(c, 0x63, b->low, a, true); - } else { + if (BytesPerWord == 8) { + maybeRex(c, bSize, b, a); + opcode(c, 0x63); + modrmSibImm(c, b, a); + } else { + if (bSize == 8) { assert(c, b->low == rax and b->high == rdx); moveMR(c, 4, a, 4, b); moveRR(c, 4, b, 8, b); - } - } else { - if (BytesPerWord == 4 and aSize == 8 and bSize == 8) { - Assembler::Memory ah(a->base, a->offset + 4, a->index, a->scale); - Assembler::Register bh(b->high); - - moveMR(c, 4, a, 4, b); - moveMR(c, 4, &ah, 4, &bh); - } else if (BytesPerWord == 8 and aSize == 4) { - encode(c, 0x63, b->low, a, true); } else { - encode(c, 0x8b, b->low, a, true); + maybeRex(c, bSize, b, a); + opcode(c, 0x8b); + modrmSibImm(c, b, a); } } break; + + case 8: + if (BytesPerWord == 4 and bSize == 8) { + Assembler::Memory ah(a->base, a->offset + 4, a->index, a->scale); + Assembler::Register bh(b->high); + + moveMR(c, 4, a, 4, b); + moveMR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, bSize, b, a); + opcode(c, 0x8b); + modrmSibImm(c, b, a); + } + break; default: abort(c); } @@ -930,41 +987,48 @@ moveRM(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize UNUSED, Assembler::Memory* b) { assert(c, aSize == bSize); + + switch (aSize) { + case 1: + maybeRex(c, bSize, a, b); + opcode(c, 0x88); + modrmSibImm(c, a, b); + break; - if (BytesPerWord == 4 and aSize == 8) { - Assembler::Register ah(a->high); - Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale); + case 2: + opcode(c, 0x66); + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + break; - moveRM(c, 4, a, 4, b); - moveRM(c, 4, &ah, 4, &bh); - } else if (BytesPerWord == 8 and aSize == 4) { - encode(c, 0x89, a->low, b, false); - } else { - switch (aSize) { - case 1: - if (BytesPerWord == 8) { - if (a->low > rbx) { - encode2(c, 0x4088, a->low, b, false); - } else { - encode(c, 0x88, a->low, b, false); - } - } else { - assert(c, a->low <= rbx); - - encode(c, 0x88, a->low, b, false); - } + case 4: + if (BytesPerWord == 8) { + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); break; - - case 2: - encode2(c, 0x6689, a->low, b, false); - break; - - case BytesPerWord: - encode(c, 0x89, a->low, b, true); - break; - - default: abort(c); + } else { + opcode(c, 0x89); + modrmSibImm(c, a, b); } + break; + + case 8: + if(BytesPerWord == 8) { + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + } else { + Assembler::Register ah(a->high); + Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale); + + moveRM(c, 4, a, 4, b); + moveRM(c, 4, &ah, 4, &bh); + } + break; + + default: abort(c); } } @@ -994,17 +1058,24 @@ moveCM(Context* c, unsigned aSize UNUSED, Assembler::Constant* a, { switch (bSize) { case 1: - encode(c, 0xc6, 0, b, false); + maybeRex(c, bSize, b); + opcode(c, 0xc6); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); c->code.append(a->value->value()); break; case 2: - encode2(c, 0x66c7, 0, b, false); + opcode(c, 0x66); + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); c->code.append2(a->value->value()); break; case 4: - encode(c, 0xc7, 0, b, false); + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); if (a->value->resolved()) { c->code.append4(a->value->value()); } else { @@ -1014,9 +1085,11 @@ moveCM(Context* c, unsigned aSize UNUSED, Assembler::Constant* a, break; case 8: { - if (BytesPerWord == 8) { + if (BytesPerWord == 8) { if(a->value->resolved() and isInt32(a->value->value())) { - encode(c, 0xc7, 0, b, true); + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); c->code.append4(a->value->value()); } else { Assembler::Register tmp(c->client->acquireTemporary()); @@ -1024,8 +1097,8 @@ moveCM(Context* c, unsigned aSize UNUSED, Assembler::Constant* a, moveRM(c, 8, &tmp, 8, b); c->client->releaseTemporary(tmp.low); } - } else { - Assembler::Constant ah(shiftMaskPromise(c, a->value, 32, 0xFFFFFFFF)); + } else { + Assembler::Constant ah(shiftMaskPromise(c, a->value, 32, 0xFFFFFFFF)); Assembler::Constant al(shiftMaskPromise(c, a->value, 0, 0xFFFFFFFF)); Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale); @@ -1045,10 +1118,9 @@ moveZRR(Context* c, unsigned aSize, Assembler::Register* a, { switch (aSize) { case 2: - rex(c); - c->code.append(0x0f); - c->code.append(0xb7); - c->code.append(0xc0 | (b->low << 3) | a->low); + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xb7); + modrm(c, 0xc0, a, b); break; default: abort(c); @@ -1061,8 +1133,10 @@ moveZMR(Context* c, unsigned aSize UNUSED, Assembler::Memory* a, { assert(c, bSize == BytesPerWord); assert(c, aSize == 2); - - encode2(c, 0x0fb7, b->low, a, true); + + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xb7); + modrmSibImm(c, b->low, a->scale, a->index, a->base, a->offset); } void @@ -1071,9 +1145,9 @@ addCarryRR(Context* c, unsigned size, Assembler::Register* a, { assert(c, BytesPerWord == 8 or size == 4); - if (size == 8) rex(c); - c->code.append(0x11); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, size, a, b); + opcode(c, 0x11); + modrm(c, 0xc0, b, a); } void @@ -1089,9 +1163,9 @@ addRR(Context* c, unsigned aSize, Assembler::Register* a, addRR(c, 4, a, 4, b); addCarryRR(c, 4, &ah, &bh); } else { - if (aSize == 8) rex(c); - c->code.append(0x01); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x01); + modrm(c, 0xc0, b, a); } } @@ -1099,12 +1173,11 @@ void addCarryCR(Context* c, unsigned size UNUSED, Assembler::Constant* a, Assembler::Register* b) { - assert(c, BytesPerWord == 8 or size == 4); int64_t v = a->value->value(); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xd0 | b->low); + maybeRex(c, size, b); + opcode(c, 0x83, 0xd0 + regCode(b)); c->code.append(v); } else { abort(c); @@ -1132,14 +1205,12 @@ addCR(Context* c, unsigned aSize, Assembler::Constant* a, addCarryCR(c, 4, &ah, &bh); } else { if (isInt32(v)) { - if (bSize == 8) rex(c); + maybeRex(c, aSize, b); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xc0 | b->low); + opcode(c, 0x83, 0xc0 + regCode(b)); c->code.append(v); } else { - c->code.append(0x81); - c->code.append(0xc0 | b->low); + opcode(c, 0x81, 0xc0 + regCode(b)); c->code.append4(v); } } else { @@ -1160,8 +1231,7 @@ subtractBorrowCR(Context* c, unsigned size UNUSED, Assembler::Constant* a, int64_t v = a->value->value(); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xd8 | b->low); + opcode(c, 0x83, 0xd8 + regCode(b)); c->code.append(v); } else { abort(c); @@ -1193,14 +1263,12 @@ subtractCR(Context* c, unsigned aSize, Assembler::Constant* a, subtractBorrowCR(c, 4, &ah, &bh); } else { if (isInt32(v)) { - if (bSize == 8) rex(c); + maybeRex(c, aSize, b); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xe8 | b->low); + opcode(c, 0x83, 0xe8 + regCode(b)); c->code.append(v); } else { - c->code.append(0x81); - c->code.append(0xe8 | b->low); + opcode(c, 0x81, 0xe8 + regCode(b)); c->code.append4(v); } } else { @@ -1219,9 +1287,9 @@ subtractBorrowRR(Context* c, unsigned size, Assembler::Register* a, { assert(c, BytesPerWord == 8 or size == 4); - if (size == 8) rex(c); - c->code.append(0x19); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, size, a, b); + opcode(c, 0x19); + modrm(c, 0xc0, b, a); } void @@ -1229,7 +1297,7 @@ subtractRR(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize UNUSED, Assembler::Register* b) { assert(c, aSize == bSize); - + if (BytesPerWord == 4 and aSize == 8) { Assembler::Register ah(a->high); Assembler::Register bh(b->high); @@ -1237,9 +1305,9 @@ subtractRR(Context* c, unsigned aSize, Assembler::Register* a, subtractRR(c, 4, a, 4, b); subtractBorrowRR(c, 4, &ah, &bh); } else { - if (aSize == 8) rex(c); - c->code.append(0x29); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x29); + modrm(c, 0xc0, b, a); } } @@ -1249,6 +1317,7 @@ andRR(Context* c, unsigned aSize, Assembler::Register* a, { assert(c, aSize == bSize); + if (BytesPerWord == 4 and aSize == 8) { Assembler::Register ah(a->high); Assembler::Register bh(b->high); @@ -1256,9 +1325,9 @@ andRR(Context* c, unsigned aSize, Assembler::Register* a, andRR(c, 4, a, 4, b); andRR(c, 4, &ah, 4, &bh); } else { - if (aSize == 8) rex(c); - c->code.append(0x21); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x21); + modrm(c, 0xc0, b, a); } } @@ -1283,14 +1352,12 @@ andCR(Context* c, unsigned aSize, Assembler::Constant* a, andCR(c, 4, &ah, 4, &bh); } else { if (isInt32(v)) { - if (bSize == 8) rex(c); + maybeRex(c, aSize, b); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xe0 | b->low); + opcode(c, 0x83, 0xe0 + regCode(b)); c->code.append(v); } else { - c->code.append(0x81); - c->code.append(0xe0 | b->low); + opcode(c, 0x81, 0xe0 + regCode(b)); c->code.append4(v); } } else { @@ -1315,9 +1382,9 @@ orRR(Context* c, unsigned aSize, Assembler::Register* a, orRR(c, 4, a, 4, b); orRR(c, 4, &ah, 4, &bh); } else { - if (aSize == 8) rex(c); - c->code.append(0x09); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x09); + modrm(c, 0xc0, b, a); } } @@ -1342,14 +1409,12 @@ orCR(Context* c, unsigned aSize, Assembler::Constant* a, orCR(c, 4, &ah, 4, &bh); } else { if (isInt32(v)) { - if (bSize == 8) rex(c); + maybeRex(c, aSize, b); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xc8 | b->low); + opcode(c, 0x83, 0xc8 + regCode(b)); c->code.append(v); } else { - c->code.append(0x81); - c->code.append(0xc8 | b->low); + opcode(c, 0x81, 0xc8 + regCode(b)); c->code.append4(v); } } else { @@ -1373,9 +1438,9 @@ xorRR(Context* c, unsigned aSize, Assembler::Register* a, xorRR(c, 4, a, 4, b); xorRR(c, 4, &ah, 4, &bh); } else { - if (aSize == 8) rex(c); - c->code.append(0x31); - c->code.append(0xc0 | (a->low << 3) | b->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x31); + modrm(c, 0xc0, b, a); } } @@ -1400,14 +1465,12 @@ xorCR(Context* c, unsigned aSize, Assembler::Constant* a, xorCR(c, 4, &ah, 4, &bh); } else { if (isInt32(v)) { - if (bSize == 8) rex(c); + maybeRex(c, aSize, b); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xf0 | b->low); + opcode(c, 0x83, 0xf0 + regCode(b)); c->code.append(v); } else { - c->code.append(0x81); - c->code.append(0xf0 | b->low); + opcode(c, 0x81, 0xf0 + regCode(b)); c->code.append4(v); } } else { @@ -1426,6 +1489,7 @@ multiplyRR(Context* c, unsigned aSize, Assembler::Register* a, { assert(c, aSize == bSize); + if (BytesPerWord == 4 and aSize == 8) { assert(c, b->high == rdx); assert(c, b->low != rax); @@ -1444,16 +1508,14 @@ multiplyRR(Context* c, unsigned aSize, Assembler::Register* a, addRR(c, 4, &bh, 4, b); // mul a->low,%eax%edx - c->code.append(0xf7); - c->code.append(0xe0 | a->low); + opcode(c, 0xf7, 0xe0 + a->low); addRR(c, 4, b, 4, &bh); moveRR(c, 4, &axdx, 4, b); } else { - if (aSize == 8) rex(c); - c->code.append(0x0f); - c->code.append(0xaf); - c->code.append(0xc0 | (b->low << 3) | a->low); + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0xaf); + modrm(c, 0xc0, a, b); } } @@ -1462,11 +1524,11 @@ compareRR(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize UNUSED, Assembler::Register* b) { assert(c, aSize == bSize); - assert(c, BytesPerWord == 8 or aSize == 4); - if (aSize == 8) rex(c); - c->code.append(0x39); - c->code.append(0xc0 | (a->low << 3) | b->low); + + maybeRex(c, aSize, a, b); + opcode(c, 0x39); + modrm(c, 0xc0, b, a); } void @@ -1478,14 +1540,12 @@ compareCR(Context* c, unsigned aSize, Assembler::Constant* a, if (a->value->resolved() and isInt32(a->value->value())) { int64_t v = a->value->value(); - if (aSize == 8) rex(c); + maybeRex(c, aSize, b); if (isInt8(v)) { - c->code.append(0x83); - c->code.append(0xf8 | b->low); + opcode(c, 0x83, 0xf8 + regCode(b)); c->code.append(v); } else { - c->code.append(0x81); - c->code.append(0xf8 | b->low); + opcode(c, 0x81, 0xf8 + regCode(b)); c->code.append4(v); } } else { @@ -1515,14 +1575,14 @@ multiplyCR(Context* c, unsigned aSize, Assembler::Constant* a, int64_t v = a->value->value(); if (v != 1) { if (isInt32(v)) { - if (bSize == 8) rex(c); + maybeRex(c, bSize, b, b); if (isInt8(v)) { - c->code.append(0x6b); - c->code.append(0xc0 | (b->low << 3) | b->low); + opcode(c, 0x6b); + modrm(c, 0xc0, b, b); c->code.append(v); } else { - c->code.append(0x69); - c->code.append(0xc0 | (b->low << 3) | b->low); + opcode(c, 0x69); + modrm(c, 0xc0, b, b); c->code.append4(v); } } else { @@ -1545,7 +1605,9 @@ compareRM(Context* c, unsigned aSize, Assembler::Register* a, if (BytesPerWord == 8 and aSize == 4) { moveRR(c, 4, a, 8, a); } - encode(c, 0x39, a->low, b, true); + maybeRex(c, bSize, a, b); + opcode(c, 0x39); + modrmSibImm(c, a, b); } void @@ -1557,7 +1619,9 @@ compareCM(Context* c, unsigned aSize, Assembler::Constant* a, if (a->value->resolved()) { int64_t v = a->value->value(); - encode(c, isInt8(v) ? 0x83 : 0x81, 7, b, true); + maybeRex(c, aSize, b); + opcode(c, isInt8(v) ? 0x83 : 0x81); + modrmSibImm(c, rdi, b->scale, b->index, b->base, b->offset); if (isInt8(v)) { c->code.append(v); @@ -1575,8 +1639,8 @@ compareCM(Context* c, unsigned aSize, Assembler::Constant* a, } void -longCompare(Context* c, Assembler::Operand* al, Assembler::Operand* ah, - Assembler::Register* bl, Assembler::Operand* bh, +longCompare(Context* c, Assembler::Operand* al, UNUSED Assembler::Operand* ah, + Assembler::Register* bl, UNUSED Assembler::Operand* bh, BinaryOperationType compare) { ResolvedPromise negativePromise(-1); @@ -1591,19 +1655,17 @@ longCompare(Context* c, Assembler::Operand* al, Assembler::Operand* ah, if (BytesPerWord == 8) { compare(c, 8, al, 8, bl); - c->code.append(0x0f); - c->code.append(0x8c); // jl + opcode(c, 0x0f, 0x8c); // jl unsigned less = c->code.length(); c->code.append4(0); - c->code.append(0x0f); - c->code.append(0x8f); // jg + opcode(c, 0x0f, 0x8f); // jg unsigned greater = c->code.length(); c->code.append4(0); moveCR(c, 4, &zero, 4, bl); - c->code.append(0xe9); // jmp + opcode(c, 0xe9); // jmp unsigned nextFirst = c->code.length(); c->code.append4(0); @@ -1612,7 +1674,7 @@ longCompare(Context* c, Assembler::Operand* al, Assembler::Operand* ah, moveCR(c, 4, &negative, 4, bl); - c->code.append(0xe9); // jmp + opcode(c, 0xe9); // jmp unsigned nextSecond = c->code.length(); c->code.append4(0); @@ -1629,25 +1691,21 @@ longCompare(Context* c, Assembler::Operand* al, Assembler::Operand* ah, } else { compare(c, 4, ah, 4, bh); - c->code.append(0x0f); - c->code.append(0x8c); // jl + opcode(c, 0x0f, 0x8c); //jl unsigned less = c->code.length(); c->code.append4(0); - c->code.append(0x0f); - c->code.append(0x8f); // jg + opcode(c, 0x0f, 0x8f); //jg unsigned greater = c->code.length(); c->code.append4(0); compare(c, 4, al, 4, bl); - c->code.append(0x0f); - c->code.append(0x82); // ja + opcode(c, 0x0f, 0x82); //ja unsigned above = c->code.length(); c->code.append4(0); - c->code.append(0x0f); - c->code.append(0x87); // jb + opcode(c, 0x0f, 0x87); //jb unsigned below = c->code.length(); c->code.append4(0); @@ -1665,7 +1723,7 @@ longCompare(Context* c, Assembler::Operand* al, Assembler::Operand* ah, moveCR(c, 4, &negative, 4, bl); - c->code.append(0xe9); // jmp + opcode(c, 0xe9); // jmp unsigned nextSecond = c->code.length(); c->code.append4(0); @@ -1689,7 +1747,6 @@ void divideRR(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize UNUSED, Assembler::Register* b UNUSED) { - assert(c, BytesPerWord == 8 or aSize == 4); assert(c, aSize == bSize); assert(c, b->low == rax); @@ -1697,18 +1754,16 @@ divideRR(Context* c, unsigned aSize, Assembler::Register* a, c->client->save(rdx); - if (aSize == 8) rex(c); - c->code.append(0x99); // cdq - if (aSize == 8) rex(c); - c->code.append(0xf7); - c->code.append(0xf8 | a->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x99); // cdq + maybeRex(c, aSize, b, a); + opcode(c, 0xf7, 0xf8 + regCode(a)); } void remainderRR(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize UNUSED, Assembler::Register* b) { - assert(c, BytesPerWord == 8 or aSize == 4); assert(c, aSize == bSize); assert(c, b->low == rax); @@ -1716,11 +1771,10 @@ remainderRR(Context* c, unsigned aSize, Assembler::Register* a, c->client->save(rdx); - if (aSize == 8) rex(c); - c->code.append(0x99); // cdq - if (aSize == 8) rex(c); - c->code.append(0xf7); - c->code.append(0xf8 | a->low); + maybeRex(c, aSize, a, b); + opcode(c, 0x99); // cdq + maybeRex(c, aSize, b, a); + opcode(c, 0xf7, 0xf8 + regCode(a)); Assembler::Register dx(rdx); moveRR(c, BytesPerWord, &dx, BytesPerWord, b); @@ -1760,10 +1814,10 @@ longCompareRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, } void -doShift(Context* c, void (*shift) +doShift(Context* c, UNUSED void (*shift) (Context*, unsigned, Assembler::Register*, unsigned, Assembler::Register*), - int type, unsigned aSize, Assembler::Constant* a, + int type, UNUSED unsigned aSize, Assembler::Constant* a, unsigned bSize, Assembler::Register* b) { int64_t v = a->value->value(); @@ -1775,13 +1829,11 @@ doShift(Context* c, void (*shift) moveCR(c, 4, a, 4, &cx); shift(c, aSize, &cx, bSize, b); } else { - if (bSize == 8) rex(c); + maybeRex(c, bSize, b); if (v == 1) { - c->code.append(0xd1); - c->code.append(type | b->low); + opcode(c, 0xd1, type + regCode(b)); } else if (isInt8(v)) { - c->code.append(0xc1); - c->code.append(type | b->low); + opcode(c, 0xc1, type + regCode(b)); c->code.append(v); } else { abort(c); @@ -1790,36 +1842,32 @@ doShift(Context* c, void (*shift) } void -shiftLeftRR(Context* c, unsigned aSize, Assembler::Register* a, +shiftLeftRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a, unsigned bSize, Assembler::Register* b) { assert(c, a->low == rcx); - + if (BytesPerWord == 4 and bSize == 8) { // shld - c->code.append(0x0f); - c->code.append(0xa5); - c->code.append(0xc0 | (b->low << 3) | b->high); + opcode(c, 0x0f, 0xa5); + modrm(c, 0xc0, b->high, b->low); // shl - c->code.append(0xd3); - c->code.append(0xe0 | b->low); + opcode(c, 0xd3, 0xe0 + b->low); ResolvedPromise promise(32); Assembler::Constant constant(&promise); compareCR(c, aSize, &constant, aSize, a); - c->code.append(0x0f); - c->code.append(0x8c); // jl + opcode(c, 0x0f, 0x8c); //jl c->code.append4(2 + 2); Assembler::Register bh(b->high); moveRR(c, 4, b, 4, &bh); // 2 bytes xorRR(c, 4, b, 4, b); // 2 bytes } else { - if (bSize == 8) rex(c); - c->code.append(0xd3); - c->code.append(0xe0 | b->low); + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xe0 + regCode(b)); } } @@ -1831,40 +1879,34 @@ shiftLeftCR(Context* c, unsigned aSize, Assembler::Constant* a, } void -shiftRightRR(Context* c, unsigned aSize, Assembler::Register* a, +shiftRightRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a, unsigned bSize, Assembler::Register* b) { assert(c, a->low == rcx); - if (BytesPerWord == 4 and bSize == 8) { // shrd - c->code.append(0x0f); - c->code.append(0xad); - c->code.append(0xc0 | (b->high << 3) | b->low); + opcode(c, 0x0f, 0xad); + modrm(c, 0xc0, b->low, b->high); // sar - c->code.append(0xd3); - c->code.append(0xf8 | b->high); + opcode(c, 0xd3, 0xf8 + b->high); ResolvedPromise promise(32); Assembler::Constant constant(&promise); compareCR(c, aSize, &constant, aSize, a); - c->code.append(0x0f); - c->code.append(0x8c); // jl + opcode(c, 0x0f, 0x8c); //jl c->code.append4(2 + 3); Assembler::Register bh(b->high); moveRR(c, 4, &bh, 4, b); // 2 bytes // sar 31,high - c->code.append(0xc1); - c->code.append(0xf8 | b->high); + opcode(c, 0xc1, 0xf8 + b->high); c->code.append(31); } else { - if (bSize == 8) rex(c); - c->code.append(0xd3); - c->code.append(0xf8 | b->low); + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xf8 + regCode(b)); } } @@ -1876,36 +1918,32 @@ shiftRightCR(Context* c, unsigned aSize, Assembler::Constant* a, } void -unsignedShiftRightRR(Context* c, unsigned aSize, Assembler::Register* a, +unsignedShiftRightRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a, unsigned bSize, Assembler::Register* b) { assert(c, a->low == rcx); if (BytesPerWord == 4 and bSize == 8) { // shrd - c->code.append(0x0f); - c->code.append(0xad); - c->code.append(0xc0 | (b->high << 3) | b->low); + opcode(c, 0x0f, 0xad); + modrm(c, 0xc0, b->low, b->high); // shr - c->code.append(0xd3); - c->code.append(0xe8 | b->high); + opcode(c, 0xd3, 0xe8 + b->high); ResolvedPromise promise(32); Assembler::Constant constant(&promise); compareCR(c, aSize, &constant, aSize, a); - c->code.append(0x0f); - c->code.append(0x8c); // jl + opcode(c, 0x0f, 0x8c); //jl c->code.append4(2 + 2); Assembler::Register bh(b->high); moveRR(c, 4, &bh, 4, b); // 2 bytes xorRR(c, 4, &bh, 4, &bh); // 2 bytes } else { - if (bSize == 8) rex(c); - c->code.append(0xd3); - c->code.append(0xe8 | b->low); + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xe8 + regCode(b)); } } @@ -2015,7 +2053,7 @@ class MyArchitecture: public Assembler::Architecture { } virtual unsigned registerCount() { - return 8;//BytesPerWord == 4 ? 8 : 16; + return (BytesPerWord == 4 ? 8 : 16); } virtual int stack() { @@ -2063,9 +2101,13 @@ class MyArchitecture: public Assembler::Architecture { } virtual unsigned frameFootprint(unsigned footprint) { +#ifdef __MINGW32__ + return max(footprint, StackAlignmentInWords); +#else return max(footprint > argumentRegisterCount() ? footprint - argumentRegisterCount() : 0, StackAlignmentInWords); +#endif } virtual unsigned argumentFootprint(unsigned footprint) { @@ -2073,13 +2115,27 @@ class MyArchitecture: public Assembler::Architecture { } virtual unsigned argumentRegisterCount() { - return (BytesPerWord == 4 ? 0 : 6); +#ifdef __MINGW32__ + if (BytesPerWord == 8) return 4; else +#else + if (BytesPerWord == 8) return 6; else +#endif + return 0; } virtual int argumentRegister(unsigned index) { assert(&c, BytesPerWord == 8); - switch (index) { +#ifdef __MINGW32__ + case 0: + return rcx; + case 1: + return rdx; + case 2: + return r8; + case 3: + return r9; +#else case 0: return rdi; case 1: @@ -2092,6 +2148,7 @@ class MyArchitecture: public Assembler::Architecture { return r8; case 5: return r9; +#endif default: abort(&c); } @@ -2114,7 +2171,7 @@ class MyArchitecture: public Assembler::Architecture { { if (BytesPerWord == 4 or op == Call or op == Jump) { uint8_t* instruction = static_cast(returnAddress) - 5; - + assert(&c, ((op == Call or op == LongCall) and *instruction == 0xE8) or ((op == Jump or op == LongJump) and *instruction == 0xE9)); @@ -2611,3 +2668,5 @@ makeAssembler(System* system, Allocator* allocator, Zone* zone, } // namespace vm + +#endif //(defined __i386__) || (defined __x86_64__) diff --git a/src/x86.h b/src/x86.h index d746083c3b..a7f8e4dd8f 100644 --- a/src/x86.h +++ b/src/x86.h @@ -58,11 +58,24 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t*, # define THREAD_REGISTER(context) (context->uc_mcontext.gregs[REG_RBX]) extern "C" uint64_t +# ifdef __MINGW32__ +vmNativeCall(void* function, void* stack, unsigned stackSize, + unsigned returnType); +# else vmNativeCall(void* function, void* stack, unsigned stackSize, void* gprTable, void* sseTable, unsigned returnType); +# endif namespace vm { +# ifdef __MINGW32__ +inline uint64_t +dynamicCall(void* function, uint64_t* arguments, UNUSED uint8_t* argumentTypes, + unsigned argumentCount, unsigned, unsigned returnType) +{ + return vmNativeCall(function, arguments, argumentCount, returnType); +} +# else inline uint64_t dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes, unsigned argumentCount, unsigned, unsigned returnType) @@ -103,6 +116,7 @@ dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes, (gprIndex ? gprTable : 0), (sseIndex ? sseTable : 0), returnType); } +#endif } // namespace vm diff --git a/test/Finalizers.java b/test/Finalizers.java new file mode 100644 index 0000000000..b7844988bf --- /dev/null +++ b/test/Finalizers.java @@ -0,0 +1,34 @@ +public class Finalizers { + private static boolean finalized = false; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + protected void finalize() { + finalized = true; + } + + public static void main(String[] args) { + new Finalizers(); + + expect(! finalized); + + System.gc(); + + expect(finalized); + + new Finalizers2(); + + finalized = false; + + expect(! finalized); + + System.gc(); + + expect(finalized); + } + + private static class Finalizers2 extends Finalizers { } + +} diff --git a/test/Subroutine.java b/test/Subroutine.java index 3ad78b00b8..1a631e25d5 100644 --- a/test/Subroutine.java +++ b/test/Subroutine.java @@ -3,13 +3,14 @@ public class Subroutine { if (! v) throw new RuntimeException(); } - // This test is intended to cover the jsr and ret instructions. + // These tests are intended to cover the jsr and ret instructions. // However, recent Sun javac versions avoid generating these // instructions by default, so we must compile this class using // -source 1.2 -target 1.1 -XDjsrlimit=0. // // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4381996 // + private static void test(boolean throw_, boolean predicate) { int x = 42; int y = 99; @@ -32,10 +33,159 @@ public class Subroutine { } } + private static Object test2(int path) { + try { + try { + switch (path) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + return null; + } catch (DummyException e) { + e.printStackTrace(); + return null; + } + } + + private static Object test3(int path1, int path2, int path3) { + try { + try { + switch (path1) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + try { + switch (path2) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + try { + switch (path3) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + } + } + return null; + } catch (DummyException e) { + e.printStackTrace(); + return null; + } + } + + private static long test4(int path) { + try { + try { + switch (path) { + case 1: + return 42L; + + case 2: { + int a = 42; + return 52L; + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + return 0L; + } catch (DummyException e) { + e.printStackTrace(); + return 0L; + } + } + public static void main(String[] args) { test(false, false); test(false, true); test(true, false); + + String.valueOf(test2(1)); + String.valueOf(test2(2)); + String.valueOf(test2(3)); + + String.valueOf(test3(1, 1, 1)); + String.valueOf(test3(2, 1, 1)); + String.valueOf(test3(3, 1, 1)); + + String.valueOf(test3(1, 2, 1)); + String.valueOf(test3(2, 2, 1)); + String.valueOf(test3(3, 2, 1)); + + String.valueOf(test3(1, 3, 1)); + String.valueOf(test3(2, 3, 1)); + String.valueOf(test3(3, 3, 1)); + + String.valueOf(test3(1, 1, 2)); + String.valueOf(test3(2, 1, 2)); + String.valueOf(test3(3, 1, 2)); + + String.valueOf(test3(1, 2, 2)); + String.valueOf(test3(2, 2, 2)); + String.valueOf(test3(3, 2, 2)); + + String.valueOf(test3(1, 3, 2)); + String.valueOf(test3(2, 3, 2)); + String.valueOf(test3(3, 3, 2)); + + String.valueOf(test3(1, 1, 3)); + String.valueOf(test3(2, 1, 3)); + String.valueOf(test3(3, 1, 3)); + + String.valueOf(test3(1, 2, 3)); + String.valueOf(test3(2, 2, 3)); + String.valueOf(test3(3, 2, 3)); + + String.valueOf(test3(1, 3, 3)); + String.valueOf(test3(2, 3, 3)); + String.valueOf(test3(3, 3, 3)); + + String.valueOf(test4(1)); + String.valueOf(test4(2)); + String.valueOf(test4(3)); } private static class DummyException extends RuntimeException { } diff --git a/test/Tree.java b/test/Tree.java index 55858ab8d7..ded42a27ea 100644 --- a/test/Tree.java +++ b/test/Tree.java @@ -88,26 +88,4 @@ public class Tree { isEqual(printMap(map), "a=A, b=B, c=C, q=Q, y=Y, z=Z"); } - - private static class MyEntry implements Map.Entry { - public final K key; - public V value; - - public MyEntry(K key, V value) { - this.key = key; - this.value = value; - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public void setValue(V value) { - this.value = value; - } - } } diff --git a/test/Zip.java b/test/Zip.java index b18d5f1a0b..2aea1760f1 100644 --- a/test/Zip.java +++ b/test/Zip.java @@ -9,7 +9,9 @@ public class Zip { ZipFile file = new ZipFile("build/classpath.jar"); byte[] buffer = new byte[4096]; - for (Enumeration e = file.entries(); e.hasMoreElements();) { + for (Enumeration e = file.entries(); + e.hasMoreElements();) + { ZipEntry entry = e.nextElement(); InputStream in = file.getInputStream(entry); try { diff --git a/vm.pro b/vm.pro index 9e790fbc02..3ba4620c0e 100644 --- a/vm.pro +++ b/vm.pro @@ -73,7 +73,9 @@ # Thread.run is called by name in the VM --keepclassmembernames class java.lang.Thread { void run(); } +-keepclassmembers class java.lang.Thread { + private static void run(java.lang.Thread); + } # when continuations are enabled, the VM may call these methods by name: