diff --git a/classpath/avian/Addendum.java b/classpath/avian/Addendum.java index 248ee68ec1..b363ecfc7c 100644 --- a/classpath/avian/Addendum.java +++ b/classpath/avian/Addendum.java @@ -12,5 +12,6 @@ package avian; public class Addendum { public Object pool; - public Object annotationTable; + public Object annotationTable; + public Object signature; } diff --git a/classpath/avian/ClassAddendum.java b/classpath/avian/ClassAddendum.java index 4382e7f717..276be33fe9 100644 --- a/classpath/avian/ClassAddendum.java +++ b/classpath/avian/ClassAddendum.java @@ -10,4 +10,8 @@ package avian; -public class ClassAddendum extends Addendum { } +public class ClassAddendum extends Addendum { + public Object[] interfaceTable; + public Object[] innerClassTable; + public Object[] methodTable; +} diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 3e18f4c0df..1d2ad9370e 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -26,8 +26,6 @@ public class Classes { public static native VMClass defineVMClass (ClassLoader loader, byte[] b, int offset, int length); - public static native VMClass vmClass(Object o); - public static native VMClass primitiveClass(char name); public static native void initialize(VMClass vmClass); @@ -53,7 +51,7 @@ public class Classes { return c; } catch (ClassNotFoundException e) { NoClassDefFoundError error = new NoClassDefFoundError - (new String(nameBytes, offset, length, false)); + (new String(nameBytes, offset, length)); error.initCause(e); throw error; } @@ -96,7 +94,7 @@ public class Classes { case 's': { byte[] data = (byte[]) Singleton.getObject(pool, read2(in) - 1); - return new String(data, 0, data.length - 1, false); + return new String(data, 0, data.length - 1); } case 'e': { @@ -106,7 +104,7 @@ public class Classes { return Enum.valueOf (SystemClassLoader.getClass (loadVMClass(loader, typeName, 1, typeName.length - 3)), - new String(name, 0, name.length - 1, false)); + new String(name, 0, name.length - 1)); } case 'c':{ @@ -143,7 +141,7 @@ public class Classes { for (int i = 2; i < annotation.length; i += 2) { byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1); - annotation[i] = new String(name, 0, name.length - 1, false); + annotation[i] = new String(name, 0, name.length - 1); annotation[i + 1] = parseAnnotationValue(loader, pool, in); } diff --git a/classpath/avian/MethodAddendum.java b/classpath/avian/MethodAddendum.java index a2e40a5261..597c169179 100644 --- a/classpath/avian/MethodAddendum.java +++ b/classpath/avian/MethodAddendum.java @@ -12,4 +12,5 @@ package avian; public class MethodAddendum extends Addendum { public Object exceptionTable; + public Object annotationDefault; } diff --git a/classpath/avian/OpenJDK.java b/classpath/avian/OpenJDK.java index 877a7c2289..de9c6f1f40 100644 --- a/classpath/avian/OpenJDK.java +++ b/classpath/avian/OpenJDK.java @@ -10,14 +10,55 @@ package avian; +import java.net.URL; +import java.net.MalformedURLException; +import java.security.CodeSource; import java.security.AllPermission; import java.security.Permissions; import java.security.ProtectionDomain; +import java.security.cert.Certificate; public class OpenJDK { - public static ProtectionDomain getProtectionDomain() { + public static ProtectionDomain getProtectionDomain(VMClass c) { + CodeSource source = null; + if (c.source != null) { + try { + source = new CodeSource + (new URL(new String(c.source, 0, c.source.length - 1)), + (Certificate[]) null); + } catch (MalformedURLException ignored) { } + } + Permissions p = new Permissions(); p.add(new AllPermission()); - return new ProtectionDomain(null, p); + + return new ProtectionDomain(source, p); + } + + private static byte[] replace(int a, int b, byte[] s, int offset, + int length) + { + byte[] array = new byte[length]; + for (int i = 0; i < length; ++i) { + byte c = s[i]; + array[i] = (byte) (c == a ? b : c); + } + return array; + } + + public static Class getDeclaringClass(VMClass c) { + try { + String name = new String + (replace('/', '.', c.name, 0, c.name.length - 1), 0, + c.name.length - 1); + int index = name.lastIndexOf("$"); + if (index == -1) { + return null; + } else { + return c.loader.loadClass(name.substring(0, index)); + } + } catch (ClassNotFoundException e) { + return null; + } } } diff --git a/classpath/avian/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java index 6edd8b7a68..defb9d490a 100644 --- a/classpath/avian/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -12,6 +12,7 @@ package avian; import java.net.URL; import java.net.MalformedURLException; +import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.ArrayList; @@ -34,17 +35,59 @@ public class SystemClassLoader extends ClassLoader { return c == null ? null : getClass(c); } - private native boolean resourceExists(String name); + private native String resourceURLPrefix(String name); protected URL findResource(String name) { - if (resourceExists(name)) { + String prefix = resourceURLPrefix(name); + if (prefix != null) { try { - return new URL("resource:" + name); + return new URL(prefix + name); } catch (MalformedURLException ignored) { } } return null; } + // OpenJDK's java.lang.ClassLoader.getResource makes use of + // sun.misc.Launcher to load bootstrap resources, which is not + // appropriate for the Avian build, so we override it to ensure we + // get the behavior we want. This implementation is the same as + // that of Avian's java.lang.ClassLoader.getResource. + public URL getResource(String path) { + URL url = null; + ClassLoader parent = getParent(); + if (parent != null) { + url = parent.getResource(path); + } + + if (url == null) { + url = findResource(path); + } + + return url; + } + + // As above, we override this method to avoid inappropriate behavior + // in OpenJDK's java.lang.ClassLoader.getResources. + public Enumeration getResources(String name) throws IOException { + Collection urls = new ArrayList(5); + + ClassLoader parent = getParent(); + if (parent != null) { + for (Enumeration e = parent.getResources(name); + e.hasMoreElements();) + { + urls.add(e.nextElement()); + } + } + + URL url = findResource(name); + if (url != null) { + urls.add(url); + } + + return Collections.enumeration(urls); + } + protected Enumeration findResources(String name) { Collection urls = new ArrayList(1); URL url = findResource(name); diff --git a/classpath/avian/VMClass.java b/classpath/avian/VMClass.java index b32ff7f689..5a3fdc662f 100644 --- a/classpath/avian/VMClass.java +++ b/classpath/avian/VMClass.java @@ -25,7 +25,8 @@ public class VMClass { public VMMethod[] virtualTable; public VMField[] fieldTable; public VMMethod[] methodTable; - public avian.ClassAddendum addendum; + public ClassAddendum addendum; public Object staticTable; public ClassLoader loader; + public byte[] source; } diff --git a/classpath/avian/VMField.java b/classpath/avian/VMField.java index 699a68e5f8..8df0d426c5 100644 --- a/classpath/avian/VMField.java +++ b/classpath/avian/VMField.java @@ -15,6 +15,7 @@ public class VMField { public byte code; public short flags; public short offset; + public int nativeID; public byte[] name; public byte[] spec; public FieldAddendum addendum; diff --git a/classpath/avian/file/Handler.java b/classpath/avian/file/Handler.java new file mode 100644 index 0000000000..14ff26db37 --- /dev/null +++ b/classpath/avian/file/Handler.java @@ -0,0 +1,44 @@ +/* Copyright (c) 2011, 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.file; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new FileURLConnection(url); + } + + private static class FileURLConnection extends URLConnection { + public FileURLConnection(URL url) { + super(url); + } + + public int getContentLength() { + return (int) new File(url.getFile()).length(); + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(url.getFile()); + } + + public void connect() { + // ignore + } + } +} diff --git a/classpath/avian/jar/Handler.java b/classpath/avian/jar/Handler.java new file mode 100644 index 0000000000..fcd79625c3 --- /dev/null +++ b/classpath/avian/jar/Handler.java @@ -0,0 +1,84 @@ +/* Copyright (c) 2011, 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.jar; + +import java.net.URL; +import java.net.MalformedURLException; +import java.net.URLStreamHandler; +import java.net.JarURLConnection; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.jar.JarFile; +import java.util.jar.JarEntry; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new MyJarURLConnection(url); + } + + protected void parseURL(URL url, String s, int start, int end) + throws MalformedURLException + { + // skip "jar:" + s = s.toString().substring(4); + int index = s.indexOf("!/"); + if (index < 0) { + throw new MalformedURLException(); + } + + URL file = new URL(s.substring(0, index)); + if (! "file".equals(file.getProtocol())) { + throw new RuntimeException + ("protocol " + file.getProtocol() + " not yet supported"); + } + + url.set("jar", "", -1, s, null); + } + + private static class MyJarURLConnection extends JarURLConnection { + private final JarFile file; + private final JarEntry entry; + + public MyJarURLConnection(URL url) { + super(url); + + String s = url.getFile(); + int index = s.indexOf("!/"); + + try { + this.file = new JarFile(new URL(s.substring(0, index)).getFile()); + this.entry = this.file.getJarEntry(s.substring(index + 2)); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public JarFile getJarFile() throws IOException { + return file; + } + + public int getContentLength() { + return entry.getSize(); + } + + public InputStream getInputStream() throws IOException { + return file.getInputStream(entry); + } + + public void connect() { + // ignore + } + } +} diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index 5c023242cd..779682c8c0 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -17,6 +17,7 @@ #include "jni-util.h" #include "errno.h" #include "fcntl.h" +#include "ctype.h" #ifdef PLATFORM_WINDOWS @@ -128,6 +129,61 @@ namespace { #endif } +class Locale { // represents an ISO two-char language/country pair + static const unsigned FIELDLEN = 2; + static const unsigned FIELDSIZE = FIELDLEN + 1; + + static const char* DEFAULT_LANGUAGE; + static const char* DEFAULT_REGION; + + char language[FIELDSIZE]; + char region[FIELDSIZE]; + + bool isLanguage(const char* language) { + if (!language) return false; + unsigned len = strlen(language); + if (len != FIELDLEN) return false; + const char* p = language - 1; + while (islower(*++p)) ; + if (*p != '\0') return false; + return true; + } + + bool isRegion(const char* region) { + if (!region) return false; + unsigned len = strlen(region); + if (len != FIELDLEN) return false; + const char* p = region - 1; + while (isupper(*++p)) ; + if (*p != '\0') return false; + return true; + } + +public: + Locale(const char* language = "") { + Locale l(language, ""); + *this = l; + } + + Locale(const char* language, const char* region) { + language = isLanguage(language) ? language : DEFAULT_LANGUAGE; + region = isRegion(region) ? region : DEFAULT_REGION; + memcpy(this->language, language, FIELDSIZE); + memcpy(this->region, region, FIELDSIZE); + } + + Locale& operator=(const Locale& l) { + memcpy(language, l.language, FIELDSIZE); + memcpy(region, l.region, FIELDSIZE); + return *this; + } + + const char* getLanguage() { return reinterpret_cast(language); } + const char* getRegion() { return reinterpret_cast(region); } +}; +const char* Locale::DEFAULT_LANGUAGE = "en"; +const char* Locale::DEFAULT_REGION = ""; + #ifdef PLATFORM_WINDOWS extern "C" JNIEXPORT void JNICALL Java_java_lang_Runtime_exec(JNIEnv* e, jclass, @@ -221,6 +277,82 @@ Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid, jlong tid) return exitCode; } + +Locale getLocale() { + const char* lang = ""; + const char* reg = ""; + unsigned langid = GetUserDefaultUILanguage(); + unsigned prilang = langid & 0x3ff; + unsigned sublang = langid >> 10; + + switch (prilang) { + case 0x004: { + lang = "zh"; + switch (sublang) { + case 0x01: reg = "CN"; break; + case 0x02: reg = "TW"; break; + case 0x03: reg = "HK"; break; + case 0x04: reg = "SG"; break; + } + } break; + case 0x006: lang = "da"; reg = "DK"; break; + case 0x007: lang = "de"; reg = "DE"; break; + case 0x009: { + lang = "en"; + switch (sublang) { + case 0x01: reg = "US"; break; + case 0x02: reg = "GB"; break; + case 0x03: reg = "AU"; break; + case 0x04: reg = "CA"; break; + case 0x05: reg = "NZ"; break; + case 0x06: reg = "IE"; break; + case 0x07: reg = "ZA"; break; + case 0x10: reg = "IN"; break; + } + } break; + case 0x00a: { + lang = "es"; + switch (sublang) { + case 0x01: case 0x03: reg = "ES"; break; + case 0x02: reg = "MX"; break; + } + } break; + case 0x00c: { + lang = "fr"; + switch (sublang) { + case 0x01: reg = "FR"; break; + case 0x02: reg = "BE"; break; + case 0x03: reg = "CA"; break; + } + } break; + case 0x010: lang = "it"; reg = "IT"; break; + case 0x011: lang = "ja"; reg = "JP"; break; + case 0x012: lang = "ko"; reg = "KR"; break; + case 0x013: { + lang = "nl"; + switch (sublang) { + case 0x01: reg = "NL"; break; + case 0x02: reg = "BE"; break; + } + } break; + case 0x014: lang = "no"; reg = "NO"; break; + case 0x015: lang = "pl"; reg = "PL"; break; + case 0x016: { + lang = "pt"; + switch (sublang) { + case 0x01: reg = "BR"; break; + case 0x02: reg = "PT"; break; + } + } break; + case 0x018: lang = "ro"; reg = "RO"; break; + case 0x019: lang = "ru"; reg = "RU"; break; + case 0x01d: lang = "sv"; reg = "SE"; break; + default: lang = "en"; + } + + Locale locale(lang, reg); + return locale; +} #else extern "C" JNIEXPORT void JNICALL Java_java_lang_Runtime_exec(JNIEnv* e, jclass, @@ -331,6 +463,32 @@ Java_java_lang_Runtime_waitFor(JNIEnv*, jclass, jlong pid, jlong) return exitCode; } + +Locale getLocale() { + Locale fallback; + + const char* LANG = getenv("LANG"); + if (!LANG || strcmp(LANG, "C") == 0) return fallback; + + int len = strlen(LANG); + char buf[len + 1]; // + 1 for the '\0' char + memcpy(buf, LANG, len + 1); + + char* tracer = buf; + const char* reg; + + while (*tracer && *tracer != '_') ++tracer; + if (!*tracer) return fallback; + *tracer = '\0'; + reg = ++tracer; + + while (*tracer && *tracer != '.') ++tracer; + if (tracer == reg) return fallback; + *tracer = '\0'; + + Locale locale(buf, reg); + return locale; +} #endif extern "C" JNIEXPORT jstring JNICALL @@ -437,6 +595,13 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, r = e->NewStringUTF(getenv("HOME")); } #endif + else if (strcmp(chars, "user.language") == 0) { + Locale locale = getLocale(); + if (strlen(locale.getLanguage())) r = e->NewStringUTF(locale.getLanguage()); + } else if (strcmp(chars, "user.region") == 0) { + Locale locale = getLocale(); + if (strlen(locale.getRegion())) r = e->NewStringUTF(locale.getRegion()); + } e->ReleaseStringUTFChars(name, chars); } diff --git a/classpath/java-util-zip.cpp b/classpath/java-util-zip.cpp index a7d969839a..f883e528bc 100644 --- a/classpath/java-util-zip.cpp +++ b/classpath/java-util-zip.cpp @@ -121,7 +121,7 @@ Java_java_util_zip_Deflater_deflate (JNIEnv* e, jclass, jlong peer, jbyteArray input, jint inputOffset, jint inputLength, jbyteArray output, jint outputOffset, jint outputLength, - jintArray results) + jboolean finish, jintArray results) { z_stream* s = reinterpret_cast(peer); @@ -145,7 +145,7 @@ Java_java_util_zip_Deflater_deflate s->next_out = reinterpret_cast(out); s->avail_out = outputLength; - int r = deflate(s, Z_SYNC_FLUSH); + int r = deflate(s, finish ? Z_FINISH : Z_NO_FLUSH); jint resultArray[3] = { r, inputLength - s->avail_in, outputLength - s->avail_out }; diff --git a/classpath/java/io/ByteArrayOutputStream.java b/classpath/java/io/ByteArrayOutputStream.java index 61fb51759e..f0668da2da 100644 --- a/classpath/java/io/ByteArrayOutputStream.java +++ b/classpath/java/io/ByteArrayOutputStream.java @@ -98,6 +98,10 @@ public class ByteArrayOutputStream extends OutputStream { return array; } + public String toString(String encoding) throws UnsupportedEncodingException { + return new String(toByteArray(), encoding); + } + private static class Cell { public byte[] array; public int offset; diff --git a/classpath/java/io/File.java b/classpath/java/io/File.java index 3c97db5e4a..3eb8ad2f03 100644 --- a/classpath/java/io/File.java +++ b/classpath/java/io/File.java @@ -10,7 +10,7 @@ package java.io; -public class File { +public class File implements Serializable { private static final String FileSeparator = System.getProperty("file.separator"); @@ -82,6 +82,10 @@ public class File { } } + public String toString() { + return getPath(); + } + public String getPath() { return path; } diff --git a/classpath/java/io/FilterOutputStream.java b/classpath/java/io/FilterOutputStream.java new file mode 100644 index 0000000000..d22ff7b2ca --- /dev/null +++ b/classpath/java/io/FilterOutputStream.java @@ -0,0 +1,40 @@ +/* Copyright (c) 2011, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.io; + +public class FilterOutputStream extends OutputStream { + protected OutputStream out; + + public FilterOutputStream(OutputStream out) { + this.out = out; + } + + public void close() throws IOException { + out.close(); + } + + public void flush() throws IOException { + out.flush(); + } + + public void write(byte[] b) throws IOException { + out.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + public void write(int b) throws IOException { + out.write(b); + } + +} diff --git a/classpath/java/io/ObjectInputStream.java b/classpath/java/io/ObjectInputStream.java index 20fa5cdfb8..a039046517 100644 --- a/classpath/java/io/ObjectInputStream.java +++ b/classpath/java/io/ObjectInputStream.java @@ -10,6 +10,8 @@ package java.io; +import avian.VMClass; + import java.util.HashMap; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -110,7 +112,7 @@ public class ObjectInputStream extends InputStream { StringBuilder sb = new StringBuilder(); int c; - while ((c = r.read()) != -1 && ! Character.isWhitespace((char) c)) { + while ((c = r.read()) != -1 && ! Character.isWhitespace((char) c) && c != ')') { sb.append((char) c); } if (c != -1) { @@ -149,7 +151,6 @@ public class ObjectInputStream extends InputStream { throws IOException, ClassNotFoundException { skipSpace(); - switch (r.read()) { case 'a': return deserializeArray(map); @@ -203,7 +204,7 @@ public class ObjectInputStream extends InputStream { return o; } - private static native Object makeInstance(Class c); + private static native Object makeInstance(VMClass c); private Object deserializeObject(HashMap map) throws IOException, ClassNotFoundException @@ -211,11 +212,11 @@ public class ObjectInputStream extends InputStream { read('('); int id = (int) readLongToken(); Class c = Class.forName(readStringToken()); - Object o = makeInstance(c); + Object o = makeInstance(c.vmClass); map.put(id, o); - - for (Field f: c.getFields()) { + + for (Field f: c.getAllFields()) { int modifiers = f.getModifiers(); if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { try { diff --git a/classpath/java/io/ObjectOutputStream.java b/classpath/java/io/ObjectOutputStream.java index 80cac7857b..7e45f9d2cc 100644 --- a/classpath/java/io/ObjectOutputStream.java +++ b/classpath/java/io/ObjectOutputStream.java @@ -39,7 +39,7 @@ public class ObjectOutputStream extends OutputStream { } public void writeObject(Object o) throws IOException { - writeObject(o, new IdentityHashMap(), 0); + writeObject(o, new IdentityHashMap(), new int[] {0}); } public void writeBoolean(boolean v) { @@ -87,7 +87,7 @@ public class ObjectOutputStream extends OutputStream { } private void writeObject(Object o, IdentityHashMap map, - int nextId) + int[] nextId) throws IOException { if (o == null) { @@ -95,7 +95,7 @@ public class ObjectOutputStream extends OutputStream { } else { Integer id = map.get(o); if (id == null) { - map.put(o, nextId); + map.put(o, nextId[0]); Class c = o.getClass(); if (c.isArray()) { @@ -113,7 +113,7 @@ public class ObjectOutputStream extends OutputStream { } private void serializeArray(Object o, IdentityHashMap map, - int nextId) + int[] nextId) throws IOException { Class c = o.getClass(); @@ -121,7 +121,7 @@ public class ObjectOutputStream extends OutputStream { int length = Array.getLength(o); out.print("a("); - out.print(nextId++); + out.print(nextId[0]++); out.print(" "); out.print(c.getName()); out.print(" "); @@ -155,17 +155,17 @@ public class ObjectOutputStream extends OutputStream { } private void serializeObject(Object o, IdentityHashMap map, - int nextId) + int[] nextId) throws IOException { Class c = o.getClass(); out.print("l("); - out.print(nextId++); + out.print(nextId[0]++); out.print(" "); out.print(c.getName()); - for (Field f: c.getFields()) { + for (Field f: c.getAllFields()) { int modifiers = f.getModifiers(); if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { out.print(" "); diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index b550ae5509..22b645793a 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -29,6 +29,7 @@ import java.lang.annotation.Annotation; import java.io.InputStream; import java.io.IOException; import java.net.URL; +import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.security.ProtectionDomain; @@ -424,6 +425,25 @@ public final class Class implements Type, AnnotatedElement { return array; } + private static void getAllFields(VMClass vmClass, ArrayList fields) { + if (vmClass.super_ != null) { + getAllFields(vmClass.super_, fields); + } + if (vmClass.fieldTable != null) { + Classes.link(vmClass); + + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + fields.add(new Field(vmClass.fieldTable[i])); + } + } + } + + public Field[] getAllFields() { + ArrayList fields = new ArrayList(); + getAllFields(vmClass, fields); + return fields.toArray(new Field[fields.size()]); + } + private int countMethods(boolean publicOnly) { int count = 0; if (vmClass.methodTable != null) { @@ -515,7 +535,7 @@ public final class Class implements Type, AnnotatedElement { } public Class getSuperclass() { - return SystemClassLoader.getClass(vmClass.super_); + return (vmClass.super_ == null ? null : SystemClassLoader.getClass(vmClass.super_)); } public boolean isArray() { diff --git a/classpath/java/lang/ClassLoader.java b/classpath/java/lang/ClassLoader.java index b2ee0b7f75..e245e4c9f1 100644 --- a/classpath/java/lang/ClassLoader.java +++ b/classpath/java/lang/ClassLoader.java @@ -93,7 +93,7 @@ public abstract class ClassLoader { avian.Classes.link(c.vmClass, this); } - private ClassLoader getParent() { + public final ClassLoader getParent() { return parent; } diff --git a/classpath/java/lang/String.java b/classpath/java/lang/String.java index 788400dbac..7f6ff1edfc 100644 --- a/classpath/java/lang/String.java +++ b/classpath/java/lang/String.java @@ -494,6 +494,42 @@ public final class String public String replaceAll(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceAll(replacement); } + + public String replace(CharSequence target, CharSequence replace) { + if (target.length() == 0) { + return this.infuse(replace.toString()); + } + + String targetString = target.toString(); + String replaceString = replace.toString(); + + int targetSize = target.length(); + + StringBuilder returnValue = new StringBuilder(); + String unhandled = this; + + int index = -1; + while ((index = unhandled.indexOf(targetString)) != -1) { + returnValue.append(unhandled.substring(0, index)).append(replaceString); + unhandled = unhandled.substring(index + targetSize, + unhandled.length()); + } + + returnValue.append(unhandled); + return returnValue.toString(); + } + + private String infuse(String infuseWith) { + StringBuilder retVal = new StringBuilder(); + + String me = this; + for (int i = 0; i < me.length(); i++) { + retVal.append(infuseWith).append(me.substring(i, i + 1)); + } + + retVal.append(infuseWith); + return retVal.toString(); + } public native String intern(); diff --git a/classpath/java/lang/System.java b/classpath/java/lang/System.java index 954c62d116..9506fb3457 100644 --- a/classpath/java/lang/System.java +++ b/classpath/java/lang/System.java @@ -20,6 +20,8 @@ import java.io.FileDescriptor; import java.util.Properties; public abstract class System { + private static final long NanoTimeBaseInMillis = currentTimeMillis(); + private static Property properties; private static SecurityManager securityManager; @@ -94,6 +96,10 @@ public abstract class System { public static native int identityHashCode(Object o); + public static long nanoTime() { + return (currentTimeMillis() - NanoTimeBaseInMillis) * 1000000; + } + public static String mapLibraryName(String name) { if (name != null) { return doMapLibraryName(name); diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 4638c61990..524cb7613a 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -205,7 +205,7 @@ public class Field extends AccessibleObject { } else { throw new IllegalArgumentException ("needed " + getType() + ", got " - + Class.getName(Classes.vmClass(target)) + + + value.getClass().getName() + " when setting " + Class.getName(vmField.class_) + "." + getName()); } break; diff --git a/classpath/java/lang/reflect/InvocationHandler.java b/classpath/java/lang/reflect/InvocationHandler.java index 21932bae6e..9541797ad3 100644 --- a/classpath/java/lang/reflect/InvocationHandler.java +++ b/classpath/java/lang/reflect/InvocationHandler.java @@ -11,5 +11,5 @@ package java.lang.reflect; public interface InvocationHandler { - public Object invoke(Object proxy, Method method, Object[] arguments); + public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable; } diff --git a/classpath/java/net/JarURLConnection.java b/classpath/java/net/JarURLConnection.java new file mode 100644 index 0000000000..ee50574a02 --- /dev/null +++ b/classpath/java/net/JarURLConnection.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2011, 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.net; + +import java.io.IOException; +import java.util.jar.JarFile; + +public abstract class JarURLConnection extends URLConnection { + public JarURLConnection(URL url) { + super(url); + } + + public abstract JarFile getJarFile() throws IOException; +} diff --git a/classpath/java/net/URL.java b/classpath/java/net/URL.java index 2b405b307e..874eec964b 100644 --- a/classpath/java/net/URL.java +++ b/classpath/java/net/URL.java @@ -73,13 +73,17 @@ public final class URL { { if ("resource".equals(protocol)) { return new avian.resource.Handler(); + } else if ("file".equals(protocol)) { + return new avian.file.Handler(); + } else if ("jar".equals(protocol)) { + return new avian.jar.Handler(); } else { throw new MalformedURLException("unknown protocol: " + protocol); } } - protected void set(String protocol, String host, int port, String file, - String ref) + public void set(String protocol, String host, int port, String file, + String ref) { this.protocol = protocol; this.host = host; diff --git a/classpath/java/net/URLStreamHandler.java b/classpath/java/net/URLStreamHandler.java index 928f0285ba..5c72deada1 100644 --- a/classpath/java/net/URLStreamHandler.java +++ b/classpath/java/net/URLStreamHandler.java @@ -13,7 +13,9 @@ package java.net; import java.io.IOException; public abstract class URLStreamHandler { - protected void parseURL(URL url, String s, int start, int end) { + protected void parseURL(URL url, String s, int start, int end) + throws MalformedURLException + { String protocol = s.substring(0, start - 1); s = s.substring(start, end); diff --git a/classpath/java/security/CodeSource.java b/classpath/java/security/CodeSource.java index 030597fa22..296f38c128 100644 --- a/classpath/java/security/CodeSource.java +++ b/classpath/java/security/CodeSource.java @@ -10,4 +10,17 @@ package java.security; -public class CodeSource { } +import java.net.URL; +import java.security.cert.Certificate; + +public class CodeSource { + private final URL url; + + public CodeSource(URL url, Certificate[] certificates) { + this.url = url; + } + + public URL getLocation() { + return url; + } +} diff --git a/classpath/java/security/ProtectionDomain.java b/classpath/java/security/ProtectionDomain.java index f7670e28a4..25673c8ee3 100644 --- a/classpath/java/security/ProtectionDomain.java +++ b/classpath/java/security/ProtectionDomain.java @@ -20,4 +20,8 @@ public class ProtectionDomain { this.codeSource = codeSource; this.permissions = permissions; } + + public CodeSource getCodeSource() { + return codeSource; + } } diff --git a/classpath/java/security/cert/Certificate.java b/classpath/java/security/cert/Certificate.java new file mode 100644 index 0000000000..8b32abb2e4 --- /dev/null +++ b/classpath/java/security/cert/Certificate.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2011, 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.cert; + +public abstract class Certificate { } diff --git a/classpath/java/util/ArrayList.java b/classpath/java/util/ArrayList.java index e99c6ceade..698f3f79df 100644 --- a/classpath/java/util/ArrayList.java +++ b/classpath/java/util/ArrayList.java @@ -10,7 +10,7 @@ package java.util; -public class ArrayList extends AbstractList { +public class ArrayList extends AbstractList implements java.io.Serializable { private static final int MinimumCapacity = 16; private Object[] array; diff --git a/classpath/java/util/Collections.java b/classpath/java/util/Collections.java index 050c1ebb02..40c133f753 100644 --- a/classpath/java/util/Collections.java +++ b/classpath/java/util/Collections.java @@ -52,6 +52,23 @@ public class Collections { return array; } + public static final List EMPTY_LIST + = new UnmodifiableList(new ArrayList(0)); + + public static final List emptyList() { + return EMPTY_LIST; + } + + public static final Map emptyMap() { + return (Map) new UnmodifiableMap( + new HashMap(0)); + } + + public static final Set emptySet() { + return (Set) new UnmodifiableSet( + new HashSet(0)); + } + static String toString(Collection c) { StringBuilder sb = new StringBuilder(); sb.append("["); @@ -293,8 +310,153 @@ public class Collections { } } + public static List unmodifiableList(List list) { + return new UnmodifiableList(list); + } + + static class UnmodifiableList implements List { + + private List inner; + + UnmodifiableList(List l) { + this.inner = l; + } + + public T get(int index) { + return inner.get(index); + } + + public T set(int index, T value) { + throw new UnsupportedOperationException("not supported"); + } + + public T remove(int index) { + throw new UnsupportedOperationException("not supported"); + } + + public boolean remove(Object o) { + throw new UnsupportedOperationException("not supported"); + } + + public boolean add(T element) { + throw new UnsupportedOperationException("not supported"); + } + + public void add(int index, T element) { + throw new UnsupportedOperationException("not supported"); + } + + public Iterator iterator() { + return inner.iterator(); + } + + public int indexOf(Object value) { + return inner.indexOf(value); + } + + public int lastIndexOf(Object value) { + return inner.lastIndexOf(value); + } + + public boolean isEmpty() { + return inner.isEmpty(); + } + + public ListIterator listIterator(int index) { + return inner.listIterator(index); + } + + public ListIterator listIterator() { + return inner.listIterator(); + } + + public int size() { + return inner.size(); + } + + public boolean contains(Object element) { + return inner.contains(element); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException("not supported"); + } + + public Object[] toArray() { + return inner.toArray(); + } + + public S[] toArray(S[] array) { + return inner.toArray(array); + } + + public void clear() { + throw new UnsupportedOperationException("not supported"); + } + } + + public static Map unmodifiableMap(Map m) { + return new UnmodifiableMap(m); + } + + static class UnmodifiableMap implements Map { + private Map inner; + + UnmodifiableMap(Map m) { + this.inner = m; + } + + public void clear() { + throw new UnsupportedOperationException("not supported"); + } + + public boolean containsKey(Object key) { + return inner.containsKey(key); + } + + public boolean containsValue(Object value) { + return inner.containsValue(value); + } + + public Set> entrySet() { + return unmodifiableSet(inner.entrySet()); + } + + public V get(Object key) { + return inner.get(key); + } + + public boolean isEmpty() { + return inner.isEmpty(); + } + + public Set keySet() { + return unmodifiableSet(inner.keySet()); + } + + public V put(K key, V value) { + throw new UnsupportedOperationException("not supported"); + } + + public void putAll(Map t) { + throw new UnsupportedOperationException("not supported"); + } + + public V remove(Object key) { + throw new UnsupportedOperationException("not supported"); + } + + public int size() { + return inner.size(); + } + + public Collection values() { + return unmodifiableSet((Set)inner.values()); + } + } + static class UnmodifiableSet implements Set { - Set inner; + private Set inner; UnmodifiableSet(Set inner) { this.inner = inner; @@ -396,7 +558,5 @@ public class Collections { public int compare(T o1, T o2) { return - cmp.compare(o1, o2); } - } - } diff --git a/classpath/java/util/Locale.java b/classpath/java/util/Locale.java index c53b6f4ed1..373506bdec 100644 --- a/classpath/java/util/Locale.java +++ b/classpath/java/util/Locale.java @@ -11,12 +11,18 @@ package java.util; public class Locale { - public static final Locale ENGLISH = new Locale("en", "us"); + private static final Locale DEFAULT; + public static final Locale ENGLISH = new Locale("en", ""); private final String language; private final String country; private final String variant; + static { + DEFAULT = new Locale(System.getProperty("user.language"), + System.getProperty("user.region")); + } + public Locale(String language, String country, String variant) { this.language = language; this.country = country; @@ -24,11 +30,11 @@ public class Locale { } public Locale(String language, String country) { - this(language, country, null); + this(language, country, ""); } public Locale(String language) { - this(language, null); + this(language, ""); } public String getLanguage() { @@ -44,6 +50,15 @@ public class Locale { } public static Locale getDefault() { - return ENGLISH; + return DEFAULT; + } + + public final String toString() { + boolean hasLanguage = language != ""; + boolean hasCountry = country != ""; + boolean hasVariant = variant != ""; + + if (!hasLanguage && !hasCountry) return ""; + return language + (hasCountry || hasVariant ? '_' + country : "") + (hasVariant ? '_' + variant : ""); } } diff --git a/classpath/java/util/Properties.java b/classpath/java/util/Properties.java index b71caa14ee..10b5c90687 100644 --- a/classpath/java/util/Properties.java +++ b/classpath/java/util/Properties.java @@ -85,7 +85,18 @@ public class Properties extends Hashtable { } abstract int readCharacter() throws IOException; - + + char readUtf16() throws IOException { + char c = 0; + for (int i = 0; i < 4; ++i) { + int digit = Character.digit((char)readCharacter(), 16); + if (digit == -1) throw new IOException("Invalid Unicode escape encountered."); + c <<= 4; + c |= digit; + } + return c; + } + void parse(Map map) throws IOException { @@ -148,6 +159,13 @@ public class Properties extends Hashtable { } break; + case 'u': + if (escaped) { + append(readUtf16()); + } else { + append(c); + } break; + default: append(c); break; diff --git a/classpath/java/util/RandomAccess.java b/classpath/java/util/RandomAccess.java new file mode 100644 index 0000000000..9d8fd4691f --- /dev/null +++ b/classpath/java/util/RandomAccess.java @@ -0,0 +1,14 @@ +/* Copyright (c) 2011, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public interface RandomAccess { +} diff --git a/classpath/java/util/Vector.java b/classpath/java/util/Vector.java index 30ff6d9a9f..e95c8df414 100644 --- a/classpath/java/util/Vector.java +++ b/classpath/java/util/Vector.java @@ -10,7 +10,7 @@ package java.util; -public class Vector extends AbstractList { +public class Vector extends AbstractList implements java.io.Serializable { private final ArrayList list; public Vector(int capacity) { @@ -81,6 +81,10 @@ public class Vector extends AbstractList { remove(index); } + public synchronized void removeAllElements() { + list.clear(); + } + public synchronized boolean remove(Object element) { return list.remove(element); } diff --git a/classpath/java/util/jar/JarEntry.java b/classpath/java/util/jar/JarEntry.java new file mode 100644 index 0000000000..9f92c29713 --- /dev/null +++ b/classpath/java/util/jar/JarEntry.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2011, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.jar; + +import java.util.zip.ZipEntry; + +public abstract class JarEntry extends ZipEntry { } diff --git a/classpath/java/util/jar/JarFile.java b/classpath/java/util/jar/JarFile.java new file mode 100644 index 0000000000..f86fd6ac36 --- /dev/null +++ b/classpath/java/util/jar/JarFile.java @@ -0,0 +1,81 @@ +/* Copyright (c) 2011, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.jar; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; + +public class JarFile extends ZipFile { + public JarFile(String name) throws IOException { + super(name); + } + + public JarFile(File file) throws IOException { + super(file); + } + + public Enumeration entries() { + return (Enumeration) makeEnumeration(JarEntryFactory.Instance); + } + + public JarEntry getJarEntry(String name) { + return (JarEntry) getEntry(JarEntryFactory.Instance, name); + } + + private static class MyJarEntry extends JarEntry implements MyEntry { + public final Window window; + public final int pointer; + + public MyJarEntry(Window window, int pointer) { + this.window = window; + this.pointer = pointer; + } + + public String getName() { + try { + return entryName(window, pointer); + } catch (IOException e) { + return null; + } + } + + public int getCompressedSize() { + try { + return compressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public int getSize() { + try { + return uncompressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public int pointer() { + return pointer; + } + } + + private static class JarEntryFactory implements EntryFactory { + public static final JarEntryFactory Instance = new JarEntryFactory(); + + public ZipEntry makeEntry(Window window, int pointer) { + return new MyJarEntry(window, pointer); + } + } +} diff --git a/classpath/java/util/zip/CRC32.java b/classpath/java/util/zip/CRC32.java new file mode 100644 index 0000000000..628167725b --- /dev/null +++ b/classpath/java/util/zip/CRC32.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2011, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.zip; + +public class CRC32 { + private static final int Polynomial = 0x04C11DB7; + private static final int Width = 32; + private static final int Top = 1 << (Width - 1); + private static final int InitialRemainder = 0xFFFFFFFF; + private static final long ResultXor = 0xFFFFFFFFL; + + private static final int[] table = new int[256]; + + static { + for (int dividend = 0; dividend < 256; ++ dividend) { + int remainder = dividend << (Width - 8); + for (int bit = 8; bit > 0; --bit) { + remainder = ((remainder & Top) != 0) + ? (remainder << 1) ^ Polynomial + : (remainder << 1); + } + table[dividend] = remainder; + } + } + + private int remainder = InitialRemainder; + + public void reset() { + remainder = InitialRemainder; + } + + public void update(int b) { + remainder = table[reflect(b, 8) ^ (remainder >>> (Width - 8))] + ^ (remainder << 8); + } + + public void update(byte[] array, int offset, int length) { + for (int i = 0; i < length; ++i) { + update(array[offset + i] & 0xFF); + } + } + + public void update(byte[] array) { + update(array, 0, array.length); + } + + public long getValue() { + return (reflect(remainder, Width) ^ ResultXor) & 0xFFFFFFFFL; + } + + private static int reflect(int x, int n) { + int reflection = 0; + for (int i = 0; i < n; ++i) { + if ((x & 1) != 0) { + reflection |= (1 << ((n - 1) - i)); + } + x = (x >>> 1); + } + return reflection; + } +} diff --git a/classpath/java/util/zip/Deflater.java b/classpath/java/util/zip/Deflater.java index a5d4511683..8368bb0817 100644 --- a/classpath/java/util/zip/Deflater.java +++ b/classpath/java/util/zip/Deflater.java @@ -27,14 +27,19 @@ public class Deflater { private boolean needDictionary; private boolean finished; private final boolean nowrap; + private boolean finish; - public Deflater(boolean nowrap) { + public Deflater(int level, boolean nowrap) { this.nowrap = nowrap; - peer = make(nowrap, DEFAULT_LEVEL); + peer = make(nowrap, level); + } + + public Deflater(int level) { + this(level, false); } public Deflater() { - this(false); + this(DEFAULT_LEVEL); } private void check() { @@ -85,16 +90,15 @@ public class Deflater { peer = make(nowrap, DEFAULT_LEVEL); input = null; offset = length = 0; + finish = false; needDictionary = finished = false; } - public int deflate(byte[] output) throws DataFormatException { + public int deflate(byte[] output) { return deflate(output, 0, output.length); } - public int deflate(byte[] output, int offset, int length) - throws DataFormatException - { + public int deflate(byte[] output, int offset, int length) { final int zlibResult = 0; final int inputCount = 1; final int outputCount = 2; @@ -110,10 +114,10 @@ public class Deflater { int[] results = new int[3]; deflate(peer, input, this.offset, this.length, - output, offset, length, results); + output, offset, length, finish, results); if (results[zlibResult] < 0) { - throw new DataFormatException(); + throw new AssertionError(); } switch (results[zlibResult]) { @@ -132,10 +136,15 @@ public class Deflater { return results[outputCount]; } + public void finish() { + finish = true; + } + private static native void deflate (long peer, byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, + boolean finish, int[] results); public void end() { diff --git a/classpath/java/util/zip/DeflaterOutputStream.java b/classpath/java/util/zip/DeflaterOutputStream.java index e55668e53c..3ee3fcdbb8 100644 --- a/classpath/java/util/zip/DeflaterOutputStream.java +++ b/classpath/java/util/zip/DeflaterOutputStream.java @@ -52,23 +52,25 @@ public class DeflaterOutputStream extends OutputStream { } else if (length == 0) { return; } - - for (int i = 0; i < length; i+= buffer.length) { - deflater.setInput(b, offset + i, Math.min(buffer.length, length - i)); - while (deflater.getRemaining() > 0) { - try { - int len = deflater.deflate(buffer, 0, buffer.length); - if (len > 0) { - out.write(buffer, 0, len); - } - } catch (DataFormatException e) { - e.printStackTrace(); - } - } + + deflater.setInput(b, offset, length); + while (deflater.getRemaining() > 0) { + deflate(); + } + } + + private void deflate() throws IOException { + int len = deflater.deflate(buffer, 0, buffer.length); + if (len > 0) { + out.write(buffer, 0, len); } } public void close() throws IOException { + deflater.finish(); + while (! deflater.finished()) { + deflate(); + } out.close(); deflater.dispose(); } diff --git a/classpath/java/util/zip/ZipEntry.java b/classpath/java/util/zip/ZipEntry.java index f2d944907f..9f18369c52 100644 --- a/classpath/java/util/zip/ZipEntry.java +++ b/classpath/java/util/zip/ZipEntry.java @@ -13,4 +13,5 @@ package java.util.zip; public abstract class ZipEntry { public abstract String getName(); public abstract int getCompressedSize(); + public abstract int getSize(); } diff --git a/classpath/java/util/zip/ZipFile.java b/classpath/java/util/zip/ZipFile.java index d59885496f..c80e7104f2 100644 --- a/classpath/java/util/zip/ZipFile.java +++ b/classpath/java/util/zip/ZipFile.java @@ -63,17 +63,27 @@ public class ZipFile { return index.size(); } + protected Enumeration makeEnumeration + (EntryFactory factory) + { + return new MyEnumeration(factory, window, index.values().iterator()); + } + public Enumeration entries() { - return new MyEnumeration(window, index.values().iterator()); + return makeEnumeration(ZipEntryFactory.Instance); + } + + protected ZipEntry getEntry(EntryFactory factory, String name) { + Integer pointer = index.get(name); + return (pointer == null ? null : factory.makeEntry(window, pointer)); } public ZipEntry getEntry(String name) { - Integer pointer = index.get(name); - return (pointer == null ? null : new MyZipEntry(window, pointer)); + return getEntry(ZipEntryFactory.Instance, name); } public InputStream getInputStream(ZipEntry entry) throws IOException { - int pointer = ((MyZipEntry) entry).pointer; + int pointer = ((MyEntry) entry).pointer(); int method = compressionMethod(window, pointer); int size = compressedSize(window, pointer); InputStream in = new MyInputStream(file, fileData(window, pointer), size); @@ -126,7 +136,7 @@ public class ZipFile { return get2(w, p + 28); } - private static String entryName(Window w, int p) throws IOException { + protected static String entryName(Window w, int p) throws IOException { int length = entryNameLength(w, p); return new String(w.data, w.seek(p + 46, length), length); } @@ -135,10 +145,14 @@ public class ZipFile { return get2(w, p + 10); } - private static int compressedSize(Window w, int p) throws IOException { + protected static int compressedSize(Window w, int p) throws IOException { return get4(w, p + 20); } + protected static int uncompressedSize(Window w, int p) throws IOException { + return get4(w, p + 24); + } + private static int fileNameLength(Window w, int p) throws IOException { return get2(w, p + 28); } @@ -186,7 +200,7 @@ public class ZipFile { file.close(); } - private static class Window { + protected static class Window { private final RandomAccessFile file; public final byte[] data; public int start; @@ -231,7 +245,11 @@ public class ZipFile { } } - private static class MyZipEntry extends ZipEntry { + protected interface MyEntry { + public int pointer(); + } + + private static class MyZipEntry extends ZipEntry implements MyEntry { public final Window window; public final int pointer; @@ -255,13 +273,41 @@ public class ZipFile { return 0; } } + + public int getSize() { + try { + return uncompressedSize(window, pointer); + } catch (IOException e) { + return 0; + } + } + + public int pointer() { + return pointer; + } + } + + protected interface EntryFactory { + public ZipEntry makeEntry(Window window, int pointer); + } + + private static class ZipEntryFactory implements EntryFactory { + public static final ZipEntryFactory Instance = new ZipEntryFactory(); + + public ZipEntry makeEntry(Window window, int pointer) { + return new MyZipEntry(window, pointer); + } } private static class MyEnumeration implements Enumeration { + private final EntryFactory factory; private final Window window; private final Iterator iterator; - public MyEnumeration(Window window, Iterator iterator) { + public MyEnumeration(EntryFactory factory, Window window, + Iterator iterator) + { + this.factory = factory; this.window = window; this.iterator = iterator; } @@ -271,7 +317,7 @@ public class ZipFile { } public ZipEntry nextElement() { - return new MyZipEntry(window, iterator.next()); + return factory.makeEntry(window, iterator.next()); } } diff --git a/classpath/sun/misc/Cleaner.java b/classpath/sun/misc/Cleaner.java new file mode 100644 index 0000000000..d4dd733f48 --- /dev/null +++ b/classpath/sun/misc/Cleaner.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2011, 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 sun.misc; + +public class Cleaner { } diff --git a/makefile b/makefile index a0057198cd..d390389a9a 100644 --- a/makefile +++ b/makefile @@ -90,7 +90,19 @@ ifneq ($(openjdk),) openjdk-jar-dep = $(build)/openjdk-jar.dep classpath-jar-dep = $(openjdk-jar-dep) javahome = $(embed-prefix)/javahomeJar - javahome-files = lib/zi lib/currency.data + javahome-files = lib/zi lib/currency.data lib/security/java.security \ + lib/security/java.policy lib/security/cacerts + + local-policy = lib/security/local_policy.jar + ifeq ($(shell test -e $(openjdk)/$(local-policy) && echo found),found) + javahome-files += $(local-policy) + endif + + export-policy = lib/security/US_export_policy.jar + ifeq ($(shell test -e $(openjdk)/$(export-policy) && echo found),found) + javahome-files += $(export-policy) + endif + ifeq ($(platform),windows) javahome-files += lib/tzmappings endif @@ -304,7 +316,7 @@ ifeq ($(platform),windows) exe-suffix = .exe lflags = -L$(lib) $(common-lflags) -lws2_32 -mwindows -mconsole - cflags = -I$(inc) $(common-cflags) + cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 ifeq (,$(filter mingw32 cygwin,$(build-platform))) openjdk-extra-cflags += -I$(src)/openjdk/caseSensitive @@ -359,9 +371,22 @@ ifeq ($(mode),stress-major) endif ifeq ($(mode),fast) optimization-cflags = -O3 -g3 -DNDEBUG + use-lto = true endif ifeq ($(mode),small) optimization-cflags = -Os -g3 -DNDEBUG + use-lto = true +endif + +ifeq ($(use-lto),true) +# only try to use LTO when GCC 4.6.0 or greater is available + gcc-major := $(shell $(cc) -dumpversion | cut -f1 -d.) + gcc-minor := $(shell $(cc) -dumpversion | cut -f2 -d.) + ifeq ($(shell expr 4 \< $(gcc-major) \ + \| \( 4 \<= $(gcc-major) \& 6 \<= $(gcc-minor) \)),1) + optimization-cflags += -flto + lflags += $(optimization-cflags) + endif endif cflags += $(optimization-cflags) @@ -544,12 +569,14 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/Callback.java \ $(classpath-src)/avian/CallbackReceiver.java \ $(classpath-src)/avian/ClassAddendum.java \ + $(classpath-src)/avian/Classes.java \ $(classpath-src)/avian/ConstantPool.java \ $(classpath-src)/avian/Continuations.java \ $(classpath-src)/avian/FieldAddendum.java \ $(classpath-src)/avian/IncompatibleContinuationException.java \ $(classpath-src)/avian/Machine.java \ $(classpath-src)/avian/MethodAddendum.java \ + $(classpath-src)/avian/Singleton.java \ $(classpath-src)/avian/Stream.java \ $(classpath-src)/avian/SystemClassLoader.java \ $(classpath-src)/avian/VMClass.java \ @@ -590,6 +617,11 @@ ifeq ($(continuations),true) extra.DynamicWind endif +ifeq ($(tails),true) + tail-tests = \ + extra.Tails +endif + class-name = $(patsubst $(1)/%.class,%,$(2)) class-names = $(foreach x,$(2),$(call class-name,$(1),$(x))) @@ -622,7 +654,7 @@ test: build $(library-path) /bin/sh $(test)/test.sh 2>/dev/null \ $(test-executable) $(mode) "$(test-flags)" \ $(call class-names,$(test-build),$(test-classes)) \ - $(continuation-tests) + $(continuation-tests) $(tail-tests) .PHONY: tarball tarball: @@ -873,7 +905,7 @@ $(generator): $(generator-objects) @echo "linking $(@)" $(build-ld) $(^) $(build-lflags) -o $(@) -$(openjdk-objects): $(build)/openjdk/%.o: $(openjdk-src)/%.c \ +$(openjdk-objects): $(build)/openjdk/%-openjdk.o: $(openjdk-src)/%.c \ $(openjdk-headers-dep) @echo "compiling $(@)" @mkdir -p $(dir $(@)) @@ -882,7 +914,7 @@ $(openjdk-objects): $(build)/openjdk/%.o: $(openjdk-src)/%.c \ $(optimization-cflags) -w -c $(build)/openjdk/$(notdir $(<)) \ $(call output,$(@)) -$(openjdk-local-objects): $(build)/openjdk/%.o: $(src)/openjdk/%.c \ +$(openjdk-local-objects): $(build)/openjdk/%-openjdk.o: $(src)/openjdk/%.c \ $(openjdk-headers-dep) @echo "compiling $(@)" @mkdir -p $(dir $(@)) @@ -912,5 +944,6 @@ $(openjdk-jar-dep): $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/rt.jar")" && \ $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/jsse.jar")" && \ $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/jce.jar")" && \ + $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/ext/sunjce_provider.jar")" && \ $(jar) xf "$$($(native-path) "$(openjdk)/jre/lib/resources.jar")") @touch $(@) diff --git a/openjdk-src.mk b/openjdk-src.mk index 190c8622e9..b050f75c94 100644 --- a/openjdk-src.mk +++ b/openjdk-src.mk @@ -298,7 +298,7 @@ endif openjdk-local-sources = \ $(src)/openjdk/my_net_util.c -c-objects = $(foreach x,$(1),$(patsubst $(2)/%.c,$(3)/%.o,$(x))) +c-objects = $(foreach x,$(1),$(patsubst $(2)/%.c,$(3)/%-openjdk.o,$(x))) openjdk-objects = \ $(call c-objects,$(openjdk-sources),$(openjdk-src),$(build)/openjdk) diff --git a/openjdk.pro b/openjdk.pro index 81031f3bc9..dd9e07a910 100644 --- a/openjdk.pro +++ b/openjdk.pro @@ -104,7 +104,8 @@ } -keepclassmembers class java.io.FileDescriptor { - private int fd; + private int fd; + private long handle; } -keep class java.net.InetAddress { @@ -132,7 +133,8 @@ } -keepclassmembers class java.io.FileOutputStream { - private java.io.FileDescriptor fd; + private java.io.FileDescriptor fd; + private boolean append; } # changed in native code via sun.misc.Unsafe (todo: handle other @@ -174,6 +176,10 @@ public UnixFileSystem(); } +-keep class java.io.WinNTFileSystem { + public WinNTFileSystem(); + } + -keep class java.io.File { private java.lang.String path; } diff --git a/readme.txt b/readme.txt index 708c948d13..a17e553962 100644 --- a/readme.txt +++ b/readme.txt @@ -51,7 +51,7 @@ Supported Platforms Avian can currently target the following platforms: - Linux (i386, x86_64 and ARM) + Linux (i386, x86_64, ARM, and 32-bit PowerPC) Windows (i386 and x86_64) Mac OS X (i386, x86_64 and 32-bit PowerPC) @@ -260,7 +260,58 @@ an LZMA-enabled version: You can reduce the size futher for embedded builds by using ProGuard and the supplied openjdk.pro configuration file (see "Embedding with -ProGuard and a Boot Image" below). +ProGuard and a Boot Image" below). Also see app.mk in +git://oss.readytalk.com/avian-swt-examples.git for an example of using +Avian, OpenJDK, ProGuard, and UPX in concert. + +Here are some examples of how to install OpenJDK and build Avian with +it on various OSes: + + Debian-based Linux: + # conventional build: + apt-get install openjdk-6-jdk + make openjdk=/usr/lib/jvm/java-6-openjdk test + + # stand-alone build: + apt-get install openjdk-6-jdk + apt-get source openjdk-6-jdk + apt-get build-dep openjdk-6-jdk + (cd openjdk-6-6b18-1.8.3 && ./debian/rules patch) + make openjdk=/usr/lib/jvm/java-6-openjdk \ + openjdk-src=$(pwd)/openjdk-6-6b18-1.8.3/build/openjdk/jdk/src \ + test + + Mac OS X: + # Prerequisite: install MacPorts (http://www.macports.org/) + sudo port selfupdate + + # conventional build: + sudo port install openjdk6 + make openjdk=/opt/local/share/java/openjdk6 test + + # stand-alone build: + sudo port fetch openjdk6 + sudo port patch openjdk6 + make openjdk=/opt/local/share/java/openjdk6 \ + openjdk-src=/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_java_openjdk6/work/jdk/src \ + test + + Windows (Cygwin): + # conventional build: + # Prerequisite: download and install the latest Windows OpenJDK + # build from http://www.openscg.com/se/openjdk/ + make openjdk=/cygdrive/c/OpenSCG/openjdk-6.21 test + + # stand-alone build: + # Prerequisite: install OpenSCG build as above, plus the + # corresponding source bundle from + # http://download.java.net/openjdk/jdk6/promoted/, e.g.: + wget http://download.java.net/openjdk/jdk6/promoted/b21/openjdk-6-src-b21-20_jan_2011.tar.gz + mkdir openjdk + (cd openjdk && tar xzf openjdk-6-src-b21-20_jan_2011.tar.gz) + make openjdk=/cygdrive/c/OpenSCG/openjdk-6.21 \ + openjdk-src=$(pwd)/openjdk/jdk/src \ + test Installing diff --git a/src/arm.cpp b/src/arm.cpp index 38ba3a6ce4..42cfcf724b 100644 --- a/src/arm.cpp +++ b/src/arm.cpp @@ -664,13 +664,19 @@ appendPoolEvent(Context* c, MyBlock* b, unsigned offset, PoolOffset* head, b->poolEventTail = e; } +bool +needJump(MyBlock* b) +{ + return b->next or b->size != (b->size & PoolOffsetMask); +} + unsigned padding(MyBlock* b, unsigned offset) { unsigned total = 0; for (PoolEvent* e = b->poolEventHead; e; e = e->next) { if (e->offset <= offset) { - if (b->next) { + if (needJump(b)) { total += BytesPerWord; } for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { @@ -2363,7 +2369,7 @@ class MyAssembler: public Assembler { unsigned entry = dstOffset + poolSize; - if (b->next) { + if (needJump(b)) { entry += BytesPerWord; } @@ -2381,7 +2387,7 @@ class MyAssembler: public Assembler { poolSize += BytesPerWord; } - if (b->next) { + if (needJump(b)) { write4(dst + dstOffset, ::b((poolSize + BytesPerWord - 8) >> 2)); } diff --git a/src/builtin.cpp b/src/builtin.cpp index 240ec9628a..06b45f0646 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -42,11 +42,37 @@ search(Thread* t, object loader, object name, object resolveSystemClassThrow(Thread* t, object loader, object spec) { - return resolveSystemClass(t, loader, spec, true); + return resolveSystemClass + (t, loader, spec, true, Machine::ClassNotFoundExceptionType); } } // namespace +extern "C" JNIEXPORT void JNICALL +Avian_avian_Classes_acquireClassLock +(Thread* t, object, uintptr_t*) +{ + acquire(t, t->m->classLock); +} + +extern "C" JNIEXPORT void JNICALL +Avian_avian_Classes_releaseClassLock +(Thread* t, object, uintptr_t*) +{ + release(t, t->m->classLock); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Classes_resolveVMClass +(Thread* t, object, uintptr_t* arguments) +{ + object loader = reinterpret_cast(arguments[0]); + object spec = reinterpret_cast(arguments[1]); + + return reinterpret_cast + (resolveClass(t, loader, spec, true, Machine::ClassNotFoundExceptionType)); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_avian_SystemClassLoader_findLoadedVMClass (Thread* t, object, uintptr_t* arguments) @@ -68,7 +94,7 @@ Avian_avian_SystemClassLoader_findVMClass } extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_SystemClassLoader_resourceExists +Avian_avian_SystemClassLoader_resourceURLPrefix (Thread* t, object, uintptr_t* arguments) { object loader = reinterpret_cast(arguments[0]); @@ -78,13 +104,10 @@ Avian_avian_SystemClassLoader_resourceExists THREAD_RUNTIME_ARRAY(t, char, n, stringLength(t, name) + 1); stringChars(t, name, RUNTIME_ARRAY_BODY(n)); - unsigned length; - bool r = static_cast(systemClassLoaderFinder(t, loader))->stat - (RUNTIME_ARRAY_BODY(n), &length) == System::TypeFile; + const char* name = static_cast + (systemClassLoaderFinder(t, loader))->urlPrefix(RUNTIME_ARRAY_BODY(n)); -// fprintf(stderr, "resource %s exists? %d\n", n, r); - - return r; + return name ? reinterpret_cast(makeString(t, "%s", name)) : 0; } else { throwNew(t, Machine::NullPointerExceptionType); } diff --git a/src/classpath-avian.cpp b/src/classpath-avian.cpp index 9efba455e4..89c6cdc13d 100644 --- a/src/classpath-avian.cpp +++ b/src/classpath-avian.cpp @@ -10,6 +10,7 @@ #include "machine.h" #include "classpath-common.h" +#include "process.h" using namespace vm; @@ -63,6 +64,12 @@ class MyClasspath : public Classpath { t->m->processor->invoke(t, method, 0, t->javaThread); } + virtual void + resolveNative(Thread* t, object method) + { + vm::resolveNative(t, method); + } + virtual void boot(Thread*) { @@ -589,30 +596,6 @@ Avian_java_lang_Thread_yield t->m->system->yield(); } -extern "C" JNIEXPORT void JNICALL -Avian_avian_Classes_acquireClassLock -(Thread* t, object, uintptr_t*) -{ - acquire(t, t->m->classLock); -} - -extern "C" JNIEXPORT void JNICALL -Avian_avian_Classes_releaseClassLock -(Thread* t, object, uintptr_t*) -{ - release(t, t->m->classLock); -} - -extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_Classes_resolveVMClass -(Thread* t, object, uintptr_t* arguments) -{ - object loader = reinterpret_cast(arguments[0]); - object spec = reinterpret_cast(arguments[1]); - - return reinterpret_cast(resolveClass(t, loader, spec)); -} - extern "C" JNIEXPORT int64_t JNICALL Avian_avian_Classes_primitiveClass (Thread* t, object, uintptr_t* arguments) diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 61bbc06dbf..1b31b2ac8e 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -11,6 +11,7 @@ #include "machine.h" #include "classpath-common.h" #include "util.h" +#include "process.h" #ifdef PLATFORM_WINDOWS @@ -59,6 +60,7 @@ typedef int socklen_t; #else // not PLATFORM_WINDOWS # include +# include # include # include # include @@ -105,6 +107,185 @@ CREAT(string_t path, int mode) namespace local { +const int JMM_VERSION_1_0 = 0x20010000; + +struct jmmOptionalSupport { + unsigned isLowMemoryDetectionSupported : 1; + unsigned isCompilationTimeMonitoringSupported : 1; + unsigned isThreadContentionMonitoringSupported : 1; + unsigned isCurrentThreadCpuTimeSupported : 1; + unsigned isOtherThreadCpuTimeSupported : 1; + unsigned isBootClassPathSupported : 1; + unsigned isObjectMonitorUsageSupported : 1; + unsigned isSynchronizerUsageSupported : 1; +}; + +typedef unsigned jmmLongAttribute; +typedef unsigned jmmBoolAttribute; +typedef unsigned jmmStatisticType; +typedef unsigned jmmThresholdType; +typedef unsigned jmmVMGlobalType; +typedef unsigned jmmVMGlobalOrigin; + +struct jmmVMGlobal { + jstring name; + jvalue value; + jmmVMGlobalType type; + jmmVMGlobalOrigin origin; + unsigned writeable : 1; + unsigned external : 1; + unsigned reserved : 30; + void* reserved1; + void* reserved2; +}; + +struct jmmExtAttributeInfo { + const char* name; + char type; + const char* description; +}; + +struct jmmGCStat { + jlong gc_index; + jlong start_time; + jlong end_time; + jobjectArray usage_before_gc; + jobjectArray usage_after_gc; + jint gc_ext_attribute_values_size; + jvalue* gc_ext_attribute_values; + jint num_gc_ext_attributes; +}; + +struct JmmInterface { + void* reserved1; + void* reserved2; + + jint + (JNICALL *GetVersion) + (JNIEnv*); + + jint + (JNICALL *GetOptionalSupport) + (JNIEnv*, jmmOptionalSupport*); + + jobject + (JNICALL *GetInputArguments) + (JNIEnv*); + + jint + (JNICALL *GetThreadInfo) + (JNIEnv*, jlongArray, jint, jobjectArray); + + jobjectArray + (JNICALL *GetInputArgumentArray) + (JNIEnv*); + + jobjectArray + (JNICALL *GetMemoryPools) + (JNIEnv*, jobject); + + jobjectArray + (JNICALL *GetMemoryManagers) + (JNIEnv*, jobject); + + jobject + (JNICALL *GetMemoryPoolUsage) + (JNIEnv*, jobject); + + jobject + (JNICALL *GetPeakMemoryPoolUsage) + (JNIEnv*, jobject); + + void* reserved4; + + jobject + (JNICALL *GetMemoryUsage) + (JNIEnv*, jboolean); + + jlong + (JNICALL *GetLongAttribute) + (JNIEnv*, jobject, jmmLongAttribute); + + jboolean (JNICALL *GetBoolAttribute) + (JNIEnv*, jmmBoolAttribute); + + jboolean + (JNICALL *SetBoolAttribute) + (JNIEnv*, jmmBoolAttribute, jboolean); + + jint + (JNICALL *GetLongAttributes) + (JNIEnv*, jobject, jmmLongAttribute*, jint, jlong*); + + jobjectArray + (JNICALL *FindCircularBlockedThreads) + (JNIEnv*); + + jlong + (JNICALL *GetThreadCpuTime) + (JNIEnv*, jlong); + + jobjectArray + (JNICALL *GetVMGlobalNames) + (JNIEnv*); + + jint + (JNICALL *GetVMGlobals) + (JNIEnv*, jobjectArray, jmmVMGlobal*, jint); + + jint + (JNICALL *GetInternalThreadTimes) + (JNIEnv*, jobjectArray, jlongArray); + + jboolean + (JNICALL *ResetStatistic) + (JNIEnv*, jvalue, jmmStatisticType); + + void + (JNICALL *SetPoolSensor) + (JNIEnv*, jobject, jmmThresholdType, jobject); + + jlong + (JNICALL *SetPoolThreshold) + (JNIEnv*, jobject, jmmThresholdType, jlong); + + jobject + (JNICALL *GetPoolCollectionUsage) + (JNIEnv*, jobject); + + jint + (JNICALL *GetGCExtAttributeInfo) + (JNIEnv*, jobject, jmmExtAttributeInfo*, jint); + + void + (JNICALL *GetLastGCStat) + (JNIEnv*, jobject, jmmGCStat*); + + jlong + (JNICALL *GetThreadCpuTimeWithKind) + (JNIEnv*, jlong, jboolean); + + void* reserved5; + + jint + (JNICALL *DumpHeap0) + (JNIEnv*, jstring, jboolean); + + jobjectArray + (JNICALL *FindDeadlocks) + (JNIEnv*, jboolean); + + void + (JNICALL *SetVMGlobal) + (JNIEnv*, jstring, jvalue ); + + void* reserved6; + + jobjectArray + (JNICALL *DumpThreads) + (JNIEnv*, jlongArray, jboolean, jboolean); +}; + const unsigned InterfaceVersion = 4; const unsigned PageSize = 4 * 1024; const int VirtualFileBase = 1000000000; @@ -224,6 +405,9 @@ class MyClasspath : public Classpath { sb.append("/lib/jce.jar"); sb.append(s->pathSeparator()); sb.append(javaHome); + sb.append("/lib/ext/sunjce_provider.jar"); + sb.append(s->pathSeparator()); + sb.append(javaHome); sb.append("/lib/resources.jar"); sb.append('\0'); @@ -273,7 +457,10 @@ class MyClasspath : public Classpath { } array = charArray; + } else { + expect(t, objectClass(t, array) == type(t, Machine::CharArrayType)); } + return vm::makeString(t, array, offset, length, 0); } @@ -298,10 +485,13 @@ class MyClasspath : public Classpath { setObjectClass(t, thread, type(t, Machine::ThreadType)); threadPriority(t, thread) = NormalPriority; threadGroup(t, thread) = group; - threadContextClassLoader(t, thread) = root(t, Machine::BootLoader); + threadContextClassLoader(t, thread) = root(t, Machine::AppLoader); PROTECT(t, thread); + object blockerLock = makeJobject(t); + set(t, thread, ThreadBlockerLock, blockerLock); + const unsigned BufferSize = 256; char buffer[BufferSize]; unsigned length = vm::snprintf(buffer, BufferSize, "Thread-%p", thread); @@ -328,6 +518,24 @@ class MyClasspath : public Classpath { release(t, t->javaThread); } + virtual void + resolveNative(Thread* t, object method) + { + if (strcmp(reinterpret_cast("sun/font/FontManager"), + &byteArrayBody(t, className(t, methodClass(t, method)), 0)) == 0 + and strcmp(reinterpret_cast("initIDs"), + &byteArrayBody(t, methodName(t, method), 0)) == 0 + and strcmp(reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, method), 0)) == 0) + { + PROTECT(t, method); + + expect(t, loadLibrary(t, libraryPath, "fontmanager", true, true)); + } + + vm::resolveNative(t, method); + } + virtual void boot(Thread* t) { @@ -339,11 +547,8 @@ class MyClasspath : public Classpath { #ifdef AVIAN_OPENJDK_SRC interceptFileOperations(t); #else // not AVIAN_OPENJDK_SRC - if (loadLibrary(t, libraryPath, "verify", true, true) == 0 - or loadLibrary(t, libraryPath, "java", true, true) == 0) - { - abort(t); - } + expect(t, loadLibrary(t, libraryPath, "verify", true, true)); + expect(t, loadLibrary(t, libraryPath, "java", true, true)); #endif // not AVIAN_OPENJDK_SRC object constructor = resolveMethod @@ -400,8 +605,16 @@ class MyClasspath : public Classpath { unsigned filePathField; unsigned fileDescriptorFdField; unsigned fileInputStreamFdField; + unsigned zipFileJzfileField; + unsigned zipEntryNameField; + unsigned zipEntryTimeField; + unsigned zipEntryCrcField; + unsigned zipEntrySizeField; + unsigned zipEntryCsizeField; + unsigned zipEntryMethodField; bool ranNetOnLoad; char buffer[BufferSize]; + JmmInterface jmmInterface; }; struct JVM_ExceptionTableEntryType { @@ -782,6 +995,8 @@ readBytesFromFile(Thread* t, object method, uintptr_t* arguments) (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField); if (fd >= VirtualFileBase) { + PROTECT(t, dst); + ACQUIRE(t, t->m->referenceLock); object region = arrayBody @@ -925,6 +1140,442 @@ closeFile(Thread* t, object method, uintptr_t* arguments) } } +class ZipFile { + public: + class Entry { + public: + Entry(unsigned hash, const uint8_t* start, Entry* next): + hash(hash), start(start), next(next), entry(0) + { } + + Entry(int64_t entry): + hash(0), start(0), next(0), entry(entry) + { } + + Entry(): + hash(0), start(0), next(0), entry(0) + { } + + unsigned hash; + const uint8_t* start; + Entry* next; + int64_t entry; + }; + + ZipFile(Thread* t, System::Region* region, unsigned entryCount): + region(region), + entryCount(entryCount), + indexSize(nextPowerOfTwo(entryCount)), + index(reinterpret_cast + (t->m->heap->allocate(sizeof(ZipFile::Entry*) * indexSize))), + file(0) + { + memset(index, 0, sizeof(ZipFile::Entry*) * indexSize); + } + + ZipFile(int64_t file): + region(0), entryCount(0), indexSize(0), index(0), file(file) + { } + + System::Region* region; + unsigned entryCount; + unsigned indexSize; + Entry** index; + int64_t file; + Entry entries[0]; +}; + +int64_t JNICALL +openZipFile(Thread* t, object method, uintptr_t* arguments) +{ + object path = reinterpret_cast(arguments[0]); + int mode = arguments[1]; + int64_t lastModified; memcpy(&lastModified, arguments + 2, 8); + + MyClasspath* cp = static_cast(t->m->classpath); + + THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + + EmbeddedFile ef(cp, RUNTIME_ARRAY_BODY(p), stringLength(t, path)); + if (ef.jar) { + if (ef.jarLength == 0 or ef.pathLength == 0) { + throwNew(t, Machine::FileNotFoundExceptionType); + } + + Finder* finder = getFinder(t, ef.jar, ef.jarLength); + if (finder == 0) { + throwNew(t, Machine::FileNotFoundExceptionType); + } + + System::Region* r = finder->find(ef.path); + if (r == 0) { + throwNew(t, Machine::FileNotFoundExceptionType); + } + + const uint8_t* start = r->start(); + const uint8_t* end = start + r->length(); + unsigned entryCount = 0; + for (const uint8_t* p = end - CentralDirectorySearchStart; p > start;) { + if (get4(p) == CentralDirectorySignature) { + p = start + centralDirectoryOffset(p); + + while (p < end) { + if (get4(p) == EntrySignature) { + ++ entryCount; + + p = endOfEntry(p); + } else { + goto make; + } + } + } else { + -- p; + } + } + + make: + ZipFile* file = new + (t->m->heap->allocate + (sizeof(ZipFile) + (sizeof(ZipFile::Entry) * entryCount))) + ZipFile(t, r, entryCount); + + { unsigned position = 0; + for (const uint8_t* p = end - CentralDirectorySearchStart; p > start;) { + if (get4(p) == CentralDirectorySignature) { + p = start + centralDirectoryOffset(p); + + while (p < end) { + if (get4(p) == EntrySignature) { + unsigned h = hash(fileName(p), fileNameLength(p)); + unsigned i = h & (file->indexSize - 1); + + file->index[i] = new (file->entries + (position++)) + ZipFile::Entry(h, p, file->index[i]); + + p = endOfEntry(p); + } else { + goto exit; + } + } + } else { + -- p; + } + } + } + + exit: + return reinterpret_cast(file); + } else { + return reinterpret_cast + (new (t->m->heap->allocate(sizeof(ZipFile))) ZipFile + (longValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, path, mode, lastModified)))); + } +} + +int64_t JNICALL +getZipFileEntryCount(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return file->entryCount; + } else { + return intValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file)); + } +} + +ZipFile::Entry* +find(ZipFile* file, const char* path, unsigned pathLength) +{ + unsigned i = hash(path) & (file->indexSize - 1); + for (ZipFile::Entry* e = file->index[i]; e; e = e->next) { + const uint8_t* p = e->start; + if (equal(path, pathLength, fileName(p), fileNameLength(p))) { + return e; + } + } + return 0; +} + +int64_t JNICALL +getZipFileEntry(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + object path = reinterpret_cast(arguments[2]); + bool addSlash = arguments[3]; + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 2); + stringChars(t, path, RUNTIME_ARRAY_BODY(p)); + replace('\\', '/', RUNTIME_ARRAY_BODY(p)); + if (addSlash) { + RUNTIME_ARRAY_BODY(p)[stringLength(t, path)] = '/'; + RUNTIME_ARRAY_BODY(p)[stringLength(t, path) + 1] = 0; + } + + return reinterpret_cast(find(file, p, stringLength(t, path))); + } else { + int64_t entry = longValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file, path, addSlash)); + + return entry ? reinterpret_cast + (new (t->m->heap->allocate(sizeof(ZipFile::Entry))) + ZipFile::Entry(entry)) : 0; + } +} + +int64_t JNICALL +getNextZipFileEntry(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + int index = arguments[2]; + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return reinterpret_cast(file->entries + index); + } else { + int64_t entry = longValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file, index)); + + return entry ? reinterpret_cast + (new (t->m->heap->allocate(sizeof(ZipFile::Entry))) + ZipFile::Entry(entry)) : 0; + } +} + +void JNICALL +initializeZipEntryFields(Thread* t, object method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + int64_t peer; memcpy(&peer, arguments + 1, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + PROTECT(t, this_); + + MyClasspath* cp = static_cast(t->m->classpath); + + unsigned nameLength = fileNameLength(entry->start); + object array = makeByteArray(t, nameLength + 1); + memcpy(&byteArrayBody(t, array, 0), fileName(entry->start), nameLength); + byteArrayBody(t, array, nameLength) = 0; + + object name = t->m->classpath->makeString + (t, array, 0, byteArrayLength(t, array) - 1); + + set(t, this_, cp->zipEntryNameField, name); + + cast(this_, cp->zipEntryTimeField) + = fileTime(entry->start); + cast(this_, cp->zipEntryCrcField) + = fileCRC(entry->start); + cast(this_, cp->zipEntrySizeField) + = uncompressedSize(entry->start); + cast(this_, cp->zipEntryCsizeField) + = compressedSize(entry->start); + cast(this_, cp->zipEntryMethodField) + = compressionMethod(entry->start); + } else { + t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + this_, entry->entry); + } +} + +int64_t JNICALL +getZipFileEntryMethod(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + return compressionMethod(entry->start); + } else { + return intValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, entry->entry)); + } +} + +int64_t JNICALL +getZipFileEntryCompressedSize(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + return compressedSize(entry->start); + } else { + return longValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, entry->entry)); + } +} + +int64_t JNICALL +getZipFileEntryUncompressedSize(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + ZipFile::Entry* entry = reinterpret_cast(peer); + if (entry->start) { + return uncompressedSize(entry->start); + } else { + return longValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, entry->entry)); + } +} + +void JNICALL +freeZipFileEntry(Thread* t, object method, uintptr_t* arguments) +{ + int64_t filePeer; memcpy(&filePeer, arguments, 8); + int64_t entryPeer; memcpy(&entryPeer, arguments + 2, 8); + + ZipFile* file = reinterpret_cast(filePeer); + ZipFile::Entry* entry = reinterpret_cast(entryPeer); + if (file->region == 0) { + t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file, entry->entry); + } +} + +int64_t JNICALL +readZipFileEntry(Thread* t, object method, uintptr_t* arguments) +{ + int64_t filePeer; memcpy(&filePeer, arguments, 8); + int64_t entryPeer; memcpy(&entryPeer, arguments + 2, 8); + int64_t position; memcpy(&position, arguments + 4, 8); + object buffer = reinterpret_cast(arguments[6]); + int offset = arguments[7]; + int length = arguments[8]; + + ZipFile* file = reinterpret_cast(filePeer); + ZipFile::Entry* entry = reinterpret_cast(entryPeer); + if (file->region) { + unsigned size = uncompressedSize(entry->start); + if (position >= size) { + return -1; + } + + if (position + length > size) { + length = size - position; + } + + memcpy(&byteArrayBody(t, buffer, offset), + fileData(file->region->start() + localHeaderOffset(entry->start)) + + position, + length); + + return length; + } else { + return intValue + (t, t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file, entry->entry, position, buffer, offset, length)); + } +} + +int64_t JNICALL +getZipMessage(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return 0; + } else { + return reinterpret_cast + (t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file)); + } +} + +int64_t JNICALL +getJarFileMetaInfEntryNames(Thread* t, object method, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + MyClasspath* cp = static_cast(t->m->classpath); + + int64_t peer = cast(this_, cp->zipFileJzfileField); + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + return 0; + } else { + PROTECT(t, method); + + // OpenJDK's Java_java_util_jar_JarFile_getMetaInfEntryNames + // implementation expects to find a pointer to an instance of its + // jzfile structure in the ZipFile.jzfile field of the object we + // pass in. However, we can't pass this_ in, because its + // ZipFile.jzfile field points to a ZipFile instance, not a + // jzfile. So we pass in a temporary object instead which has the + // desired pointer at the same offset. We assume here that + // ZipFile.jzfile is the first field in that class and that + // Java_java_util_jar_JarFile_getMetaInfEntryNames will not look + // for any other fields in the object. + object pseudoThis = makeLong(t, file->file); + + return reinterpret_cast + (t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + pseudoThis)); + } +} + +void JNICALL +closeZipFile(Thread* t, object method, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + ZipFile* file = reinterpret_cast(peer); + if (file->region) { + file->region->dispose(); + t->m->heap->free(file, sizeof(ZipFile) + + (sizeof(ZipFile::Entry) * file->entryCount)); + } else { + t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + 0, file->file); + + t->m->heap->free(file, sizeof(ZipFile)); + } +} + int64_t JNICALL getBootstrapResource(Thread* t, object, uintptr_t* arguments) { @@ -1040,56 +1691,174 @@ interceptFileOperations(Thread* t) MyClasspath* cp = static_cast(t->m->classpath); { object fileClass = resolveClass - (t, root(t, Machine::BootLoader), "java/io/File"); - if (fileClass == 0) return; + (t, root(t, Machine::BootLoader), "java/io/File", false); - object filePathField = findFieldInClass2 - (t, fileClass, "path", "Ljava/lang/String;"); - if (filePathField == 0) return; - - cp->filePathField = fieldOffset(t, filePathField); + if (fileClass) { + object filePathField = findFieldInClass2 + (t, fileClass, "path", "Ljava/lang/String;"); + + if (filePathField) { + cp->filePathField = fieldOffset(t, filePathField); + } + } } { object fileDescriptorClass = resolveClass - (t, root(t, Machine::BootLoader), "java/io/FileDescriptor"); - if (fileDescriptorClass == 0) return; + (t, root(t, Machine::BootLoader), "java/io/FileDescriptor", false); - object fileDescriptorFdField = findFieldInClass2 - (t, fileDescriptorClass, "fd", "I"); - if (fileDescriptorFdField == 0) return; + if (fileDescriptorClass) { + object fileDescriptorFdField = findFieldInClass2 + (t, fileDescriptorClass, "fd", "I"); - cp->fileDescriptorFdField = fieldOffset(t, fileDescriptorFdField); + if (fileDescriptorFdField) { + cp->fileDescriptorFdField = fieldOffset(t, fileDescriptorFdField); + } + } } { object fileInputStreamClass = resolveClass - (t, root(t, Machine::BootLoader), "java/io/FileInputStream"); - if (fileInputStreamClass == 0) return; + (t, root(t, Machine::BootLoader), "java/io/FileInputStream", false); - PROTECT(t, fileInputStreamClass); + if (fileInputStreamClass) { + PROTECT(t, fileInputStreamClass); - object fileInputStreamFdField = findFieldInClass2 - (t, fileInputStreamClass, "fd", "Ljava/io/FileDescriptor;"); - if (fileInputStreamFdField == 0) return; + object fileInputStreamFdField = findFieldInClass2 + (t, fileInputStreamClass, "fd", "Ljava/io/FileDescriptor;"); - cp->fileInputStreamFdField = fieldOffset(t, fileInputStreamFdField); + if (fileInputStreamFdField) { + cp->fileInputStreamFdField = fieldOffset(t, fileInputStreamFdField); - intercept(t, fileInputStreamClass, "open", "(Ljava/lang/String;)V", - voidPointer(openFile)); + intercept(t, fileInputStreamClass, "open", "(Ljava/lang/String;)V", + voidPointer(openFile)); - intercept(t, fileInputStreamClass, "read", "()I", - voidPointer(readByteFromFile)); + intercept(t, fileInputStreamClass, "read", "()I", + voidPointer(readByteFromFile)); - intercept(t, fileInputStreamClass, "readBytes", "([BII)I", - voidPointer(readBytesFromFile)); + intercept(t, fileInputStreamClass, "readBytes", "([BII)I", + voidPointer(readBytesFromFile)); - intercept(t, fileInputStreamClass, "skip", "(J)J", - voidPointer(skipBytesInFile)); + intercept(t, fileInputStreamClass, "skip", "(J)J", + voidPointer(skipBytesInFile)); - intercept(t, fileInputStreamClass, "available", "()I", - voidPointer(availableBytesInFile)); + intercept(t, fileInputStreamClass, "available", "()I", + voidPointer(availableBytesInFile)); - intercept(t, fileInputStreamClass, "close0", "()V", - voidPointer(closeFile)); + intercept(t, fileInputStreamClass, "close0", "()V", + voidPointer(closeFile)); + } + } + } + + { object zipEntryClass = resolveClass + (t, root(t, Machine::BootLoader), "java/util/zip/ZipEntry", false); + + if (zipEntryClass) { + PROTECT(t, zipEntryClass); + + object zipEntryNameField = findFieldInClass2 + (t, zipEntryClass, "name", "Ljava/lang/String;"); + + if (zipEntryNameField) { + cp->zipEntryNameField = fieldOffset(t, zipEntryNameField); + + object zipEntryTimeField = findFieldInClass2 + (t, zipEntryClass, "time", "J"); + + if (zipEntryTimeField) { + cp->zipEntryTimeField = fieldOffset(t, zipEntryTimeField); + + object zipEntryCrcField = findFieldInClass2 + (t, zipEntryClass, "crc", "J"); + + if (zipEntryCrcField) { + cp->zipEntryCrcField = fieldOffset(t, zipEntryCrcField); + + object zipEntrySizeField = findFieldInClass2 + (t, zipEntryClass, "size", "J"); + + if (zipEntrySizeField) { + cp->zipEntrySizeField = fieldOffset(t, zipEntrySizeField); + + object zipEntryCsizeField = findFieldInClass2 + (t, zipEntryClass, "csize", "J"); + + if (zipEntryCsizeField) { + cp->zipEntryCsizeField = fieldOffset(t, zipEntryCsizeField); + + object zipEntryMethodField = findFieldInClass2 + (t, zipEntryClass, "method", "I"); + + if (zipEntryMethodField) { + cp->zipEntryMethodField = fieldOffset + (t, zipEntryMethodField); + + intercept(t, zipEntryClass, "initFields", "(J)V", + voidPointer(initializeZipEntryFields)); + } + } + } + } + } + } + } + } + + { object zipFileClass = resolveClass + (t, root(t, Machine::BootLoader), "java/util/zip/ZipFile", false); + + if (zipFileClass) { + PROTECT(t, zipFileClass); + + object zipFileJzfileField = findFieldInClass2 + (t, zipFileClass, "jzfile", "J"); + + if (zipFileJzfileField) { + cp->zipFileJzfileField = fieldOffset(t, zipFileJzfileField); + + intercept(t, zipFileClass, "open", "(Ljava/lang/String;IJ)J", + voidPointer(openZipFile)); + + intercept(t, zipFileClass, "getTotal", "(J)I", + voidPointer(getZipFileEntryCount)); + + intercept(t, zipFileClass, "getEntry", "(JLjava/lang/String;Z)J", + voidPointer(getZipFileEntry)); + + intercept(t, zipFileClass, "getNextEntry", "(JI)J", + voidPointer(getNextZipFileEntry)); + + intercept(t, zipFileClass, "getMethod", "(J)I", + voidPointer(getZipFileEntryMethod)); + + intercept(t, zipFileClass, "freeEntry", "(JJ)V", + voidPointer(freeZipFileEntry)); + + intercept(t, zipFileClass, "read", "(JJJ[BII)I", + voidPointer(readZipFileEntry)); + + intercept(t, zipFileClass, "getCSize", "(J)J", + voidPointer(getZipFileEntryCompressedSize)); + + intercept(t, zipFileClass, "getSize", "(J)J", + voidPointer(getZipFileEntryUncompressedSize)); + + intercept(t, zipFileClass, "getZipMessage", "(J)Ljava/lang/String;", + voidPointer(getZipMessage)); + + intercept(t, zipFileClass, "close", "(J)V", + voidPointer(closeZipFile)); + } + } + } + + { object jarFileClass = resolveClass + (t, root(t, Machine::BootLoader), "java/util/jar/JarFile", false); + + if (jarFileClass) { + intercept(t, jarFileClass, "getMetaInfEntryNames", + "()[Ljava/lang/String;", + voidPointer(getJarFileMetaInfEntryNames)); + } } { @@ -1103,18 +1872,19 @@ interceptFileOperations(Thread* t) object fsClass = resolveClass (t, root(t, Machine::BootLoader), fsClassName, false); - if (fsClass == 0) return; - PROTECT(t, fsClass); + if (fsClass) { + PROTECT(t, fsClass); - intercept(t, fsClass, gbaMethodName, "(Ljava/io/File;)I", - voidPointer(getFileAttributes)); + intercept(t, fsClass, gbaMethodName, "(Ljava/io/File;)I", + voidPointer(getFileAttributes)); - intercept(t, fsClass, "checkAccess", "(Ljava/io/File;I)Z", - voidPointer(checkFileAccess)); + intercept(t, fsClass, "checkAccess", "(Ljava/io/File;I)Z", + voidPointer(checkFileAccess)); - intercept(t, fsClass, "getLength", "(Ljava/io/File;)J", - voidPointer(getFileLength)); + intercept(t, fsClass, "getLength", "(Ljava/io/File;)J", + voidPointer(getFileLength)); + } } intercept(t, type(t, Machine::ClassLoaderType), "loadLibrary", @@ -1130,10 +1900,23 @@ interceptFileOperations(Thread* t) voidPointer(getBootstrapResources)); } +object +getClassMethodTable(Thread* t, object c) +{ + object addendum = classAddendum(t, c); + if (addendum) { + object table = classAddendumMethodTable(t, addendum); + if (table) { + return table; + } + } + return classMethodTable(t, c); +} + unsigned countMethods(Thread* t, object c, bool publicOnly) { - object table = classMethodTable(t, c); + object table = getClassMethodTable(t, c); unsigned count = 0; for (unsigned i = 0; i < arrayLength(t, table); ++i) { object vmMethod = arrayBody(t, table, i); @@ -1167,7 +1950,7 @@ countFields(Thread* t, object c, bool publicOnly) unsigned countConstructors(Thread* t, object c, bool publicOnly) { - object table = classMethodTable(t, c); + object table = getClassMethodTable(t, c); unsigned count = 0; for (unsigned i = 0; i < arrayLength(t, table); ++i) { object vmMethod = arrayBody(t, table, i); @@ -1356,13 +2139,20 @@ setProperty(Thread* t, object method, object properties, object interruptLock(Thread* t, object thread) { - if (threadInterruptLock(t, thread) == 0) { + object lock = threadInterruptLock(t, thread); + + loadMemoryBarrier(); + + if (lock == 0) { PROTECT(t, thread); ACQUIRE(t, t->m->referenceLock); if (threadInterruptLock(t, thread) == 0) { object head = makeMonitorNode(t, 0, 0); object lock = makeMonitor(t, 0, 0, 0, head, head, 0); + + storeStoreMemoryBarrier(); + set(t, thread, ThreadInterruptLock, lock); } } @@ -1444,6 +2234,13 @@ Avian_sun_misc_Unsafe_registerNatives // ignore } +extern "C" JNIEXPORT int64_t +Avian_sun_misc_Unsafe_addressSize +(Thread*, object, uintptr_t*) +{ + return BytesPerWord; +} + extern "C" JNIEXPORT int64_t Avian_sun_misc_Unsafe_defineClass__Ljava_lang_String_2_3BIILjava_lang_ClassLoader_2Ljava_security_ProtectionDomain_2 (Thread* t, object, uintptr_t* arguments) @@ -1563,6 +2360,16 @@ Avian_sun_misc_Unsafe_putObject set(t, o, offset, reinterpret_cast(value)); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_getShort__Ljava_lang_Object_2J +(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + + return cast(o, offset); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_getInt__Ljava_lang_Object_2J (Thread*, object, uintptr_t* arguments) @@ -1573,6 +2380,16 @@ Avian_sun_misc_Unsafe_getInt__Ljava_lang_Object_2J return cast(o, offset); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_getFloat__Ljava_lang_Object_2J +(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + + return cast(o, offset); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_getIntVolatile (Thread*, object, uintptr_t* arguments) @@ -1585,6 +2402,16 @@ Avian_sun_misc_Unsafe_getIntVolatile return result; } +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_getLong__Ljava_lang_Object_2J +(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + + return cast(o, offset); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_getLongVolatile (Thread* t, object, uintptr_t* arguments) @@ -1611,6 +2438,28 @@ Avian_sun_misc_Unsafe_getLongVolatile return result; } +extern "C" JNIEXPORT void JNICALL +Avian_sun_misc_Unsafe_putByte__Ljava_lang_Object_2JB +(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + int8_t value = arguments[4]; + + cast(o, offset) = value; +} + +extern "C" JNIEXPORT void JNICALL +Avian_sun_misc_Unsafe_putShort__Ljava_lang_Object_2JS +(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + int16_t value = arguments[4]; + + cast(o, offset) = value; +} + extern "C" JNIEXPORT void JNICALL Avian_sun_misc_Unsafe_putInt__Ljava_lang_Object_2JI (Thread*, object, uintptr_t* arguments) @@ -1622,6 +2471,17 @@ Avian_sun_misc_Unsafe_putInt__Ljava_lang_Object_2JI cast(o, offset) = value; } +extern "C" JNIEXPORT void JNICALL +Avian_sun_misc_Unsafe_putFloat__Ljava_lang_Object_2JF +(Thread*, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + int32_t value = arguments[4]; + + cast(o, offset) = value; +} + extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_getBoolean (Thread*, object, uintptr_t* arguments) @@ -1666,6 +2526,19 @@ Avian_sun_misc_Unsafe_getObjectVolatile return value; } +extern "C" JNIEXPORT void JNICALL +Avian_sun_misc_Unsafe_putObjectVolatile +(Thread* t, object, uintptr_t* arguments) +{ + object o = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + object value = reinterpret_cast(arguments[4]); + + storeStoreMemoryBarrier(); + set(t, o, offset, reinterpret_cast(value)); + storeLoadMemoryBarrier(); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_compareAndSwapInt (Thread*, object, uintptr_t* arguments) @@ -1744,6 +2617,26 @@ Avian_sun_misc_Unsafe_setMemory memset(reinterpret_cast(p), v, count); } +extern "C" JNIEXPORT void JNICALL +Avian_sun_misc_Unsafe_putByte__JB +(Thread*, object, uintptr_t* arguments) +{ + int64_t p; memcpy(&p, arguments + 1, 8); + int8_t v = arguments[3]; + + *reinterpret_cast(p) = v; +} + +extern "C" JNIEXPORT void JNICALL +Avian_sun_misc_Unsafe_putShort__JS +(Thread*, object, uintptr_t* arguments) +{ + int64_t p; memcpy(&p, arguments + 1, 8); + int16_t v = arguments[3]; + + *reinterpret_cast(p) = v; +} + extern "C" JNIEXPORT void JNICALL Avian_sun_misc_Unsafe_putLong__JJ (Thread*, object, uintptr_t* arguments) @@ -1782,6 +2675,24 @@ Avian_sun_misc_Unsafe_getInt__J return *reinterpret_cast(p); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_getLong__J +(Thread*, object, uintptr_t* arguments) +{ + int64_t p; memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_getFloat__J +(Thread*, object, uintptr_t* arguments) +{ + int64_t p; memcpy(&p, arguments + 1, 8); + + return *reinterpret_cast(p); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_pageSize (Thread*, object, uintptr_t*) @@ -2015,12 +2926,18 @@ jvmInitProperties(Thread* t, uintptr_t* arguments) # endif local::setProperty(t, method, *properties, "java.io.tmpdir", "/tmp"); local::setProperty(t, method, *properties, "user.home", getenv("HOME")); - local::setProperty(t, method, *properties, "user.dir", getenv("PWD")); + + char buffer[PATH_MAX]; + local::setProperty(t, method, *properties, "user.dir", + getcwd(buffer, PATH_MAX)); #endif local::setProperty(t, method, *properties, "java.protocol.handler.pkgs", "avian"); + local::setProperty(t, method, *properties, "java.vm.vendor", + "Avian Contributors"); + local::setProperty (t, method, *properties, "java.home", static_cast(t->m->classpath)->javaHome); @@ -2099,7 +3016,10 @@ EXPORT(JVM_GC)() } extern "C" JNIEXPORT jlong JNICALL -EXPORT(JVM_MaxObjectInspectionAge)(void) { abort(); } +EXPORT(JVM_MaxObjectInspectionAge)(void) +{ + return 0; +} extern "C" JNIEXPORT void JNICALL EXPORT(JVM_TraceInstructions)(jboolean) { abort(); } @@ -2128,7 +3048,13 @@ EXPORT(JVM_MaxMemory)() extern "C" JNIEXPORT jint JNICALL EXPORT(JVM_ActiveProcessorCount)() { - return 1; +#ifdef PLATFORM_WINDOWS + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwNumberOfProcessors; +#else + return sysconf(_SC_NPROCESSORS_ONLN); +#endif } uint64_t @@ -2175,7 +3101,7 @@ EXPORT(JVM_FindLibraryEntry)(void* library, const char* name) extern "C" JNIEXPORT jboolean JNICALL EXPORT(JVM_IsSupportedJNIVersion)(jint version) { - return version <= JNI_VERSION_1_4; + return version <= JNI_VERSION_1_6; } extern "C" JNIEXPORT jboolean JNICALL @@ -2188,7 +3114,7 @@ jvmFillInStackTrace(Thread* t, uintptr_t* arguments) object trace = getTrace(t, 1); set(t, *throwable, ThrowableTrace, trace); - + return 1; } @@ -2238,19 +3164,31 @@ extern "C" JNIEXPORT jboolean JNICALL EXPORT(JVM_IsSilentCompiler)(Thread*, jclass) { abort(); } extern "C" JNIEXPORT jboolean JNICALL -EXPORT(JVM_CompileClass)(Thread*, jclass, jclass) { abort(); } +EXPORT(JVM_CompileClass)(Thread*, jclass, jclass) +{ + return false; +} extern "C" JNIEXPORT jboolean JNICALL -EXPORT(JVM_CompileClasses)(Thread*, jclass, jstring) { abort(); } +EXPORT(JVM_CompileClasses)(Thread*, jclass, jstring) +{ + return false; +} extern "C" JNIEXPORT jobject JNICALL EXPORT(JVM_CompilerCommand)(Thread*, jclass, jobject) { abort(); } extern "C" JNIEXPORT void JNICALL -EXPORT(JVM_EnableCompiler)(Thread*, jclass) { abort(); } +EXPORT(JVM_EnableCompiler)(Thread*, jclass) +{ + // ignore +} extern "C" JNIEXPORT void JNICALL -EXPORT(JVM_DisableCompiler)(Thread*, jclass) { abort(); } +EXPORT(JVM_DisableCompiler)(Thread*, jclass) +{ + // ignore +} extern "C" JNIEXPORT void JNICALL EXPORT(JVM_StartThread)(Thread* t, jobject thread) @@ -2477,8 +3415,21 @@ EXPORT(JVM_GetSystemPackage)(Thread*, jstring) return 0; } +uint64_t +jvmGetSystemPackages(Thread* t, uintptr_t*) +{ + return reinterpret_cast + (makeLocalReference + (t, makeObjectArray + (t, resolveClass + (t, root(t, Machine::BootLoader), "java/lang/Package"), 0))); +} + extern "C" JNIEXPORT jobjectArray JNICALL -EXPORT(JVM_GetSystemPackages)(Thread*) { abort(); } +EXPORT(JVM_GetSystemPackages)(Thread* t) +{ + return reinterpret_cast(run(t, jvmGetSystemPackages, 0)); +} extern "C" JNIEXPORT jobject JNICALL EXPORT(JVM_AllocateNewObject)(Thread*, jobject, jclass, @@ -2489,7 +3440,40 @@ EXPORT(JVM_AllocateNewArray)(Thread*, jobject, jclass, jint) { abort(); } extern "C" JNIEXPORT jobject JNICALL -EXPORT(JVM_LatestUserDefinedLoader)(Thread*) { abort(); } +EXPORT(JVM_LatestUserDefinedLoader)(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + class Visitor: public Processor::StackVisitor { + public: + Visitor(Thread* t): + t(t), loader(0) + { } + + virtual bool visit(Processor::StackWalker* walker) { + object loader = classLoader(t, methodClass(t, walker->method())); + if (loader + and loader != root(t, Machine::BootLoader) + and strcmp + (&byteArrayBody(t, className(t, objectClass(t, loader)), 0), + reinterpret_cast + ("sun/reflect/DelegatingClassLoader"))) + { + this->loader = loader; + return false; + } else { + return true; + } + } + + Thread* t; + object loader; + } v(t); + + t->m->processor->walkStack(t, &v); + + return makeLocalReference(t, v.loader); +} extern "C" JNIEXPORT jclass JNICALL EXPORT(JVM_LoadClass0)(Thread*, jobject, jclass, @@ -2503,12 +3487,62 @@ EXPORT(JVM_GetArrayLength)(Thread* t, jobject array) return cast(*array, BytesPerWord); } +uint64_t +jvmGetArrayElement(Thread* t, uintptr_t* arguments) +{ + jobject array = reinterpret_cast(arguments[0]); + jint index = arguments[1]; + + switch (byteArrayBody(t, className(t, objectClass(t, *array)), 1)) { + case 'Z': + return reinterpret_cast + (makeLocalReference + (t, makeBoolean(t, cast(*array, ArrayBody + index)))); + case 'B': + return reinterpret_cast + (makeLocalReference + (t, makeByte(t, cast(*array, ArrayBody + index)))); + case 'C': + return reinterpret_cast + (makeLocalReference + (t, makeChar(t, cast(*array, ArrayBody + (index * 2))))); + case 'S': + return reinterpret_cast + (makeLocalReference + (t, makeShort(t, cast(*array, ArrayBody + (index * 2))))); + case 'I': + return reinterpret_cast + (makeLocalReference + (t, makeInt(t, cast(*array, ArrayBody + (index * 4))))); + case 'F': + return reinterpret_cast + (makeLocalReference + (t, makeFloat(t, cast(*array, ArrayBody + (index * 4))))); + case 'J': + return reinterpret_cast + (makeLocalReference + (t, makeLong(t, cast(*array, ArrayBody + (index * 8))))); + case 'D': + return reinterpret_cast + (makeLocalReference + (t, makeDouble(t, cast(*array, ArrayBody + (index * 8))))); + case 'L': + case '[': + return reinterpret_cast + (makeLocalReference + (t, cast(*array, ArrayBody + (index * BytesPerWord)))); + default: + abort(t); + } +} + extern "C" JNIEXPORT jobject JNICALL EXPORT(JVM_GetArrayElement)(Thread* t, jobject array, jint index) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(array), + index }; - return makeLocalReference(t, objectArrayBody(t, *array, index)); + return reinterpret_cast(run(t, jvmGetArrayElement, arguments)); } extern "C" JNIEXPORT jvalue JNICALL @@ -2520,21 +3554,47 @@ EXPORT(JVM_SetArrayElement)(Thread* t, jobject array, jint index, { ENTER(t, Thread::ActiveState); - set(t, *array, ArrayBody + (index * BytesPerWord), (value ? *value : 0)); + switch (byteArrayBody(t, className(t, objectClass(t, *array)), 1)) { + case 'Z': + cast(*array, ArrayBody + index) = booleanValue(t, *value); + break; + case 'B': + cast(*array, ArrayBody + index) = byteValue(t, *value); + break; + case 'C': + cast(*array, ArrayBody + (index * 2)) = charValue(t, *value); + break; + case 'S': + cast(*array, ArrayBody + (index * 2)) = shortValue(t, *value); + break; + case 'I': + cast(*array, ArrayBody + (index * 4)) = intValue(t, *value); + break; + case 'F': + cast(*array, ArrayBody + (index * 4)) = floatValue(t, *value); + break; + case 'J': + cast(*array, ArrayBody + (index * 8)) = longValue(t, *value); + break; + case 'D': + cast(*array, ArrayBody + (index * 8)) = doubleValue(t, *value); + break; + case 'L': + case '[': + set(t, *array, ArrayBody + (index * BytesPerWord), (value ? *value : 0)); + break; + default: + abort(t); + } } extern "C" JNIEXPORT void JNICALL EXPORT(JVM_SetPrimitiveArrayElement)(Thread*, jobject, jint, jvalue, unsigned char) { abort(); } -uint64_t -jvmNewArray(Thread* t, uintptr_t* arguments) +object +makeNewArray(Thread* t, object c, unsigned length) { - jclass elementClass = reinterpret_cast(arguments[0]); - jint length = arguments[1]; - - object c = jclassVmClass(t, *elementClass); - if (classVmFlags(t, c) & PrimitiveFlag) { const char* name = reinterpret_cast (&byteArrayBody(t, local::getClassName(t, c), 0)); @@ -2542,32 +3602,34 @@ jvmNewArray(Thread* t, uintptr_t* arguments) switch (*name) { case 'b': if (name[1] == 'o') { - return reinterpret_cast - (makeLocalReference(t, makeBooleanArray(t, length))); + return makeBooleanArray(t, length); } else { - return reinterpret_cast - (makeLocalReference(t, makeByteArray(t, length))); + return makeByteArray(t, length); } - case 'c': return reinterpret_cast - (makeLocalReference(t, makeCharArray(t, length))); - case 'd': return reinterpret_cast - (makeLocalReference(t, makeDoubleArray(t, length))); - case 'f': return reinterpret_cast - (makeLocalReference(t, makeFloatArray(t, length))); - case 'i': return reinterpret_cast - (makeLocalReference(t, makeIntArray(t, length))); - case 'l': return reinterpret_cast - (makeLocalReference(t, makeLongArray(t, length))); - case 's': return reinterpret_cast - (makeLocalReference(t, makeShortArray(t, length))); + case 'c': return makeCharArray(t, length); + case 'd': return makeDoubleArray(t, length); + case 'f': return makeFloatArray(t, length); + case 'i': return makeIntArray(t, length); + case 'l': return makeLongArray(t, length); + case 's': return makeShortArray(t, length); default: abort(t); } } else { - return reinterpret_cast - (makeLocalReference(t, makeObjectArray(t, c, length))); + return makeObjectArray(t, c, length); } } +uint64_t +jvmNewArray(Thread* t, uintptr_t* arguments) +{ + jclass elementClass = reinterpret_cast(arguments[0]); + jint length = arguments[1]; + + return reinterpret_cast + (makeLocalReference + (t, makeNewArray(t, jclassVmClass(t, *elementClass), length))); +} + extern "C" JNIEXPORT jobject JNICALL EXPORT(JVM_NewArray)(Thread* t, jclass elementClass, jint length) { @@ -2577,16 +3639,51 @@ EXPORT(JVM_NewArray)(Thread* t, jclass elementClass, jint length) return reinterpret_cast(run(t, jvmNewArray, arguments)); } +uint64_t +jvmNewMultiArray(Thread* t, uintptr_t* arguments) +{ + jclass elementClass = reinterpret_cast(arguments[0]); + jintArray dimensions = reinterpret_cast(arguments[1]); + + THREAD_RUNTIME_ARRAY(t, int32_t, counts, intArrayLength(t, *dimensions)); + for (int i = intArrayLength(t, *dimensions) - 1; i >= 0; --i) { + RUNTIME_ARRAY_BODY(counts)[i] = intArrayBody(t, *dimensions, i); + if (UNLIKELY(RUNTIME_ARRAY_BODY(counts)[i] < 0)) { + throwNew(t, Machine::NegativeArraySizeExceptionType, "%d", + RUNTIME_ARRAY_BODY(counts)[i]); + return 0; + } + } + + object array = makeNewArray + (t, jclassVmClass(t, *elementClass), RUNTIME_ARRAY_BODY(counts)[0]); + PROTECT(t, array); + + populateMultiArray(t, array, RUNTIME_ARRAY_BODY(counts), 0, + intArrayLength(t, *dimensions)); + + return reinterpret_cast(makeLocalReference(t, array)); +} + extern "C" JNIEXPORT jobject JNICALL -EXPORT(JVM_NewMultiArray)(Thread*, jclass, jintArray) { abort(); } +EXPORT(JVM_NewMultiArray)(Thread* t, jclass elementClass, + jintArray dimensions) +{ + uintptr_t arguments[] = { reinterpret_cast(elementClass), + reinterpret_cast(dimensions) }; + + return reinterpret_cast(run(t, jvmNewMultiArray, arguments)); +} extern "C" JNIEXPORT jclass JNICALL EXPORT(JVM_GetCallerClass)(Thread* t, int target) { ENTER(t, Thread::ActiveState); - return makeLocalReference - (t, getJClass(t, methodClass(t, getCaller(t, target)))); + object method = getCaller(t, target); + + return method ? makeLocalReference + (t, getJClass(t, methodClass(t, method))) : 0; } extern "C" JNIEXPORT jclass JNICALL @@ -2629,8 +3726,27 @@ EXPORT(JVM_FindPrimitiveClass)(Thread* t, const char* name) } } +uint64_t +jvmResolveClass(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "link", + "(Lavian/VMClass;)V"); + + t->m->processor->invoke(t, method, 0, jclassVmClass(t, *c)); + + return 1; +} + extern "C" JNIEXPORT void JNICALL -EXPORT(JVM_ResolveClass)(Thread*, jclass) { abort(); } +EXPORT(JVM_ResolveClass)(Thread* t, jclass c) +{ + uintptr_t arguments[] = { reinterpret_cast(c) }; + + run(t, jvmResolveClass, arguments); +} uint64_t jvmFindClassFromClassLoader(Thread* t, uintptr_t* arguments) @@ -2640,21 +3756,10 @@ jvmFindClassFromClassLoader(Thread* t, uintptr_t* arguments) jobject loader = reinterpret_cast(arguments[2]); jboolean throwError = arguments[3]; - THREAD_RESOURCE(t, jboolean, throwError, { - if (t->exception and throwError) { - object exception = t->exception; - t->exception = 0; - - t->exception = makeThrowable - (t, Machine::NoClassDefFoundErrorType, - throwableMessage(t, exception), - throwableTrace(t, exception), - throwableCause(t, exception)); - } - }); - object c = resolveClass - (t, loader ? *loader : root(t, Machine::BootLoader), name); + (t, loader ? *loader : root(t, Machine::BootLoader), name, true, + throwError ? Machine::NoClassDefFoundErrorType + : Machine::ClassNotFoundExceptionType); if (init) { PROTECT(t, c); @@ -2762,28 +3867,27 @@ jvmGetClassInterfaces(Thread* t, uintptr_t* arguments) { jclass c = reinterpret_cast(arguments[0]); - object table = classInterfaceTable(t, jclassVmClass(t, *c)); - if (table) { - PROTECT(t, table); + object addendum = classAddendum(t, jclassVmClass(t, *c)); + if (addendum) { + object table = classAddendumInterfaceTable(t, addendum); + if (table) { + PROTECT(t, table); - unsigned stride = - (classFlags(t, jclassVmClass(t, *c)) & ACC_INTERFACE) == 0 ? 2 : 1; + object array = makeObjectArray(t, arrayLength(t, table)); + PROTECT(t, array); - object array = makeObjectArray - (t, type(t, Machine::JclassType), arrayLength(t, table) / stride); - PROTECT(t, array); + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + object c = getJClass(t, arrayBody(t, table, i)); + set(t, array, ArrayBody + (i * BytesPerWord), c); + } - for (unsigned i = 0; i < objectArrayLength(t, array); ++i) { - object interface = getJClass(t, arrayBody(t, table, i * stride)); - set(t, array, ArrayBody + (i * BytesPerWord), interface); + return reinterpret_cast(makeLocalReference(t, array)); } - - return reinterpret_cast(makeLocalReference(t, array)); - } else { - return reinterpret_cast - (makeLocalReference - (t, makeObjectArray(t, type(t, Machine::JclassType), 0))); } + + return reinterpret_cast + (makeLocalReference + (t, makeObjectArray(t, type(t, Machine::JclassType), 0))); } extern "C" JNIEXPORT jobjectArray JNICALL @@ -2850,22 +3954,25 @@ EXPORT(JVM_SetClassSigners)(Thread* t, jclass c, jobjectArray signers) } uint64_t -jvmGetProtectionDomain(Thread* t, uintptr_t*) +jvmGetProtectionDomain(Thread* t, uintptr_t* arguments) { - object openJDK = resolveClass - (t, root(t, Machine::BootLoader), "avian/OpenJDK"); + jclass c = reinterpret_cast(arguments[0]); object method = resolveMethod - (t, openJDK, "getProtectionDomain", "()Ljava/security/ProtectionDomain;"); + (t, root(t, Machine::BootLoader), "avian/OpenJDK", "getProtectionDomain", + "(Lavian/VMClass;)Ljava/security/ProtectionDomain;"); return reinterpret_cast - (makeLocalReference(t, t->m->processor->invoke(t, method, 0))); + (makeLocalReference + (t, t->m->processor->invoke(t, method, 0, jclassVmClass(t, *c)))); } extern "C" JNIEXPORT jobject JNICALL -EXPORT(JVM_GetProtectionDomain)(Thread* t, jclass) +EXPORT(JVM_GetProtectionDomain)(Thread* t, jclass c) { - return reinterpret_cast(run(t, jvmGetProtectionDomain, 0)); + uintptr_t arguments[] = { reinterpret_cast(c) }; + + return reinterpret_cast(run(t, jvmGetProtectionDomain, arguments)); } extern "C" JNIEXPORT void JNICALL @@ -2887,18 +3994,32 @@ EXPORT(JVM_IsPrimitiveClass)(Thread* t, jclass c) return (classVmFlags(t, jclassVmClass(t, *c)) & PrimitiveFlag) != 0; } +uint64_t +jvmGetComponentType(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + if (classArrayDimensions(t, jclassVmClass(t, *c))) { + uint8_t n = byteArrayBody(t, className(t, jclassVmClass(t, *c)), 1); + if (n != 'L' and n != '[') { + return reinterpret_cast + (makeLocalReference(t, getJClass(t, primitiveClass(t, n)))); + } else { + return reinterpret_cast + (makeLocalReference + (t, getJClass(t, classStaticTable(t, jclassVmClass(t, *c))))); + } + } else { + return 0; + } +} + extern "C" JNIEXPORT jclass JNICALL EXPORT(JVM_GetComponentType)(Thread* t, jclass c) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c) }; - uint8_t n = byteArrayBody(t, className(t, jclassVmClass(t, *c)), 1); - if (n != 'L' and n != '[') { - return makeLocalReference(t, getJClass(t, primitiveClass(t, n))); - } else { - return makeLocalReference - (t, getJClass(t, classStaticTable(t, jclassVmClass(t, *c)))); - } + return reinterpret_cast(run(t, jvmGetComponentType, arguments)); } extern "C" JNIEXPORT jint JNICALL @@ -2909,21 +4030,101 @@ EXPORT(JVM_GetClassModifiers)(Thread* t, jclass c) return classFlags(t, jclassVmClass(t, *c)); } +uint64_t +jvmGetDeclaredClasses(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + object addendum = classAddendum(t, jclassVmClass(t, *c)); + if (addendum) { + object table = classAddendumInnerClassTable(t, addendum); + if (table) { + PROTECT(t, table); + + unsigned count = 0; + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (innerClassReferenceOuter(t, arrayBody(t, table, i))) { + ++ count; + } + } + + object result = makeObjectArray(t, count); + PROTECT(t, result); + + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (innerClassReferenceOuter(t, arrayBody(t, table, i))) { + object inner = getJClass + (t, resolveClass + (t, classLoader(t, jclassVmClass(t, *c)), referenceName + (t, innerClassReferenceInner(t, arrayBody(t, table, i))))); + + -- count; + set(t, result, ArrayBody + (count * BytesPerWord), inner); + } + } + + return reinterpret_cast(makeLocalReference(t, result)); + } + } + + return reinterpret_cast + (makeLocalReference(t, makeObjectArray(t, 0))); +} + extern "C" JNIEXPORT jobjectArray JNICALL -EXPORT(JVM_GetDeclaredClasses)(Thread*, jclass) { abort(); } +EXPORT(JVM_GetDeclaredClasses)(Thread* t, jclass c) +{ + uintptr_t arguments[] = { reinterpret_cast(c) }; + + return reinterpret_cast(run(t, jvmGetDeclaredClasses, arguments)); +} + +uint64_t +jvmGetDeclaringClass(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "avian/OpenJDK", "getDeclaringClass", + "(Lavian/VMClass;)Ljava/lang/Class;"); + + return reinterpret_cast + (makeLocalReference + (t, t->m->processor->invoke(t, method, 0, jclassVmClass(t, *c)))); +} extern "C" JNIEXPORT jclass JNICALL -EXPORT(JVM_GetDeclaringClass)(Thread*, jclass) +EXPORT(JVM_GetDeclaringClass)(Thread* t, jclass c) { - // todo: implement properly + uintptr_t arguments[] = { reinterpret_cast(c) }; + + return reinterpret_cast(run(t, jvmGetDeclaringClass, arguments)); +} + +uint64_t +jvmGetClassSignature(Thread* t, uintptr_t* arguments) +{ + jclass c = reinterpret_cast(arguments[0]); + + object addendum = classAddendum(t, jclassVmClass(t, *c)); + if (addendum) { + object signature = addendumSignature(t, addendum); + if (signature) { + return reinterpret_cast + (makeLocalReference + (t, t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1))); + } + } return 0; } extern "C" JNIEXPORT jstring JNICALL -EXPORT(JVM_GetClassSignature)(Thread*, jclass) +EXPORT(JVM_GetClassSignature)(Thread* t, jclass c) { - // todo: implement properly - return 0; + uintptr_t arguments[] = { reinterpret_cast(c) }; + + return reinterpret_cast(run(t, jvmGetClassSignature, arguments)); } extern "C" JNIEXPORT jbyteArray JNICALL @@ -2942,7 +4143,7 @@ jvmGetClassDeclaredMethods(Thread* t, uintptr_t* arguments) jclass c = reinterpret_cast(arguments[0]); jboolean publicOnly = arguments[1]; - object table = classMethodTable(t, jclassVmClass(t, *c)); + object table = getClassMethodTable(t, jclassVmClass(t, *c)); if (table) { PROTECT(t, table); @@ -2983,16 +4184,30 @@ jvmGetClassDeclaredMethods(Thread* t, uintptr_t* arguments) methodAddendum(t, vmMethod)); PROTECT(t, exceptionTypes); - object signature = t->m->classpath->makeString - (t, methodSpec(t, vmMethod), 0, byteArrayLength - (t, methodSpec(t, vmMethod)) - 1); + object signature; + object annotationTable; + object annotationDefault; + object addendum = methodAddendum(t, vmMethod); + if (addendum) { + signature = addendumSignature(t, addendum); + if (signature) { + signature = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); + } - object annotationTable = methodAddendum(t, vmMethod) == 0 - ? 0 : addendumAnnotationTable(t, methodAddendum(t, vmMethod)); + annotationTable = addendumAnnotationTable(t, addendum); - if (annotationTable) { + annotationDefault = methodAddendumAnnotationDefault(t, addendum); + } else { + signature = 0; + annotationTable = 0; + annotationDefault = 0; + } + + if (annotationTable or annotationDefault) { PROTECT(t, signature); PROTECT(t, annotationTable); + PROTECT(t, annotationDefault); object runtimeData = getClassRuntimeData(t, jclassVmClass(t, *c)); @@ -3002,8 +4217,8 @@ jvmGetClassDeclaredMethods(Thread* t, uintptr_t* arguments) object method = makeJmethod (t, true, *c, i, name, returnType, parameterTypes, exceptionTypes, - methodFlags(t, vmMethod), signature, 0, annotationTable, 0, 0, 0, 0, - 0, 0, 0); + methodFlags(t, vmMethod), signature, 0, annotationTable, 0, + annotationDefault, 0, 0, 0, 0, 0); assert(t, ai < objectArrayLength(t, array)); @@ -3063,12 +4278,21 @@ jvmGetClassDeclaredFields(Thread* t, uintptr_t* arguments) type = getJClass(t, type); - object signature = t->m->classpath->makeString - (t, fieldSpec(t, vmField), 0, byteArrayLength - (t, fieldSpec(t, vmField)) - 1); + object signature; + object annotationTable; + object addendum = fieldAddendum(t, vmField); + if (addendum) { + signature = addendumSignature(t, addendum); + if (signature) { + signature = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); + } - object annotationTable = fieldAddendum(t, vmField) == 0 - ? 0 : addendumAnnotationTable(t, fieldAddendum(t, vmField)); + annotationTable = addendumAnnotationTable(t, addendum); + } else { + signature = 0; + annotationTable = 0; + } if (annotationTable) { PROTECT(t, signature); @@ -3114,7 +4338,7 @@ jvmGetClassDeclaredConstructors(Thread* t, uintptr_t* arguments) jclass c = reinterpret_cast(arguments[0]); jboolean publicOnly = arguments[1]; - object table = classMethodTable(t, jclassVmClass(t, *c)); + object table = getClassMethodTable(t, jclassVmClass(t, *c)); if (table) { PROTECT(t, table); @@ -3145,12 +4369,21 @@ jvmGetClassDeclaredConstructors(Thread* t, uintptr_t* arguments) methodAddendum(t, vmMethod)); PROTECT(t, exceptionTypes); - object signature = t->m->classpath->makeString - (t, methodSpec(t, vmMethod), 0, byteArrayLength - (t, methodSpec(t, vmMethod)) - 1); + object signature; + object annotationTable; + object addendum = methodAddendum(t, vmMethod); + if (addendum) { + signature = addendumSignature(t, addendum); + if (signature) { + signature = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); + } - object annotationTable = methodAddendum(t, vmMethod) == 0 - ? 0 : addendumAnnotationTable(t, methodAddendum(t, vmMethod)); + annotationTable = addendumAnnotationTable(t, addendum); + } else { + signature = 0; + annotationTable = 0; + } if (annotationTable) { PROTECT(t, signature); @@ -3653,7 +4886,32 @@ EXPORT(JVM_Lseek)(jint fd, jlong offset, jint seek) } extern "C" JNIEXPORT jint JNICALL -EXPORT(JVM_SetLength)(jint, jlong) { abort(); } +EXPORT(JVM_SetLength)(jint fd, jlong length) +{ +#ifdef PLATFORM_WINDOWS + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + long high = length >> 32; + DWORD r = SetFilePointer(h, static_cast(length), &high, FILE_BEGIN); + if (r == 0xFFFFFFFF and GetLastError() != NO_ERROR) { + errno = EIO; + return -1; + } + + if (SetEndOfFile(h)) { + return 0; + } else { + errno = EIO; + return -1; + } +#else + return ftruncate(fd, length); +#endif +} extern "C" JNIEXPORT jint JNICALL EXPORT(JVM_Sync)(jint fd) @@ -3764,7 +5022,17 @@ EXPORT(JVM_SendTo)(jint, char*, int, int, struct sockaddr*, int) { abort(); } extern "C" JNIEXPORT jint JNICALL -EXPORT(JVM_SocketAvailable)(jint, jint*) { abort(); } +EXPORT(JVM_SocketAvailable)(jint socket, jint* count) +{ +#ifdef PLATFORM_WINDOWS + unsigned long c = *count; + int r = ioctlsocket(socket, FIONREAD, &c); + *count = c; + return r; +#else + return ioctl(socket, FIONREAD, count) < 0 ? 0 : 1; +#endif +} extern "C" JNIEXPORT jint JNICALL EXPORT(JVM_GetSockName)(jint socket, struct sockaddr* address, @@ -3780,7 +5048,7 @@ extern "C" JNIEXPORT jint JNICALL EXPORT(JVM_GetSockOpt)(jint socket, int level, int optionName, char* optionValue, int* optionLength) { - socklen_t length; + socklen_t length = *optionLength; int rv = getsockopt(socket, level, optionName, optionValue, &length); *optionLength = length; return rv; @@ -3844,8 +5112,103 @@ EXPORT(JVM_RawMonitorExit)(void* lock) (local::globalMachine->localThread->get())->systemThread); } +int JNICALL +GetVersion(Thread*) +{ + return JMM_VERSION_1_0; +} + +jint JNICALL +GetOptionalSupport(Thread*, jmmOptionalSupport* support) +{ + memset(support, 0, sizeof(jmmOptionalSupport)); + return 0; +} + +jlong JNICALL +GetLongAttribute(Thread* t, jobject, jmmLongAttribute attribute) +{ + const unsigned JMM_JVM_INIT_DONE_TIME_MS = 7; + + switch (attribute) { + case JMM_JVM_INIT_DONE_TIME_MS: + return 0; + + default: + abort(t); + } +} + +jboolean JNICALL +GetBoolAttribute(Thread* t, jmmBoolAttribute attribute) +{ + const unsigned JMM_THREAD_CPU_TIME = 24; + + switch (attribute) { + case JMM_THREAD_CPU_TIME: + return false; + + default: + abort(t); + } +} + +uint64_t +getMemoryManagers(Thread* t, uintptr_t*) +{ + return reinterpret_cast + (makeLocalReference + (t, makeObjectArray + (t, resolveClass + (t, root(t, Machine::BootLoader), + "java/lang/management/MemoryManagerMXBean"), 0))); +} + +jobjectArray JNICALL +GetMemoryManagers(Thread* t, jobject) +{ + return reinterpret_cast(run(t, getMemoryManagers, 0)); +} + +uint64_t +getMemoryPools(Thread* t, uintptr_t*) +{ + return reinterpret_cast + (makeLocalReference + (t, makeObjectArray + (t, resolveClass + (t, root(t, Machine::BootLoader), + "java/lang/management/MemoryPoolMXBean"), 0))); +} + +jobjectArray JNICALL +GetMemoryPools(Thread* t, jobject) +{ + return reinterpret_cast(run(t, getMemoryPools, 0)); +} + extern "C" JNIEXPORT void* JNICALL -EXPORT(JVM_GetManagement)(jint) { abort(); } +EXPORT(JVM_GetManagement)(jint version) +{ + if (version == JMM_VERSION_1_0) { + JmmInterface* interface + = &(static_cast + (local::globalMachine->classpath)->jmmInterface); + + memset(interface, 0, sizeof(JmmInterface)); + + interface->GetVersion = GetVersion; + interface->GetOptionalSupport = GetOptionalSupport; + interface->GetLongAttribute = GetLongAttribute; + interface->GetBoolAttribute = GetBoolAttribute; + interface->GetMemoryManagers = GetMemoryManagers; + interface->GetMemoryPools = GetMemoryPools; + + return interface; + } else { + return 0; + } +} extern "C" JNIEXPORT jobject JNICALL EXPORT(JVM_InitAgentProperties)(Thread*, jobject) { abort(); } diff --git a/src/common.h b/src/common.h index 5c7160e54c..33e402579f 100644 --- a/src/common.h +++ b/src/common.h @@ -61,11 +61,7 @@ typedef uint64_t uintptr_t; namespace vm { -inline intptr_t& -alias(void* p, unsigned offset) -{ - return *reinterpret_cast(static_cast(p) + offset); -} +typedef intptr_t intptr_alias_t; } // namespace vm @@ -99,11 +95,6 @@ alias(void* p, unsigned offset) namespace vm { typedef intptr_t __attribute__((__may_alias__)) intptr_alias_t; -inline intptr_alias_t& -alias(void* p, unsigned offset) -{ - return *reinterpret_cast(static_cast(p) + offset); -} } // namespace vm @@ -178,6 +169,12 @@ inline void* operator new(size_t, void* p) throw() { return p; } namespace vm { +inline intptr_alias_t& +alias(void* p, unsigned offset) +{ + return *reinterpret_cast(static_cast(p) + offset); +} + #ifdef _MSC_VER template @@ -537,6 +534,16 @@ replace(char a, char b, char* dst, const char* src) dst[i] = 0; } +inline bool +equal(const void* a, unsigned al, const void* b, unsigned bl) +{ + if (al == bl) { + return memcmp(a, b, al) == 0; + } else { + return false; + } +} + class Machine; class Thread; diff --git a/src/compile.cpp b/src/compile.cpp index c9b00f1d9b..c29f403e61 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -52,11 +52,11 @@ const bool Continuations = true; const bool Continuations = false; #endif -const unsigned MaxNativeCallFootprint = 4; +const unsigned MaxNativeCallFootprint = BytesPerWord == 8 ? 4 : 5; const unsigned InitialZoneCapacityInBytes = 64 * 1024; -const unsigned ExecutableAreaSizeInBytes = 16 * 1024 * 1024; +const unsigned ExecutableAreaSizeInBytes = 30 * 1024 * 1024; enum Root { CallTable, @@ -235,7 +235,8 @@ class MyThread: public Thread { : makeArchitecture(m->system, useNativeFeatures)), transition(0), traceContext(0), - stackLimit(0) + stackLimit(0), + methodLockIsClean(true) { arch->acquire(); } @@ -256,6 +257,7 @@ class MyThread: public Thread { Context* transition; TraceContext* traceContext; uintptr_t stackLimit; + bool methodLockIsClean; }; void @@ -1202,7 +1204,8 @@ class Context { dirtyRoots(false), leaf(true), eventLog(t->m->system, t->m->heap, 1024), - protector(this) + protector(this), + resource(this) { } Context(MyThread* t): @@ -1227,7 +1230,8 @@ class Context { dirtyRoots(false), leaf(true), eventLog(t->m->system, t->m->heap, 0), - protector(this) + protector(this), + resource(this) { } ~Context() { @@ -1244,6 +1248,10 @@ class Context { if (executableAllocator) { executableAllocator->free(executableStart, executableSize); } + + eventLog.dispose(); + + zone.dispose(); } MyThread* thread; @@ -1268,6 +1276,7 @@ class Context { bool leaf; Vector eventLog; MyProtector protector; + MyResource resource; }; unsigned @@ -1975,16 +1984,23 @@ void releaseLock(MyThread* t, object method, void* stack) { if (methodFlags(t, method) & ACC_SYNCHRONIZED) { - object lock; - if (methodFlags(t, method) & ACC_STATIC) { - lock = methodClass(t, method); - } else { - lock = *localObject - (t, stackForFrame(t, stack, method), method, - savedTargetIndex(t, method)); - } + if (t->methodLockIsClean) { + object lock; + if (methodFlags(t, method) & ACC_STATIC) { + lock = methodClass(t, method); + } else { + lock = *localObject + (t, stackForFrame(t, stack, method), method, + savedTargetIndex(t, method)); + } - release(t, lock); + release(t, lock); + } else { + // got an exception while trying to acquire the lock for a + // synchronized method -- don't try to release it, since we + // never succeeded in acquiring it. + t->methodLockIsClean = true; + } } } @@ -2232,27 +2248,109 @@ void compile(MyThread* t, FixedAllocator* allocator, BootContext* bootContext, object method); +object +resolveMethod(Thread* t, object pair) +{ + object reference = pairSecond(t, pair); + PROTECT(t, reference); + + object class_ = resolveClassInObject + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), reference, + ReferenceClass); + + return findInHierarchy + (t, class_, referenceName(t, reference), referenceSpec(t, reference), + findMethodInClass, Machine::NoSuchMethodErrorType); +} + +int64_t +prepareMethodForCall(MyThread* t, object target) +{ + if (unresolved(t, methodAddress(t, target))) { + PROTECT(t, target); + + compile(t, codeAllocator(t), 0, target); + } + + if (methodFlags(t, target) & ACC_NATIVE) { + t->trace->nativeMethod = target; + } + return methodAddress(t, target); +} + int64_t findInterfaceMethodFromInstance(MyThread* t, object method, object instance) { if (instance) { - object target = findInterfaceMethod(t, method, objectClass(t, instance)); - - if (unresolved(t, methodAddress(t, target))) { - PROTECT(t, target); - - compile(t, codeAllocator(t), 0, target); - } - - if (methodFlags(t, target) & ACC_NATIVE) { - t->trace->nativeMethod = target; - } - return methodAddress(t, target); + return prepareMethodForCall + (t, findInterfaceMethod(t, method, objectClass(t, instance))); } else { throwNew(t, Machine::NullPointerExceptionType); } } +int64_t +findInterfaceMethodFromInstanceAndReference +(MyThread* t, object pair, object instance) +{ + PROTECT(t, instance); + + object method = resolveMethod(t, pair); + + return findInterfaceMethodFromInstance(t, method, instance); +} + +int64_t +findSpecialMethodFromReference(MyThread* t, object pair) +{ + PROTECT(t, pair); + + object target = resolveMethod(t, pair); + + object class_ = methodClass(t, pairFirst(t, pair)); + if (isSpecialMethod(t, target, class_)) { + target = findVirtualMethod(t, target, classSuper(t, class_)); + } + + assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); + + return prepareMethodForCall(t, target); +} + +int64_t +findStaticMethodFromReference(MyThread* t, object pair) +{ + object target = resolveMethod(t, pair); + + assert(t, methodFlags(t, target) & ACC_STATIC); + + return prepareMethodForCall(t, target); +} + +int64_t +findVirtualMethodFromReference(MyThread* t, object pair, object instance) +{ + PROTECT(t, instance); + + object target = resolveMethod(t, pair); + + target = findVirtualMethod(t, target, objectClass(t, instance)); + + assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); + + return prepareMethodForCall(t, target); +} + +int64_t +getJClassFromReference(MyThread* t, object pair) +{ + return reinterpret_cast + (getJClass + (t, resolveClass + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), + referenceName(t, pairSecond(t, pair))))); +} + int64_t compareDoublesG(uint64_t bi, uint64_t ai) { @@ -2575,6 +2673,16 @@ makeBlankObjectArray(MyThread* t, object class_, int32_t length) } } +uint64_t +makeBlankObjectArrayFromReference(MyThread* t, object pair, + int32_t length) +{ + return makeBlankObjectArray + (t, resolveClass + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), + referenceName(t, pairSecond(t, pair))), length); +} + uint64_t makeBlankArray(MyThread* t, unsigned type, int32_t length) { @@ -2665,6 +2773,18 @@ acquireMonitorForObject(MyThread* t, object o) } } +void +acquireMonitorForObjectOnEntrance(MyThread* t, object o) +{ + if (LIKELY(o)) { + t->methodLockIsClean = false; + acquire(t, o); + t->methodLockIsClean = true; + } else { + throwNew(t, Machine::NullPointerExceptionType); + } +} + void releaseMonitorForObject(MyThread* t, object o) { @@ -2709,6 +2829,17 @@ makeMultidimensionalArray(MyThread* t, object class_, int32_t dimensions, (t, class_, static_cast(t->stack) + offset, dimensions)); } +uint64_t +makeMultidimensionalArrayFromReference(MyThread* t, object pair, + int32_t dimensions, + int32_t offset) +{ + return makeMultidimensionalArray + (t, resolveClass + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), + referenceName(t, pairSecond(t, pair))), dimensions, offset); +} + void NO_RETURN throwArrayIndexOutOfBounds(MyThread* t) { @@ -2751,12 +2882,214 @@ checkCast(MyThread* t, object class_, object o) } } +void +checkCastFromReference(MyThread* t, object pair, object o) +{ + PROTECT(t, o); + + object c = resolveClass + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), + referenceName(t, pairSecond(t, pair))); + + checkCast(t, c, o); +} + +object +resolveField(Thread* t, object pair) +{ + object reference = pairSecond(t, pair); + PROTECT(t, reference); + + object class_ = resolveClassInObject + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), reference, + ReferenceClass); + + return findInHierarchy + (t, class_, referenceName(t, reference), referenceSpec(t, reference), + findFieldInClass, Machine::NoSuchFieldErrorType); +} + +uint64_t +getFieldValue(Thread* t, object target, object field) +{ + switch (fieldCode(t, field)) { + case ByteField: + case BooleanField: + return cast(target, fieldOffset(t, field)); + + case CharField: + case ShortField: + return cast(target, fieldOffset(t, field)); + + case FloatField: + case IntField: + return cast(target, fieldOffset(t, field)); + + case DoubleField: + case LongField: + return cast(target, fieldOffset(t, field)); + + case ObjectField: + return cast(target, fieldOffset(t, field)); + + default: + abort(t); + } +} + +uint64_t +getStaticFieldValueFromReference(MyThread* t, object pair) +{ + object field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, fieldClass(t, field)); + + ACQUIRE_FIELD_FOR_READ(t, field); + + return getFieldValue(t, classStaticTable(t, fieldClass(t, field)), field); +} + +uint64_t +getFieldValueFromReference(MyThread* t, object pair, object instance) +{ + PROTECT(t, instance); + + object field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_READ(t, field); + + return getFieldValue(t, instance, field); +} + +void +setStaticLongFieldValueFromReference(MyThread* t, object pair, uint64_t value) +{ + object field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, fieldClass(t, field)); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, fieldClass(t, field)), fieldOffset(t, field)) = value; +} + +void +setLongFieldValueFromReference(MyThread* t, object pair, object instance, + uint64_t value) +{ + PROTECT(t, instance); + + object field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(instance, fieldOffset(t, field)) = value; +} + +void +setStaticObjectFieldValueFromReference(MyThread* t, object pair, object value) +{ + PROTECT(t, value); + + object field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, fieldClass(t, field)); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + set(t, classStaticTable(t, fieldClass(t, field)), fieldOffset(t, field), + value); +} + +void +setObjectFieldValueFromReference(MyThread* t, object pair, object instance, + object value) +{ + PROTECT(t, instance); + PROTECT(t, value); + + object field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + set(t, instance, fieldOffset(t, field), value); +} + +void +setFieldValue(MyThread* t, object target, object field, uint32_t value) +{ + switch (fieldCode(t, field)) { + case ByteField: + case BooleanField: + cast(target, fieldOffset(t, field)) = value; + break; + + case CharField: + case ShortField: + cast(target, fieldOffset(t, field)) = value; + break; + + case FloatField: + case IntField: + cast(target, fieldOffset(t, field)) = value; + break; + + default: + abort(t); + } +} + +void +setStaticFieldValueFromReference(MyThread* t, object pair, uint32_t value) +{ + object field = resolveField(t, pair); + PROTECT(t, field); + + initClass(t, fieldClass(t, field)); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setFieldValue(t, classStaticTable(t, fieldClass(t, field)), field, value); +} + +void +setFieldValueFromReference(MyThread* t, object pair, object instance, + uint32_t value) +{ + PROTECT(t, instance); + object field = resolveField(t, pair); + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_WRITE(t, field); + + setFieldValue(t, instance, field, value); +} + uint64_t instanceOf64(Thread* t, object class_, object o) { return instanceOf(t, class_, o); } +uint64_t +instanceOfFromReference(Thread* t, object pair, object o) +{ + PROTECT(t, o); + + object c = resolveClass + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), + referenceName(t, pairSecond(t, pair))); + + return instanceOf64(t, c, o); +} + uint64_t makeNewGeneral64(Thread* t, object class_) { @@ -2769,6 +3102,15 @@ makeNew64(Thread* t, object class_) return reinterpret_cast(makeNew(t, class_)); } +uint64_t +makeNewFromReference(Thread* t, object pair) +{ + return makeNewGeneral64 + (t, resolveClass + (t, classLoader(t, methodClass(t, pairFirst(t, pair))), + referenceName(t, pairSecond(t, pair)))); +} + uint64_t getJClass64(Thread* t, object class_) { @@ -2837,6 +3179,29 @@ pushReturnValue(MyThread* t, Frame* frame, unsigned code, } } +Compiler::Operand* +popField(MyThread* t, Frame* frame, int code) +{ + switch (code) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: + return frame->popInt(); + + case DoubleField: + case LongField: + return frame->popLong(); + + case ObjectField: + return frame->popObject(); + + default: abort(t); + } +} + Compiler::OperandType operandTypeForFieldCode(Thread* t, unsigned code) { @@ -3004,6 +3369,74 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall) return tailCall; } +unsigned +methodReferenceParameterFootprint(Thread* t, object reference, bool isStatic) +{ + return parameterFootprint + (t, reinterpret_cast + (&byteArrayBody(t, referenceSpec(t, reference), 0)), isStatic); +} + +int +methodReferenceReturnCode(Thread* t, object reference) +{ + unsigned parameterCount; + unsigned returnCode; + scanMethodSpec + (t, reinterpret_cast + (&byteArrayBody(t, referenceSpec(t, reference), 0)), ¶meterCount, + &returnCode); + + return returnCode; +} + +void +compileReferenceInvoke(MyThread* t, Frame* frame, Compiler::Operand* method, + object reference, bool isStatic, bool tailCall) +{ + unsigned parameterFootprint + = methodReferenceParameterFootprint(t, reference, isStatic); + + int returnCode = methodReferenceReturnCode(t, reference); + + unsigned rSize = resultSize(t, returnCode); + + Compiler::Operand* result = frame->c->stackCall + (method, + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0), + rSize, + operandTypeForFieldCode(t, returnCode), + parameterFootprint); + + frame->pop(parameterFootprint); + + if (rSize) { + pushReturnValue(t, frame, returnCode, result); + } +} + +void +compileDirectReferenceInvoke(MyThread* t, Frame* frame, Thunk thunk, + object reference, bool isStatic, bool tailCall) +{ + Compiler* c = frame->c; + + PROTECT(t, reference); + + object pair = makePair(t, frame->context->method, reference); + + compileReferenceInvoke + (t, frame, c->call + (c->constant(getThunk(t, thunk), Compiler::AddressType), + 0, + frame->trace(0, 0), + BytesPerWord, + Compiler::AddressType, + 2, c->register_(t->arch->thread()), frame->append(pair)), + reference, isStatic, tailCall); +} + void handleMonitorEvent(MyThread* t, Frame* frame, intptr_t function) { @@ -3044,7 +3477,7 @@ handleEntrance(MyThread* t, Frame* frame) } handleMonitorEvent - (t, frame, getThunk(t, acquireMonitorForObjectThunk)); + (t, frame, getThunk(t, acquireMonitorForObjectOnEntranceThunk)); } void @@ -3113,17 +3546,32 @@ returnsNext(MyThread* t, object code, unsigned ip) } bool -isTailCall(MyThread* t, object code, unsigned ip, object caller, object callee) +isTailCall(MyThread* t, object code, unsigned ip, object caller, + int calleeReturnCode) { return TailCalls and ((methodFlags(t, caller) & ACC_SYNCHRONIZED) == 0) and (not inTryBlock(t, code, ip - 1)) and (not needsReturnBarrier(t, caller)) and (methodReturnCode(t, caller) == VoidField - or methodReturnCode(t, caller) == methodReturnCode(t, callee)) + or methodReturnCode(t, caller) == calleeReturnCode) and returnsNext(t, code, ip); } +bool +isTailCall(MyThread* t, object code, unsigned ip, object caller, object callee) +{ + return isTailCall(t, code, ip, caller, methodReturnCode(t, callee)); +} + +bool +isReferenceTailCall(MyThread* t, object code, unsigned ip, object caller, + object calleeReference) +{ + return isTailCall + (t, code, ip, caller, methodReferenceReturnCode(t, calleeReference)); +} + void compile(MyThread* t, Frame* initialFrame, unsigned ip, int exceptionHandlerStart = -1); @@ -3524,19 +3972,34 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case anewarray: { uint16_t index = codeReadInt16(t, code, ip); - object class_ = resolveClassInPool(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); + + PROTECT(t, reference); + + object class_ = resolveClassInPool(t, context->method, index - 1, false); Compiler::Operand* length = frame->popInt(); + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = makeBlankObjectArrayThunk; + } else { + argument = makePair(t, context->method, reference); + thunk = makeBlankObjectArrayFromReferenceThunk; + } + frame->pushObject (c->call - (c->constant - (getThunk(t, makeBlankObjectArrayThunk), Compiler::AddressType), + (c->constant(getThunk(t, thunk), Compiler::AddressType), 0, frame->trace(0, 0), BytesPerWord, Compiler::ObjectType, - 3, c->register_(t->arch->thread()), frame->append(class_), length)); + 3, c->register_(t->arch->thread()), frame->append(argument), + length)); } break; case areturn: { @@ -3594,17 +4057,33 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case checkcast: { uint16_t index = codeReadInt16(t, code, ip); - object class_ = resolveClassInPool(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); + + PROTECT(t, reference); + + object class_ = resolveClassInPool(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = checkCastThunk; + } else { + argument = makePair(t, context->method, reference); + thunk = checkCastFromReferenceThunk; + } Compiler::Operand* instance = c->peek(1, 0); c->call - (c->constant(getThunk(t, checkCastThunk), Compiler::AddressType), + (c->constant(getThunk(t, thunk), Compiler::AddressType), 0, frame->trace(0, 0), 0, Compiler::VoidType, - 3, c->register_(t->arch->thread()), frame->append(class_), instance); + 3, c->register_(t->arch->thread()), frame->append(argument), + instance); } break; case d2f: { @@ -3813,140 +4292,177 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case getstatic: { uint16_t index = codeReadInt16(t, code, ip); - object field = resolveField(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); - if ((fieldFlags(t, field) & ACC_VOLATILE) - and BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - PROTECT(t, field); + PROTECT(t, reference); - c->call - (c->constant - (getThunk(t, acquireMonitorForObjectThunk), Compiler::AddressType), - 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, - c->register_(t->arch->thread()), - frame->append(field)); - } + object field = resolveField(t, context->method, index - 1, false); - Compiler::Operand* table; - - if (instruction == getstatic) { - assert(t, fieldFlags(t, field) & ACC_STATIC); - - PROTECT(t, field); - - if (fieldClass(t, field) != methodClass(t, context->method) - and classNeedsInit(t, fieldClass(t, field))) - { - c->call - (c->constant - (getThunk(t, tryInitClassThunk), Compiler::AddressType), - 0, - frame->trace(0, 0), - 0, - Compiler::VoidType, - 2, c->register_(t->arch->thread()), - frame->append(fieldClass(t, field))); - } - - table = frame->append(classStaticTable(t, fieldClass(t, field))); - } else { - assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); - - table = frame->popObject(); - - if (inTryBlock(t, code, ip - 3)) { - c->saveLocals(); - frame->trace(0, 0); - } - } - - switch (fieldCode(t, field)) { - case ByteField: - case BooleanField: - frame->pushInt - (c->load - (1, 1, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), - BytesPerWord)); - break; - - case CharField: - frame->pushInt - (c->loadz - (2, 2, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), - BytesPerWord)); - break; - - case ShortField: - frame->pushInt - (c->load - (2, 2, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), - BytesPerWord)); - break; - - case FloatField: - frame->pushInt - (c->load - (4, 4, c->memory - (table, Compiler::FloatType, fieldOffset(t, field), 0, 1), - BytesPerWord)); - break; - - case IntField: - frame->pushInt - (c->load - (4, 4, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), - BytesPerWord)); - break; - - case DoubleField: - frame->pushLong - (c->load - (8, 8, c->memory - (table, Compiler::FloatType, fieldOffset(t, field), 0, 1), 8)); - break; - - case LongField: - frame->pushLong - (c->load - (8, 8, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), 8)); - break; - - case ObjectField: - frame->pushObject - (c->load - (BytesPerWord, BytesPerWord, - c->memory - (table, Compiler::ObjectType, fieldOffset(t, field), 0, 1), - BytesPerWord)); - break; - - default: - abort(t); - } - - if (fieldFlags(t, field) & ACC_VOLATILE) { - if (BytesPerWord == 4 + if (LIKELY(field)) { + if ((fieldFlags(t, field) & ACC_VOLATILE) + and BytesPerWord == 4 and (fieldCode(t, field) == DoubleField or fieldCode(t, field) == LongField)) { + PROTECT(t, field); + c->call (c->constant - (getThunk(t, releaseMonitorForObjectThunk), - Compiler::AddressType), + (getThunk(t, acquireMonitorForObjectThunk), Compiler::AddressType), 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, c->register_(t->arch->thread()), frame->append(field)); - } else { - c->loadBarrier(); } + + Compiler::Operand* table; + + if (instruction == getstatic) { + assert(t, fieldFlags(t, field) & ACC_STATIC); + + PROTECT(t, field); + + if (fieldClass(t, field) != methodClass(t, context->method) + and classNeedsInit(t, fieldClass(t, field))) + { + c->call + (c->constant + (getThunk(t, tryInitClassThunk), Compiler::AddressType), + 0, + frame->trace(0, 0), + 0, + Compiler::VoidType, + 2, c->register_(t->arch->thread()), + frame->append(fieldClass(t, field))); + } + + table = frame->append(classStaticTable(t, fieldClass(t, field))); + } else { + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + + table = frame->popObject(); + + if (inTryBlock(t, code, ip - 3)) { + c->saveLocals(); + frame->trace(0, 0); + } + } + + switch (fieldCode(t, field)) { + case ByteField: + case BooleanField: + frame->pushInt + (c->load + (1, 1, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), + BytesPerWord)); + break; + + case CharField: + frame->pushInt + (c->loadz + (2, 2, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), + BytesPerWord)); + break; + + case ShortField: + frame->pushInt + (c->load + (2, 2, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), + BytesPerWord)); + break; + + case FloatField: + frame->pushInt + (c->load + (4, 4, c->memory + (table, Compiler::FloatType, fieldOffset(t, field), 0, 1), + BytesPerWord)); + break; + + case IntField: + frame->pushInt + (c->load + (4, 4, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), + BytesPerWord)); + break; + + case DoubleField: + frame->pushLong + (c->load + (8, 8, c->memory + (table, Compiler::FloatType, fieldOffset(t, field), 0, 1), 8)); + break; + + case LongField: + frame->pushLong + (c->load + (8, 8, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1), 8)); + break; + + case ObjectField: + frame->pushObject + (c->load + (BytesPerWord, BytesPerWord, + c->memory + (table, Compiler::ObjectType, fieldOffset(t, field), 0, 1), + BytesPerWord)); + break; + + default: + abort(t); + } + + if (fieldFlags(t, field) & ACC_VOLATILE) { + if (BytesPerWord == 4 + and (fieldCode(t, field) == DoubleField + or fieldCode(t, field) == LongField)) + { + c->call + (c->constant + (getThunk(t, releaseMonitorForObjectThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, + c->register_(t->arch->thread()), + frame->append(field)); + } else { + c->loadBarrier(); + } + } + } else { + int fieldCode = vm::fieldCode + (t, byteArrayBody(t, referenceSpec(t, reference), 0)); + + object pair = makePair(t, context->method, reference); + + unsigned rSize = resultSize(t, fieldCode); + Compiler::OperandType rType = operandTypeForFieldCode(t, fieldCode); + + Compiler::Operand* result; + if (instruction == getstatic) { + result = c->call + (c->constant + (getThunk(t, getStaticFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 2, + c->register_(t->arch->thread()), frame->append(pair)); + } else { + Compiler::Operand* instance = frame->popObject(); + + result = c->call + (c->constant + (getThunk(t, getFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 3, + c->register_(t->arch->thread()), frame->append(pair), + instance); + } + + pushReturnValue(t, frame, fieldCode, result); } } break; @@ -4213,14 +4729,34 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case instanceof: { uint16_t index = codeReadInt16(t, code, ip); - object class_ = resolveClassInPool(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); + + PROTECT(t, reference); + + object class_ = resolveClassInPool(t, context->method, index - 1, false); + + Compiler::Operand* instance = frame->popObject(); + + object argument; + Thunk thunk; + TraceElement* trace; + if (LIKELY(class_)) { + argument = class_; + thunk = instanceOf64Thunk; + trace = 0; + } else { + argument = makePair(t, context->method, reference); + thunk = instanceOfFromReferenceThunk; + trace = frame->trace(0, 0); + } frame->pushInt (c->call - (c->constant(getThunk(t, instanceOf64Thunk), Compiler::AddressType), - 0, 0, 4, Compiler::IntegerType, - 3, c->register_(t->arch->thread()), frame->append(class_), - frame->popObject())); + (c->constant(getThunk(t, thunk), Compiler::AddressType), + 0, trace, 4, Compiler::IntegerType, + 3, c->register_(t->arch->thread()), frame->append(argument), + instance)); } break; case invokeinterface: { @@ -4229,30 +4765,48 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint16_t index = codeReadInt16(t, code, ip); ip += 2; - object target = resolveMethod(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); - assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); + PROTECT(t, reference); - unsigned parameterFootprint = methodParameterFootprint(t, target); + object target = resolveMethod(t, context->method, index - 1, false); - unsigned instance = parameterFootprint - 1; + object argument; + Thunk thunk; + unsigned parameterFootprint; + int returnCode; + bool tailCall; + if (LIKELY(target)) { + assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); - int returnCode = methodReturnCode(t, target); + argument = target; + thunk = findInterfaceMethodFromInstanceThunk; + parameterFootprint = methodParameterFootprint(t, target); + returnCode = methodReturnCode(t, target); + tailCall = isTailCall(t, code, ip, context->method, target); + } else { + argument = makePair(t, context->method, reference); + thunk = findInterfaceMethodFromInstanceAndReferenceThunk; + parameterFootprint = methodReferenceParameterFootprint + (t, reference, false); + returnCode = methodReferenceReturnCode(t, reference); + tailCall = isReferenceTailCall + (t, code, ip, context->method, reference); + } unsigned rSize = resultSize(t, returnCode); Compiler::Operand* result = c->stackCall (c->call - (c->constant - (getThunk(t, findInterfaceMethodFromInstanceThunk), - Compiler::AddressType), + (c->constant(getThunk(t, thunk), Compiler::AddressType), 0, frame->trace(0, 0), BytesPerWord, Compiler::AddressType, - 3, c->register_(t->arch->thread()), frame->append(target), - c->peek(1, instance)), - 0, + 3, c->register_(t->arch->thread()), frame->append(argument), + c->peek(1, parameterFootprint - 1)), + tailCall ? Compiler::TailJump : 0, frame->trace(0, 0), rSize, operandTypeForFieldCode(t, returnCode), @@ -4269,18 +4823,30 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, context->leaf = false; uint16_t index = codeReadInt16(t, code, ip); - object target = resolveMethod(t, context->method, index - 1); - object class_ = methodClass(t, context->method); - if (isSpecialMethod(t, target, class_)) { - target = findVirtualMethod(t, target, classSuper(t, class_)); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); + + PROTECT(t, reference); + + object target = resolveMethod(t, context->method, index - 1, false); + + if (LIKELY(target)) { + object class_ = methodClass(t, context->method); + if (isSpecialMethod(t, target, class_)) { + target = findVirtualMethod(t, target, classSuper(t, class_)); + } + + assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); + + bool tailCall = isTailCall(t, code, ip, context->method, target); + + compileDirectInvoke(t, frame, target, tailCall); + } else { + compileDirectReferenceInvoke + (t, frame, findSpecialMethodFromReferenceThunk, reference, false, + isReferenceTailCall(t, code, ip, context->method, reference)); } - - assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); - - bool tailCall = isTailCall(t, code, ip, context->method, target); - - compileDirectInvoke(t, frame, target, tailCall); } break; case invokestatic: { @@ -4288,13 +4854,24 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint16_t index = codeReadInt16(t, code, ip); - object target = resolveMethod(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); - assert(t, methodFlags(t, target) & ACC_STATIC); + PROTECT(t, reference); - if (not intrinsic(t, frame, target)) { - bool tailCall = isTailCall(t, code, ip, context->method, target); - compileDirectInvoke(t, frame, target, tailCall); + object target = resolveMethod(t, context->method, index - 1, false); + + if (LIKELY(target)) { + assert(t, methodFlags(t, target) & ACC_STATIC); + + if (not intrinsic(t, frame, target)) { + bool tailCall = isTailCall(t, code, ip, context->method, target); + compileDirectInvoke(t, frame, target, tailCall); + } + } else { + compileDirectReferenceInvoke + (t, frame, findStaticMethodFromReferenceThunk, reference, true, + isReferenceTailCall(t, code, ip, context->method, reference)); } } break; @@ -4303,36 +4880,70 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint16_t index = codeReadInt16(t, code, ip); - object target = resolveMethod(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); - assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); + PROTECT(t, reference); - unsigned parameterFootprint = methodParameterFootprint(t, target); + object target = resolveMethod(t, context->method, index - 1, false); - unsigned offset = ClassVtable + (methodOffset(t, target) * BytesPerWord); + if (LIKELY(target)) { + assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); - Compiler::Operand* instance = c->peek(1, parameterFootprint - 1); + bool tailCall = isTailCall(t, code, ip, context->method, target); - unsigned rSize = resultSize(t, methodReturnCode(t, target)); + if (LIKELY(methodVirtual(t, target))) { + unsigned parameterFootprint = methodParameterFootprint(t, target); - bool tailCall = isTailCall(t, code, ip, context->method, target); + unsigned offset = ClassVtable + + (methodOffset(t, target) * BytesPerWord); - Compiler::Operand* result = c->stackCall - (c->memory - (c->and_ - (BytesPerWord, c->constant(PointerMask, Compiler::IntegerType), - c->memory(instance, Compiler::ObjectType, 0, 0, 1)), - Compiler::ObjectType, offset, 0, 1), - tailCall ? Compiler::TailJump : 0, - frame->trace(0, 0), - rSize, - operandTypeForFieldCode(t, methodReturnCode(t, target)), - parameterFootprint); + Compiler::Operand* instance = c->peek(1, parameterFootprint - 1); - frame->pop(parameterFootprint); + unsigned rSize = resultSize(t, methodReturnCode(t, target)); - if (rSize) { - pushReturnValue(t, frame, methodReturnCode(t, target), result); + Compiler::Operand* result = c->stackCall + (c->memory + (c->and_ + (BytesPerWord, c->constant(PointerMask, Compiler::IntegerType), + c->memory(instance, Compiler::ObjectType, 0, 0, 1)), + Compiler::ObjectType, offset, 0, 1), + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0), + rSize, + operandTypeForFieldCode(t, methodReturnCode(t, target)), + parameterFootprint); + + frame->pop(parameterFootprint); + + if (rSize) { + pushReturnValue(t, frame, methodReturnCode(t, target), result); + } + } else { + // OpenJDK generates invokevirtual calls to private methods + // (e.g. readObject and writeObject for serialization), so + // we must handle such cases here. + + compileDirectInvoke(t, frame, target, tailCall); + } + } else { + PROTECT(t, reference); + + object pair = makePair(t, context->method, reference); + + compileReferenceInvoke + (t, frame, c->call + (c->constant(getThunk(t, findVirtualMethodFromReferenceThunk), + Compiler::AddressType), + 0, + frame->trace(0, 0), + BytesPerWord, + Compiler::AddressType, + 3, c->register_(t->arch->thread()), frame->append(pair), + c->peek(1, methodReferenceParameterFootprint + (t, reference, false) - 1)), + reference, false, isReferenceTailCall + (t, code, ip, context->method, reference)); } } break; @@ -4502,22 +5113,44 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, if (singletonIsObject(t, pool, index - 1)) { object v = singletonObject(t, pool, index - 1); + + loadMemoryBarrier(); + if (objectClass(t, v) == type(t, Machine::ReferenceType)) { - v = resolveClassInPool(t, context->method, index - 1); + object reference = v; + PROTECT(t, reference); + + v = resolveClassInPool(t, context->method, index - 1, false); + + if (UNLIKELY(v == 0)) { + frame->pushObject + (c->call + (c->constant + (getThunk(t, getJClassFromReferenceThunk), + Compiler::AddressType), + 0, + frame->trace(0, 0), + BytesPerWord, + Compiler::ObjectType, + 2, c->register_(t->arch->thread()), + frame->append(makePair(t, context->method, reference)))); + } } - if (objectClass(t, v) == type(t, Machine::ClassType)) { - frame->pushObject - (c->call - (c->constant - (getThunk(t, getJClass64Thunk), Compiler::AddressType), - 0, - frame->trace(0, 0), - BytesPerWord, - Compiler::ObjectType, - 2, c->register_(t->arch->thread()), frame->append(v))); - } else { - frame->pushObject(frame->append(v)); + if (v) { + if (objectClass(t, v) == type(t, Machine::ClassType)) { + frame->pushObject + (c->call + (c->constant + (getThunk(t, getJClass64Thunk), Compiler::AddressType), + 0, + frame->trace(0, 0), + BytesPerWord, + Compiler::ObjectType, + 2, c->register_(t->arch->thread()), frame->append(v))); + } else { + frame->pushObject(frame->append(v)); + } } } else { frame->pushInt @@ -4746,8 +5379,22 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint16_t index = codeReadInt16(t, code, ip); uint8_t dimensions = codeBody(t, code, ip++); - object class_ = resolveClassInPool(t, context->method, index - 1); - PROTECT(t, class_); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); + + PROTECT(t, reference); + + object class_ = resolveClassInPool(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + thunk = makeMultidimensionalArrayThunk; + } else { + argument = makePair(t, context->method, reference); + thunk = makeMultidimensionalArrayFromReferenceThunk; + } unsigned offset = localOffset @@ -4756,12 +5403,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* result = c->call (c->constant - (getThunk(t, makeMultidimensionalArrayThunk), Compiler::AddressType), + (getThunk(t, thunk), Compiler::AddressType), 0, frame->trace(0, 0), BytesPerWord, Compiler::ObjectType, - 4, c->register_(t->arch->thread()), frame->append(class_), + 4, c->register_(t->arch->thread()), frame->append(argument), c->constant(dimensions, Compiler::IntegerType), c->constant(offset, Compiler::IntegerType)); @@ -4772,28 +5419,35 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case new_: { uint16_t index = codeReadInt16(t, code, ip); - object class_ = resolveClassInPool(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); - if (classVmFlags(t, class_) & (WeakReferenceFlag | HasFinalizerFlag)) { - frame->pushObject - (c->call - (c->constant - (getThunk(t, makeNewGeneral64Thunk), Compiler::AddressType), - 0, - frame->trace(0, 0), - BytesPerWord, - Compiler::ObjectType, - 2, c->register_(t->arch->thread()), frame->append(class_))); + PROTECT(t, reference); + + object class_ = resolveClassInPool(t, context->method, index - 1, false); + + object argument; + Thunk thunk; + if (LIKELY(class_)) { + argument = class_; + if (classVmFlags(t, class_) & (WeakReferenceFlag | HasFinalizerFlag)) { + thunk = makeNewGeneral64Thunk; + } else { + thunk = makeNew64Thunk; + } } else { - frame->pushObject - (c->call - (c->constant(getThunk(t, makeNew64Thunk), Compiler::AddressType), - 0, - frame->trace(0, 0), - BytesPerWord, - Compiler::ObjectType, - 2, c->register_(t->arch->thread()), frame->append(class_))); + argument = makePair(t, context->method, reference); + thunk = makeNewFromReferenceThunk; } + + frame->pushObject + (c->call + (c->constant(getThunk(t, thunk), Compiler::AddressType), + 0, + frame->trace(0, 0), + BytesPerWord, + Compiler::ObjectType, + 2, c->register_(t->arch->thread()), frame->append(argument))); } break; case newarray: { @@ -4826,165 +5480,238 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case putstatic: { uint16_t index = codeReadInt16(t, code, ip); - object field = resolveField(t, context->method, index - 1); + object reference = singletonObject + (t, codePool(t, methodCode(t, context->method)), index - 1); - object staticTable = 0; + PROTECT(t, reference); - if (instruction == putstatic) { - assert(t, fieldFlags(t, field) & ACC_STATIC); + object field = resolveField(t, context->method, index - 1, false); - if (fieldClass(t, field) != methodClass(t, context->method) - and classNeedsInit(t, fieldClass(t, field))) - { - PROTECT(t, field); + if (LIKELY(field)) { + int fieldCode = vm::fieldCode(t, field); - c->call - (c->constant - (getThunk(t, tryInitClassThunk), Compiler::AddressType), - 0, - frame->trace(0, 0), - 0, - Compiler::VoidType, - 2, c->register_(t->arch->thread()), - frame->append(fieldClass(t, field))); - } + object staticTable = 0; - staticTable = classStaticTable(t, fieldClass(t, field)); - } else { - assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + if (instruction == putstatic) { + assert(t, fieldFlags(t, field) & ACC_STATIC); - if (inTryBlock(t, code, ip - 3)) { - c->saveLocals(); - frame->trace(0, 0); - } - } + if (fieldClass(t, field) != methodClass(t, context->method) + and classNeedsInit(t, fieldClass(t, field))) + { + PROTECT(t, field); - if (fieldFlags(t, field) & ACC_VOLATILE) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - PROTECT(t, field); + c->call + (c->constant + (getThunk(t, tryInitClassThunk), Compiler::AddressType), + 0, + frame->trace(0, 0), + 0, + Compiler::VoidType, + 2, c->register_(t->arch->thread()), + frame->append(fieldClass(t, field))); + } - c->call - (c->constant - (getThunk(t, acquireMonitorForObjectThunk), - Compiler::AddressType), - 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, - c->register_(t->arch->thread()), frame->append(field)); + staticTable = classStaticTable(t, fieldClass(t, field)); } else { - c->storeStoreBarrier(); + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + + if (inTryBlock(t, code, ip - 3)) { + c->saveLocals(); + frame->trace(0, 0); + } } - } - Compiler::Operand* value; - switch (fieldCode(t, field)) { - case ByteField: - case BooleanField: - case CharField: - case ShortField: - case FloatField: - case IntField: { - value = frame->popInt(); - } break; + if (fieldFlags(t, field) & ACC_VOLATILE) { + if (BytesPerWord == 4 + and (fieldCode == DoubleField or fieldCode == LongField)) + { + PROTECT(t, field); - case DoubleField: - case LongField: { - value = frame->popLong(); - } break; + c->call + (c->constant + (getThunk(t, acquireMonitorForObjectThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, + c->register_(t->arch->thread()), frame->append(field)); + } else { + c->storeStoreBarrier(); + } + } - case ObjectField: { - value = frame->popObject(); - } break; + Compiler::Operand* value = popField(t, frame, fieldCode); - default: abort(t); - } + Compiler::Operand* table; - Compiler::Operand* table; + if (instruction == putstatic) { + PROTECT(t, field); - if (instruction == putstatic) { - PROTECT(t, field); + table = frame->append(staticTable); + } else { + table = frame->popObject(); + } - table = frame->append(staticTable); - } else { - table = frame->popObject(); - } + switch (fieldCode) { + case ByteField: + case BooleanField: + c->store + (BytesPerWord, value, 1, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); + break; - switch (fieldCode(t, field)) { - case ByteField: - case BooleanField: - c->store - (BytesPerWord, value, 1, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); - break; - - case CharField: - case ShortField: - c->store - (BytesPerWord, value, 2, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); - break; + case CharField: + case ShortField: + c->store + (BytesPerWord, value, 2, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); + break; - case FloatField: - c->store - (BytesPerWord, value, 4, c->memory - (table, Compiler::FloatType, fieldOffset(t, field), 0, 1)); - break; + case FloatField: + c->store + (BytesPerWord, value, 4, c->memory + (table, Compiler::FloatType, fieldOffset(t, field), 0, 1)); + break; - case IntField: - c->store - (BytesPerWord, value, 4, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); - break; + case IntField: + c->store + (BytesPerWord, value, 4, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); + break; - case DoubleField: - c->store - (8, value, 8, c->memory - (table, Compiler::FloatType, fieldOffset(t, field), 0, 1)); - break; + case DoubleField: + c->store + (8, value, 8, c->memory + (table, Compiler::FloatType, fieldOffset(t, field), 0, 1)); + break; - case LongField: - c->store - (8, value, 8, c->memory - (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); - break; + case LongField: + c->store + (8, value, 8, c->memory + (table, Compiler::IntegerType, fieldOffset(t, field), 0, 1)); + break; - case ObjectField: - if (instruction == putfield) { - c->call - (c->constant - (getThunk(t, setMaybeNullThunk), Compiler::AddressType), - 0, - frame->trace(0, 0), - 0, - Compiler::VoidType, - 4, c->register_(t->arch->thread()), table, - c->constant(fieldOffset(t, field), Compiler::IntegerType), value); - } else { - c->call - (c->constant(getThunk(t, setThunk), Compiler::AddressType), - 0, 0, 0, Compiler::VoidType, - 4, c->register_(t->arch->thread()), table, - c->constant(fieldOffset(t, field), Compiler::IntegerType), value); + case ObjectField: + if (instruction == putfield) { + c->call + (c->constant + (getThunk(t, setMaybeNullThunk), Compiler::AddressType), + 0, + frame->trace(0, 0), + 0, + Compiler::VoidType, + 4, c->register_(t->arch->thread()), table, + c->constant(fieldOffset(t, field), Compiler::IntegerType), + value); + } else { + c->call + (c->constant(getThunk(t, setThunk), Compiler::AddressType), + 0, 0, 0, Compiler::VoidType, + 4, c->register_(t->arch->thread()), table, + c->constant(fieldOffset(t, field), Compiler::IntegerType), + value); + } + break; + + default: abort(t); } - break; - default: abort(t); - } + if (fieldFlags(t, field) & ACC_VOLATILE) { + if (BytesPerWord == 4 + and (fieldCode == DoubleField or fieldCode == LongField)) + { + c->call + (c->constant + (getThunk(t, releaseMonitorForObjectThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, + c->register_(t->arch->thread()), frame->append(field)); + } else { + c->storeLoadBarrier(); + } + } + } else { + int fieldCode = vm::fieldCode + (t, byteArrayBody(t, referenceSpec(t, reference), 0)); - if (fieldFlags(t, field) & ACC_VOLATILE) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - c->call - (c->constant - (getThunk(t, releaseMonitorForObjectThunk), - Compiler::AddressType), - 0, frame->trace(0, 0), 0, Compiler::VoidType, 2, - c->register_(t->arch->thread()), frame->append(field)); - } else { - c->storeLoadBarrier(); + Compiler::Operand* value = popField(t, frame, fieldCode); + unsigned rSize = resultSize(t, fieldCode); + Compiler::OperandType rType = operandTypeForFieldCode(t, fieldCode); + + object pair = makePair(t, context->method, reference); + + switch (fieldCode) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: { + if (instruction == putstatic) { + c->call + (c->constant + (getThunk(t, setStaticFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 3, + c->register_(t->arch->thread()), frame->append(pair), + value); + } else { + Compiler::Operand* instance = frame->popObject(); + + c->call + (c->constant + (getThunk(t, setFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 4, + c->register_(t->arch->thread()), frame->append(pair), + instance, value); + } + } break; + + case DoubleField: + case LongField: { + if (instruction == putstatic) { + c->call + (c->constant + (getThunk(t, setStaticLongFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 4, + c->register_(t->arch->thread()), frame->append(pair), + static_cast(0), value); + } else { + Compiler::Operand* instance = frame->popObject(); + + c->call + (c->constant + (getThunk(t, setLongFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 5, + c->register_(t->arch->thread()), frame->append(pair), + instance, static_cast(0), value); + } + } break; + + case ObjectField: { + if (instruction == putstatic) { + c->call + (c->constant + (getThunk(t, setStaticObjectFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 3, + c->register_(t->arch->thread()), frame->append(pair), + value); + } else { + Compiler::Operand* instance = frame->popObject(); + + c->call + (c->constant + (getThunk(t, setObjectFieldValueFromReferenceThunk), + Compiler::AddressType), + 0, frame->trace(0, 0), rSize, rType, 4, + c->register_(t->arch->thread()), frame->append(pair), + instance, value); + } + } break; + + default: abort(t); } } } break; @@ -5156,14 +5883,86 @@ logCompile(MyThread* t, const void* code, unsigned size, const char* class_, } } -object -translateExceptionHandlerTable(MyThread* t, Compiler* c, object method, - intptr_t start) +int +resolveIpForwards(Context* context, int start, int end) { - object oldTable = codeExceptionHandlerTable(t, methodCode(t, method)); + while (start < end and context->visitTable[start] == 0) { + ++ start; + } + + if (start >= end) { + return -1; + } else { + return start; + } +} + +int +resolveIpBackwards(Context* context, int start, int end) +{ + while (start >= end and context->visitTable[start] == 0) { + -- start; + } + + if (start < end) { + return -1; + } else { + return start; + } +} + +object +truncateIntArray(Thread* t, object array, unsigned length) +{ + expect(t, intArrayLength(t, array) > length); + + PROTECT(t, array); + + object newArray = makeIntArray(t, length); + memcpy(&intArrayBody(t, newArray, 0), &intArrayBody(t, array, 0), + length * 4); + + return newArray; +} + +object +truncateArray(Thread* t, object array, unsigned length) +{ + expect(t, arrayLength(t, array) > length); + + PROTECT(t, array); + + object newArray = makeArray(t, length); + memcpy(&arrayBody(t, newArray, 0), &arrayBody(t, array, 0), + length * BytesPerWord); + + return newArray; +} + +object +truncateLineNumberTable(Thread* t, object table, unsigned length) +{ + expect(t, lineNumberTableLength(t, table) > length); + + PROTECT(t, table); + + object newTable = makeLineNumberTable(t, length); + memcpy(lineNumberTableBody(t, newTable, 0), + lineNumberTableBody(t, table, 0), + length * sizeof(LineNumber)); + + return newTable; +} + +object +translateExceptionHandlerTable(MyThread* t, Context* context, intptr_t start) +{ + Compiler* c = context->compiler; + + object oldTable = codeExceptionHandlerTable + (t, methodCode(t, context->method)); if (oldTable) { - PROTECT(t, method); PROTECT(t, oldTable); unsigned length = exceptionHandlerTableLength(t, oldTable); @@ -5174,32 +5973,52 @@ translateExceptionHandlerTable(MyThread* t, Compiler* c, object method, object newTable = makeArray(t, length + 1); PROTECT(t, newTable); - set(t, newTable, ArrayBody, newIndex); - - for (unsigned i = 0; i < length; ++i) { + unsigned ni = 0; + for (unsigned oi = 0; oi < length; ++ oi) { ExceptionHandler* oldHandler = exceptionHandlerTableBody - (t, oldTable, i); + (t, oldTable, oi); - intArrayBody(t, newIndex, i * 3) - = c->machineIp(exceptionHandlerStart(oldHandler))->value() - start; + int handlerStart = resolveIpForwards + (context, exceptionHandlerStart(oldHandler), + exceptionHandlerEnd(oldHandler)); - intArrayBody(t, newIndex, (i * 3) + 1) - = c->machineIp(exceptionHandlerEnd(oldHandler))->value() - start; + if (LIKELY(handlerStart >= 0)) { + int handlerEnd = resolveIpBackwards + (context, exceptionHandlerEnd(oldHandler), + exceptionHandlerStart(oldHandler)); - intArrayBody(t, newIndex, (i * 3) + 2) - = c->machineIp(exceptionHandlerIp(oldHandler))->value() - start; + assert(t, handlerEnd >= 0); - object type; - if (exceptionHandlerCatchType(oldHandler)) { - type = resolveClassInPool - (t, method, exceptionHandlerCatchType(oldHandler) - 1); - } else { - type = 0; + intArrayBody(t, newIndex, ni * 3) + = c->machineIp(handlerStart)->value() - start; + + intArrayBody(t, newIndex, (ni * 3) + 1) + = c->machineIp(handlerEnd)->value() - start; + + intArrayBody(t, newIndex, (ni * 3) + 2) + = c->machineIp(exceptionHandlerIp(oldHandler))->value() - start; + + object type; + if (exceptionHandlerCatchType(oldHandler)) { + type = resolveClassInPool + (t, context->method, exceptionHandlerCatchType(oldHandler) - 1); + } else { + type = 0; + } + + set(t, newTable, ArrayBody + ((ni + 1) * BytesPerWord), type); + + ++ ni; } - - set(t, newTable, ArrayBody + ((i + 1) * BytesPerWord), type); } + if (UNLIKELY(ni < length)) { + newIndex = truncateIntArray(t, newIndex, ni * 3); + newTable = truncateArray(t, newTable, ni + 1); + } + + set(t, newTable, ArrayBody, newIndex); + return newTable; } else { return 0; @@ -5207,23 +6026,36 @@ translateExceptionHandlerTable(MyThread* t, Compiler* c, object method, } object -translateLineNumberTable(MyThread* t, Compiler* c, object code, intptr_t start) +translateLineNumberTable(MyThread* t, Context* context, intptr_t start) { - object oldTable = codeLineNumberTable(t, code); + object oldTable = codeLineNumberTable(t, methodCode(t, context->method)); if (oldTable) { - PROTECT(t, code); PROTECT(t, oldTable); unsigned length = lineNumberTableLength(t, oldTable); object newTable = makeLineNumberTable(t, length); - for (unsigned i = 0; i < length; ++i) { - LineNumber* oldLine = lineNumberTableBody(t, oldTable, i); - LineNumber* newLine = lineNumberTableBody(t, newTable, i); + unsigned ni = 0; + for (unsigned oi = 0; oi < length; ++oi) { + LineNumber* oldLine = lineNumberTableBody(t, oldTable, oi); + LineNumber* newLine = lineNumberTableBody(t, newTable, ni); - lineNumberIp(newLine) - = c->machineIp(lineNumberIp(oldLine))->value() - start; + int ip = resolveIpForwards + (context, lineNumberIp(oldLine), oi + 1 < length + ? lineNumberIp(lineNumberTableBody(t, oldTable, oi + 1)) - 1 + : lineNumberIp(oldLine) + 1); - lineNumberLine(newLine) = lineNumberLine(oldLine); + if (LIKELY(ip >= 0)) { + lineNumberIp(newLine) + = context->compiler->machineIp(ip)->value() - start; + + lineNumberLine(newLine) = lineNumberLine(oldLine); + + ++ ni; + } + } + + if (UNLIKELY(ni < length)) { + newTable = truncateLineNumberTable(t, newTable, ni); } return newTable; @@ -5940,13 +6772,12 @@ finish(MyThread* t, FixedAllocator* allocator, Context* context) } { object newExceptionHandlerTable = translateExceptionHandlerTable - (t, c, context->method, reinterpret_cast(start)); + (t, context, reinterpret_cast(start)); PROTECT(t, newExceptionHandlerTable); object newLineNumberTable = translateLineNumberTable - (t, c, methodCode(t, context->method), - reinterpret_cast(start)); + (t, context, reinterpret_cast(start)); object code = methodCode(t, context->method); @@ -6116,24 +6947,27 @@ compile(MyThread* t, Context* context) THREAD_RUNTIME_ARRAY(t, bool, visited, visitCount); memset(RUNTIME_ARRAY_BODY(visited), 0, visitCount * sizeof(bool)); - while (visitCount) { - bool progress = false; + bool progress = true; + while (progress) { + progress = false; for (unsigned i = 0; i < exceptionHandlerTableLength(t, eht); ++i) { - c->restoreState(state); - ExceptionHandler* eh = exceptionHandlerTableBody(t, eht, i); - unsigned start = exceptionHandlerStart(eh); + int start = resolveIpForwards + (context, exceptionHandlerStart(eh), exceptionHandlerEnd(eh)); if ((not RUNTIME_ARRAY_BODY(visited)[i]) + and start >= 0 and context->visitTable[start]) { - -- visitCount; RUNTIME_ARRAY_BODY(visited)[i] = true; progress = true; - THREAD_RUNTIME_ARRAY(t, uint8_t, stackMap, - codeMaxStack(t, methodCode(t, context->method))); + c->restoreState(state); + + THREAD_RUNTIME_ARRAY + (t, uint8_t, stackMap, + codeMaxStack(t, methodCode(t, context->method))); Frame frame2(&frame, RUNTIME_ARRAY_BODY(stackMap)); unsigned end = exceptionHandlerEnd(eh); @@ -6161,8 +6995,6 @@ compile(MyThread* t, Context* context) eventIndex = calculateFrameMaps(t, context, 0, eventIndex); } } - - assert(t, progress); } } @@ -6502,7 +7334,7 @@ invokeNative(MyThread* t) static_cast(t)->trace->nativeMethod = 0; }); - resolveNative(t, t->trace->nativeMethod); + t->m->classpath->resolveNative(t, t->trace->nativeMethod); result = invokeNative2(t, t->trace->nativeMethod); @@ -7139,7 +7971,7 @@ class ArgumentList { default: addInt(cast(objectArrayBody(t, arguments, index++), BytesPerWord)); - break; + break; } } } @@ -7198,6 +8030,17 @@ invoke(Thread* thread, object method, ArgumentList* arguments) { MyThread* t = static_cast(thread); + if (false) { + PROTECT(t, method); + + compile(t, local::codeAllocator(static_cast(t)), 0, + resolveMethod + (t, root(t, Machine::AppLoader), + "foo/ClassName", + "methodName", + "()V")); + } + uintptr_t stackLimit = t->stackLimit; uintptr_t stackPosition = reinterpret_cast(&t); if (stackLimit == 0) { @@ -7300,7 +8143,7 @@ class SignalHandler: public System::SignalHandler { t->exception = vm::root(t, root); } - // printTrace(t, t->exception); + //printTrace(t, t->exception); object continuation; findUnwindTarget(t, ip, frame, stack, &continuation); @@ -7456,8 +8299,8 @@ class MyProcessor: public Processor { { return vm::makeClass (t, flags, vmFlags, fixedSize, arrayElementSize, arrayDimensions, - 0, objectMask, name, sourceFile, super, interfaceTable, - virtualTable, fieldTable, methodTable, staticTable, addendum, loader, + 0, objectMask, name, sourceFile, super, interfaceTable, virtualTable, + fieldTable, methodTable, staticTable, addendum, loader, 0, vtableLength); } @@ -7472,17 +8315,6 @@ class MyProcessor: public Processor { } } - virtual bool - isInitializing(Thread* t, object c) - { - for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { - if (s->class_ == c) { - return true; - } - } - return false; - } - virtual void visitObjects(Thread* vmt, Heap::Visitor* v) { @@ -8754,6 +9586,8 @@ compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) uintptr_t virtualThunk(MyThread* t, unsigned index) { + ACQUIRE(t, t->m->classLock); + if (root(t, VirtualThunks) == 0 or wordArrayLength(t, root(t, VirtualThunks)) <= index * 2) { @@ -8767,14 +9601,10 @@ virtualThunk(MyThread* t, unsigned index) } if (wordArrayBody(t, root(t, VirtualThunks), index * 2) == 0) { - ACQUIRE(t, t->m->classLock); - - if (wordArrayBody(t, root(t, VirtualThunks), index * 2) == 0) { - unsigned size; - uintptr_t thunk = compileVirtualThunk(t, index, &size); - wordArrayBody(t, root(t, VirtualThunks), index * 2) = thunk; - wordArrayBody(t, root(t, VirtualThunks), (index * 2) + 1) = size; - } + unsigned size; + uintptr_t thunk = compileVirtualThunk(t, index, &size); + wordArrayBody(t, root(t, VirtualThunks), index * 2) = thunk; + wordArrayBody(t, root(t, VirtualThunks), (index * 2) + 1) = size; } return wordArrayBody(t, root(t, VirtualThunks), index * 2); diff --git a/src/compiler.cpp b/src/compiler.cpp index 01522a8ef7..c61bd223ad 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -218,7 +218,8 @@ class LogicalInstruction { class Resource { public: Resource(bool reserved = false): - value(0), site(0), freezeCount(0), referenceCount(0), reserved(reserved) + value(0), site(0), previousAcquired(0), nextAcquired(0), freezeCount(0), + referenceCount(0), reserved(reserved) { } virtual void freeze(Context*, Value*) = 0; @@ -229,6 +230,8 @@ class Resource { Value* value; Site* site; + Resource* previousAcquired; + Resource* nextAcquired; uint8_t freezeCount; uint8_t referenceCount; bool reserved; @@ -326,7 +329,7 @@ class Value: public Compiler::Operand { Site* target; Value* buddy; Value* nextWord; - int8_t home; + int16_t home; ValueType type; uint8_t wordIndex; }; @@ -379,6 +382,7 @@ class Context { (static_cast (zone->allocate(sizeof(RegisterResource) * registerLimit))), frameResources(0), + acquiredResources(0), firstConstant(0), lastConstant(0), machineCode(0), @@ -426,6 +430,7 @@ class Context { uint8_t floatRegisterLimit; RegisterResource* registerResources; FrameResource* frameResources; + Resource* acquiredResources; ConstantPoolNode* firstConstant; ConstantPoolNode* lastConstant; uint8_t* machineCode; @@ -527,7 +532,8 @@ class IpPromise: public Promise { } virtual bool resolved() { - return c->machineCode != 0; + return c->machineCode != 0 + and c->logicalCode[logicalIp]->machineOffset->resolved(); } Context* c; @@ -2585,6 +2591,12 @@ acquire(Context* c, Resource* resource, Value* value, Site* site) steal(c, resource, value); } + if (c->acquiredResources) { + c->acquiredResources->previousAcquired = resource; + resource->nextAcquired = c->acquiredResources; + } + c->acquiredResources = resource; + resource->value = value; resource->site = site; } @@ -2604,6 +2616,21 @@ release(Context* c, Resource* resource, Value* value UNUSED, Site* site UNUSED) assert(c, buddies(resource->value, value)); assert(c, site == resource->site); + + Resource* next = resource->nextAcquired; + if (next) { + next->previousAcquired = resource->previousAcquired; + resource->nextAcquired = 0; + } + + Resource* previous = resource->previousAcquired; + if (previous) { + previous->nextAcquired = next; + resource->previousAcquired = 0; + } else { + assert(c, c->acquiredResources == resource); + c->acquiredResources = next; + } resource->value = 0; resource->site = 0; @@ -5526,6 +5553,10 @@ resetFrame(Context* c, Event* e) FrameIterator::Element el = it.next(c); clearSites(c, el.value); } + + while (c->acquiredResources) { + clearSites(c, c->acquiredResources->value); + } } void diff --git a/src/finder.cpp b/src/finder.cpp index 2199dd5207..88006be320 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -32,6 +32,17 @@ append(Allocator* allocator, const char* a, const char* b, const char* c) return p; } +const char* +append(Allocator* allocator, const char* a, const char* b) +{ + unsigned al = strlen(a); + unsigned bl = strlen(b); + char* p = static_cast(allocator->allocate((al + bl) + 1)); + memcpy(p, a, al); + memcpy(p + al, b, bl + 1); + return p; +} + const char* copy(Allocator* allocator, const char* a) { @@ -41,16 +52,6 @@ copy(Allocator* allocator, const char* a) return p; } -bool -equal(const void* a, unsigned al, const void* b, unsigned bl) -{ - if (al == bl) { - return memcmp(a, b, al) == 0; - } else { - return false; - } -} - class Element { public: class Iterator { @@ -65,6 +66,8 @@ class Element { virtual System::Region* find(const char* name) = 0; virtual System::FileType stat(const char* name, unsigned* length, bool tryDirectory) = 0; + virtual const char* urlPrefix() = 0; + virtual const char* sourceUrl() = 0; virtual void dispose() = 0; Element* next; @@ -133,7 +136,9 @@ class DirectoryElement: public Element { }; DirectoryElement(System* s, Allocator* allocator, const char* name): - s(s), allocator(allocator), name(name) + s(s), allocator(allocator), name(name), + urlPrefix_(append(allocator, "file:", name, "/")), + sourceUrl_(append(allocator, "file:", name)) { } virtual Element::Iterator* iterator() { @@ -167,14 +172,26 @@ class DirectoryElement: public Element { return type; } + virtual const char* urlPrefix() { + return urlPrefix_; + } + + virtual const char* sourceUrl() { + return sourceUrl_; + } + virtual void dispose() { allocator->free(name, strlen(name) + 1); + allocator->free(urlPrefix_, strlen(urlPrefix_) + 1); + allocator->free(sourceUrl_, strlen(sourceUrl_) + 1); allocator->free(this, sizeof(*this)); } System* s; Allocator* allocator; const char* name; + const char* urlPrefix_; + const char* sourceUrl_; }; class PointerRegion: public System::Region { @@ -233,9 +250,6 @@ class DataRegion: public System::Region { class JarIndex { public: - static const unsigned LocalHeaderSize = 30; - static const unsigned HeaderSize = 46; - enum CompressionMethod { Stored = 0, Deflated = 8 @@ -262,78 +276,6 @@ class JarIndex { memset(table, 0, sizeof(Node*) * capacity); } - static uint16_t get2(const uint8_t* p) { - return - (static_cast(p[1]) << 8) | - (static_cast(p[0]) ); - } - - static uint32_t get4(const uint8_t* p) { - return - (static_cast(p[3]) << 24) | - (static_cast(p[2]) << 16) | - (static_cast(p[1]) << 8) | - (static_cast(p[0]) ); - } - - static uint32_t signature(const uint8_t* p) { - return get4(p); - } - - static uint16_t compressionMethod(const uint8_t* centralHeader) { - return get2(centralHeader + 10); - } - - static uint32_t compressedSize(const uint8_t* centralHeader) { - return get4(centralHeader + 20); - } - - static uint32_t uncompressedSize(const uint8_t* centralHeader) { - return get4(centralHeader + 24); - } - - static uint16_t fileNameLength(const uint8_t* centralHeader) { - return get2(centralHeader + 28); - } - - static uint16_t extraFieldLength(const uint8_t* centralHeader) { - return get2(centralHeader + 30); - } - - static uint16_t commentFieldLength(const uint8_t* centralHeader) { - return get2(centralHeader + 32); - } - - static uint32_t localHeaderOffset(const uint8_t* centralHeader) { - return get4(centralHeader + 42); - } - - static uint16_t localFileNameLength(const uint8_t* localHeader) { - return get2(localHeader + 26); - } - - static uint16_t localExtraFieldLength(const uint8_t* localHeader) { - return get2(localHeader + 28); - } - - static uint32_t centralDirectoryOffset(const uint8_t* centralHeader) { - return get4(centralHeader + 16); - } - - static const uint8_t* fileName(const uint8_t* centralHeader) { - return centralHeader + 46; - } - - static const uint8_t* fileData(const uint8_t* localHeader) { - return localHeader + LocalHeaderSize + localFileNameLength(localHeader) + - localExtraFieldLength(localHeader); - } - - static const uint8_t* endOfEntry(const uint8_t* p) { - return p + HeaderSize + fileNameLength(p) + extraFieldLength(p) + - commentFieldLength(p); - } - static JarIndex* make(System* s, Allocator* allocator, unsigned capacity) { return new (allocator->allocate(sizeof(JarIndex) + (sizeof(Node*) * capacity))) @@ -347,14 +289,14 @@ class JarIndex { const uint8_t* start = region->start(); const uint8_t* end = start + region->length(); - const uint8_t* p = end - 22; + const uint8_t* p = end - CentralDirectorySearchStart; // Find end of central directory record while (p > start) { - if (signature(p) == 0x06054b50) { + if (signature(p) == CentralDirectorySignature) { p = region->start() + centralDirectoryOffset(p); while (p < end) { - if (signature(p) == 0x02014b50) { + if (signature(p) == EntrySignature) { index = index->add(hash(fileName(p), fileNameLength(p)), p); p = endOfEntry(p); @@ -495,8 +437,8 @@ class JarElement: public Element { virtual const char* next(unsigned* size) { if (position < index->position) { JarIndex::Node* n = index->nodes + (position++); - *size = JarIndex::fileNameLength(n->entry); - return reinterpret_cast(JarIndex::fileName(n->entry)); + *size = fileNameLength(n->entry); + return reinterpret_cast(fileName(n->entry)); } else { return 0; } @@ -513,7 +455,10 @@ class JarElement: public Element { }; JarElement(System* s, Allocator* allocator, const char* name): - s(s), allocator(allocator), name(name), region(0), index(0) + s(s), allocator(allocator), name(name), + urlPrefix_(name ? append(allocator, "jar:file:", name, "!/") : 0), + sourceUrl_(name ? append(allocator, "file:", name) : 0), + region(0), index(0) { } JarElement(System* s, Allocator* allocator, const uint8_t* jarData, @@ -521,6 +466,8 @@ class JarElement: public Element { s(s), allocator(allocator), name(0), + urlPrefix_(name ? append(allocator, "jar:file:", name, "!/") : 0), + sourceUrl_(name ? append(allocator, "file:", name) : 0), region(new (allocator->allocate(sizeof(PointerRegion))) PointerRegion(s, allocator, jarData, jarLength)), index(JarIndex::open(s, allocator, region)) @@ -570,12 +517,22 @@ class JarElement: public Element { : System::TypeDoesNotExist); } + virtual const char* urlPrefix() { + return urlPrefix_; + } + + virtual const char* sourceUrl() { + return sourceUrl_; + } + virtual void dispose() { dispose(sizeof(*this)); } virtual void dispose(unsigned size) { allocator->free(name, strlen(name) + 1); + allocator->free(urlPrefix_, strlen(urlPrefix_) + 1); + allocator->free(sourceUrl_, strlen(sourceUrl_) + 1); if (index) { index->dispose(); } @@ -588,6 +545,8 @@ class JarElement: public Element { System* s; Allocator* allocator; const char* name; + const char* urlPrefix_; + const char* sourceUrl_; System::Region* region; JarIndex* index; }; @@ -620,6 +579,14 @@ class BuiltinElement: public JarElement { } } + virtual const char* urlPrefix() { + return "resource:"; + } + + virtual const char* sourceUrl() { + return 0; + } + virtual void dispose() { library->disposeAll(); if (libraryName) { @@ -662,6 +629,33 @@ void add(System* s, Element** first, Element** last, Allocator* allocator, const char* name, unsigned nameLength, const char* bootLibrary); +void +addTokens(System* s, Element** first, Element** last, Allocator* allocator, + const char* jarName, unsigned jarNameBase, const char* tokens, + unsigned tokensLength, const char* bootLibrary) +{ + for (Tokenizer t(tokens, tokensLength, ' '); t.hasMore();) { + Tokenizer::Token token(t.next()); + + RUNTIME_ARRAY(char, n, jarNameBase + token.length + 1); + memcpy(RUNTIME_ARRAY_BODY(n), jarName, jarNameBase); + memcpy(RUNTIME_ARRAY_BODY(n) + jarNameBase, token.s, token.length); + RUNTIME_ARRAY_BODY(n)[jarNameBase + token.length] = 0; + + add(s, first, last, allocator, RUNTIME_ARRAY_BODY(n), + jarNameBase + token.length, bootLibrary); + } +} + +bool +continuationLine(const uint8_t* base, unsigned total, unsigned* start, + unsigned* length) +{ + return readLine(base, total, start, length) + and *length > 0 + and base[*start] == ' '; +} + void addJar(System* s, Element** first, Element** last, Allocator* allocator, const char* name, const char* bootLibrary) @@ -673,6 +667,8 @@ addJar(System* s, Element** first, Element** last, Allocator* allocator, JarElement* e = new (allocator->allocate(sizeof(JarElement))) JarElement(s, allocator, name); + unsigned nameBase = baseName(name, s->fileSeparator()); + add(first, last, e); System::Region* region = e->find("META-INF/MANIFEST.MF"); @@ -680,29 +676,60 @@ addJar(System* s, Element** first, Element** last, Allocator* allocator, unsigned start = 0; unsigned length; while (readLine(region->start(), region->length(), &start, &length)) { + unsigned multilineTotal = 0; + const unsigned PrefixLength = 12; - if (strncmp("Class-Path: ", reinterpret_cast - (region->start() + start), PrefixLength) == 0) + if (length > PrefixLength + and strncmp("Class-Path: ", reinterpret_cast + (region->start() + start), PrefixLength) == 0) { - for (Tokenizer t(reinterpret_cast - (region->start() + start + PrefixLength), - length - PrefixLength, ' '); - t.hasMore();) - { - Tokenizer::Token token(t.next()); + { unsigned nextStart = start + length; + unsigned nextLength; + while (continuationLine + (region->start(), region->length(), &nextStart, &nextLength)) + { + multilineTotal += nextLength; + nextStart += nextLength; + } + } - unsigned base = baseName(name, s->fileSeparator()); + const char* line = reinterpret_cast + (region->start() + start + PrefixLength); - RUNTIME_ARRAY(char, n, base + token.length + 1); - memcpy(RUNTIME_ARRAY_BODY(n), name, base); - memcpy(RUNTIME_ARRAY_BODY(n) + base, token.s, token.length); - RUNTIME_ARRAY_BODY(n)[base + token.length] = 0; - - add(s, first, last, allocator, RUNTIME_ARRAY_BODY(n), - base + token.length, bootLibrary); + unsigned lineLength = length - PrefixLength; + + if (multilineTotal) { + RUNTIME_ARRAY + (char, n, (length - PrefixLength) + multilineTotal + 1); + + memcpy(RUNTIME_ARRAY_BODY(n), line, lineLength); + + unsigned offset = lineLength; + { unsigned nextStart = start + length; + unsigned nextLength; + while (continuationLine + (region->start(), region->length(), &nextStart, + &nextLength)) + { + unsigned continuationLength = nextLength - 1; + + memcpy(RUNTIME_ARRAY_BODY(n) + offset, + region->start() + nextStart + 1, continuationLength); + + offset += continuationLength; + nextStart += nextLength; + } + } + + addTokens(s, first, last, allocator, name, nameBase, + RUNTIME_ARRAY_BODY(n), offset, bootLibrary); + } else { + addTokens(s, first, last, allocator, name, nameBase, line, + lineLength, bootLibrary); } } - start += length; + + start += length + multilineTotal; } region->dispose(); @@ -854,6 +881,30 @@ class MyFinder: public Finder { return System::TypeDoesNotExist; } + virtual const char* urlPrefix(const char* name) { + for (Element* e = path_; e; e = e->next) { + unsigned length; + System::FileType type = e->stat(name, &length, true); + if (type != System::TypeDoesNotExist) { + return e->urlPrefix(); + } + } + + return 0; + } + + virtual const char* sourceUrl(const char* name) { + for (Element* e = path_; e; e = e->next) { + unsigned length; + System::FileType type = e->stat(name, &length, true); + if (type != System::TypeDoesNotExist) { + return e->sourceUrl(); + } + } + + return 0; + } + virtual const char* path() { return pathString; } diff --git a/src/finder.h b/src/finder.h index 7546fb8b9c..ed03ee9473 100644 --- a/src/finder.h +++ b/src/finder.h @@ -17,6 +17,94 @@ namespace vm { +const unsigned LocalHeaderSize = 30; +const unsigned HeaderSize = 46; + +const unsigned CentralDirectorySignature = 0x06054b50; +const unsigned EntrySignature = 0x02014b50; + +const unsigned CentralDirectorySearchStart = 22; + +inline uint16_t get2(const uint8_t* p) { + return + (static_cast(p[1]) << 8) | + (static_cast(p[0]) ); +} + +inline uint32_t get4(const uint8_t* p) { + return + (static_cast(p[3]) << 24) | + (static_cast(p[2]) << 16) | + (static_cast(p[1]) << 8) | + (static_cast(p[0]) ); +} + +inline uint32_t signature(const uint8_t* p) { + return get4(p); +} + +inline uint16_t compressionMethod(const uint8_t* centralHeader) { + return get2(centralHeader + 10); +} + +inline uint32_t fileTime(const uint8_t* centralHeader) { + return get4(centralHeader + 12); +} + +inline uint32_t fileCRC(const uint8_t* centralHeader) { + return get4(centralHeader + 16); +} + +inline uint32_t compressedSize(const uint8_t* centralHeader) { + return get4(centralHeader + 20); +} + +inline uint32_t uncompressedSize(const uint8_t* centralHeader) { + return get4(centralHeader + 24); +} + +inline uint16_t fileNameLength(const uint8_t* centralHeader) { + return get2(centralHeader + 28); +} + +inline uint16_t extraFieldLength(const uint8_t* centralHeader) { + return get2(centralHeader + 30); +} + +inline uint16_t commentFieldLength(const uint8_t* centralHeader) { + return get2(centralHeader + 32); +} + +inline uint32_t localHeaderOffset(const uint8_t* centralHeader) { + return get4(centralHeader + 42); +} + +inline uint16_t localFileNameLength(const uint8_t* localHeader) { + return get2(localHeader + 26); +} + +inline uint16_t localExtraFieldLength(const uint8_t* localHeader) { + return get2(localHeader + 28); +} + +inline uint32_t centralDirectoryOffset(const uint8_t* centralHeader) { + return get4(centralHeader + 16); +} + +inline const uint8_t* fileName(const uint8_t* centralHeader) { + return centralHeader + 46; +} + +inline const uint8_t* fileData(const uint8_t* localHeader) { + return localHeader + LocalHeaderSize + localFileNameLength(localHeader) + + localExtraFieldLength(localHeader); +} + +inline const uint8_t* endOfEntry(const uint8_t* p) { + return p + HeaderSize + fileNameLength(p) + extraFieldLength(p) + + commentFieldLength(p); +} + inline bool readLine(const uint8_t* base, unsigned total, unsigned* start, unsigned* length) @@ -79,6 +167,8 @@ class Finder { virtual System::FileType stat(const char* name, unsigned* length, bool tryDirectory = false) = 0; + virtual const char* urlPrefix(const char* name) = 0; + virtual const char* sourceUrl(const char* name) = 0; virtual const char* path() = 0; virtual void dispose() = 0; }; diff --git a/src/heap.cpp b/src/heap.cpp index c2474cf653..909eee40eb 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -1434,7 +1434,7 @@ visitDirtyFixies(Context* c, Fixie** p) while (*p) { Fixie* f = *p; - bool wasDirty = false; + bool wasDirty UNUSED = false; bool clean = true; uintptr_t* mask = f->mask(); @@ -1527,7 +1527,7 @@ void collect(Context* c, Segment::Map* map, unsigned start, unsigned end, bool* dirty, bool expectDirty UNUSED) { - bool wasDirty = false; + bool wasDirty UNUSED = false; for (Segment::Map::Iterator it(map, start, end); it.hasMore();) { wasDirty = true; if (map->child) { diff --git a/src/interpret.cpp b/src/interpret.cpp index 3da5ca97d5..afb56c70ff 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -26,8 +26,6 @@ const unsigned FrameMethodOffset = 2; const unsigned FrameIpOffset = 3; const unsigned FrameFootprint = 4; -class ClassInitList; - class Thread: public vm::Thread { public: Thread(Machine* m, object javaThread, vm::Thread* parent): @@ -35,39 +33,16 @@ class Thread: public vm::Thread { ip(0), sp(0), frame(-1), - code(0), - classInitList(0) + code(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) { @@ -322,15 +297,29 @@ setLocalLong(Thread* t, unsigned index, uint64_t value) void pushFrame(Thread* t, object method) { - if (t->frame >= 0) { - pokeInt(t, t->frame + FrameIpOffset, t->ip); - } - t->ip = 0; + PROTECT(t, method); unsigned parameterFootprint = methodParameterFootprint(t, method); unsigned base = t->sp - parameterFootprint; unsigned locals = parameterFootprint; + if (methodFlags(t, method) & ACC_SYNCHRONIZED) { + // Try to acquire the monitor before doing anything else. + // Otherwise, if we were to push the frame first, we risk trying + // to release a monitor we never successfully acquired when we try + // to pop the frame back off. + if (methodFlags(t, method) & ACC_STATIC) { + acquire(t, methodClass(t, method)); + } else { + acquire(t, peekObject(t, base)); + } + } + + if (t->frame >= 0) { + pokeInt(t, t->frame + FrameIpOffset, t->ip); + } + t->ip = 0; + if ((methodFlags(t, method) & ACC_NATIVE) == 0) { t->code = methodCode(t, method); @@ -349,14 +338,6 @@ pushFrame(Thread* t, object method) pokeInt(t, frame + FrameBaseOffset, base); pokeObject(t, frame + FrameMethodOffset, method); pokeInt(t, t->frame + FrameIpOffset, 0); - - if (methodFlags(t, method) & ACC_SYNCHRONIZED) { - if (methodFlags(t, method) & ACC_STATIC) { - acquire(t, methodClass(t, method)); - } else { - acquire(t, peekObject(t, base)); - } - } } void @@ -371,15 +352,6 @@ popFrame(Thread* t) release(t, peekObject(t, frameBase(t, t->frame))); } } - - if (UNLIKELY(methodVmFlags(t, method) & ClassInitFlag) - and t->classInitList - and t->classInitList->class_ == methodClass(t, method)) - { - t->classInitList->pop(); - - postInitClass(t, methodClass(t, method)); - } t->sp = frameBase(t, t->frame); t->frame = frameNext(t, t->frame); @@ -685,32 +657,6 @@ invokeNative(Thread* t, object method) } } -bool -classInit2(Thread* t, object class_, unsigned ipOffset) -{ - PROTECT(t, class_); - - if (preInitClass(t, class_)) { - ClassInitList::push(t, class_); - - t->code = classInitializer(t, class_); - t->ip -= ipOffset; - return true; - } else { - return false; - } -} - -inline bool -classInit(Thread* t, object class_, unsigned ipOffset) -{ - if (UNLIKELY(classVmFlags(t, class_) & NeedInitFlag)) { - return classInit2(t, class_, ipOffset); - } else { - return false; - } -} - inline void store(Thread* t, unsigned index) { @@ -816,9 +762,7 @@ interpret3(Thread* t, const int base) goto throw_; } - if (UNLIKELY(classInit(t, methodClass(t, frameMethod(t, frame)), 0))) { - goto invoke; - } + initClass(t, methodClass(t, frameMethod(t, frame))); loop: instruction = codeBody(t, code, ip++); @@ -1460,27 +1404,11 @@ interpret3(Thread* t, const int base) assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); - if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE) - and BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField))) - { - PROTECT(t, field); - acquire(t, field); - } + PROTECT(t, field); + + ACQUIRE_FIELD_FOR_READ(t, field); pushField(t, popObject(t), field); - - if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - release(t, field); - } else { - loadMemoryBarrier(); - } - } } else { exception = makeThrowable(t, Machine::NullPointerExceptionType); goto throw_; @@ -1496,28 +1424,11 @@ interpret3(Thread* t, const int base) PROTECT(t, field); - if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke; + initClass(t, fieldClass(t, field)); - if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE) - and BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField))) - { - acquire(t, field); - } + ACQUIRE_FIELD_FOR_READ(t, field); pushField(t, classStaticTable(t, fieldClass(t, field)), field); - - if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - release(t, field); - } else { - loadMemoryBarrier(); - } - } } goto loop; case goto_: { @@ -1891,7 +1802,10 @@ interpret3(Thread* t, const int base) object class_ = methodClass(t, frameMethod(t, frame)); if (isSpecialMethod(t, method, class_)) { class_ = classSuper(t, class_); - if (UNLIKELY(classInit(t, class_, 3))) goto invoke; + PROTECT(t, method); + PROTECT(t, class_); + + initClass(t, class_); code = findVirtualMethod(t, method, class_); } else { @@ -1911,7 +1825,7 @@ interpret3(Thread* t, const int base) object method = resolveMethod(t, frameMethod(t, frame), index - 1); PROTECT(t, method); - if (UNLIKELY(classInit(t, methodClass(t, method), 3))) goto invoke; + initClass(t, methodClass(t, method)); code = method; } goto invoke; @@ -1924,7 +1838,10 @@ interpret3(Thread* t, const int base) unsigned parameterFootprint = methodParameterFootprint(t, method); if (LIKELY(peekObject(t, sp - parameterFootprint))) { object class_ = objectClass(t, peekObject(t, sp - parameterFootprint)); - if (UNLIKELY(classInit(t, class_, 3))) goto invoke; + PROTECT(t, method); + PROTECT(t, class_); + + initClass(t, class_); code = findVirtualMethod(t, method, class_); goto invoke; @@ -2385,7 +2302,7 @@ interpret3(Thread* t, const int base) object class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); PROTECT(t, class_); - if (UNLIKELY(classInit(t, class_, 3))) goto invoke; + initClass(t, class_); pushObject(t, make(t, class_)); } goto loop; @@ -2460,80 +2377,61 @@ interpret3(Thread* t, const int base) assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); PROTECT(t, field); - if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - acquire(t, field); - } else { - storeStoreMemoryBarrier(); - } - } + { ACQUIRE_FIELD_FOR_WRITE(t, field); - switch (fieldCode(t, field)) { - case ByteField: - case BooleanField: - case CharField: - case ShortField: - case FloatField: - case IntField: { - int32_t value = popInt(t); - object o = popObject(t); - if (LIKELY(o)) { - switch (fieldCode(t, field)) { - case ByteField: - case BooleanField: - cast(o, fieldOffset(t, field)) = value; - break; + switch (fieldCode(t, field)) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: { + int32_t value = popInt(t); + object o = popObject(t); + if (LIKELY(o)) { + switch (fieldCode(t, field)) { + case ByteField: + case BooleanField: + cast(o, fieldOffset(t, field)) = value; + break; - case CharField: - case ShortField: - cast(o, fieldOffset(t, field)) = value; - break; + case CharField: + case ShortField: + cast(o, fieldOffset(t, field)) = value; + break; - case FloatField: - case IntField: - cast(o, fieldOffset(t, field)) = value; - break; + case FloatField: + case IntField: + cast(o, fieldOffset(t, field)) = value; + break; + } + } else { + exception = makeThrowable(t, Machine::NullPointerExceptionType); } - } else { - exception = makeThrowable(t, Machine::NullPointerExceptionType); - } - } break; + } break; - case DoubleField: - case LongField: { - int64_t value = popLong(t); - object o = popObject(t); - if (LIKELY(o)) { - cast(o, fieldOffset(t, field)) = value; - } else { - exception = makeThrowable(t, Machine::NullPointerExceptionType); - } - } break; + case DoubleField: + case LongField: { + int64_t value = popLong(t); + object o = popObject(t); + if (LIKELY(o)) { + cast(o, fieldOffset(t, field)) = value; + } else { + exception = makeThrowable(t, Machine::NullPointerExceptionType); + } + } break; - case ObjectField: { - object value = popObject(t); - object o = popObject(t); - if (LIKELY(o)) { - set(t, o, fieldOffset(t, field), value); - } else { - exception = makeThrowable(t, Machine::NullPointerExceptionType); - } - } break; + case ObjectField: { + object value = popObject(t); + object o = popObject(t); + if (LIKELY(o)) { + set(t, o, fieldOffset(t, field), value); + } else { + exception = makeThrowable(t, Machine::NullPointerExceptionType); + } + } break; - default: abort(t); - } - - if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - release(t, field); - } else { - storeLoadMemoryBarrier(); + default: abort(t); } } @@ -2551,18 +2449,9 @@ interpret3(Thread* t, const int base) PROTECT(t, field); - if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - acquire(t, field); - } else { - storeStoreMemoryBarrier(); - } - } + ACQUIRE_FIELD_FOR_WRITE(t, field); - if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke; + initClass(t, fieldClass(t, field)); object table = classStaticTable(t, fieldClass(t, field)); @@ -2603,17 +2492,6 @@ interpret3(Thread* t, const int base) default: abort(t); } - - if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { - if (BytesPerWord == 4 - and (fieldCode(t, field) == DoubleField - or fieldCode(t, field) == LongField)) - { - release(t, field); - } else { - storeLoadMemoryBarrier(); - } - } } goto loop; case ret: { @@ -3048,7 +2926,7 @@ class MyProcessor: public Processor { return vm::makeClass (t, flags, vmFlags, fixedSize, arrayElementSize, arrayDimensions, 0, objectMask, name, sourceFile, super, interfaceTable, virtualTable, - fieldTable, methodTable, addendum, staticTable, loader, 0); + fieldTable, methodTable, addendum, staticTable, loader, 0, 0); } virtual void @@ -3057,26 +2935,6 @@ class MyProcessor: public Processor { // ignore } - virtual bool - isInitializing(vm::Thread* vmt, object c) - { - Thread* t = static_cast(vmt); - - for (ClassInitList* list = t->classInitList; list; list = list->next) { - if (list->class_ == c) { - return true; - } - } - - for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { - if (s->class_ == c) { - return true; - } - } - - return false; - } - virtual void visitObjects(vm::Thread* vmt, Heap::Visitor* v) { @@ -3089,10 +2947,6 @@ 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 3fb07db9ad..d797b3092c 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -20,12 +20,6 @@ namespace { namespace local { -const uintptr_t InterfaceMethodID -= (static_cast(1) << (BitsPerWord - 1)); - -const uintptr_t NonVirtualMethodID -= (static_cast(1) << (BitsPerWord - 2)); - jint JNICALL AttachCurrentThread(Machine* m, Thread** t, void*) { @@ -430,30 +424,78 @@ GetDirectBufferCapacity(Thread*, jobject) return -1; } +uint64_t +getObjectClass(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + + return reinterpret_cast + (makeLocalReference(t, getJClass(t, objectClass(t, *o)))); +} + jclass JNICALL GetObjectClass(Thread* t, jobject o) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o) }; - return makeLocalReference(t, getJClass(t, objectClass(t, *o))); + return reinterpret_cast(run(t, getObjectClass, arguments)); } -jboolean JNICALL -IsInstanceOf(Thread* t, jobject o, jclass c) +uint64_t +getSuperclass(Thread* t, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + jclass c = reinterpret_cast(arguments[0]); + + object super = classSuper(t, jclassVmClass(t, *c)); + + return super ? reinterpret_cast + (makeLocalReference(t, getJClass(t, super))) : 0; +} + +jclass JNICALL +GetSuperclass(Thread* t, jclass c) +{ + uintptr_t arguments[] = { reinterpret_cast(c) }; + + return reinterpret_cast(run(t, getSuperclass, arguments)); +} + +uint64_t +isInstanceOf(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + jclass c = reinterpret_cast(arguments[1]); return instanceOf(t, jclassVmClass(t, *c), *o); } jboolean JNICALL -IsAssignableFrom(Thread* t, jclass b, jclass a) +IsInstanceOf(Thread* t, jobject o, jclass c) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + reinterpret_cast(c) }; + + return run(t, isInstanceOf, arguments); +} + +uint64_t +isAssignableFrom(Thread* t, uintptr_t* arguments) +{ + jclass b = reinterpret_cast(arguments[0]); + jclass a = reinterpret_cast(arguments[1]); return isAssignableFrom(t, jclassVmClass(t, *a), jclassVmClass(t, *b)); } +jboolean JNICALL +IsAssignableFrom(Thread* t, jclass b, jclass a) +{ + uintptr_t arguments[] = { reinterpret_cast(b), + reinterpret_cast(a) }; + + return run(t, isAssignableFrom, arguments); +} + object findMethod(Thread* t, jclass c, const char* name, const char* spec) { @@ -467,7 +509,11 @@ findMethod(Thread* t, jclass c, const char* name, const char* spec) jint methodID(Thread* t, object method) { - if (methodNativeID(t, method) == 0) { + int id = methodNativeID(t, method); + + loadMemoryBarrier(); + + if (id == 0) { PROTECT(t, method); ACQUIRE(t, t->m->referenceLock); @@ -475,6 +521,9 @@ methodID(Thread* t, object method) if (methodNativeID(t, method) == 0) { setRoot(t, Machine::JNIMethodTable, vectorAppend (t, root(t, Machine::JNIMethodTable), method)); + + storeStoreMemoryBarrier(); + methodNativeID(t, method) = vectorSize (t, root(t, Machine::JNIMethodTable)); } @@ -531,9 +580,11 @@ GetStaticMethodID(Thread* t, jclass c, const char* name, const char* spec) return run(t, getStaticMethodID, arguments); } -inline object +object getMethod(Thread* t, jmethodID m) { + assert(t, m); + object method = vectorBody(t, root(t, Machine::JNIMethodTable), m - 1); assert(t, (methodFlags(t, method) & ACC_STATIC) == 0); @@ -855,9 +906,11 @@ CallVoidMethod(Thread* t, jobject o, jmethodID m, ...) va_end(a); } -inline object +object getStaticMethod(Thread* t, jmethodID m) { + assert(t, m); + object method = vectorBody(t, root(t, Machine::JNIMethodTable), m - 1); assert(t, methodFlags(t, method) & ACC_STATIC); @@ -1115,6 +1168,31 @@ CallStaticVoidMethod(Thread* t, jclass c, jmethodID m, ...) va_end(a); } +jint +fieldID(Thread* t, object field) +{ + int id = fieldNativeID(t, field); + + loadMemoryBarrier(); + + if (id == 0) { + PROTECT(t, field); + + ACQUIRE(t, t->m->referenceLock); + + if (fieldNativeID(t, field) == 0) { + setRoot(t, Machine::JNIFieldTable, vectorAppend + (t, root(t, Machine::JNIFieldTable), field)); + + storeStoreMemoryBarrier(); + + fieldNativeID(t, field) = vectorSize(t, root(t, Machine::JNIFieldTable)); + } + } + + return fieldNativeID(t, field); +} + uint64_t getFieldID(Thread* t, uintptr_t* arguments) { @@ -1122,7 +1200,7 @@ getFieldID(Thread* t, uintptr_t* arguments) const char* name = reinterpret_cast(arguments[1]); const char* spec = reinterpret_cast(arguments[2]); - return fieldOffset(t, resolveField(t, jclassVmClass(t, *c), name, spec)); + return fieldID(t, resolveField(t, jclassVmClass(t, *c), name, spec)); } jfieldID JNICALL @@ -1145,293 +1223,937 @@ GetStaticFieldID(Thread* t, jclass c, const char* name, const char* spec) return run(t, getFieldID, arguments); } +object +getField(Thread* t, jfieldID f) +{ + assert(t, f); + + object field = vectorBody(t, root(t, Machine::JNIFieldTable), f - 1); + + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + + return field; +} + +uint64_t +getObjectField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return reinterpret_cast + (makeLocalReference(t, cast(*o, fieldOffset(t, field)))); +} + jobject JNICALL GetObjectField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return makeLocalReference(t, cast(*o, field)); + return reinterpret_cast(run(t, getObjectField, arguments)); +} + +uint64_t +getBooleanField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast(*o, fieldOffset(t, field)); } jboolean JNICALL GetBooleanField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return run(t, getBooleanField, arguments); +} + +uint64_t +getByteField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast(*o, fieldOffset(t, field)); } jbyte JNICALL GetByteField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return run(t, getByteField, arguments); +} + +uint64_t +getCharField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast(*o, fieldOffset(t, field)); } jchar JNICALL GetCharField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return run(t, getCharField, arguments); +} + +uint64_t +getShortField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast(*o, fieldOffset(t, field)); } jshort JNICALL GetShortField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return run(t, getShortField, arguments); +} + +uint64_t +getIntField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast(*o, fieldOffset(t, field)); } jint JNICALL GetIntField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return run(t, getIntField, arguments); +} + +uint64_t +getLongField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast(*o, fieldOffset(t, field)); } jlong JNICALL GetLongField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return run(t, getLongField, arguments); +} + +uint64_t +getFloatField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return floatToBits(cast(*o, fieldOffset(t, field))); } jfloat JNICALL GetFloatField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return bitsToFloat(run(t, getFloatField, arguments)); +} + +uint64_t +getDoubleField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return doubleToBits(cast(*o, fieldOffset(t, field))); } jdouble JNICALL GetDoubleField(Thread* t, jobject o, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field }; - return cast(*o, field); + return bitsToDouble(run(t, getDoubleField, arguments)); +} + +uint64_t +setObjectField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jobject v = reinterpret_cast(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + set(t, *o, fieldOffset(t, field), (v ? *v : 0)); + + return 1; } void JNICALL SetObjectField(Thread* t, jobject o, jfieldID field, jobject v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + reinterpret_cast(v) }; - set(t, *o, field, (v ? *v : 0)); + run(t, setObjectField, arguments); +} + +uint64_t +setBooleanField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jboolean v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetBooleanField(Thread* t, jobject o, jfieldID field, jboolean v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + v }; - cast(*o, field) = v; + run(t, setBooleanField, arguments); +} + +uint64_t +setByteField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jbyte v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetByteField(Thread* t, jobject o, jfieldID field, jbyte v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + v }; - cast(*o, field) = v; + run(t, setByteField, arguments); +} + +uint64_t +setCharField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jchar v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetCharField(Thread* t, jobject o, jfieldID field, jchar v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + v }; - cast(*o, field) = v; + run(t, setCharField, arguments); +} + +uint64_t +setShortField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jshort v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetShortField(Thread* t, jobject o, jfieldID field, jshort v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + v }; - cast(*o, field) = v; + run(t, setShortField, arguments); +} + +uint64_t +setIntField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jint v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetIntField(Thread* t, jobject o, jfieldID field, jint v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + v }; - cast(*o, field) = v; + run(t, setIntField, arguments); +} + +uint64_t +setLongField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jlong v; memcpy(&v, arguments + 2, sizeof(jlong)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetLongField(Thread* t, jobject o, jfieldID field, jlong v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[2 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(o); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jlong)); - cast(*o, field) = v; + run(t, setLongField, arguments); +} + +uint64_t +setFloatField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jfloat v = bitsToFloat(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetFloatField(Thread* t, jobject o, jfieldID field, jfloat v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(o), + field, + floatToBits(v) }; - cast(*o, field) = v; + run(t, setFloatField, arguments); +} + +uint64_t +setDoubleField(Thread* t, uintptr_t* arguments) +{ + jobject o = reinterpret_cast(arguments[0]); + object field = getField(t, arguments[1]); + jdouble v; memcpy(&v, arguments + 2, sizeof(jdouble)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast(*o, fieldOffset(t, field)) = v; + + return 1; } void JNICALL SetDoubleField(Thread* t, jobject o, jfieldID field, jdouble v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[2 + (sizeof(jdouble) / BytesPerWord)]; + arguments[0] = reinterpret_cast(o); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jdouble)); - cast(*o, field) = v; + run(t, setDoubleField, arguments); +} + +object +getStaticField(Thread* t, jfieldID f) +{ + assert(t, f); + + object field = vectorBody(t, root(t, Machine::JNIFieldTable), f - 1); + + assert(t, fieldFlags(t, field) & ACC_STATIC); + + return field; +} + +uint64_t +getStaticObjectField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return reinterpret_cast + (makeLocalReference + (t, cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)))); } jobject JNICALL -GetStaticObjectField(Thread* t, jclass c, jfieldID field) +GetStaticObjectField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return makeLocalReference - (t, cast(classStaticTable(t, jclassVmClass(t, *c)), field)); + return reinterpret_cast(run(t, getStaticObjectField, arguments)); +} + +uint64_t +getStaticBooleanField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } jboolean JNICALL -GetStaticBooleanField(Thread* t, jclass c, jfieldID field) +GetStaticBooleanField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return run(t, getStaticBooleanField, arguments); +} + +uint64_t +getStaticByteField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } jbyte JNICALL -GetStaticByteField(Thread* t, jclass c, jfieldID field) +GetStaticByteField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return run(t, getStaticByteField, arguments); +} + +uint64_t +getStaticCharField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } jchar JNICALL -GetStaticCharField(Thread* t, jclass c, jfieldID field) +GetStaticCharField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return run(t, getStaticCharField, arguments); +} + +uint64_t +getStaticShortField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } jshort JNICALL -GetStaticShortField(Thread* t, jclass c, jfieldID field) +GetStaticShortField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return run(t, getStaticShortField, arguments); +} + +uint64_t +getStaticIntField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } jint JNICALL -GetStaticIntField(Thread* t, jclass c, jfieldID field) +GetStaticIntField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return run(t, getStaticIntField, arguments); +} + +uint64_t +getStaticLongField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } jlong JNICALL -GetStaticLongField(Thread* t, jclass c, jfieldID field) +GetStaticLongField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return run(t, getStaticLongField, arguments); +} + +uint64_t +getStaticFloatField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return floatToBits + (cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field))); } jfloat JNICALL -GetStaticFloatField(Thread* t, jclass c, jfieldID field) +GetStaticFloatField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return bitsToFloat(run(t, getStaticFloatField, arguments)); +} + +uint64_t +getStaticDoubleField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_READ(t, field); + + return doubleToBits + (cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field))); } jdouble JNICALL -GetStaticDoubleField(Thread* t, jclass c, jfieldID field) +GetStaticDoubleField(Thread* t, jobject c, jfieldID field) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field }; - return cast(classStaticTable(t, jclassVmClass(t, *c)), field); + return bitsToDouble(run(t, getStaticDoubleField, arguments)); } -void JNICALL -SetStaticObjectField(Thread* t, jclass c, jfieldID field, jobject v) +uint64_t +setStaticObjectField(Thread* t, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + jobject c = reinterpret_cast(arguments[0]); - set(t, classStaticTable(t, jclassVmClass(t, *c)), field, (v ? *v : 0)); -} + initClass(t, jclassVmClass(t, *c)); -void JNICALL -SetStaticBooleanField(Thread* t, jclass c, jfieldID field, jboolean v) -{ - ENTER(t, Thread::ActiveState); + object field = getStaticField(t, arguments[1]); + jobject v = reinterpret_cast(arguments[2]); - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + set(t, classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field), + (v ? *v : 0)); + + return 1; } void JNICALL -SetStaticByteField(Thread* t, jclass c, jfieldID field, jbyte v) +SetStaticObjectField(Thread* t, jobject c, jfieldID field, jobject v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field, + reinterpret_cast(v) }; - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticObjectField, arguments); +} + +uint64_t +setStaticBooleanField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jboolean v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; } void JNICALL -SetStaticCharField(Thread* t, jclass c, jfieldID field, jchar v) +SetStaticBooleanField(Thread* t, jobject c, jfieldID field, jboolean v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field, + v }; - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticBooleanField, arguments); +} + +uint64_t +setStaticByteField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jbyte v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; } void JNICALL -SetStaticShortField(Thread* t, jclass c, jfieldID field, jshort v) +SetStaticByteField(Thread* t, jobject c, jfieldID field, jbyte v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field, + v }; - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticByteField, arguments); +} + +uint64_t +setStaticCharField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jchar v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; } void JNICALL -SetStaticIntField(Thread* t, jclass c, jfieldID field, jint v) +SetStaticCharField(Thread* t, jobject c, jfieldID field, jchar v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field, + v }; - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticCharField, arguments); +} + +uint64_t +setStaticShortField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jshort v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; } void JNICALL -SetStaticLongField(Thread* t, jclass c, jfieldID field, jlong v) +SetStaticShortField(Thread* t, jobject c, jfieldID field, jshort v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field, + v }; - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticShortField, arguments); +} + +uint64_t +setStaticIntField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jint v = arguments[2]; + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; } void JNICALL -SetStaticFloatField(Thread* t, jclass c, jfieldID field, jfloat v) +SetStaticIntField(Thread* t, jobject c, jfieldID field, jint v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(c), + field, + v }; - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticIntField, arguments); +} + +uint64_t +setStaticLongField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jlong v; memcpy(&v, arguments + 2, sizeof(jlong)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; } void JNICALL -SetStaticDoubleField(Thread* t, jclass c, jfieldID field, jdouble v) +SetStaticLongField(Thread* t, jobject c, jfieldID field, jlong v) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[2 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(c); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jlong)); - cast(classStaticTable(t, jclassVmClass(t, *c)), field) = v; + run(t, setStaticLongField, arguments); +} + +uint64_t +setStaticFloatField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jfloat v = bitsToFloat(arguments[2]); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; +} + +void JNICALL +SetStaticFloatField(Thread* t, jobject c, jfieldID field, jfloat v) +{ + uintptr_t arguments[] = { reinterpret_cast(c), + field, + floatToBits(v) }; + + run(t, setStaticFloatField, arguments); +} + +uint64_t +setStaticDoubleField(Thread* t, uintptr_t* arguments) +{ + jobject c = reinterpret_cast(arguments[0]); + + initClass(t, jclassVmClass(t, *c)); + + object field = getStaticField(t, arguments[1]); + jdouble v; memcpy(&v, arguments + 2, sizeof(jdouble)); + + PROTECT(t, field); + ACQUIRE_FIELD_FOR_WRITE(t, field); + + cast + (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; + + return 1; +} + +void JNICALL +SetStaticDoubleField(Thread* t, jobject c, jfieldID field, jdouble v) +{ + uintptr_t arguments[2 + (sizeof(jdouble) / BytesPerWord)]; + arguments[0] = reinterpret_cast(c); + arguments[1] = field; + memcpy(arguments + 2, &v, sizeof(jdouble)); + + run(t, setStaticDoubleField, arguments); } jobject JNICALL @@ -2290,8 +3012,6 @@ append(char** p, const char* value, unsigned length, char tail) uint64_t boot(Thread* t, uintptr_t*) { - t->javaThread = t->m->classpath->makeThread(t, 0); - setRoot(t, Machine::NullPointerException, makeThrowable (t, Machine::NullPointerExceptionType)); @@ -2358,6 +3078,7 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) envTable->GetDirectBufferCapacity = local::GetDirectBufferCapacity; envTable->DeleteLocalRef = local::DeleteLocalRef; envTable->GetObjectClass = local::GetObjectClass; + envTable->GetSuperclass = local::GetSuperclass; envTable->IsInstanceOf = local::IsInstanceOf; envTable->IsAssignableFrom = local::IsAssignableFrom; envTable->GetFieldID = local::GetFieldID; diff --git a/src/machine.cpp b/src/machine.cpp index 501901607b..0128558ce9 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -364,8 +364,18 @@ finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, object* p) object finalizer = *p; *p = finalizerNext(t, finalizer); - finalizerNext(t, finalizer) = t->m->finalizeQueue; - t->m->finalizeQueue = finalizer; + + void (*function)(Thread*, object); + memcpy(&function, &finalizerFinalize(t, finalizer), BytesPerWord); + + if (function) { + finalizerNext(t, finalizer) = t->m->finalizeQueue; + t->m->finalizeQueue = finalizer; + } else { + set(t, finalizer, FinalizerQueueTarget, finalizerTarget(t, finalizer)); + set(t, finalizer, FinalizerQueueNext, root(t, Machine::ObjectsToFinalize)); + setRoot(t, Machine::ObjectsToFinalize, finalizer); + } } void @@ -379,26 +389,34 @@ referenceTargetUnreachable(Thread* t, Heap::Visitor* v, object* p) v->visit(p); jreferenceTarget(t, *p) = 0; - if (jreferenceQueue(t, *p) - and t->m->heap->status(jreferenceQueue(t, *p)) != Heap::Unreachable) - { - // queue is reachable - add the reference + if (objectClass(t, *p) == type(t, Machine::CleanerType)) { + object reference = *p; + *p = jreferenceVmNext(t, reference); - v->visit(&jreferenceQueue(t, *p)); + set(t, reference, CleanerQueueNext, root(t, Machine::ObjectsToClean)); + setRoot(t, Machine::ObjectsToClean, reference); + } else { + if (jreferenceQueue(t, *p) + and t->m->heap->status(jreferenceQueue(t, *p)) != Heap::Unreachable) + { + // queue is reachable - add the reference - object q = jreferenceQueue(t, *p); + v->visit(&jreferenceQueue(t, *p)); - if (referenceQueueFront(t, q)) { - set(t, *p, JreferenceJNext, referenceQueueFront(t, q)); - } else { - set(t, *p, JreferenceJNext, *p); + object q = jreferenceQueue(t, *p); + + if (referenceQueueFront(t, q)) { + set(t, *p, JreferenceJNext, referenceQueueFront(t, q)); + } else { + set(t, *p, JreferenceJNext, *p); + } + set(t, q, ReferenceQueueFront, *p); + + jreferenceQueue(t, *p) = 0; } - set(t, q, ReferenceQueueFront, *p); - jreferenceQueue(t, *p) = 0; + *p = jreferenceVmNext(t, *p); } - - *p = jreferenceVmNext(t, *p); } void @@ -602,8 +620,8 @@ postCollect(Thread* t) uint64_t invoke(Thread* t, uintptr_t* arguments) { - object m = reinterpret_cast(arguments[0]); - object o = reinterpret_cast(arguments[1]); + object m = *reinterpret_cast(arguments[0]); + object o = *reinterpret_cast(arguments[1]); t->m->processor->invoke(t, m, o); @@ -611,19 +629,22 @@ invoke(Thread* t, uintptr_t* arguments) } void -finalizeObject(Thread* t, object o) +finalizeObject(Thread* t, object o, const char* name) { 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 (vm::strcmp(reinterpret_cast("finalize"), + if (vm::strcmp(reinterpret_cast(name), &byteArrayBody(t, methodName(t, m), 0)) == 0 and vm::strcmp(reinterpret_cast("()V"), &byteArrayBody(t, methodSpec(t, m), 0)) == 0) { - uintptr_t arguments[] = { reinterpret_cast(m), - reinterpret_cast(o) }; + PROTECT(t, m); + PROTECT(t, o); + + uintptr_t arguments[] = { reinterpret_cast(&m), + reinterpret_cast(&o) }; run(t, invoke, arguments); @@ -736,7 +757,6 @@ parseUtf8(Thread* t, Stream& s, unsigned length) value = v; } - byteArrayBody(t, value, vi) = 0; return value; } @@ -949,8 +969,20 @@ addInterfaces(Thread* t, object class_, object map) } } +object +getClassAddendum(Thread* t, object class_, object pool) +{ + object addendum = classAddendum(t, class_); + if (addendum == 0) { + addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, 0); + set(t, class_, ClassAddendum, addendum); + } + return addendum; +} + void -parseInterfaceTable(Thread* t, Stream& s, object class_, object pool) +parseInterfaceTable(Thread* t, Stream& s, object class_, object pool, + Machine::Type throwType) { PROTECT(t, class_); PROTECT(t, pool); @@ -963,14 +995,27 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool) } unsigned count = s.read2(); + object table = 0; + PROTECT(t, table); + + if (count) { + table = makeArray(t, count); + + object addendum = getClassAddendum(t, class_, pool); + set(t, addendum, ClassAddendumInterfaceTable, table); + } + for (unsigned i = 0; i < count; ++i) { object name = referenceName(t, singletonObject(t, pool, s.read2() - 1)); PROTECT(t, name); - object interface = resolveClass(t, classLoader(t, class_), name); + object interface = resolveClass + (t, classLoader(t, class_), name, true, throwType); PROTECT(t, interface); + set(t, table, ArrayBody + (i * BytesPerWord), interface); + hashMapInsertMaybe(t, map, name, interface, byteArrayHash, byteArrayEqual); addInterfaces(t, interface, map); @@ -1043,6 +1088,8 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) unsigned value = 0; + addendum = 0; + unsigned code = fieldCode (t, byteArrayBody(t, singletonObject(t, pool, spec - 1), 0)); @@ -1055,14 +1102,28 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) &byteArrayBody(t, name, 0)) == 0) { value = s.read2(); + } else if (vm::strcmp(reinterpret_cast("Signature"), + &byteArrayBody(t, name, 0)) == 0) + { + if (addendum == 0) { + addendum = makeFieldAddendum(t, pool, 0, 0); + } + + set(t, addendum, AddendumSignature, + singletonObject(t, pool, s.read2() - 1)); } else if (vm::strcmp(reinterpret_cast ("RuntimeVisibleAnnotations"), &byteArrayBody(t, name, 0)) == 0) { + if (addendum == 0) { + addendum = makeFieldAddendum(t, pool, 0, 0); + } + object body = makeByteArray(t, length); s.read(reinterpret_cast(&byteArrayBody(t, body, 0)), length); - addendum = makeFieldAddendum(t, pool, body); + + set(t, addendum, AddendumAnnotationTable, body); } else { s.skip(length); } @@ -1074,6 +1135,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) code, flags, 0, // offset + 0, // native ID singletonObject(t, pool, name - 1), singletonObject(t, pool, spec - 1), addendum, @@ -1269,20 +1331,6 @@ parseCode(Thread* t, Stream& s, object pool) return code; } -void -scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount, - unsigned* returnCode) -{ - unsigned count = 0; - MethodSpecIterator it(t, s); - for (; it.hasNext(); it.next()) { - ++ count; - } - - *parameterCount = count; - *returnCode = fieldCode(t, *it.returnSpec()); -} - object addInterfaceMethods(Thread* t, object class_, object virtualMap, unsigned* virtualCount, bool makeList) @@ -1396,6 +1444,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) unsigned name = s.read2(); unsigned spec = s.read2(); + addendum = 0; code = 0; unsigned attributeCount = s.read2(); @@ -1411,7 +1460,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) &byteArrayBody(t, name, 0)) == 0) { if (addendum == 0) { - addendum = makeMethodAddendum(t, pool, 0, 0); + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0); } unsigned exceptionCount = s.read2(); object body = makeShortArray(t, exceptionCount); @@ -1419,16 +1468,40 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) shortArrayBody(t, body, i) = s.read2(); } set(t, addendum, MethodAddendumExceptionTable, body); + } else if (vm::strcmp(reinterpret_cast + ("AnnotationDefault"), + &byteArrayBody(t, name, 0)) == 0) + { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0); + } + + object body = makeByteArray(t, length); + s.read(reinterpret_cast(&byteArrayBody(t, body, 0)), + length); + + set(t, addendum, MethodAddendumAnnotationDefault, body); + } else if (vm::strcmp(reinterpret_cast("Signature"), + &byteArrayBody(t, name, 0)) == 0) + { + if (addendum == 0) { + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0); + } + + set(t, addendum, AddendumSignature, + singletonObject(t, pool, s.read2() - 1)); } else if (vm::strcmp(reinterpret_cast ("RuntimeVisibleAnnotations"), &byteArrayBody(t, name, 0)) == 0) { if (addendum == 0) { - addendum = makeMethodAddendum(t, pool, 0, 0); + addendum = makeMethodAddendum(t, pool, 0, 0, 0, 0); } + object body = makeByteArray(t, length); s.read(reinterpret_cast(&byteArrayBody(t, body, 0)), length); + set(t, addendum, AddendumAnnotationTable, body); } else { s.skip(length); @@ -1581,6 +1654,10 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) if (abstractVirtuals) { PROTECT(t, vtable); + object addendum = getClassAddendum(t, class_, pool); + set(t, addendum, ClassAddendumMethodTable, + classMethodTable(t, class_)); + unsigned oldLength = arrayLength(t, classMethodTable(t, class_)); object newMethodTable = makeArray (t, oldLength + listSize(t, abstractVirtuals)); @@ -1641,6 +1718,9 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) void parseAttributeTable(Thread* t, Stream& s, object class_, object pool) { + PROTECT(t, class_); + PROTECT(t, pool); + unsigned attributeCount = s.read2(); for (unsigned j = 0; j < attributeCount; ++j) { object name = singletonObject(t, pool, s.read2() - 1); @@ -1650,16 +1730,46 @@ parseAttributeTable(Thread* t, Stream& s, object class_, object pool) &byteArrayBody(t, name, 0)) == 0) { set(t, class_, ClassSourceFile, singletonObject(t, pool, s.read2() - 1)); + } else if (vm::strcmp(reinterpret_cast("Signature"), + &byteArrayBody(t, name, 0)) == 0) + { + object addendum = getClassAddendum(t, class_, pool); + set(t, addendum, AddendumSignature, + singletonObject(t, pool, s.read2() - 1)); + } else if (vm::strcmp(reinterpret_cast("InnerClasses"), + &byteArrayBody(t, name, 0)) == 0) + { + unsigned innerClassCount = s.read2(); + object table = makeArray(t, innerClassCount); + PROTECT(t, table); + + for (unsigned i = 0; i < innerClassCount; ++i) { + int16_t inner = s.read2(); + int16_t outer = s.read2(); + int16_t name = s.read2(); + int16_t flags = s.read2(); + + object reference = makeInnerClassReference + (t, inner ? singletonObject(t, pool, inner - 1) : 0, + outer ? singletonObject(t, pool, outer - 1) : 0, + name ? singletonObject(t, pool, name - 1) : 0, + flags); + + set(t, table, ArrayBody + (i * BytesPerWord), reference); + } + + object addendum = getClassAddendum(t, class_, pool); + set(t, addendum, ClassAddendumInnerClassTable, table); } else if (vm::strcmp(reinterpret_cast ("RuntimeVisibleAnnotations"), &byteArrayBody(t, name, 0)) == 0) { object body = makeByteArray(t, length); + PROTECT(t, body); s.read(reinterpret_cast(&byteArrayBody(t, body, 0)), length); - object addendum = makeClassAddendum(t, pool, body); - - set(t, class_, ClassAddendum, addendum); + object addendum = getClassAddendum(t, class_, pool); + set(t, addendum, AddendumAnnotationTable, body); } else { s.skip(length); } @@ -1722,6 +1832,7 @@ updateBootstrapClass(Thread* t, object bootstrapClass, object class_) set(t, bootstrapClass, ClassFieldTable, classFieldTable(t, class_)); set(t, bootstrapClass, ClassMethodTable, classMethodTable(t, class_)); set(t, bootstrapClass, ClassStaticTable, classStaticTable(t, class_)); + set(t, bootstrapClass, ClassAddendum, classAddendum(t, class_)); updateClassTables(t, bootstrapClass, class_); } @@ -1773,8 +1884,26 @@ makeArrayClass(Thread* t, object loader, unsigned dimensions, object spec, return c; } +void +saveLoadedClass(Thread* t, object loader, object c) +{ + PROTECT(t, loader); + PROTECT(t, c); + + ACQUIRE(t, t->m->classLock); + + if (classLoaderMap(t, loader) == 0) { + object map = makeHashMap(t, 0, 0); + set(t, loader, ClassLoaderMap, map); + } + + hashMapInsert + (t, classLoaderMap(t, loader), className(t, c), c, byteArrayHash); +} + object -makeArrayClass(Thread* t, object loader, object spec, bool throw_) +makeArrayClass(Thread* t, object loader, object spec, bool throw_, + Machine::Type throwType) { PROTECT(t, loader); PROTECT(t, spec); @@ -1816,20 +1945,32 @@ makeArrayClass(Thread* t, object loader, object spec, bool throw_) byteArrayEqual); if (elementClass == 0) { - elementClass = resolveClass(t, loader, elementSpec, throw_); + elementClass = resolveClass(t, loader, elementSpec, throw_, throwType); if (elementClass == 0) return 0; } PROTECT(t, elementClass); - object class_ = findLoadedClass(t, classLoader(t, elementClass), spec); + ACQUIRE(t, t->m->classLock); - return class_ ? class_ : makeArrayClass + object class_ = findLoadedClass(t, classLoader(t, elementClass), spec); + if (class_) { + return class_; + } + + class_ = makeArrayClass (t, classLoader(t, elementClass), dimensions, spec, elementClass); + + PROTECT(t, class_); + + saveLoadedClass(t, classLoader(t, elementClass), class_); + + return class_; } object -resolveArrayClass(Thread* t, object loader, object spec, bool throw_) +resolveArrayClass(Thread* t, object loader, object spec, bool throw_, + Machine::Type throwType) { object c = hashMapFind (t, root(t, Machine::BootstrapClassMap), spec, byteArrayHash, @@ -1849,7 +1990,7 @@ resolveArrayClass(Thread* t, object loader, object spec, bool throw_) if (c) { return c; } else { - return makeArrayClass(t, loader, spec, throw_); + return makeArrayClass(t, loader, spec, throw_, throwType); } } } @@ -2166,8 +2307,13 @@ class HeapClient: public Heap::Client { void doCollect(Thread* t, Heap::CollectionType type) { + expect(t, not t->m->collecting); + + t->m->collecting = true; + THREAD_RESOURCE0(t, t->m->collecting = false); + #ifdef VM_STRESS - bool stress = (t->flags |= Thread::StressFlag); + bool stress = (t->flags & Thread::StressFlag) != 0; if (not stress) atomicOr(&(t->flags), Thread::StressFlag); #endif @@ -2198,20 +2344,17 @@ doCollect(Thread* t, Heap::CollectionType type) if (not stress) atomicAnd(&(t->flags), ~Thread::StressFlag); #endif - object f = t->m->finalizeQueue; + object finalizeQueue = t->m->finalizeQueue; t->m->finalizeQueue = 0; - for (; f; f = finalizerNext(t, f)) { + for (; finalizeQueue; finalizeQueue = finalizerNext(t, finalizeQueue)) { void (*function)(Thread*, object); - memcpy(&function, &finalizerFinalize(t, f), BytesPerWord); - if (function) { - function(t, finalizerTarget(t, f)); - } else { - setRoot(t, Machine::ObjectsToFinalize, makePair - (t, finalizerTarget(t, f), root(t, Machine::ObjectsToFinalize))); - } + memcpy(&function, &finalizerFinalize(t, finalizeQueue), BytesPerWord); + function(t, finalizerTarget(t, finalizeQueue)); } - if (root(t, Machine::ObjectsToFinalize) and m->finalizeThread == 0) { + if ((root(t, Machine::ObjectsToFinalize) or root(t, Machine::ObjectsToClean)) + and m->finalizeThread == 0) + { m->finalizeThread = m->processor->makeThread (m, root(t, Machine::FinalizerThread), m->rootThread); @@ -2224,6 +2367,28 @@ doCollect(Thread* t, Heap::CollectionType type) } } +uint64_t +invokeLoadClass(Thread* t, uintptr_t* arguments) +{ + object method = reinterpret_cast(arguments[0]); + object loader = reinterpret_cast(arguments[1]); + object specString = reinterpret_cast(arguments[2]); + + return reinterpret_cast + (t->m->processor->invoke(t, method, loader, specString)); +} + +bool +isInitializing(Thread* t, object c) +{ + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return true; + } + } + return false; +} + } // namespace namespace vm { @@ -2257,6 +2422,7 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, referenceLock(0), shutdownLock(0), libraries(0), + errorLog(0), types(0), roots(0), finalizers(0), @@ -2265,6 +2431,7 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, weakReferences(0), tenuredWeakReferences(0), unsafe(false), + collecting(false), triedBuiltinOnLoad(false), dumpedHeapOnOOM(false), heapPoolIndex(0) @@ -2388,6 +2555,7 @@ Thread::init() setRoot(this, Machine::ClassRuntimeDataTable, makeVector(this, 0, 0)); setRoot(this, Machine::MethodRuntimeDataTable, makeVector(this, 0, 0)); setRoot(this, Machine::JNIMethodTable, makeVector(this, 0, 0)); + setRoot(this, Machine::JNIFieldTable, makeVector(this, 0, 0)); m->localThread->set(this); @@ -3009,13 +3177,12 @@ isAssignableFrom(Thread* t, object a, object b) } } - for (; b; b = classSuper(t, b)) { - object itable = classInterfaceTable(t, b); - if (itable) { - for (unsigned i = 0; i < arrayLength(t, itable); i += 2) { - if (arrayBody(t, itable, i) == a) { - return true; - } + object itable = classInterfaceTable(t, b); + if (itable) { + unsigned stride = (classFlags(t, b) & ACC_INTERFACE) ? 1 : 2; + for (unsigned i = 0; i < arrayLength(t, itable); i += stride) { + if (arrayBody(t, itable, i) == a) { + return true; } } } @@ -3057,7 +3224,7 @@ classInitializer(Thread* t, object class_) return o; } } - abort(t); + return 0; } unsigned @@ -3141,7 +3308,8 @@ primitiveSize(Thread* t, unsigned code) } object -parseClass(Thread* t, object loader, const uint8_t* data, unsigned size) +parseClass(Thread* t, object loader, const uint8_t* data, unsigned size, + Machine::Type throwType) { PROTECT(t, loader); @@ -3189,22 +3357,25 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size) 0, // addendum 0, // static table loader, + 0, // source 0);// vtable length PROTECT(t, class_); unsigned super = s.read2(); if (super) { object sc = resolveClass - (t, loader, referenceName(t, singletonObject(t, pool, super - 1))); + (t, loader, referenceName(t, singletonObject(t, pool, super - 1)), + true, throwType); set(t, class_, ClassSuper, sc); classVmFlags(t, class_) |= (classVmFlags(t, sc) - & (ReferenceFlag | WeakReferenceFlag | HasFinalizerFlag)); + & (ReferenceFlag | WeakReferenceFlag | HasFinalizerFlag + | NeedInitFlag)); } - parseInterfaceTable(t, s, class_, pool); + parseInterfaceTable(t, s, class_, pool, throwType); parseFieldTable(t, s, class_, pool); @@ -3245,7 +3416,8 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size) } object -resolveSystemClass(Thread* t, object loader, object spec, bool throw_) +resolveSystemClass(Thread* t, object loader, object spec, bool throw_, + Machine::Type throwType) { PROTECT(t, loader); PROTECT(t, spec); @@ -3256,6 +3428,8 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_) (t, classLoaderMap(t, loader), spec, byteArrayHash, byteArrayEqual); if (class_ == 0) { + PROTECT(t, class_); + if (classLoaderParent(t, loader)) { class_ = resolveSystemClass (t, classLoaderParent(t, loader), spec, false); @@ -3265,7 +3439,7 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_) } if (byteArrayBody(t, spec, 0) == '[') { - class_ = resolveArrayClass(t, loader, spec, throw_); + class_ = resolveArrayClass(t, loader, spec, throw_, throwType); } else { THREAD_RUNTIME_ARRAY(t, char, file, byteArrayLength(t, spec) + 6); memcpy(RUNTIME_ARRAY_BODY(file), @@ -3287,7 +3461,8 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_) { THREAD_RESOURCE(t, System::Region*, region, region->dispose()); // parse class file - class_ = parseClass(t, loader, region->start(), region->length()); + class_ = parseClass + (t, loader, region->start(), region->length(), throwType); } if (Verbose) { @@ -3296,6 +3471,20 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_) class_); } + { const char* source = static_cast + (systemClassLoaderFinder(t, loader))->sourceUrl + (RUNTIME_ARRAY_BODY(file)); + + if (source) { + unsigned length = strlen(source); + object array = makeByteArray(t, length + 1); + memcpy(&byteArrayBody(t, array, 0), source, length); + array = internByteArray(t, array); + + set(t, class_, ClassSource, array); + } + } + object bootstrapClass = hashMapFind (t, root(t, Machine::BootstrapClassMap), spec, byteArrayHash, byteArrayEqual); @@ -3310,12 +3499,9 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_) } if (class_) { - PROTECT(t, class_); - hashMapInsert(t, classLoaderMap(t, loader), spec, class_, byteArrayHash); } else if (throw_) { - throwNew(t, Machine::ClassNotFoundExceptionType, "%s", - &byteArrayBody(t, spec, 0)); + throwNew(t, throwType, "%s", &byteArrayBody(t, spec, 0)); } } @@ -3335,13 +3521,12 @@ findLoadedClass(Thread* t, object loader, object spec) } object -resolveClass(Thread* t, object loader, object spec, bool throw_) +resolveClass(Thread* t, object loader, object spec, bool throw_, + Machine::Type throwType) { if (objectClass(t, loader) == type(t, Machine::SystemClassLoaderType)) { - return resolveSystemClass(t, loader, spec, throw_); + return resolveSystemClass(t, loader, spec, throw_, throwType); } else { - expect(t, throw_); - PROTECT(t, loader); PROTECT(t, spec); @@ -3351,7 +3536,7 @@ resolveClass(Thread* t, object loader, object spec, bool throw_) } if (byteArrayBody(t, spec, 0) == '[') { - c = resolveArrayClass(t, loader, spec, throw_); + c = resolveArrayClass(t, loader, spec, throw_, throwType); } else { if (root(t, Machine::LoadClassMethod) == 0) { object m = resolveMethod @@ -3381,31 +3566,39 @@ resolveClass(Thread* t, object loader, object spec, bool throw_) (&byteArrayBody(t, spec, 0))); object specString = makeString(t, "%s", RUNTIME_ARRAY_BODY(s)); + PROTECT(t, specString); + + uintptr_t arguments[] = { reinterpret_cast(method), + reinterpret_cast(loader), + reinterpret_cast(specString) }; + + object jc = reinterpret_cast + (runRaw(t, invokeLoadClass, arguments)); - object jc = t->m->processor->invoke(t, method, loader, specString); if (LIKELY(jc)) { c = jclassVmClass(t, jc); + } else if (t->exception) { + if (throw_) { + object e = type(t, throwType) == objectClass(t, t->exception) + ? t->exception + : makeThrowable(t, throwType, specString, 0, t->exception); + t->exception = 0; + vm::throw_(t, e); + } else { + t->exception = 0; + } } } if (LIKELY(c)) { PROTECT(t, c); - ACQUIRE(t, t->m->classLock); - - if (classLoaderMap(t, loader) == 0) { - object map = makeHashMap(t, 0, 0); - set(t, loader, ClassLoaderMap, map); - } - - hashMapInsert - (t, classLoaderMap(t, loader), spec, c, byteArrayHash); - - return c; - } else { - throwNew(t, Machine::ClassNotFoundExceptionType, "%s", - &byteArrayBody(t, spec, 0)); + saveLoadedClass(t, loader, c); + } else if (throw_) { + throwNew(t, throwType, "%s", &byteArrayBody(t, spec, 0)); } + + return c; } } @@ -3484,7 +3677,11 @@ classNeedsInit(Thread* t, object c) bool preInitClass(Thread* t, object c) { - if (classVmFlags(t, c) & NeedInitFlag) { + int flags = classVmFlags(t, c); + + loadMemoryBarrier(); + + if (flags & NeedInitFlag) { PROTECT(t, c); ACQUIRE(t, t->m->classLock); @@ -3493,7 +3690,7 @@ preInitClass(Thread* t, object c) // If the class is currently being initialized and this the thread // which is initializing it, we should not try to initialize it // recursively. - if (t->m->processor->isInitializing(t, c)) { + if (isInitializing(t, c)) { return false; } @@ -3540,12 +3737,21 @@ initClass(Thread* t, object c) { PROTECT(t, c); + object super = classSuper(t, c); + if (super) { + initClass(t, super); + } + if (preInitClass(t, c)) { OBJECT_RESOURCE(t, c, postInitClass(t, c)); - Thread::ClassInitStack stack(t, c); + object initializer = classInitializer(t, c); - t->m->processor->invoke(t, classInitializer(t, c), 0); + if (initializer) { + Thread::ClassInitStack stack(t, c); + + t->m->processor->invoke(t, initializer, 0); + } } } @@ -3697,7 +3903,7 @@ addFinalizer(Thread* t, object target, void (*finalize)(Thread*, object)) void* function; memcpy(&function, &finalize, BytesPerWord); - object f = makeFinalizer(t, 0, function, 0); + object f = makeFinalizer(t, 0, function, 0, 0, 0); finalizerTarget(t, f) = target; finalizerNext(t, f) = t->m->finalizers; t->m->finalizers = f; @@ -3887,42 +4093,44 @@ printTrace(Thread* t, object exception) for (object e = exception; e; e = throwableCause(t, e)) { if (e != exception) { - fprintf(stderr, "caused by: "); + fprintf(errorLog(t), "caused by: "); } - fprintf(stderr, "%s", &byteArrayBody + fprintf(errorLog(t), "%s", &byteArrayBody (t, className(t, objectClass(t, e)), 0)); if (throwableMessage(t, e)) { object m = throwableMessage(t, e); THREAD_RUNTIME_ARRAY(t, char, message, stringLength(t, m) + 1); stringChars(t, m, RUNTIME_ARRAY_BODY(message)); - fprintf(stderr, ": %s\n", RUNTIME_ARRAY_BODY(message)); + fprintf(errorLog(t), ": %s\n", RUNTIME_ARRAY_BODY(message)); } else { - fprintf(stderr, "\n"); + fprintf(errorLog(t), "\n"); } object trace = throwableTrace(t, e); - for (unsigned i = 0; i < objectArrayLength(t, trace); ++i) { - object e = objectArrayBody(t, trace, i); - const int8_t* class_ = &byteArrayBody - (t, className(t, methodClass(t, traceElementMethod(t, e))), 0); - const int8_t* method = &byteArrayBody - (t, methodName(t, traceElementMethod(t, e)), 0); - int line = t->m->processor->lineNumber - (t, traceElementMethod(t, e), traceElementIp(t, e)); + if (trace) { + for (unsigned i = 0; i < objectArrayLength(t, trace); ++i) { + object e = objectArrayBody(t, trace, i); + const int8_t* class_ = &byteArrayBody + (t, className(t, methodClass(t, traceElementMethod(t, e))), 0); + const int8_t* method = &byteArrayBody + (t, methodName(t, traceElementMethod(t, e)), 0); + int line = t->m->processor->lineNumber + (t, traceElementMethod(t, e), traceElementIp(t, e)); - fprintf(stderr, " at %s.%s ", class_, method); + fprintf(errorLog(t), " at %s.%s ", class_, method); - switch (line) { - case NativeLine: - fprintf(stderr, "(native)\n"); - break; - case UnknownLine: - fprintf(stderr, "(unknown line)\n"); - break; - default: - fprintf(stderr, "(line %d)\n", line); + switch (line) { + case NativeLine: + fprintf(errorLog(t), "(native)\n"); + break; + case UnknownLine: + fprintf(errorLog(t), "(unknown line)\n"); + break; + default: + fprintf(errorLog(t), "(line %d)\n", line); + } } } @@ -3931,7 +4139,7 @@ printTrace(Thread* t, object exception) } } - fflush(stderr); + fflush(errorLog(t)); } object @@ -3989,14 +4197,18 @@ makeTrace(Thread* t, Thread* target) void runFinalizeThread(Thread* t) { - object list = 0; - PROTECT(t, list); + object finalizeList = 0; + PROTECT(t, finalizeList); + + object cleanList = 0; + PROTECT(t, cleanList); while (true) { { ACQUIRE(t, t->m->stateLock); while (t->m->finalizeThread - and root(t, Machine::ObjectsToFinalize) == 0) + and root(t, Machine::ObjectsToFinalize) == 0 + and root(t, Machine::ObjectsToClean) == 0) { ENTER(t, Thread::IdleState); t->m->stateLock->wait(t->systemThread, 0); @@ -4005,13 +4217,20 @@ runFinalizeThread(Thread* t) if (t->m->finalizeThread == 0) { return; } else { - list = root(t, Machine::ObjectsToFinalize); + finalizeList = root(t, Machine::ObjectsToFinalize); setRoot(t, Machine::ObjectsToFinalize, 0); + + cleanList = root(t, Machine::ObjectsToClean); + setRoot(t, Machine::ObjectsToClean, 0); } } - for (; list; list = pairSecond(t, list)) { - finalizeObject(t, pairFirst(t, list)); + for (; finalizeList; finalizeList = finalizerQueueNext(t, finalizeList)) { + finalizeObject(t, finalizerQueueTarget(t, finalizeList), "finalize"); + } + + for (; cleanList; cleanList = cleanerQueueNext(t, cleanList)) { + finalizeObject(t, cleanList, "clean"); } } } @@ -4072,23 +4291,60 @@ defineClass(Thread* t, object loader, const uint8_t* buffer, unsigned length) PROTECT(t, loader); object c = parseClass(t, loader, buffer, length); + + // char name[byteArrayLength(t, className(t, c))]; + // memcpy(name, &byteArrayBody(t, className(t, c), 0), + // byteArrayLength(t, className(t, c))); + // replace('/', '-', name); - if (c) { - PROTECT(t, c); - ACQUIRE(t, t->m->classLock); + // const unsigned BufferSize = 1024; + // char path[BufferSize]; + // snprintf(path, BufferSize, "/tmp/avian-define-class/%s.class", name); - if (classLoaderMap(t, loader) == 0) { - object map = makeHashMap(t, 0, 0); - set(t, loader, ClassLoaderMap, map); - } + // FILE* file = fopen(path, "wb"); + // if (file) { + // fwrite(buffer, length, 1, file); + // fclose(file); + // } - hashMapInsert - (t, classLoaderMap(t, loader), className(t, c), c, byteArrayHash); - } + PROTECT(t, c); + + saveLoadedClass(t, loader, c); return c; } +void +populateMultiArray(Thread* t, object array, int32_t* counts, + unsigned index, unsigned dimensions) +{ + if (index + 1 == dimensions or counts[index] == 0) { + return; + } + + PROTECT(t, array); + + object spec = className(t, objectClass(t, array)); + PROTECT(t, spec); + + object elementSpec = makeByteArray(t, byteArrayLength(t, spec) - 1); + memcpy(&byteArrayBody(t, elementSpec, 0), + &byteArrayBody(t, spec, 1), + byteArrayLength(t, spec) - 1); + + object class_ = resolveClass + (t, classLoader(t, objectClass(t, array)), elementSpec); + PROTECT(t, class_); + + for (int32_t i = 0; i < counts[index]; ++i) { + object a = makeArray(t, counts[index + 1]); + setObjectClass(t, a, class_); + set(t, array, ArrayBody + (i * BytesPerWord), a); + + populateMultiArray(t, a, counts, index + 1, dimensions); + } +} + void noop() { } diff --git a/src/machine.h b/src/machine.h index 6c3633000f..2a69b8e038 100644 --- a/src/machine.h +++ b/src/machine.h @@ -33,6 +33,12 @@ #define ACQUIRE_OBJECT(t, x) \ ObjectMonitorResource MAKE_NAME(monitorResource_) (t, x) +#define ACQUIRE_FIELD_FOR_READ(t, field) \ + FieldReadResource MAKE_NAME(monitorResource_) (t, field) + +#define ACQUIRE_FIELD_FOR_WRITE(t, field) \ + FieldWriteResource MAKE_NAME(monitorResource_) (t, field) + #define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_) (t, x) #define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state) @@ -132,8 +138,8 @@ enum StackTag { ObjectTag }; -const int NativeLine = -1; -const int UnknownLine = -2; +const int NativeLine = -2; +const int UnknownLine = -1; // class vmFlags: const unsigned ReferenceFlag = 1 << 0; @@ -1250,9 +1256,11 @@ class Machine { ClassRuntimeDataTable, MethodRuntimeDataTable, JNIMethodTable, + JNIFieldTable, ShutdownHooks, FinalizerThread, ObjectsToFinalize, + ObjectsToClean, NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, @@ -1298,6 +1306,7 @@ class Machine { System::Monitor* referenceLock; System::Monitor* shutdownLock; System::Library* libraries; + FILE* errorLog; object types; object roots; object finalizers; @@ -1306,6 +1315,7 @@ class Machine { object weakReferences; object tenuredWeakReferences; bool unsafe; + bool collecting; bool triedBuiltinOnLoad; bool dumpedHeapOnOOM; JavaVMVTable javaVMVTable; @@ -1557,6 +1567,9 @@ class Classpath { virtual void runThread(Thread* t) = 0; + virtual void + resolveNative(Thread* t, object method) = 0; + virtual void boot(Thread* t) = 0; @@ -2250,7 +2263,11 @@ markHashTaken(Thread* t, object o) inline uint32_t takeHash(Thread*, object o) { - return reinterpret_cast(o) / BytesPerWord; + // some broken code implicitly relies on System.identityHashCode + // always returning a non-negative number (e.g. old versions of + // com/sun/xml/bind/v2/util/CollisionCheckStack.hash), hence the "& + // 0x7FFFFFFF": + return (reinterpret_cast(o) / BytesPerWord) & 0x7FFFFFFF; } inline uint32_t @@ -2429,6 +2446,20 @@ fieldSize(Thread* t, object field) return fieldSize(t, fieldCode(t, field)); } +inline void +scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount, + unsigned* returnCode) +{ + unsigned count = 0; + MethodSpecIterator it(t, s); + for (; it.hasNext(); it.next()) { + ++ count; + } + + *parameterCount = count; + *returnCode = fieldCode(t, *it.returnSpec()); +} + object findLoadedClass(Thread* t, object loader, object spec); @@ -2444,21 +2475,26 @@ object parseUtf8(Thread* t, const char* data, unsigned length); object -parseClass(Thread* t, object loader, const uint8_t* data, unsigned length); +parseClass(Thread* t, object loader, const uint8_t* data, unsigned length, + Machine::Type throwType = Machine::NoClassDefFoundErrorType); object -resolveClass(Thread* t, object loader, object name, bool throw_ = true); +resolveClass(Thread* t, object loader, object name, bool throw_ = true, + Machine::Type throwType = Machine::NoClassDefFoundErrorType); inline object -resolveClass(Thread* t, object loader, const char* name, bool throw_ = true) +resolveClass(Thread* t, object loader, const char* name, bool throw_ = true, + Machine::Type throwType = Machine::NoClassDefFoundErrorType) { PROTECT(t, loader); object n = makeByteArray(t, "%s", name); - return resolveClass(t, loader, n, throw_); + return resolveClass(t, loader, n, throw_, throwType); } object -resolveSystemClass(Thread* t, object loader, object name, bool throw_ = true); +resolveSystemClass +(Thread* t, object loader, object name, bool throw_ = true, + Machine::Type throwType = Machine::NoClassDefFoundErrorType); inline object resolveSystemClass(Thread* t, object loader, const char* name) @@ -2597,6 +2633,7 @@ inline void NO_RETURN throw_(Thread* t, object e) { assert(t, t->exception == 0); + assert(t, e); expect(t, not t->checkpoint->noThrow); @@ -2637,11 +2674,11 @@ findInHierarchyOrNull(Thread* t, object class_, object name, object spec, inline object findInHierarchy(Thread* t, object class_, object name, object spec, object (*find)(Thread*, object, object, object), - Machine::Type errorType) + Machine::Type errorType, bool throw_ = true) { object o = findInHierarchyOrNull(t, class_, name, spec, find); - if (o == 0) { + if (throw_ and o == 0) { throwNew(t, errorType, "%s %s not found in %s", &byteArrayBody(t, name, 0), &byteArrayBody(t, spec, 0), @@ -2765,11 +2802,13 @@ atomicCompareAndSwapObject(Thread* t, object target, unsigned offset, // Queue Algorithm: http://www.cs.rochester.edu/u/michael/PODC96.html inline void -monitorAtomicAppendAcquire(Thread* t, object monitor) +monitorAtomicAppendAcquire(Thread* t, object monitor, object node) { - PROTECT(t, monitor); + if (node == 0) { + PROTECT(t, monitor); - object node = makeMonitorNode(t, t, 0); + node = makeMonitorNode(t, t, 0); + } while (true) { object tail = monitorAcquireTail(t, monitor); @@ -2849,14 +2888,15 @@ monitorTryAcquire(Thread* t, object monitor) } inline void -monitorAcquire(Thread* t, object monitor) +monitorAcquire(Thread* t, object monitor, object node = 0) { if (not monitorTryAcquire(t, monitor)) { PROTECT(t, monitor); + PROTECT(t, node); ACQUIRE(t, t->lock); - monitorAtomicAppendAcquire(t, monitor); + monitorAtomicAppendAcquire(t, monitor, node); // note that we don't try to acquire the lock until we're first in // line, both because it's fair and because we don't support @@ -2980,6 +3020,11 @@ monitorWait(Thread* t, object monitor, int64_t time) PROTECT(t, monitor); + // pre-allocate monitor node so we don't get an OutOfMemoryError + // when we try to re-acquire the monitor below + object monitorNode = makeMonitorNode(t, t, 0); + PROTECT(t, monitorNode); + { ACQUIRE(t, t->lock); monitorAppendWait(t, monitor); @@ -2994,7 +3039,7 @@ monitorWait(Thread* t, object monitor, int64_t time) interrupted = t->lock->wait(t->systemThread, time); } - monitorAcquire(t, monitor); + monitorAcquire(t, monitor, monitorNode); monitorDepth(t, monitor) = depth; @@ -3330,93 +3375,208 @@ poolSize(Thread* t, object pool) inline object resolveClassInObject(Thread* t, object loader, object container, - unsigned classOffset) + unsigned classOffset, bool throw_ = true) { object o = cast(container, classOffset); + + loadMemoryBarrier(); + if (objectClass(t, o) == type(t, Machine::ByteArrayType)) { PROTECT(t, container); - o = resolveClass(t, loader, o); + o = resolveClass(t, loader, o, throw_); - set(t, container, classOffset, o); + if (o) { + storeStoreMemoryBarrier(); + + set(t, container, classOffset, o); + } } return o; } inline object -resolveClassInPool(Thread* t, object loader, object method, unsigned index) +resolveClassInPool(Thread* t, object loader, object method, unsigned index, + bool throw_ = true) { object o = singletonObject(t, codePool(t, methodCode(t, method)), index); + + loadMemoryBarrier(); + if (objectClass(t, o) == type(t, Machine::ReferenceType)) { PROTECT(t, method); - o = resolveClass(t, loader, referenceName(t, o)); + o = resolveClass(t, loader, referenceName(t, o), throw_); - set(t, codePool(t, methodCode(t, method)), - SingletonBody + (index * BytesPerWord), o); + if (o) { + storeStoreMemoryBarrier(); + + set(t, codePool(t, methodCode(t, method)), + SingletonBody + (index * BytesPerWord), o); + } } return o; } inline object -resolveClassInPool(Thread* t, object method, unsigned index) +resolveClassInPool(Thread* t, object method, unsigned index, + bool throw_ = true) { return resolveClassInPool(t, classLoader(t, methodClass(t, method)), - method, index); + method, index, throw_); } inline object resolve(Thread* t, object loader, object method, unsigned index, object (*find)(vm::Thread*, object, object, object), - Machine::Type errorType) + Machine::Type errorType, bool throw_ = true) { object o = singletonObject(t, codePool(t, methodCode(t, method)), index); - if (objectClass(t, o) == type(t, Machine::ReferenceType)) - { + + loadMemoryBarrier(); + + if (objectClass(t, o) == type(t, Machine::ReferenceType)) { PROTECT(t, method); object reference = o; PROTECT(t, reference); - object class_ = resolveClassInObject(t, loader, o, ReferenceClass); + object class_ = resolveClassInObject(t, loader, o, ReferenceClass, throw_); - o = findInHierarchy - (t, class_, referenceName(t, reference), referenceSpec(t, reference), - find, errorType); + if (class_) { + o = findInHierarchy + (t, class_, referenceName(t, reference), referenceSpec(t, reference), + find, errorType, throw_); - set(t, codePool(t, methodCode(t, method)), - SingletonBody + (index * BytesPerWord), o); + if (o) { + storeStoreMemoryBarrier(); + + set(t, codePool(t, methodCode(t, method)), + SingletonBody + (index * BytesPerWord), o); + } + } else { + o = 0; + } } return o; } inline object -resolveField(Thread* t, object loader, object method, unsigned index) +resolveField(Thread* t, object loader, object method, unsigned index, + bool throw_ = true) { return resolve(t, loader, method, index, findFieldInClass, - Machine::NoSuchFieldErrorType); + Machine::NoSuchFieldErrorType, throw_); } inline object -resolveField(Thread* t, object method, unsigned index) +resolveField(Thread* t, object method, unsigned index, bool throw_ = true) { return resolveField - (t, classLoader(t, methodClass(t, method)), method, index); + (t, classLoader(t, methodClass(t, method)), method, index, throw_); } +inline void +acquireFieldForRead(Thread* t, object field) +{ + if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE) + and BytesPerWord == 4 + and (fieldCode(t, field) == DoubleField + or fieldCode(t, field) == LongField))) + { + acquire(t, field); + } +} + +inline void +releaseFieldForRead(Thread* t, object field) +{ + if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { + if (BytesPerWord == 4 + and (fieldCode(t, field) == DoubleField + or fieldCode(t, field) == LongField)) + { + release(t, field); + } else { + loadMemoryBarrier(); + } + } +} + +class FieldReadResource { + public: + FieldReadResource(Thread* t, object o): o(o), protector(t, &(this->o)) { + acquireFieldForRead(protector.t, o); + } + + ~FieldReadResource() { + releaseFieldForRead(protector.t, o); + } + + private: + object o; + Thread::SingleProtector protector; +}; + +inline void +acquireFieldForWrite(Thread* t, object field) +{ + if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { + if (BytesPerWord == 4 + and (fieldCode(t, field) == DoubleField + or fieldCode(t, field) == LongField)) + { + acquire(t, field); + } else { + storeStoreMemoryBarrier(); + } + } +} + +inline void +releaseFieldForWrite(Thread* t, object field) +{ + if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) { + if (BytesPerWord == 4 + and (fieldCode(t, field) == DoubleField + or fieldCode(t, field) == LongField)) + { + release(t, field); + } else { + storeLoadMemoryBarrier(); + } + } +} + +class FieldWriteResource { + public: + FieldWriteResource(Thread* t, object o): o(o), protector(t, &(this->o)) { + acquireFieldForWrite(protector.t, o); + } + + ~FieldWriteResource() { + releaseFieldForWrite(protector.t, o); + } + + private: + object o; + Thread::SingleProtector protector; +}; + inline object -resolveMethod(Thread* t, object loader, object method, unsigned index) +resolveMethod(Thread* t, object loader, object method, unsigned index, + bool throw_ = true) { return resolve(t, loader, method, index, findMethodInClass, - Machine::NoSuchMethodErrorType); + Machine::NoSuchMethodErrorType, throw_); } inline object -resolveMethod(Thread* t, object method, unsigned index) +resolveMethod(Thread* t, object method, unsigned index, bool throw_ = true) { return resolveMethod - (t, classLoader(t, methodClass(t, method)), method, index); + (t, classLoader(t, methodClass(t, method)), method, index, throw_); } object @@ -3459,7 +3619,11 @@ getClassRuntimeData(Thread* t, object c) inline object getMethodRuntimeData(Thread* t, object method) { - if (methodRuntimeDataIndex(t, method) == 0) { + int index = methodRuntimeDataIndex(t, method); + + loadMemoryBarrier(); + + if (index == 0) { PROTECT(t, method); ACQUIRE(t, t->m->classLock); @@ -3470,6 +3634,8 @@ getMethodRuntimeData(Thread* t, object method) setRoot(t, Machine::MethodRuntimeDataTable, vectorAppend (t, root(t, Machine::MethodRuntimeDataTable), runtimeData)); + storeStoreMemoryBarrier(); + methodRuntimeDataIndex(t, method) = vectorSize (t, root(t, Machine::MethodRuntimeDataTable)); } @@ -3485,12 +3651,17 @@ getJClass(Thread* t, object c) PROTECT(t, c); object jclass = classRuntimeDataJclass(t, getClassRuntimeData(t, c)); + + loadMemoryBarrier(); + if (jclass == 0) { ACQUIRE(t, t->m->classLock); jclass = classRuntimeDataJclass(t, getClassRuntimeData(t, c)); if (jclass == 0) { jclass = t->m->classpath->makeJclass(t, c); + + storeStoreMemoryBarrier(); set(t, getClassRuntimeData(t, c), ClassRuntimeDataJclass, jclass); } @@ -3548,6 +3719,10 @@ unregisterNatives(Thread* t, object c) } } +void +populateMultiArray(Thread* t, object array, int32_t* counts, + unsigned index, unsigned dimensions); + object getCaller(Thread* t, unsigned target); @@ -3576,6 +3751,21 @@ methodClone(Thread* t, object method) methodCode(t, method)); } +inline FILE* +errorLog(Thread* t) +{ + if (t->m->errorLog == 0) { + const char* path = findProperty(t, "avian.error.log"); + if (path) { + t->m->errorLog = vm::fopen(path, "wb"); + } else { + t->m->errorLog = stderr; + } + } + + return t->m->errorLog; +} + } // namespace vm void diff --git a/src/posix.cpp b/src/posix.cpp index 3fcbb218de..2a5c7daf78 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -865,7 +865,7 @@ class MySystem: public System { }; void -handleSignal(int signal, siginfo_t* info, void* context) +handleSignal(int signal, siginfo_t*, void* context) { ucontext_t* c = static_cast(context); @@ -943,22 +943,14 @@ handleSignal(int signal, siginfo_t* info, void* context) default: abort(); } - if (system->oldHandlers[index].sa_flags & SA_SIGINFO - and system->oldHandlers[index].sa_sigaction) - { - system->oldHandlers[index].sa_sigaction(signal, info, context); - } else if (system->oldHandlers[index].sa_handler) { - system->oldHandlers[index].sa_handler(signal); - } else { - switch (signal) { - case VisitSignal: - case InterruptSignal: - case PipeSignal: - break; + switch (signal) { + case VisitSignal: + case InterruptSignal: + case PipeSignal: + break; - default: - abort(); - } + default: + abort(); } } diff --git a/src/powerpc.cpp b/src/powerpc.cpp index 36eb113914..42a69fdcd6 100644 --- a/src/powerpc.cpp +++ b/src/powerpc.cpp @@ -196,7 +196,7 @@ class MyBlock: public Assembler::Block { MyBlock(Context* context, unsigned offset): context(context), next(0), jumpOffsetHead(0), jumpOffsetTail(0), lastJumpOffsetTail(0), jumpEventHead(0), jumpEventTail(0), - lastEventOffset(0), offset(offset), start(~0), size(0) + lastEventOffset(0), offset(offset), start(~0), size(0), resolved(false) { } virtual unsigned resolve(unsigned start, Assembler::Block* next) { @@ -205,6 +205,8 @@ class MyBlock: public Assembler::Block { ::resolve(this); + this->resolved = true; + return start + size + padding(this, size); } @@ -219,6 +221,7 @@ class MyBlock: public Assembler::Block { unsigned offset; unsigned start; unsigned size; + bool resolved; }; class Task; @@ -326,13 +329,14 @@ class Offset: public Promise { { } virtual bool resolved() { - return block->start != static_cast(~0); + return block->resolved; } virtual int64_t value() { assert(c, resolved()); - return block->start + (offset - block->offset); + unsigned o = offset - block->offset; + return block->start + padding(block, o) + o; } Context* c; @@ -495,38 +499,24 @@ appendJumpEvent(Context* c, MyBlock* b, unsigned offset, JumpOffset* head, b->jumpEventTail = e; } +bool +needJump(MyBlock* b) +{ + return b->next or (not bounded(2, 16, b->size)); +} + unsigned padding(MyBlock* b, unsigned offset) { unsigned total = 0; - for (JumpEvent** e = &(b->jumpEventHead); *e;) { - if ((*e)->offset <= offset) { - for (JumpOffset** o = &((*e)->jumpOffsetHead); *o;) { - if ((*o)->task->promise->resolved() - and (*o)->task->instructionOffset->resolved()) - { - int32_t v = reinterpret_cast((*o)->task->promise->value()) - - (b->context->result + (*o)->task->instructionOffset->value()); - - if (bounded(2, 16, v)) { - // this conditional jump needs no indirection -- a direct - // jump will suffice - *o = (*o)->next; - continue; - } - } - + for (JumpEvent* e = b->jumpEventHead; e; e = e->next) { + if (e->offset <= offset) { + for (JumpOffset* o = e->jumpOffsetHead; o; o = o->next) { total += BytesPerWord; - o = &((*o)->next); } - if ((*e)->jumpOffsetHead == 0) { - *e = (*e)->next; - } else { - if (b->next) { - total += BytesPerWord; - } - e = &((*e)->next); + if (needJump(b)) { + total += BytesPerWord; } } else { break; @@ -541,6 +531,32 @@ resolve(MyBlock* b) { Context* c = b->context; + for (JumpEvent** e = &(b->jumpEventHead); *e;) { + for (JumpOffset** o = &((*e)->jumpOffsetHead); *o;) { + if ((*o)->task->promise->resolved() + and (*o)->task->instructionOffset->resolved()) + { + int32_t v = reinterpret_cast((*o)->task->promise->value()) + - (c->result + (*o)->task->instructionOffset->value()); + + if (bounded(2, 16, v)) { + // this conditional jump needs no indirection -- a direct + // jump will suffice + *o = (*o)->next; + continue; + } + } + + o = &((*o)->next); + } + + if ((*e)->jumpOffsetHead == 0) { + *e = (*e)->next; + } else { + e = &((*e)->next); + } + } + if (b->jumpOffsetHead) { if (c->jumpOffsetTail) { c->jumpOffsetTail->next = b->jumpOffsetHead; @@ -2423,16 +2439,16 @@ class MyAssembler: public Assembler { return arch_; } - virtual void checkStackOverflow(uintptr_t /*handler*/, - unsigned /*stackLimitOffsetFromThread*/) + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) { - /*Register stack(StackRegister); + Register stack(StackRegister); Memory stackLimit(ThreadRegister, stackLimitOffsetFromThread); Constant handlerConstant (new (c.zone->allocate(sizeof(ResolvedPromise))) ResolvedPromise(handler)); branchRM(&c, JumpIfGreaterOrEqual, BytesPerWord, &stack, &stackLimit, - &handlerConstant);*/ + &handlerConstant); } virtual void saveFrame(unsigned stackOffset, unsigned) { @@ -2680,7 +2696,7 @@ class MyAssembler: public Assembler { uint8_t* address = dst + dstOffset + jumpTableSize; - if (b->next) { + if (needJump(b)) { address += BytesPerWord; } @@ -2691,7 +2707,7 @@ class MyAssembler: public Assembler { assert(&c, jumpTableSize); - if (b->next) { + if (needJump(b)) { write4(dst + dstOffset, ::b(jumpTableSize + BytesPerWord)); } diff --git a/src/process.h b/src/process.h index 27f7788a6b..45de6d975c 100644 --- a/src/process.h +++ b/src/process.h @@ -56,37 +56,6 @@ isSpecialMethod(Thread* t, object method, object class_) and isSuperclass(t, methodClass(t, method), class_); } -inline void -populateMultiArray(Thread* t, object array, int32_t* counts, - unsigned index, unsigned dimensions) -{ - if (index + 1 == dimensions or counts[index] == 0) { - return; - } - - PROTECT(t, array); - - object spec = className(t, objectClass(t, array)); - PROTECT(t, spec); - - object elementSpec = makeByteArray(t, byteArrayLength(t, spec) - 1); - memcpy(&byteArrayBody(t, elementSpec, 0), - &byteArrayBody(t, spec, 1), - byteArrayLength(t, spec) - 1); - - object class_ = resolveClass - (t, classLoader(t, objectClass(t, array)), elementSpec); - PROTECT(t, class_); - - for (int32_t i = 0; i < counts[index]; ++i) { - object a = makeArray(t, counts[index + 1]); - setObjectClass(t, a, class_); - set(t, array, ArrayBody + (i * BytesPerWord), a); - - populateMultiArray(t, a, counts, index + 1, dimensions); - } -} - void resolveNative(Thread* t, object method); diff --git a/src/processor.h b/src/processor.h index 0b71ed1782..f790491439 100644 --- a/src/processor.h +++ b/src/processor.h @@ -81,9 +81,6 @@ class Processor { virtual void initVtable(Thread* t, object c) = 0; - virtual bool - isInitializing(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 4d863ecca4..9279cbb811 100644 --- a/src/thunks.cpp +++ b/src/thunks.cpp @@ -1,5 +1,9 @@ THUNK(tryInitClass) THUNK(findInterfaceMethodFromInstance) +THUNK(findInterfaceMethodFromInstanceAndReference) +THUNK(findSpecialMethodFromReference) +THUNK(findStaticMethodFromReference) +THUNK(findVirtualMethodFromReference) THUNK(compareDoublesG) THUNK(compareDoublesL) THUNK(compareFloatsG) @@ -36,17 +40,32 @@ THUNK(intToFloat) THUNK(longToDouble) THUNK(longToFloat) THUNK(makeBlankObjectArray) +THUNK(makeBlankObjectArrayFromReference) THUNK(makeBlankArray) THUNK(lookUpAddress) THUNK(setMaybeNull) THUNK(acquireMonitorForObject) +THUNK(acquireMonitorForObjectOnEntrance) THUNK(releaseMonitorForObject) THUNK(makeMultidimensionalArray) +THUNK(makeMultidimensionalArrayFromReference) THUNK(throw_) THUNK(checkCast) +THUNK(checkCastFromReference) +THUNK(getStaticFieldValueFromReference) +THUNK(getFieldValueFromReference) +THUNK(setStaticFieldValueFromReference) +THUNK(setFieldValueFromReference) +THUNK(setStaticLongFieldValueFromReference) +THUNK(setLongFieldValueFromReference) +THUNK(setStaticObjectFieldValueFromReference) +THUNK(setObjectFieldValueFromReference) THUNK(instanceOf64) +THUNK(instanceOfFromReference) THUNK(makeNewGeneral64) THUNK(makeNew64) +THUNK(makeNewFromReference) THUNK(set) THUNK(getJClass64) +THUNK(getJClassFromReference) THUNK(gcIfNecessary) diff --git a/src/type-generator.cpp b/src/type-generator.cpp index aed82c3f35..afd20cd796 100644 --- a/src/type-generator.cpp +++ b/src/type-generator.cpp @@ -2177,8 +2177,22 @@ writeNameInitialization(Output* out, Object* type) { out->write("nameClass(t, Machine::"); out->write(capitalize(typeName(type))); - out->write("Type, \"vm::"); - out->write(typeName(type)); + out->write("Type, \""); + if (equal(typeName(type), "jbyte") + or equal(typeName(type), "jboolean") + or equal(typeName(type), "jshort") + or equal(typeName(type), "jchar") + or equal(typeName(type), "jint") + or equal(typeName(type), "jlong") + or equal(typeName(type), "jfloat") + or equal(typeName(type), "jdouble") + or equal(typeName(type), "jvoid")) + { + out->write(typeName(type) + 1); + } else { + out->write("vm::"); + out->write(typeName(type)); + } out->write("\");\n"); } diff --git a/src/types.def b/src/types.def index 8b8d980ffc..1ac0d915ea 100644 --- a/src/types.def +++ b/src/types.def @@ -104,7 +104,9 @@ (type finalizer (nogc object target) (void* finalize) - (nogc object next)) + (nogc object next) + (object queueTarget) + (object queueNext)) (type hashMap (uint32_t size) @@ -159,6 +161,12 @@ (void* value) (object next)) +(type innerClassReference + (object inner) + (object outer) + (object name) + (int16_t flags)) + (type continuationContext (object next) (object before) @@ -294,6 +302,9 @@ (type phantomReference java/lang/ref/PhantomReference) +(type cleaner sun/misc/Cleaner + (object queueNext)) + (type byteArray [B (extends jobject) (array int8_t body)) diff --git a/src/vector.h b/src/vector.h index b5aa9365b9..c952763dba 100644 --- a/src/vector.h +++ b/src/vector.h @@ -33,6 +33,7 @@ class Vector { void dispose() { if (data and minimumCapacity >= 0) { allocator->free(data, capacity); + data = 0; } } diff --git a/src/x86.cpp b/src/x86.cpp index 5c250f449a..e856a536c3 100644 --- a/src/x86.cpp +++ b/src/x86.cpp @@ -969,7 +969,7 @@ sseMoveRR(Context* c, unsigned aSize, Assembler::Register* a, modrm(c, 0xc0, a, b); } else { opcode(c, 0xf2); - maybeRex(c, 4, a, b); + maybeRex(c, 4, b, a); opcode(c, 0x0f, 0x10); modrm(c, 0xc0, a, b); } @@ -2865,7 +2865,7 @@ class MyArchitecture: public Assembler::Architecture { virtual void updateCall(UnaryOperation op, void* returnAddress, void* newTarget) { - bool assertAlignment; + bool assertAlignment UNUSED; switch (op) { case AlignedCall: op = Call; diff --git a/src/zone.h b/src/zone.h index fc412f3634..426315e4ae 100644 --- a/src/zone.h +++ b/src/zone.h @@ -45,23 +45,27 @@ class Zone: public Allocator { next = seg->next; allocator->free(seg, sizeof(Segment) + seg->size); } + + segment = 0; } - bool ensure(unsigned space) { - if (segment == 0 or position + space > segment->size) { - unsigned size = max - (space, max - (minimumFootprint, segment == 0 ? 0 : segment->size * 2)) - + sizeof(Segment); + static unsigned padToPage(unsigned size) { + return (size + (LikelyPageSizeInBytes - 1)) + & ~(LikelyPageSizeInBytes - 1); + } - // pad to page size - size = (size + (LikelyPageSizeInBytes - 1)) - & ~(LikelyPageSizeInBytes - 1); + bool tryEnsure(unsigned space) { + if (segment == 0 or position + space > segment->size) { + unsigned size = padToPage + (max + (space, max + (minimumFootprint, segment == 0 ? 0 : segment->size * 2)) + + sizeof(Segment)); void* p = allocator->tryAllocate(size); if (p == 0) { - size = space + sizeof(Segment); - void* p = allocator->tryAllocate(size); + size = padToPage(space + sizeof(Segment)); + p = allocator->tryAllocate(size); if (p == 0) { return false; } @@ -73,9 +77,19 @@ class Zone: public Allocator { return true; } + void ensure(unsigned space) { + if (segment == 0 or position + space > segment->size) { + unsigned size = padToPage(space + sizeof(Segment)); + + segment = new (allocator->allocate(size)) + Segment(segment, size - sizeof(Segment)); + position = 0; + } + } + virtual void* tryAllocate(unsigned size) { size = pad(size); - if (ensure(size)) { + if (tryEnsure(size)) { void* r = segment->data + position; position += size; return r; @@ -86,8 +100,14 @@ class Zone: public Allocator { virtual void* allocate(unsigned size) { void* p = tryAllocate(size); - expect(s, p); - return p; + if (p) { + return p; + } else { + ensure(size); + void* r = segment->data + position; + position += size; + return r; + } } virtual void free(const void*, unsigned) { diff --git a/test/DefineClass.java b/test/DefineClass.java index ea3b6362ae..4cff3ecfde 100644 --- a/test/DefineClass.java +++ b/test/DefineClass.java @@ -49,7 +49,7 @@ public class DefineClass { } public static void main(String[] args) throws Exception { - //testStatic(); + testStatic(); testDerived(); } @@ -59,7 +59,7 @@ public class DefineClass { } public Class defineClass(String name, byte[] bytes) { - return super.defineClass(name, bytes, 0, bytes.length); + return defineClass(name, bytes, 0, bytes.length); } } diff --git a/test/Floats.java b/test/Floats.java index 412bd5cd6c..38ae3121b1 100644 --- a/test/Floats.java +++ b/test/Floats.java @@ -33,7 +33,91 @@ public class Floats { return f.field * a; } + private static void subdivide(double src[], int srcoff, + double left[], int leftoff, + double right[], int rightoff) + { + double x1 = src[srcoff + 0]; + double y1 = src[srcoff + 1]; + double ctrlx1 = src[srcoff + 2]; + double ctrly1 = src[srcoff + 3]; + double ctrlx2 = src[srcoff + 4]; + double ctrly2 = src[srcoff + 5]; + double x2 = src[srcoff + 6]; + double y2 = src[srcoff + 7]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 6] = x2; + right[rightoff + 7] = y2; + } + x1 = (x1 + ctrlx1) / 2.0; + y1 = (y1 + ctrly1) / 2.0; + x2 = (x2 + ctrlx2) / 2.0; + y2 = (y2 + ctrly2) / 2.0; + double centerx = (ctrlx1 + ctrlx2) / 2.0; + double centery = (ctrly1 + ctrly2) / 2.0; + ctrlx1 = (x1 + centerx) / 2.0; + ctrly1 = (y1 + centery) / 2.0; + ctrlx2 = (x2 + centerx) / 2.0; + ctrly2 = (y2 + centery) / 2.0; + centerx = (ctrlx1 + ctrlx2) / 2.0; + centery = (ctrly1 + ctrly2) / 2.0; + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx1; + left[leftoff + 5] = ctrly1; + left[leftoff + 6] = centerx; + left[leftoff + 7] = centery; + } + if (right != null) { + right[rightoff + 0] = centerx; + right[rightoff + 1] = centery; + right[rightoff + 2] = ctrlx2; + right[rightoff + 3] = ctrly2; + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + } + public static void main(String[] args) { + { double input[] = new double[8]; + double left[] = new double[8]; + double right[] = new double[8]; + + input[0] = 732.0; + input[1] = 952.0; + input[2] = 761.0; + input[3] = 942.0; + input[4] = 786.0; + input[5] = 944.0; + input[6] = 813.0; + input[7] = 939.0; + + subdivide(input, 0, left, 0, right, 0); + + expect(left[0] == 732.0); + expect(left[1] == 952.0); + expect(left[2] == 746.5); + expect(left[3] == 947.0); + expect(left[4] == 760.0); + expect(left[5] == 945.0); + expect(left[6] == 773.25); + expect(left[7] == 943.625); + + expect(right[0] == 773.25); + expect(right[1] == 943.625); + expect(right[2] == 786.5); + expect(right[3] == 942.25); + expect(right[4] == 799.5); + expect(right[5] == 941.5); + expect(right[6] == 813.0); + expect(right[7] == 939.0); + } + expect(multiply(0.5d, 0.5d) == 0.25d); expect(multiply(0.5f, 0.5f) == 0.25f); diff --git a/test/LazyLoading.java b/test/LazyLoading.java new file mode 100644 index 0000000000..ace2ebf5d0 --- /dev/null +++ b/test/LazyLoading.java @@ -0,0 +1,184 @@ +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +public class LazyLoading { + private static boolean loadLazy; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static File findClass(String name, File directory) { + for (File file: directory.listFiles()) { + if (file.isFile()) { + if (file.getName().equals(name + ".class")) { + return file; + } + } else if (file.isDirectory()) { + File result = findClass(name, file); + if (result != null) { + return result; + } + } + } + return null; + } + + private static byte[] read(File file) throws IOException { + byte[] bytes = new byte[(int) file.length()]; + FileInputStream in = new FileInputStream(file); + try { + if (in.read(bytes) != (int) file.length()) { + throw new RuntimeException(); + } + return bytes; + } finally { + in.close(); + } + } + + public static void main(String[] args) throws Exception { + Class c = new MyClassLoader(LazyLoading.class.getClassLoader()).loadClass + ("LazyLoading$Test"); + + c.getMethod("test").invoke(null); + } + + private static class MyClassLoader extends ClassLoader { + public MyClassLoader(ClassLoader parent) { + super(parent); + } + + protected Class findClass(String name) throws ClassNotFoundException { + try { + return defineClass + (name, read + (LazyLoading.findClass + (name, new File(System.getProperty("user.dir"))))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public Class loadClass(String name) throws ClassNotFoundException { + if ("LazyLoading$Test".equals(name)) { + return findClass(name); + } else if ("LazyLoading$Lazy".equals(name) + || "LazyLoading$Interface".equals(name)) + { + if (loadLazy) { + return findClass(name); + } else { + throw new ClassNotFoundException(); + } + } else { + return super.loadClass(name); + } + } + + private Class defineClass(String name, byte[] bytes) { + return defineClass(name, bytes, 0, bytes.length); + } + } + + public static class Test { + public static void test() { + doTest(); + loadLazy = true; + doTest(); + } + + private static void doTest() { + if (loadLazy) { + // anewarray + Lazy[] array = new Lazy[1]; + + // new and invokespecial + Object lazy = new Lazy(); + + // checkcast + array[0] = (Lazy) lazy; + + // instanceof + expect(lazy instanceof Lazy); + + // invokeinterface + Interface i = array[0]; + expect(i.interfaceMethod() == 42); + + // invokestatic + expect(Lazy.staticMethod() == 43); + + // invokevirtual + expect(array[0].virtualMethod() == 44); + + // ldc + expect(Lazy.class == lazy.getClass()); + + // multianewarray + Lazy[][] multiarray = new Lazy[5][6]; + multiarray[2][3] = array[0]; + expect(multiarray[2][3] == array[0]); + + // getfield + expect(array[0].intField == 45); + + // getstatic + expect(Lazy.intStaticField == 46); + + // putfield int + array[0].intField = 47; + expect(array[0].intField == 47); + + // putfield long + array[0].longField = 48; + expect(array[0].longField == 48); + + // putfield object + Object x = new Object(); + array[0].objectField = x; + expect(array[0].objectField == x); + + // putstatic int + array[0].intStaticField = 49; + expect(array[0].intStaticField == 49); + + // putstatic long + array[0].longStaticField = 50; + expect(array[0].longStaticField == 50); + + // putstatic object + Object y = new Object(); + array[0].objectStaticField = y; + expect(array[0].objectStaticField == y); + } + } + } + + private interface Interface { + public int interfaceMethod(); + } + + private static class Lazy implements Interface { + public static int intStaticField = 46; + public static long longStaticField; + public static Object objectStaticField; + + public int intField = 45; + public long longField; + public Object objectField; + + public int interfaceMethod() { + return 42; + } + + public static int staticMethod() { + return 43; + } + + public int virtualMethod() { + return 44; + } + } +} diff --git a/test/Longs.java b/test/Longs.java index 9beaf8a996..26f1e170b1 100644 --- a/test/Longs.java +++ b/test/Longs.java @@ -1,4 +1,10 @@ -public class Longs { +public class Longs { + private static volatile long volatileLong = getConstant(); + + private static long getConstant() { + return 0x123456789ABCDEFL; + } + private static void expect(boolean v) { if (! v) throw new RuntimeException(); } @@ -60,6 +66,8 @@ public class Longs { } public static void main(String[] args) throws Exception { + expect(volatileLong == getConstant()); + { long a = 0x1FFFFFFFFL; long b = -1; expect(a != b); diff --git a/test/extra/Tails.java b/test/extra/Tails.java new file mode 100644 index 0000000000..a99655531a --- /dev/null +++ b/test/extra/Tails.java @@ -0,0 +1,49 @@ +package extra; + +public class Tails { + private static final int Limit = 1000000; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static int staticMethod(Interface i, int n) { + if (n < Limit) { + return i.interfaceMethod(n + 1); + } else { + return leafMethod(n); + } + } + + private static int leafMethod(int n) { + expect(new Throwable().getStackTrace().length == 2); + + return n; + } + + public static void main(String[] args) { + expect(staticMethod(new Foo(), 0) == Limit); + } + + private interface Interface { + public int interfaceMethod(int n); + } + + private static class Foo implements Interface { + public int interfaceMethod(int n) { + if (n < Limit) { + return virtualMethod(n + 1, 1, 2, 3, 4, 5); + } else { + return leafMethod(n); + } + } + + public int virtualMethod(int n, int a, int b, int c, int d, int e) { + if (n < Limit) { + return staticMethod(this, n + 1); + } else { + return leafMethod(n); + } + } + } +}