diff --git a/.travis.yml b/.travis.yml index d18b9c7fd3..42265efb47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,9 @@ os: env: matrix: - BUILD_STEP="" - - BUILD_STEP="PUBLISH" +# disabled until/unless jfrog.org credentials are updated and someone +# decides they care about published artifacts: +# - BUILD_STEP="PUBLISH" global: - TERM=dumb - secure: rh1utD4shKmYtokItuRYEF9WsfTnvZO5XqnTU4DHTS7quHHgLihtOO2/3+B+2W2hEd5Obr2or8zx+zmzWcNUyLokZ0j/FRLWSScNkLzTtm12pupLrncY+/g1NIdfbhn+OLRIzBz6zB6m6a2qWFEJ+bScUNGD/7wZVtzkujqlDEE= diff --git a/README.md b/README.md index 9e6430e23f..18dd2576db 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Introduction Avian is a lightweight virtual machine and class library designed to provide a useful subset of Java's features, suitable for building self-contained applications. More information is available at the -project [web site](http://oss.readytalk.com/avian). +project [web site](http://readytalk.github.io/avian). If you have any trouble building, running, or embedding Avian, please post a message to our [discussion group](http://groups.google.com/group/avian). @@ -168,8 +168,8 @@ Library" below for details. These flags determine the name of the directory used for the build. The name always starts with _${platform}-${arch}_, and each non-default build option is appended to the name. For example, a debug build with -bootimage enabled on Linux/i386 would be built in -_build/linux-i386-debug-bootimage_. This allows you to build with +bootimage enabled on Linux/x86_64 would be built in +_build/linux-x86_64-debug-bootimage_. This allows you to build with several different sets of options independently and even simultaneously without doing a clean build each time. @@ -293,9 +293,10 @@ 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). Note that you'll still need to use vm.pro in that case -- openjdk.pro just adds additional constraints -specific to the OpenJDK port. 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. +specific to the OpenJDK port. Also see +[app.mk](https://github.com/ReadyTalk/avian-swt-examples/blob/master/app.mk) +in the _avian-swt-examples_ project 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: @@ -575,7 +576,7 @@ For boot image builds: Note you can use ProGuard without using a boot image and vice-versa, as desired. -The following instructions assume we are building for Linux/i386. +The following instructions assume we are building for Linux/x86_64. Please refer to the previous example for guidance on other platforms. __1.__ Build Avian, create a new directory, and populate it with the @@ -584,13 +585,13 @@ VM object files. $ make bootimage=true $ mkdir hello $ cd hello - $ ar x ../build/linux-i386-bootimage/libavian.a + $ ar x ../build/linux-x86_64-bootimage/libavian.a __2.__ Create a stage1 directory and extract the contents of the class library jar into it. $ mkdir stage1 - $ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar) + $ (cd stage1 && jar xf ../../build/linux-x86_64-bootimage/classpath.jar) __3.__ Build the Java code and add it to stage1. @@ -630,10 +631,11 @@ using the OpenJDK library.) __6.__ Build the boot and code images. - $ ../build/linux-i386-bootimage/bootimage-generator \ + $ ../build/linux-x86_64-bootimage/bootimage-generator \ -cp stage2 \ -bootimage bootimage-bin.o \ - -codeimage codeimage-bin.o + -codeimage codeimage-bin.o \ + -hostvm ../build/linux-x86_64-interpret/libjvm.so Note that you can override the default names for the start and end symbols in the boot/code image by also passing: diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 7d428b28d4..b55408834c 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -423,6 +423,27 @@ public class Classes { } } + public static VMMethod findMethod(ClassLoader loader, + String class_, + String name, + String spec) + throws ClassNotFoundException + { + VMClass c = SystemClassLoader.vmClass(loader.loadClass(class_)); + VMMethod[] methodTable = c.methodTable; + if (methodTable != null) { + link(c); + + for (int i = 0; i < methodTable.length; ++i) { + VMMethod m = methodTable[i]; + if (toString(m.name).equals(name) && toString(m.spec).equals(spec)) { + return m; + } + } + } + return null; + } + public static int findMethod(VMClass vmClass, String name, Class[] parameterTypes) { diff --git a/classpath/avian/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java index e9970e021d..5dd2ee4352 100644 --- a/classpath/avian/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -20,6 +20,8 @@ import java.util.Enumeration; import java.util.NoSuchElementException; public class SystemClassLoader extends ClassLoader { + public static native ClassLoader appLoader(); + private native VMClass findVMClass(String name) throws ClassNotFoundException; @@ -81,6 +83,8 @@ public class SystemClassLoader extends ClassLoader { if (source != null) { // todo: load attributes from JAR manifest definePackage(name, null, null, null, null, null, null, null); + } else { + definePackage(name, null, null, null, null, null, null, null); } } diff --git a/classpath/avian/http/Handler.java b/classpath/avian/http/Handler.java index e022b96940..28f36d4194 100644 --- a/classpath/avian/http/Handler.java +++ b/classpath/avian/http/Handler.java @@ -10,17 +10,111 @@ package avian.http; -import java.net.URL; -import java.net.URLStreamHandler; -import java.net.URLConnection; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.FileNotFoundException; -import java.io.File; -import java.io.FileInputStream; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.HashMap; +import java.util.Map; -public class Handler extends URLStreamHandler { - protected URLConnection openConnection(URL url) { - throw new UnsupportedOperationException(); - } +public class Handler extends URLStreamHandler +{ + public URLConnection openConnection(URL url) throws IOException + { + return new HttpURLConnection(url); + } + + class HttpURLConnection extends URLConnection + { + Socket socket; + private BufferedWriter writer; + private InputStream bin; + private Map header = new HashMap(); + private int status; + + protected HttpURLConnection(URL url) + { + super(url); + } + + @Override + public void connect() throws IOException + { + if(socket == null) + { + URLConnection con = null; + String host = url.getHost(); + int port =url.getPort(); + if(port < 0) port = 80; + socket = new Socket(host, port); + OutputStream out = socket.getOutputStream(); + writer = new BufferedWriter(new OutputStreamWriter(out)); + writer.write("GET " + url.getPath() + " HTTP/1.1"); + writer.write("\r\nHost: " + host); + writer.write("\r\n\r\n"); + writer.flush(); + bin = new BufferedInputStream(socket.getInputStream()); + readHeader(); +// System.out.println("Status: " + status); +// System.out.println("Headers: " + header); + } + } + + private void readHeader() throws IOException + { + byte[] buf = new byte[8192]; + int b = 0; + int index = 0; + while(b >= 0) + { + if(index >= 4 && buf[index-4] == '\r' && buf[index-3] == '\n' && buf[index-2] == '\r' && buf[index-1] == '\n') + { + break; + } + b = bin.read(); + buf[index] = (byte) b; + index++; + if(index >= buf.length) + { + throw new IOException("Header exceeded maximum size of 8k."); + } + } + BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, index))); + String line = reader.readLine(); + int x = line.indexOf(' '); + status = Integer.parseInt(line.substring(x + 1 , line.indexOf(' ', x+1))); + while(line != null) + { + int i = line.indexOf(':'); + if(i > 0) + { + header.put(line.substring(0, i), line.substring(i + 1) .trim()); + } + line = reader.readLine(); + } + reader.close(); + } + + @Override + public InputStream getInputStream() throws IOException + { + connect(); + return bin; + } + + @Override + public OutputStream getOutputStream() throws IOException + { + throw new UnsupportedOperationException("Can' write to HTTP Connection"); + } + } } diff --git a/classpath/java/io/BufferedInputStream.java b/classpath/java/io/BufferedInputStream.java index ca4b7033ec..25a7441304 100644 --- a/classpath/java/io/BufferedInputStream.java +++ b/classpath/java/io/BufferedInputStream.java @@ -7,9 +7,11 @@ There is NO WARRANTY for this software. See license.txt for details. */ - package java.io; +import java.io.IOException; +import java.io.InputStream; + public class BufferedInputStream extends InputStream { private final InputStream in; private final byte[] buffer; @@ -25,17 +27,16 @@ public class BufferedInputStream extends InputStream { this(in, 4096); } - private void fill() throws IOException { + private int fill() throws IOException { position = 0; limit = in.read(buffer); + + return limit; } public int read() throws IOException { - if (position >= limit) { - fill(); - if (limit == -1) { - return -1; - } + if (position >= limit && fill() == -1) { + return -1; } return buffer[position++] & 0xFF; @@ -43,7 +44,9 @@ public class BufferedInputStream extends InputStream { public int read(byte[] b, int offset, int length) throws IOException { int count = 0; - + if (position >= limit && fill() == -1) { + return -1; + } if (position < limit) { int remaining = limit - position; if (remaining > length) { @@ -57,8 +60,8 @@ public class BufferedInputStream extends InputStream { offset += remaining; length -= remaining; } - - while (length > 0) { + while (length > 0 && in.available() > 0) + { int c = in.read(b, offset, length); if (c == -1) { if (count == 0) { @@ -69,13 +72,8 @@ public class BufferedInputStream extends InputStream { offset += c; count += c; length -= c; - - if (in.available() <= 0) { - break; - } } } - return count; } @@ -87,3 +85,4 @@ public class BufferedInputStream extends InputStream { in.close(); } } + diff --git a/classpath/java/lang/Math.java b/classpath/java/lang/Math.java index eb79c3cc1b..c1d1c0bdaf 100644 --- a/classpath/java/lang/Math.java +++ b/classpath/java/lang/Math.java @@ -15,7 +15,10 @@ import java.util.Random; public final class Math { public static final double E = 2.718281828459045; public static final double PI = 3.141592653589793; - private static final Random random = new Random(); + + private static class Static { + public static final Random random = new Random(); + } private Math() { } @@ -84,7 +87,7 @@ public final class Math { } public static double random() { - return random.nextDouble(); + return Static.random.nextDouble(); } public static native double floor(double v); diff --git a/classpath/java/lang/System.java b/classpath/java/lang/System.java index 10886dbb03..f6ceabad6b 100644 --- a/classpath/java/lang/System.java +++ b/classpath/java/lang/System.java @@ -22,7 +22,9 @@ import java.util.Hashtable; import java.util.Properties; public abstract class System { - private static final long NanoTimeBaseInMillis = currentTimeMillis(); + private static class NanoTime { + public static final long BaseInMillis = currentTimeMillis(); + } private static class Static { public static Properties properties = makeProperties(); @@ -94,7 +96,7 @@ public abstract class System { public static native int identityHashCode(Object o); public static long nanoTime() { - return (currentTimeMillis() - NanoTimeBaseInMillis) * 1000000; + return (currentTimeMillis() - NanoTime.BaseInMillis) * 1000000; } public static String mapLibraryName(String name) { diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java index 5a814d1649..f0d57163b3 100644 --- a/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -187,14 +187,28 @@ public class LambdaMetafactory { return result; } - - public static CallSite metafactory(MethodHandles.Lookup caller, - String invokedName, - MethodType invokedType, - MethodType methodType, - MethodHandle methodImplementation, - MethodType instantiatedMethodType) - throws LambdaConversionException + + public static byte[] makeLambda(String invokedName, + String invokedType, + String methodType, + String implementationClass, + String implementationName, + String implementationSpec, + int implementationKind) + { + return makeLambda(invokedName, + new MethodType(invokedType), + new MethodType(methodType), + new MethodHandle(implementationClass, + implementationName, + implementationSpec, + implementationKind)); + } + + private static byte[] makeLambda(String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation) { String className; { int number; @@ -265,8 +279,19 @@ public class LambdaMetafactory { throw error; } - byte[] classData = out.toByteArray(); - + return out.toByteArray(); + } + + public static CallSite metafactory(MethodHandles.Lookup caller, + String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation, + MethodType instantiatedMethodType) + throws LambdaConversionException + { + byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation); + try { return new CallSite (new MethodHandle diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java index 4efbfbabd6..c676cadeed 100644 --- a/classpath/java/lang/invoke/MethodHandle.java +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -1,6 +1,7 @@ package java.lang.invoke; import avian.Classes; +import avian.SystemClassLoader; public class MethodHandle { static final int REF_invokeStatic = 6; @@ -17,6 +18,20 @@ public class MethodHandle { this.method = method; } + MethodHandle(String class_, + String name, + String spec, + int kind) + { + this.kind = kind; + this.loader = SystemClassLoader.appLoader(); + try { + this.method = Classes.findMethod(this.loader, class_, name, spec); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + public String toString() { StringBuilder sb = new StringBuilder(); if (method.class_ != null) { diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java index c8bf5bce62..e2030403d9 100644 --- a/classpath/java/lang/invoke/MethodType.java +++ b/classpath/java/lang/invoke/MethodType.java @@ -4,6 +4,7 @@ import static avian.Assembler.*; import avian.Assembler; import avian.Classes; +import avian.SystemClassLoader; import avian.VMClass; import java.util.List; @@ -25,6 +26,12 @@ public final class MethodType implements java.io.Serializable { this.spec = spec; } + MethodType(String spec) { + this.loader = SystemClassLoader.appLoader(); + this.spec = new byte[spec.length() + 1]; + spec.getBytes(0, spec.length(), this.spec, 0); + } + public String toMethodDescriptorString() { return Classes.makeString(spec, 0, spec.length - 1); } diff --git a/classpath/java/net/Socket.java b/classpath/java/net/Socket.java index e486f7f4e6..9a32b88f7c 100644 --- a/classpath/java/net/Socket.java +++ b/classpath/java/net/Socket.java @@ -86,18 +86,16 @@ public class Socket implements Closeable, AutoCloseable { @Override public int read(byte[] buffer) throws IOException { + if(buffer.length == 0) return 0; //spec says return 0 if buffer length is zero. int fullSize = buffer.length; - int index = 0; int size; - do { - size = recv(sock, buffer, index, Math.min(fullSize, Socket.BUFFER_SIZE)); - fullSize -= size; - index += size; - } while (fullSize != 0 && size != 0); - return index; + size = recv(sock, buffer, 0, Math.min(fullSize, Socket.BUFFER_SIZE)); + fullSize -= size; + //removed loop, because otherwise interactive protocols will not work. + if(size < 0) throw new IOException("Error while reading stream"); //as the manpage of recv says, a value below zero indicates an error. + if(size == 0) return -1; // if the stream is closed (size == 0), then return -1 to indicate end of stream. + return size; } - - } private class SocketOutputStream extends OutputStream { diff --git a/docker/Dockerfile b/docker/Dockerfile index 04796d08e8..f4aaa75032 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,13 +1,18 @@ FROM debian:jessie MAINTAINER Joshua Warner, joshuawarner32@gmail.com +RUN echo 'deb http://http.debian.net/debian jessie-backports main' >> /etc/apt/sources.list && \ + echo 'deb-src http://http.debian.net/debian jessie-backports main' >> /etc/apt/sources.list && \ + dpkg --add-architecture i386 && \ + apt-get update && \ + mkdir /var/src/ + # Install base dependencies and build tools, general debugging tools -RUN apt-get update && \ - apt-get install -y \ +RUN apt-get install -y \ build-essential \ - g++-4.8 \ + g++-4.9 \ zlib1g-dev \ - openjdk-7-jdk \ + openjdk-8-jdk \ locales \ --no-install-recommends && \ apt-get clean all @@ -20,8 +25,59 @@ RUN dpkg-reconfigure locales && \ ENV LC_ALL C.UTF-8 # Set JAVA_HOME for avian's benefit -ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64 +ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64 + +# Add i386 libraries +RUN apt-get install -y \ + libc6-dev-i386 && \ + apt-get download \ + zlib1g-dev:i386 && \ + dpkg -x *.deb / && \ + rm *.deb && \ + apt-get clean all + +# Install cross-compile toolchain and emulator for testing +RUN apt-get install -y \ + mingw-w64 \ + wget \ + unzip \ + --no-install-recommends && \ + apt-get clean all + +# Download win32 and win64 adjacent to avian +RUN cd /var/src/ && \ + wget https://github.com/ReadyTalk/win32/archive/master.zip -O win32.zip && \ + unzip win32.zip && \ + rm win32.zip && \ + mv win32-* win32 && \ + wget https://github.com/ReadyTalk/win64/archive/master.zip -O win64.zip && \ + unzip win64.zip && \ + rm win64.zip && \ + mv win64-* win64 + +# Add openjdk-src stuff +RUN apt-get install -y \ + libcups2-dev \ + libgconf2-dev && \ + mkdir /var/src/openjdk/ && \ + cd /var/src/openjdk/ && \ + apt-get source openjdk-8 && \ + apt-get clean all && \ + find /var/src/openjdk && \ + rm /var/src/openjdk/*.gz /var/src/openjdk/*.dsc && \ + cd /var/src/openjdk/ && \ + tar -xf /var/src/openjdk/openjdk*/jdk.tar.xz && \ + mv /var/src/openjdk/jdk-*/src /var/src/openjdk-src && \ + rm -rf /var/src/openjdk && \ + apt-get clean all + +# Download/extract lzma source +RUN mkdir /var/src/lzma && \ + cd /var/src/lzma && \ + apt-get install -y p7zip && \ + wget http://www.7-zip.org/a/lzma1507.7z -O lzma.7z && \ + p7zip -d lzma.7z # Avian build location -VOLUME /var/avian -WORKDIR /var/avian +VOLUME /var/src/avian +WORKDIR /var/src/avian diff --git a/docker/build.sh b/docker/build.sh index b67b599f1d..ef61007dce 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -38,4 +38,4 @@ fi DIR=$(cd $(dirname "$0") && cd .. && pwd) -docker run --rm -i -t -v "${DIR}":/var/avian ${THE_USER} "${CONTAINER}" "${@}" +docker run --rm -i -t -v "${DIR}":/var/src/avian ${THE_USER} "${CONTAINER}" "${@}" diff --git a/docker/i386/Dockerfile b/docker/i386/Dockerfile deleted file mode 100644 index eb9e5c2b0c..0000000000 --- a/docker/i386/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM joshuawarner32/avian-build -MAINTAINER Joshua Warner, joshuawarner32@gmail.com - -RUN dpkg --add-architecture i386 && \ - apt-get update && \ - apt-get install -y \ - libc6-dev-i386 && \ - apt-get download \ - zlib1g-dev:i386 && \ - dpkg -x *.deb / && \ - rm *.deb && \ - apt-get clean all diff --git a/docker/openjdk/Dockerfile b/docker/openjdk/Dockerfile deleted file mode 100644 index bda3a5d95d..0000000000 --- a/docker/openjdk/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM joshuawarner32/avian-build-i386 -MAINTAINER Joshua Warner, joshuawarner32@gmail.com - -RUN echo 'deb-src http://http.debian.net/debian jessie main' >> /etc/apt/sources.list && \ - apt-get update && \ - apt-get install -y \ - libcups2-dev \ - libgconf2-dev && \ - mkdir /var/openjdk/ && \ - cd /var/openjdk/ && \ - apt-get source openjdk-7 && \ - apt-get clean all && \ - rm /var/openjdk/*.gz /var/openjdk/*.dsc && \ - cd /var/openjdk/ && \ - tar -xzf /var/openjdk/openjdk*/jdk.tar.gz && \ - mv /var/openjdk/jdk-*/src /var/openjdk-src && \ - rm -rf /var/openjdk \ No newline at end of file diff --git a/docker/windows/Dockerfile b/docker/windows/Dockerfile deleted file mode 100644 index 88f039846a..0000000000 --- a/docker/windows/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM joshuawarner32/avian-build -MAINTAINER Joshua Warner, joshuawarner32@gmail.com - -# Install cross-compile toolchain and emulator for testing -RUN apt-get update && \ - apt-get install -y \ - mingw-w64 \ - wget \ - unzip \ - --no-install-recommends && \ - apt-get clean all - -# Download win32 and win64 adjacent to avian -RUN cd .. && \ - wget https://github.com/ReadyTalk/win32/archive/master.zip -O win32.zip && \ - unzip win32.zip && \ - rm win32.zip && \ - mv win32-* win32 && \ - wget https://github.com/ReadyTalk/win64/archive/master.zip -O win64.zip && \ - unzip win64.zip && \ - rm win64.zip && \ - mv win64-* win64 - diff --git a/include/avian/system/system.h b/include/avian/system/system.h index c1f1dc1489..e2c4ff5022 100644 --- a/include/avian/system/system.h +++ b/include/avian/system/system.h @@ -176,7 +176,7 @@ inline void NO_RETURN sysAbort(System* s) // #endif // not NDEBUG -AVIAN_EXPORT System* makeSystem(); +AVIAN_EXPORT System* makeSystem(bool reentrant = false); } // namespace vm diff --git a/makefile b/makefile index 13b788527c..a7f39a8879 100755 --- a/makefile +++ b/makefile @@ -45,6 +45,11 @@ ifneq ($(lzma),) endif ifeq ($(bootimage),true) options := $(options)-bootimage + ifeq ($(bootimage-test),true) + # this option indicates that we should AOT-compile the test + # classes as well as the class library + options := $(options)-test + endif endif ifeq ($(tails),true) options := $(options)-tails @@ -92,6 +97,12 @@ ifeq ($(platform),ios) endif endif +ifeq ($(bootimage-test),true) + ifneq ($(bootimage),true) + x := $(error "bootimage-test=true only works when bootimage=true") + endif +endif + aot-only = false root := $(shell (cd .. && pwd)) build = build/$(platform)-$(arch)$(options) @@ -109,6 +120,12 @@ wp8 ?= $(root)/wp8 classpath = avian +bootimage-classpath = $(classpath-build) + +ifeq ($(bootimage-test),true) + bootimage-classpath = $(classpath-build):$(test-build) +endif + test-executable = $(shell pwd)/$(executable) boot-classpath = $(classpath-build) embed-prefix = /avian-embedded @@ -746,13 +763,14 @@ ifeq ($(kernel),darwin) rpath = ifeq ($(platform),ios) - ifeq (,$(filter arm arm64,$(arch))) + ifeq ($(sim),true) target = iPhoneSimulator sdk = iphonesimulator$(ios-version) ifeq ($(arch),i386) arch-flag = -arch i386 else arch-flag = -arch x86_64 + arch = x86_64 endif release = Release-iphonesimulator else @@ -762,6 +780,7 @@ ifeq ($(kernel),darwin) arch-flag = -arch armv7 else arch-flag = -arch arm64 + arch = arm64 endif release = Release-iphoneos endif @@ -770,7 +789,9 @@ ifeq ($(kernel),darwin) sdk-dir = $(platform-dir)/Developer/SDKs ios-version := $(shell \ - if test -d $(sdk-dir)/$(target)8.3.sdk; then echo 8.3; \ + if test -L $(sdk-dir)/$(target)9.1.sdk; then echo 9.1; \ + elif test -L $(sdk-dir)/$(target)9.0.sdk; then echo 9.0; \ + elif test -d $(sdk-dir)/$(target)8.3.sdk; then echo 8.3; \ elif test -d $(sdk-dir)/$(target)8.2.sdk; then echo 8.2; \ elif test -d $(sdk-dir)/$(target)8.1.sdk; then echo 8.1; \ elif test -d $(sdk-dir)/$(target)8.0.sdk; then echo 8.0; \ @@ -811,39 +832,57 @@ ifeq ($(kernel),darwin) cflags += $(flags) asmflags += $(flags) lflags += $(flags) - endif - - ifeq ($(arch),i386) - ifeq ($(platform),ios) - classpath-extra-cflags += \ - -arch i386 -miphoneos-version-min=$(ios-version) - cflags += -arch i386 -miphoneos-version-min=$(ios-version) - asmflags += -arch i386 -miphoneos-version-min=$(ios-version) - lflags += -arch i386 -miphoneos-version-min=$(ios-version) + + ios-version-min=$(ios-version) + ifdef ios_deployment_target + ios-version-min=ios_deployment_target + endif + + ifeq ($(sim),true) + ifeq ($(arch),x86_64) + classpath-extra-cflags += \ + -arch x86_64 -miphoneos-version-min=$(ios-version-min) + cflags += -arch x86_64 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch x86_64 -miphoneos-version-min=$(ios-version-min) + lflags += -arch x86_64 -miphoneos-version-min=$(ios-version-min) + else + classpath-extra-cflags += \ + -arch i386 -miphoneos-version-min=$(ios-version-min) + cflags += -arch i386 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch i386 -miphoneos-version-min=$(ios-version-min) + lflags += -arch i386 -miphoneos-version-min=$(ios-version-min) + endif else - classpath-extra-cflags += \ - -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} - cflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} - asmflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} - lflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + ifeq ($(arch),arm64) + classpath-extra-cflags += \ + -arch arm64 -miphoneos-version-min=$(ios-version-min) + cflags += -arch arm64 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch arm64 -miphoneos-version-min=$(ios-version-min) + lflags += -arch arm64 -miphoneos-version-min=$(ios-version-min) + else + classpath-extra-cflags += \ + -arch armv7 -miphoneos-version-min=$(ios-version-min) + cflags += -arch armv7 -miphoneos-version-min=$(ios-version-min) + asmflags += -arch armv7 -miphoneos-version-min=$(ios-version-min) + lflags += -arch armv7 -miphoneos-version-min=$(ios-version-min) + endif + endif + else # not ios + ifeq ($(arch),i386) + classpath-extra-cflags += \ + -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + cflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + asmflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + lflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + endif + + ifeq ($(arch),x86_64) + classpath-extra-cflags += -arch x86_64 + cflags += -arch x86_64 + asmflags += -arch x86_64 + lflags += -arch x86_64 endif endif - - ifeq ($(arch),x86_64) - ifeq ($(platform),ios) - classpath-extra-cflags += \ - -arch x86_64 -miphoneos-version-min=$(ios-version) - cflags += -arch x86_64 -miphoneos-version-min=$(ios-version) - asmflags += -arch x86_64 -miphoneos-version-min=$(ios-version) - lflags += -arch x86_64 -miphoneos-version-min=$(ios-version) - else - classpath-extra-cflags += -arch x86_64 - cflags += -arch x86_64 - asmflags += -arch x86_64 - lflags += -arch x86_64 - endif - endif - cflags += -I$(JAVA_HOME)/include/darwin endif @@ -1333,6 +1372,12 @@ bootimage-generator-objects = \ $(call cpp-objects,$(bootimage-generator-sources),$(src),$(build)) bootimage-generator = $(build)/bootimage-generator +ifneq ($(mode),fast) + host-vm-options := -$(mode) +endif + +host-vm = build/$(build-platform)-$(build-arch)-interpret$(host-vm-options)/libjvm.so + bootimage-object = $(build)/bootimage-bin.o codeimage-object = $(build)/codeimage-bin.o @@ -1641,7 +1686,6 @@ debug: build vg: build $(library-path) $(vg) $(test-executable) $(test-args) - .PHONY: test test: build-test run-test @@ -2060,11 +2104,12 @@ else endif $(bootimage-object) $(codeimage-object): $(bootimage-generator) \ - $(classpath-jar-dep) + $(classpath-jar-dep) $(test-dep) @echo "generating bootimage and codeimage binaries from $(classpath-build) using $(<)" - $(<) -cp $(classpath-build) -bootimage $(bootimage-object) -codeimage $(codeimage-object) \ + $(<) -cp $(bootimage-classpath) -bootimage $(bootimage-object) -codeimage $(codeimage-object) \ -bootimage-symbols $(bootimage-symbols) \ - -codeimage-symbols $(codeimage-symbols) + -codeimage-symbols $(codeimage-symbols) \ + -hostvm $(host-vm) executable-objects = $(vm-objects) $(classpath-objects) $(driver-object) \ $(vm-heapwalk-objects) $(boot-object) $(vm-classpath-objects) \ @@ -2119,6 +2164,7 @@ $(unittest-executable): $(unittest-executable-objects) $(bootimage-generator): $(bootimage-generator-objects) $(vm-objects) echo building $(bootimage-generator) arch=$(build-arch) platform=$(bootimage-platform) + $(MAKE) process=interpret bootimage= bootimage-test= mode=$(mode) $(MAKE) mode=$(mode) \ build=$(host-build-root) \ arch=$(build-arch) \ diff --git a/src/avian/bootimage.h b/src/avian/bootimage.h index 9f6cf36b7c..d14755f8b3 100644 --- a/src/avian/bootimage.h +++ b/src/avian/bootimage.h @@ -55,10 +55,13 @@ class BootImage { } PACKED; class GcField; +class GcClass; class OffsetResolver { public: virtual unsigned fieldOffset(Thread*, GcField*) = 0; + + virtual void addClass(Thread*, GcClass*, const uint8_t*, size_t) = 0; }; #define NAME(x) Target##x diff --git a/src/avian/jnienv.h b/src/avian/jnienv.h index b6e5967d3b..9947ade6ec 100644 --- a/src/avian/jnienv.h +++ b/src/avian/jnienv.h @@ -20,6 +20,7 @@ #define EMBED_PREFIX_PROPERTY "avian.embed.prefix" #define CLASSPATH_PROPERTY "java.class.path" #define JAVA_HOME_PROPERTY "java.home" +#define REENTRANT_PROPERTY "avian.reentrant" #define BOOTCLASSPATH_PREPEND_OPTION "bootclasspath/p" #define BOOTCLASSPATH_OPTION "bootclasspath" #define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" diff --git a/src/avian/machine.h b/src/avian/machine.h index 9f5654303d..40b627cdbc 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -198,6 +198,14 @@ const unsigned ConstructorFlag = 1 << 1; #define JNI_VERSION_1_6 0x00010006 #endif +#ifndef JNI_TRUE +#define JNI_TRUE 1 +#endif + +#ifndef JNI_OK +#define JNI_OK 0 +#endif + typedef Machine JavaVM; typedef Thread JNIEnv; @@ -207,6 +215,19 @@ struct JNINativeMethod { void* function; }; +struct JavaVMOption { + char* optionString; + void* extraInfo; +}; + +struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption* options; + jboolean ignoreUnrecognized; +}; + struct JavaVMVTable { void* reserved0; void* reserved1; @@ -3737,10 +3758,10 @@ void populateMultiArray(Thread* t, GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false); -object defineClass(Thread* t, - GcClassLoader* loader, - const uint8_t* buffer, - unsigned length); +GcClass* defineClass(Thread* t, + GcClassLoader* loader, + const uint8_t* buffer, + unsigned length); inline GcMethod* methodClone(Thread* t, GcMethod* method) { diff --git a/src/avian/processor.h b/src/avian/processor.h index 56db019ace..7e9df08ae3 100644 --- a/src/avian/processor.h +++ b/src/avian/processor.h @@ -170,7 +170,8 @@ class Processor { GcTriple** calls, avian::codegen::DelayedPromise** addresses, GcMethod* method, - OffsetResolver* resolver) = 0; + OffsetResolver* resolver, + Machine* hostVM) = 0; virtual void visitRoots(Thread* t, HeapWalker* w) = 0; diff --git a/src/builtin.cpp b/src/builtin.cpp index 8cecd382ad..46b82f4748 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -183,6 +183,12 @@ extern "C" AVIAN_EXPORT int64_t JNICALL t->m->classpath->makeString(t, array, offset, length)); } +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_SystemClassLoader_appLoader(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(roots(t)->appLoader()); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_avian_SystemClassLoader_findLoadedVMClass(Thread* t, object, diff --git a/src/compile.cpp b/src/compile.cpp index 74605cc9b5..b9899d3124 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -909,14 +909,16 @@ class BootContext { GcTriple* calls, avian::codegen::DelayedPromise* addresses, Zone* zone, - OffsetResolver* resolver) + OffsetResolver* resolver, + JavaVM* hostVM) : protector(t, this), constants(constants), calls(calls), addresses(addresses), addressSentinal(addresses), zone(zone), - resolver(resolver) + resolver(resolver), + hostVM(hostVM) { } @@ -927,6 +929,7 @@ class BootContext { avian::codegen::DelayedPromise* addressSentinal; Zone* zone; OffsetResolver* resolver; + JavaVM* hostVM; }; class Context { @@ -3991,6 +3994,34 @@ void checkField(Thread* t, GcField* field, bool shouldBeStatic) } } +bool isLambda(Thread* t, + GcClassLoader* loader, + GcCharArray* bootstrapArray, + GcInvocation* invocation) +{ + GcMethod* bootstrap = cast(t, + resolve(t, + loader, + invocation->pool(), + bootstrapArray->body()[0], + findMethodInClass, + GcNoSuchMethodError::Type)); + PROTECT(t, bootstrap); + + return vm::strcmp(reinterpret_cast( + "java/lang/invoke/LambdaMetafactory"), + bootstrap->class_()->name()->body().begin()) == 0 + and vm::strcmp(reinterpret_cast("metafactory"), + bootstrap->name()->body().begin()) == 0 + and vm::strcmp( + reinterpret_cast( + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/" + "String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/" + "MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/" + "invoke/MethodType;)Ljava/lang/invoke/CallSite;"), + bootstrap->spec()->body().begin()) == 0; +} + void compile(MyThread* t, Frame* initialFrame, unsigned initialIp, @@ -5054,36 +5085,168 @@ loop: invocation->setClass(t, context->method->class_()); - unsigned index = addDynamic(t, invocation); + BootContext* bc = context->bootContext; + if (bc) { + // When we're AOT-compiling an application, we can't handle + // invokedynamic in general, since it usually implies runtime + // code generation. However, Java 8 lambda expressions are a + // special case for which we can generate code ahead of time. + // + // The only tricky part about it is that the class synthesis + // code resides in LambdaMetaFactory, which means we need to + // call out to a separate Java VM to execute it (the VM we're + // currently executing in won't work because it only knows how + // to compile code for the target machine, which might not be + // the same as the host; plus we don't want to pollute the + // runtime heap image with stuff that's only needed at compile + // time). - GcMethod* template_ = invocation->template_(); - unsigned returnCode = template_->returnCode(); - unsigned rSize = resultSize(t, returnCode); - unsigned parameterFootprint = template_->parameterFootprint(); + GcClass* c = context->method->class_(); + PROTECT(t, c); - // TODO: can we allow tailCalls in general? - // e.g. what happens if the call site is later bound to a method that can't be tail called? - // NOTE: calling isTailCall right now would cause an segfault, since - // invocation->template_()->class_() will be null. - // bool tailCall - // = isTailCall(t, code, ip, context->method, invocation->template_()); - bool tailCall = false; + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + PROTECT(t, bootstrapArray); - // todo: do we need to tell the compiler to add a load barrier - // here for VolatileCallSite instances? + if (isLambda(t, c->loader(), bootstrapArray, invocation)) { + JNIEnv* e; + if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0) == 0) { + e->vtable->PushLocalFrame(e, 256); - ir::Value* result = c->stackCall( - c->memory(c->memory(c->threadRegister(), ir::Type::object(), - TARGET_THREAD_DYNAMICTABLE), - ir::Type::object(), index * TargetBytesPerWord), - tailCall ? Compiler::TailJump : 0, frame->trace(0, 0), - operandTypeForFieldCode(t, returnCode), - frame->peekMethodArguments(parameterFootprint)); + jclass lmfClass + = e->vtable->FindClass(e, "java/lang/invoke/LambdaMetafactory"); + jmethodID makeLambda = e->vtable->GetStaticMethodID( + e, + lmfClass, + "makeLambda", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/" + "lang/String;Ljava/lang/String;Ljava/lang/String;I)[B"); - frame->popFootprint(parameterFootprint); + GcReference* reference = cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[2])); + int kind = reference->kind(); - if (rSize) { - frame->pushReturnValue(returnCode, result); + GcMethod* method + = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[2], + findMethodInClass, + GcNoSuchMethodError::Type)); + + jarray lambda = e->vtable->CallStaticObjectMethod( + e, + lmfClass, + makeLambda, + e->vtable->NewStringUTF( + e, + reinterpret_cast( + invocation->template_()->name()->body().begin())), + e->vtable->NewStringUTF( + e, + reinterpret_cast( + invocation->template_()->spec()->body().begin())), + e->vtable->NewStringUTF( + e, + reinterpret_cast( + cast( + t, + singletonObject(t, + invocation->pool(), + bootstrapArray->body()[1])) + ->body() + .begin())), + e->vtable->NewStringUTF( + e, + reinterpret_cast( + method->class_()->name()->body().begin())), + e->vtable->NewStringUTF(e, + reinterpret_cast( + method->name()->body().begin())), + e->vtable->NewStringUTF(e, + reinterpret_cast( + method->spec()->body().begin())), + kind); + + uint8_t* bytes = reinterpret_cast( + e->vtable->GetPrimitiveArrayCritical(e, lambda, 0)); + + GcClass* lambdaClass + = defineClass(t, + roots(t)->appLoader(), + bytes, + e->vtable->GetArrayLength(e, lambda)); + + bc->resolver->addClass( + t, lambdaClass, bytes, e->vtable->GetArrayLength(e, lambda)); + + e->vtable->ReleasePrimitiveArrayCritical(e, lambda, bytes, 0); + + e->vtable->PopLocalFrame(e, 0); + + THREAD_RUNTIME_ARRAY( + t, char, spec, invocation->template_()->spec()->length()); + memcpy(RUNTIME_ARRAY_BODY(spec), + invocation->template_()->spec()->body().begin(), + invocation->template_()->spec()->length()); + + GcMethod* target = resolveMethod( + t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec)); + + bool tailCall = isTailCall(t, code, ip, context->method, target); + compileDirectInvoke(t, frame, target, tailCall); + } else { + throwNew( + t, GcVirtualMachineError::Type, "unable to attach to host VM"); + } + } else { + throwNew(t, + GcVirtualMachineError::Type, + "invokedynamic not supported for AOT-compiled code except " + "in the case of lambda expressions"); + } + } else { + unsigned index = addDynamic(t, invocation); + + GcMethod* template_ = invocation->template_(); + unsigned returnCode = template_->returnCode(); + unsigned rSize = resultSize(t, returnCode); + unsigned parameterFootprint = template_->parameterFootprint(); + + // TODO: can we allow tailCalls in general? + // e.g. what happens if the call site is later bound to a method that + // can't be tail called? + // NOTE: calling isTailCall right now would cause an segfault, since + // invocation->template_()->class_() will be null. + // bool tailCall + // = isTailCall(t, code, ip, context->method, + // invocation->template_()); + bool tailCall = false; + + // todo: do we need to tell the compiler to add a load barrier + // here for VolatileCallSite instances? + + ir::Value* result + = c->stackCall(c->memory(c->memory(c->threadRegister(), + ir::Type::object(), + TARGET_THREAD_DYNAMICTABLE), + ir::Type::object(), + index * TargetBytesPerWord), + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0), + operandTypeForFieldCode(t, returnCode), + frame->peekMethodArguments(parameterFootprint)); + + frame->popFootprint(parameterFootprint); + + if (rSize) { + frame->pushReturnValue(returnCode, result); + } } } break; @@ -9134,10 +9297,12 @@ class MyProcessor : public Processor { GcTriple** calls, avian::codegen::DelayedPromise** addresses, GcMethod* method, - OffsetResolver* resolver) + OffsetResolver* resolver, + JavaVM* hostVM) { MyThread* t = static_cast(vmt); - BootContext bootContext(t, *constants, *calls, *addresses, zone, resolver); + BootContext bootContext( + t, *constants, *calls, *addresses, zone, resolver, hostVM); compile(t, &codeAllocator, &bootContext, method); diff --git a/src/interpret.cpp b/src/interpret.cpp index 67871b40d8..7dac9262b7 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -3512,7 +3512,8 @@ class MyProcessor : public Processor { GcTriple**, avian::codegen::DelayedPromise**, GcMethod*, - OffsetResolver*) + OffsetResolver*, + JavaVM*) { abort(s); } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index fdfe120347..82eb3d3169 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3617,6 +3617,7 @@ extern "C" AVIAN_EXPORT jint JNICALL const char* bootLibraries = 0; const char* classpath = 0; const char* javaHome = AVIAN_JAVA_HOME; + bool reentrant = false; const char* embedPrefix = AVIAN_EMBED_PREFIX; const char* bootClasspathPrepend = ""; const char* bootClasspath = 0; @@ -3667,6 +3668,9 @@ extern "C" AVIAN_EXPORT jint JNICALL } else if (strncmp(p, JAVA_HOME_PROPERTY "=", sizeof(JAVA_HOME_PROPERTY)) == 0) { javaHome = p + sizeof(JAVA_HOME_PROPERTY); + } else if (strncmp(p, REENTRANT_PROPERTY "=", sizeof(REENTRANT_PROPERTY)) + == 0) { + reentrant = strcmp(p + sizeof(REENTRANT_PROPERTY), "true") == 0; } else if (strncmp(p, EMBED_PREFIX_PROPERTY "=", sizeof(EMBED_PREFIX_PROPERTY)) == 0) { @@ -3689,7 +3693,7 @@ extern "C" AVIAN_EXPORT jint JNICALL ++propertyCount; } - System* s = makeSystem(); + System* s = makeSystem(reentrant); Heap* h = makeHeap(s, heapLimit); Classpath* c = makeClasspath(s, h, javaHome, embedPrefix); diff --git a/src/machine.cpp b/src/machine.cpp index 87cdf5f626..4fbae697eb 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -5889,14 +5889,14 @@ GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke) return v.method; } -object defineClass(Thread* t, - GcClassLoader* loader, - const uint8_t* buffer, - unsigned length) +GcClass* defineClass(Thread* t, + GcClassLoader* loader, + const uint8_t* buffer, + unsigned length) { PROTECT(t, loader); - object c = parseClass(t, loader, buffer, length); + GcClass* c = parseClass(t, loader, buffer, length); // char name[byteArrayLength(t, className(t, c))]; // memcpy(name, &byteArrayBody(t, className(t, c), 0), @@ -5915,7 +5915,7 @@ object defineClass(Thread* t, PROTECT(t, c); - saveLoadedClass(t, loader, cast(t, c)); + saveLoadedClass(t, loader, c); return c; } diff --git a/src/system/posix.cpp b/src/system/posix.cpp index 256f479bee..64a667887c 100644 --- a/src/system/posix.cpp +++ b/src/system/posix.cpp @@ -92,7 +92,7 @@ const int signals[] = {VisitSignal, InterruptSignal, PipeSignal}; const unsigned SignalCount = 3; class MySystem; -MySystem* system; +MySystem* globalSystem; void handleSignal(int signal, siginfo_t* info, void* context); @@ -624,16 +624,18 @@ class MySystem : public System { System::Library* next_; }; - MySystem() : threadVisitor(0), visitTarget(0) + MySystem(bool reentrant) : reentrant(reentrant), threadVisitor(0), visitTarget(0) { - expect(this, system == 0); - system = this; + if (not reentrant) { + expect(this, globalSystem == 0); + globalSystem = this; - expect(this, registerHandler(InterruptSignalIndex)); - expect(this, registerHandler(VisitSignalIndex)); - expect(this, registerHandler(PipeSignalIndex)); - - expect(this, make(&visitLock) == 0); + expect(this, registerHandler(InterruptSignalIndex)); + expect(this, registerHandler(VisitSignalIndex)); + expect(this, registerHandler(PipeSignalIndex)); + + expect(this, make(&visitLock) == 0); + } } // Returns true on success, false on failure @@ -708,6 +710,8 @@ class MySystem : public System { System::Thread* sTarget, ThreadVisitor* visitor) { + expect(this, not reentrant); + assertT(this, st != sTarget); Thread* target = static_cast(sTarget); @@ -767,7 +771,7 @@ class MySystem : public System { threadVisitor = 0; - system->visitLock->notifyAll(t); + globalSystem->visitLock->notifyAll(t); return result; #endif // not __APPLE__ @@ -938,18 +942,21 @@ class MySystem : public System { virtual void dispose() { - visitLock->dispose(); + if (not reentrant) { + visitLock->dispose(); - expect(this, unregisterHandler(InterruptSignalIndex)); - expect(this, unregisterHandler(VisitSignalIndex)); - expect(this, unregisterHandler(PipeSignalIndex)); - system = 0; + expect(this, unregisterHandler(InterruptSignalIndex)); + expect(this, unregisterHandler(VisitSignalIndex)); + expect(this, unregisterHandler(PipeSignalIndex)); + globalSystem = 0; + } ::free(this); } struct sigaction oldHandlers[SignalCount]; + bool reentrant; ThreadVisitor* threadVisitor; Thread* visitTarget; System::Monitor* visitLock; @@ -965,13 +972,13 @@ void handleSignal(int signal, siginfo_t*, void* context) switch (signal) { case VisitSignal: { - system->threadVisitor->visit(ip, stack, link); + globalSystem->threadVisitor->visit(ip, stack, link); - System::Thread* t = system->visitTarget; - system->visitTarget = 0; + System::Thread* t = globalSystem->visitTarget; + globalSystem->visitTarget = 0; - ACQUIRE_MONITOR(t, system->visitLock); - system->visitLock->notifyAll(t); + ACQUIRE_MONITOR(t, globalSystem->visitLock); + globalSystem->visitLock->notifyAll(t); } break; case InterruptSignal: @@ -987,9 +994,9 @@ void handleSignal(int signal, siginfo_t*, void* context) namespace vm { -AVIAN_EXPORT System* makeSystem() +AVIAN_EXPORT System* makeSystem(bool reentrant) { - return new (malloc(sizeof(MySystem))) MySystem(); + return new (malloc(sizeof(MySystem))) MySystem(reentrant); } } // namespace vm diff --git a/src/system/windows.cpp b/src/system/windows.cpp index 041c20995f..5833c28274 100644 --- a/src/system/windows.cpp +++ b/src/system/windows.cpp @@ -115,7 +115,7 @@ class MutexResource { }; class MySystem; -MySystem* system; +MySystem* globalSystem; DWORD WINAPI run(void* r) { @@ -655,10 +655,12 @@ class MySystem : public System { System::Library* next_; }; - MySystem() + MySystem(bool reentrant): reentrant(reentrant) { - expect(this, system == 0); - system = this; + if (not reentrant) { + expect(this, globalSystem == 0); + globalSystem = this; + } mutex = CreateMutex(0, false, 0); assertT(this, mutex); @@ -1007,21 +1009,25 @@ class MySystem : public System { virtual void dispose() { - system = 0; + if (not reentrant) { + globalSystem = 0; + } + CloseHandle(mutex); ::free(this); } HANDLE mutex; + bool reentrant; }; } // namespace namespace vm { -AVIAN_EXPORT System* makeSystem() +AVIAN_EXPORT System* makeSystem(bool reentrant) { - return new (malloc(sizeof(MySystem))) MySystem(); + return new (malloc(sizeof(MySystem))) MySystem(reentrant); } } // namespace vm diff --git a/src/tools/bootimage-generator/main.cpp b/src/tools/bootimage-generator/main.cpp index 9d5ab08769..1b271a22dd 100644 --- a/src/tools/bootimage-generator/main.cpp +++ b/src/tools/bootimage-generator/main.cpp @@ -306,10 +306,414 @@ unsigned targetFieldOffset(Thread* t, GcHashMap* typeMaps, GcField* field) return offset; } +void addClass(Thread* t, + GcClass* c, + const uint8_t* start, + size_t length, + GcHashMap* typeMaps) +{ + PROTECT(t, c); + PROTECT(t, typeMaps); + + { + class Client : public Stream::Client { + public: + Client(Thread* t) : t(t) + { + } + + virtual void NO_RETURN handleError() + { + abort(t); + } + + private: + Thread* t; + } client(t); + + Stream s(&client, start, length); + + uint32_t magic = s.read4(); + expect(t, magic == 0xCAFEBABE); + s.read2(); // minor version + s.read2(); // major version + + unsigned count = s.read2() - 1; + if (count) { + THREAD_RUNTIME_ARRAY(t, Type, types, count + 2); + RUNTIME_ARRAY_BODY(types)[0] = Type_object; + RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; + + for (unsigned i = 2; i < count + 2; ++i) { + unsigned constType = s.read1(); + switch (constType) { + case CONSTANT_Class: + case CONSTANT_String: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(2); + break; + + case CONSTANT_Integer: + case CONSTANT_Float: + RUNTIME_ARRAY_BODY(types)[i] = Type_int32_t; + s.skip(4); + break; + + case CONSTANT_NameAndType: + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(4); + break; + + case CONSTANT_Long: + RUNTIME_ARRAY_BODY(types)[i++] = Type_int64_t; + RUNTIME_ARRAY_BODY(types)[i] = Type_int64_t_pad; + s.skip(8); + break; + + case CONSTANT_Double: + RUNTIME_ARRAY_BODY(types)[i++] = Type_double; + RUNTIME_ARRAY_BODY(types)[i] = Type_double_pad; + s.skip(8); + break; + + case CONSTANT_Utf8: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(s.read2()); + break; + + case CONSTANT_MethodHandle: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(3); + break; + + case CONSTANT_MethodType: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(2); + break; + + case CONSTANT_InvokeDynamic: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(4); + break; + + default: + fprintf(stderr, "unknown class constant: %d\n", constType); + abort(t); + } + } + + GcByteArray* array + = makeByteArray(t, TypeMap::sizeInBytes(count + 2, count + 2)); + + TypeMap* map = new (array->body().begin()) + TypeMap(count + 2, count + 2, count + 2, TypeMap::PoolKind); + + for (unsigned i = 0; i < count + 2; ++i) { + expect(t, i < map->buildFixedSizeInWords); + + map->targetFixedOffsets()[i * BytesPerWord] = i * TargetBytesPerWord; + + init(new (map->fixedFields() + i) Field, + RUNTIME_ARRAY_BODY(types)[i], + i* BytesPerWord, + BytesPerWord, + i* TargetBytesPerWord, + TargetBytesPerWord); + } + + hashMapInsert( + t, + typeMaps, + reinterpret_cast(hashMapFind(t, + roots(t)->poolMap(), + reinterpret_cast(c), + objectHash, + objectEqual)), + reinterpret_cast(array), + objectHash); + } + } + + { + GcByteArray* array = 0; + PROTECT(t, array); + + unsigned count = 0; + GcVector* fields = allFields(t, typeMaps, c, &count, &array); + PROTECT(t, fields); + + THREAD_RUNTIME_ARRAY(t, Field, memberFields, count + 1); + + unsigned memberIndex; + unsigned buildMemberOffset; + unsigned targetMemberOffset; + + if (array) { + memberIndex = 0; + buildMemberOffset = 0; + targetMemberOffset = 0; + + TypeMap* map = reinterpret_cast(array->body().begin()); + + for (unsigned j = 0; j < map->fixedFieldCount; ++j) { + Field* f = map->fixedFields() + j; + + RUNTIME_ARRAY_BODY(memberFields)[memberIndex] = *f; + + targetMemberOffset = f->targetOffset + f->targetSize; + + ++memberIndex; + } + } else { + init(new (RUNTIME_ARRAY_BODY(memberFields)) Field, + Type_object, + 0, + BytesPerWord, + 0, + TargetBytesPerWord); + + memberIndex = 1; + buildMemberOffset = BytesPerWord; + targetMemberOffset = TargetBytesPerWord; + } + + const unsigned StaticHeader = 3; + + THREAD_RUNTIME_ARRAY(t, Field, staticFields, count + StaticHeader); + + init(new (RUNTIME_ARRAY_BODY(staticFields)) Field, + Type_object, + 0, + BytesPerWord, + 0, + TargetBytesPerWord); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + 1) Field, + Type_intptr_t, + BytesPerWord, + BytesPerWord, + TargetBytesPerWord, + TargetBytesPerWord); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + 2) Field, + Type_object, + BytesPerWord * 2, + BytesPerWord, + TargetBytesPerWord * 2, + TargetBytesPerWord); + + unsigned staticIndex = StaticHeader; + unsigned buildStaticOffset = BytesPerWord * StaticHeader; + unsigned targetStaticOffset = TargetBytesPerWord * StaticHeader; + + for (unsigned i = 0; i < fields->size(); ++i) { + GcField* field = cast(t, fields->body()[i]); + if (field) { + unsigned buildSize = fieldSize(t, field->code()); + unsigned targetSize = buildSize; + + Type type; + switch (field->code()) { + case ObjectField: + type = Type_object; + targetSize = TargetBytesPerWord; + break; + + case ByteField: + case BooleanField: + type = Type_int8_t; + break; + + case CharField: + case ShortField: + type = Type_int8_t; + break; + + case FloatField: + case IntField: + type = Type_int32_t; + break; + + case LongField: + case DoubleField: + type = Type_int64_t; + break; + + default: + abort(t); + } + + if (field->flags() & ACC_STATIC) { + targetStaticOffset = pad(targetStaticOffset, targetSize); + + buildStaticOffset = field->offset(); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + staticIndex) Field, + type, + buildStaticOffset, + buildSize, + targetStaticOffset, + targetSize); + + targetStaticOffset += targetSize; + + ++staticIndex; + } else { + targetMemberOffset = pad(targetMemberOffset, targetSize); + + buildMemberOffset = field->offset(); + + init(new (RUNTIME_ARRAY_BODY(memberFields) + memberIndex) Field, + type, + buildMemberOffset, + buildSize, + targetMemberOffset, + targetSize); + + targetMemberOffset += targetSize; + + ++memberIndex; + } + } else { + targetMemberOffset = pad(targetMemberOffset, TargetBytesPerWord); + } + } + + if (hashMapFind( + t, typeMaps, reinterpret_cast(c), objectHash, objectEqual) + == 0) { + GcByteArray* array = makeByteArray( + t, + TypeMap::sizeInBytes(ceilingDivide(c->fixedSize(), BytesPerWord), + memberIndex)); + + TypeMap* map = new (array->body().begin()) + TypeMap(ceilingDivide(c->fixedSize(), BytesPerWord), + ceilingDivide(targetMemberOffset, TargetBytesPerWord), + memberIndex); + + for (unsigned i = 0; i < memberIndex; ++i) { + Field* f = RUNTIME_ARRAY_BODY(memberFields) + i; + + expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); + + map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; + + map->fixedFields()[i] = *f; + } + + hashMapInsert(t, + typeMaps, + reinterpret_cast(c), + reinterpret_cast(array), + objectHash); + } + + if (c->staticTable()) { + GcByteArray* array = makeByteArray( + t, + TypeMap::sizeInBytes(singletonCount(t, c->staticTable()) + 2, + staticIndex)); + + TypeMap* map = new (array->body().begin()) + TypeMap(singletonCount(t, c->staticTable()) + 2, + ceilingDivide(targetStaticOffset, TargetBytesPerWord), + staticIndex, + TypeMap::SingletonKind); + + for (unsigned i = 0; i < staticIndex; ++i) { + Field* f = RUNTIME_ARRAY_BODY(staticFields) + i; + + expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); + + map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; + + map->fixedFields()[i] = *f; + } + + hashMapInsert(t, + typeMaps, + reinterpret_cast(c->staticTable()), + reinterpret_cast(array), + objectHash); + } + } +} + +void compileMethods(Thread* t, + GcClass* c, + Zone* zone, + GcTriple** constants, + GcTriple** calls, + GcPair** methods, + DelayedPromise** addresses, + OffsetResolver* resolver, + JavaVM* hostVM, + const char* methodName, + const char* methodSpec) +{ + PROTECT(t, c); + + if (GcArray* mtable = cast(t, c->methodTable())) { + PROTECT(t, mtable); + for (unsigned i = 0; i < mtable->length(); ++i) { + GcMethod* method = cast(t, mtable->body()[i]); + if (((methodName == 0 + or ::strcmp(reinterpret_cast(method->name()->body().begin()), + methodName) == 0) + and (methodSpec == 0 + or ::strcmp( + reinterpret_cast(method->spec()->body().begin()), + methodSpec) == 0))) { + if (method->code() or (method->flags() & ACC_NATIVE)) { + PROTECT(t, method); + + t->m->processor->compileMethod( + t, zone, constants, calls, addresses, method, resolver, hostVM); + + if (method->code()) { + *methods = makePair(t, + reinterpret_cast(method), + reinterpret_cast(*methods)); + } + } + + GcMethodAddendum* addendum = method->addendum(); + if (addendum and addendum->exceptionTable()) { + PROTECT(t, addendum); + GcShortArray* exceptionTable + = cast(t, addendum->exceptionTable()); + PROTECT(t, exceptionTable); + + // resolve exception types now to avoid trying to update + // immutable references at runtime + for (unsigned i = 0; i < exceptionTable->length(); ++i) { + uint16_t index = exceptionTable->body()[i] - 1; + + object o = singletonObject(t, addendum->pool(), index); + + if (objectClass(t, o) == type(t, GcReference::Type)) { + o = reinterpret_cast(resolveClass( + t, roots(t)->bootLoader(), cast(t, o)->name())); + + addendum->pool()->setBodyElement( + t, index, reinterpret_cast(o)); + } + } + } + } + } + } +} + GcTriple* makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, + JavaVM* hostVM, const char* className, const char* methodName, const char* methodSpec, @@ -319,20 +723,13 @@ GcTriple* makeCodeImage(Thread* t, t->m->classpath->interceptMethods(t); - GcTriple* constants = 0; - PROTECT(t, constants); - - GcTriple* calls = 0; - PROTECT(t, calls); - - GcPair* methods = 0; - PROTECT(t, methods); - - DelayedPromise* addresses = 0; + GcPair* classes = 0; + PROTECT(t, classes); class MyOffsetResolver : public OffsetResolver { public: - MyOffsetResolver(GcHashMap** typeMaps) : typeMaps(typeMaps) + MyOffsetResolver(GcHashMap** typeMaps, GcPair** classes) + : typeMaps(typeMaps), classes(classes) { } @@ -341,8 +738,22 @@ GcTriple* makeCodeImage(Thread* t, return targetFieldOffset(t, *typeMaps, field); } + virtual void addClass(Thread* t, + GcClass* c, + const uint8_t* start, + size_t length) + { + PROTECT(t, c); + + *classes = makePair( + t, reinterpret_cast(c), reinterpret_cast(*classes)); + + return ::addClass(t, c, start, length, *typeMaps); + } + GcHashMap** typeMaps; - } resolver(&typeMaps); + GcPair** classes; + } resolver(&typeMaps, &classes); Finder* finder = static_cast( roots(t)->bootLoader()->as(t)->finder()); @@ -356,354 +767,32 @@ GcTriple* makeCodeImage(Thread* t, if (false) { fprintf(stderr, "pass 1 %.*s\n", (int)nameSize - 6, name); } + GcClass* c = resolveSystemClass(t, roots(t)->bootLoader(), makeByteArray(t, "%.*s", nameSize - 6, name), true); - PROTECT(t, c); - System::Region* region = finder->find(name); - { - THREAD_RESOURCE(t, System::Region*, region, region->dispose()); + THREAD_RESOURCE(t, System::Region*, region, region->dispose()); - class Client : public Stream::Client { - public: - Client(Thread* t) : t(t) - { - } - - virtual void NO_RETURN handleError() - { - abort(t); - } - - private: - Thread* t; - } client(t); - - Stream s(&client, region->start(), region->length()); - - uint32_t magic = s.read4(); - expect(t, magic == 0xCAFEBABE); - s.read2(); // minor version - s.read2(); // major version - - unsigned count = s.read2() - 1; - if (count) { - THREAD_RUNTIME_ARRAY(t, Type, types, count + 2); - RUNTIME_ARRAY_BODY(types)[0] = Type_object; - RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; - - for (unsigned i = 2; i < count + 2; ++i) { - unsigned constType = s.read1(); - switch (constType) { - case CONSTANT_Class: - case CONSTANT_String: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(2); - break; - - case CONSTANT_Integer: - case CONSTANT_Float: - RUNTIME_ARRAY_BODY(types)[i] = Type_int32_t; - s.skip(4); - break; - - case CONSTANT_NameAndType: - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(4); - break; - - case CONSTANT_Long: - RUNTIME_ARRAY_BODY(types)[i++] = Type_int64_t; - RUNTIME_ARRAY_BODY(types)[i] = Type_int64_t_pad; - s.skip(8); - break; - - case CONSTANT_Double: - RUNTIME_ARRAY_BODY(types)[i++] = Type_double; - RUNTIME_ARRAY_BODY(types)[i] = Type_double_pad; - s.skip(8); - break; - - case CONSTANT_Utf8: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(s.read2()); - break; - - - - case CONSTANT_MethodHandle: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(3); - break; - - case CONSTANT_MethodType: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(2); - break; - - case CONSTANT_InvokeDynamic: - RUNTIME_ARRAY_BODY(types)[i] = Type_object; - s.skip(4); - break; - - default: - fprintf(stderr, "unknown class constant: %d\n", constType); - abort(t); - } - } - - GcByteArray* array - = makeByteArray(t, TypeMap::sizeInBytes(count + 2, count + 2)); - - TypeMap* map = new (array->body().begin()) - TypeMap(count + 2, count + 2, count + 2, TypeMap::PoolKind); - - for (unsigned i = 0; i < count + 2; ++i) { - expect(t, i < map->buildFixedSizeInWords); - - map->targetFixedOffsets()[i * BytesPerWord] = i - * TargetBytesPerWord; - - init(new (map->fixedFields() + i) Field, - RUNTIME_ARRAY_BODY(types)[i], - i* BytesPerWord, - BytesPerWord, - i* TargetBytesPerWord, - TargetBytesPerWord); - } - - hashMapInsert( - t, - typeMaps, - reinterpret_cast(hashMapFind(t, - roots(t)->poolMap(), - reinterpret_cast(c), - objectHash, - objectEqual)), - reinterpret_cast(array), - objectHash); - } - } - - { - GcByteArray* array = 0; - PROTECT(t, array); - - unsigned count = 0; - GcVector* fields = allFields(t, typeMaps, c, &count, &array); - PROTECT(t, fields); - - THREAD_RUNTIME_ARRAY(t, Field, memberFields, count + 1); - - unsigned memberIndex; - unsigned buildMemberOffset; - unsigned targetMemberOffset; - - if (array) { - memberIndex = 0; - buildMemberOffset = 0; - targetMemberOffset = 0; - - TypeMap* map = reinterpret_cast(array->body().begin()); - - for (unsigned j = 0; j < map->fixedFieldCount; ++j) { - Field* f = map->fixedFields() + j; - - RUNTIME_ARRAY_BODY(memberFields)[memberIndex] = *f; - - targetMemberOffset = f->targetOffset + f->targetSize; - - ++memberIndex; - } - } else { - init(new (RUNTIME_ARRAY_BODY(memberFields)) Field, - Type_object, - 0, - BytesPerWord, - 0, - TargetBytesPerWord); - - memberIndex = 1; - buildMemberOffset = BytesPerWord; - targetMemberOffset = TargetBytesPerWord; - } - - const unsigned StaticHeader = 3; - - THREAD_RUNTIME_ARRAY(t, Field, staticFields, count + StaticHeader); - - init(new (RUNTIME_ARRAY_BODY(staticFields)) Field, - Type_object, - 0, - BytesPerWord, - 0, - TargetBytesPerWord); - - init(new (RUNTIME_ARRAY_BODY(staticFields) + 1) Field, - Type_intptr_t, - BytesPerWord, - BytesPerWord, - TargetBytesPerWord, - TargetBytesPerWord); - - init(new (RUNTIME_ARRAY_BODY(staticFields) + 2) Field, - Type_object, - BytesPerWord * 2, - BytesPerWord, - TargetBytesPerWord * 2, - TargetBytesPerWord); - - unsigned staticIndex = StaticHeader; - unsigned buildStaticOffset = BytesPerWord * StaticHeader; - unsigned targetStaticOffset = TargetBytesPerWord * StaticHeader; - - for (unsigned i = 0; i < fields->size(); ++i) { - GcField* field = cast(t, fields->body()[i]); - if (field) { - unsigned buildSize = fieldSize(t, field->code()); - unsigned targetSize = buildSize; - - Type type; - switch (field->code()) { - case ObjectField: - type = Type_object; - targetSize = TargetBytesPerWord; - break; - - case ByteField: - case BooleanField: - type = Type_int8_t; - break; - - case CharField: - case ShortField: - type = Type_int8_t; - break; - - case FloatField: - case IntField: - type = Type_int32_t; - break; - - case LongField: - case DoubleField: - type = Type_int64_t; - break; - - default: - abort(t); - } - - if (field->flags() & ACC_STATIC) { - targetStaticOffset = pad(targetStaticOffset, targetSize); - - buildStaticOffset = field->offset(); - - init(new (RUNTIME_ARRAY_BODY(staticFields) + staticIndex) Field, - type, - buildStaticOffset, - buildSize, - targetStaticOffset, - targetSize); - - targetStaticOffset += targetSize; - - ++staticIndex; - } else { - targetMemberOffset = pad(targetMemberOffset, targetSize); - - buildMemberOffset = field->offset(); - - init(new (RUNTIME_ARRAY_BODY(memberFields) + memberIndex) Field, - type, - buildMemberOffset, - buildSize, - targetMemberOffset, - targetSize); - - targetMemberOffset += targetSize; - - ++memberIndex; - } - } else { - targetMemberOffset = pad(targetMemberOffset, TargetBytesPerWord); - } - } - - if (hashMapFind(t, - typeMaps, - reinterpret_cast(c), - objectHash, - objectEqual) == 0) { - GcByteArray* array = makeByteArray( - t, - TypeMap::sizeInBytes(ceilingDivide(c->fixedSize(), BytesPerWord), - memberIndex)); - - TypeMap* map = new (array->body().begin()) - TypeMap(ceilingDivide(c->fixedSize(), BytesPerWord), - ceilingDivide(targetMemberOffset, TargetBytesPerWord), - memberIndex); - - for (unsigned i = 0; i < memberIndex; ++i) { - Field* f = RUNTIME_ARRAY_BODY(memberFields) + i; - - expect(t, - f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); - - map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; - - map->fixedFields()[i] = *f; - } - - hashMapInsert(t, - typeMaps, - reinterpret_cast(c), - reinterpret_cast(array), - objectHash); - } - - if (c->staticTable()) { - GcByteArray* array = makeByteArray( - t, - TypeMap::sizeInBytes(singletonCount(t, c->staticTable()) + 2, - staticIndex)); - - TypeMap* map = new (array->body().begin()) - TypeMap(singletonCount(t, c->staticTable()) + 2, - ceilingDivide(targetStaticOffset, TargetBytesPerWord), - staticIndex, - TypeMap::SingletonKind); - - for (unsigned i = 0; i < staticIndex; ++i) { - Field* f = RUNTIME_ARRAY_BODY(staticFields) + i; - - expect(t, - f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); - - map->targetFixedOffsets()[f->buildOffset] = f->targetOffset; - - map->fixedFields()[i] = *f; - } - - hashMapInsert(t, - typeMaps, - reinterpret_cast(c->staticTable()), - reinterpret_cast(array), - objectHash); - } - } + addClass(t, c, region->start(), region->length(), typeMaps); } } + GcTriple* constants = 0; + PROTECT(t, constants); + + GcTriple* calls = 0; + PROTECT(t, calls); + + GcPair* methods = 0; + PROTECT(t, methods); + + DelayedPromise* addresses = 0; + for (Finder::Iterator it(finder); it.hasMore();) { size_t nameSize = 0; const char* name = it.next(&nameSize); @@ -713,73 +802,39 @@ GcTriple* makeCodeImage(Thread* t, if (false) { fprintf(stderr, "pass 2 %.*s\n", (int)nameSize - 6, name); } - GcClass* c = 0; - PROTECT(t, c); - c = resolveSystemClass(t, - roots(t)->bootLoader(), - makeByteArray(t, "%.*s", nameSize - 6, name), - true); + GcClass* c + = resolveSystemClass(t, + roots(t)->bootLoader(), + makeByteArray(t, "%.*s", nameSize - 6, name), + true); - if (GcArray* mtable = cast(t, c->methodTable())) { - PROTECT(t, mtable); - for (unsigned i = 0; i < mtable->length(); ++i) { - GcMethod* method = cast(t, mtable->body()[i]); - if (((methodName == 0 - or ::strcmp( - reinterpret_cast(method->name()->body().begin()), - methodName) == 0) - and (methodSpec == 0 - or ::strcmp(reinterpret_cast( - method->spec()->body().begin()), - methodSpec) == 0))) { - if (method->code() or (method->flags() & ACC_NATIVE)) { - PROTECT(t, method); + classes = makePair( + t, reinterpret_cast(c), reinterpret_cast(classes)); + } + } - t->m->processor->compileMethod( - t, - zone, - reinterpret_cast(&constants), - reinterpret_cast(&calls), - &addresses, - method, - &resolver); + // Each method compilation may result in the creation of new, + // synthetic classes (e.g. for lambda expressions), so we must + // iterate until we've visited them all: + while (classes) { + GcPair* myClasses = classes; + PROTECT(t, myClasses); - if (method->code()) { - methods = makePair(t, - reinterpret_cast(method), - reinterpret_cast(methods)); - } - } + classes = 0; - GcMethodAddendum* addendum = method->addendum(); - if (addendum and addendum->exceptionTable()) { - PROTECT(t, addendum); - GcShortArray* exceptionTable - = cast(t, addendum->exceptionTable()); - PROTECT(t, exceptionTable); - - // resolve exception types now to avoid trying to update - // immutable references at runtime - for (unsigned i = 0; i < exceptionTable->length(); ++i) { - uint16_t index = exceptionTable->body()[i] - 1; - - object o = singletonObject(t, addendum->pool(), index); - - if (objectClass(t, o) == type(t, GcReference::Type)) { - o = reinterpret_cast( - resolveClass(t, - roots(t)->bootLoader(), - cast(t, o)->name())); - - addendum->pool()->setBodyElement( - t, index, reinterpret_cast(o)); - } - } - } - } - } - } + for (; myClasses; myClasses = cast(t, myClasses->second())) { + compileMethods(t, + cast(t, myClasses->first()), + zone, + &constants, + &calls, + &methods, + &addresses, + &resolver, + hostVM, + methodName, + methodSpec); } } @@ -1436,6 +1491,7 @@ void writeBootImage2(Thread* t, OutputStream* codeOutput, BootImage* image, uint8_t* code, + JavaVM* hostVM, const char* className, const char* methodName, const char* methodSpec, @@ -1656,8 +1712,15 @@ void writeBootImage2(Thread* t, objectHash); } - constants = makeCodeImage( - t, &zone, image, code, className, methodName, methodSpec, typeMaps); + constants = makeCodeImage(t, + &zone, + image, + code, + hostVM, + className, + methodName, + methodSpec, + typeMaps); PROTECT(t, constants); @@ -1927,21 +1990,23 @@ uint64_t writeBootImage(Thread* t, uintptr_t* arguments) OutputStream* codeOutput = reinterpret_cast(arguments[1]); BootImage* image = reinterpret_cast(arguments[2]); uint8_t* code = reinterpret_cast(arguments[3]); - const char* className = reinterpret_cast(arguments[4]); - const char* methodName = reinterpret_cast(arguments[5]); - const char* methodSpec = reinterpret_cast(arguments[6]); + JavaVM* hostVM = reinterpret_cast(arguments[4]); + const char* className = reinterpret_cast(arguments[5]); + const char* methodName = reinterpret_cast(arguments[6]); + const char* methodSpec = reinterpret_cast(arguments[7]); - const char* bootimageStart = reinterpret_cast(arguments[7]); - const char* bootimageEnd = reinterpret_cast(arguments[8]); - const char* codeimageStart = reinterpret_cast(arguments[9]); - const char* codeimageEnd = reinterpret_cast(arguments[10]); - bool useLZMA = arguments[11]; + const char* bootimageStart = reinterpret_cast(arguments[8]); + const char* bootimageEnd = reinterpret_cast(arguments[9]); + const char* codeimageStart = reinterpret_cast(arguments[10]); + const char* codeimageEnd = reinterpret_cast(arguments[11]); + bool useLZMA = arguments[12]; writeBootImage2(t, bootimageOutput, codeOutput, image, code, + hostVM, className, methodName, methodSpec, @@ -1969,6 +2034,8 @@ class Arguments { const char* bootimage; const char* codeimage; + const char* hostvm; + char* entryClass; char* entryMethod; char* entrySpec; @@ -2008,6 +2075,7 @@ class Arguments { Arg classpath(parser, true, "cp", ""); Arg bootimage(parser, true, "bootimage", ""); Arg codeimage(parser, true, "codeimage", ""); + Arg hostvm(parser, false, "hostvm", ""); Arg entry( parser, false, "entry", "[.[]]"); Arg bootimageSymbols(parser, @@ -2028,6 +2096,7 @@ class Arguments { this->classpath = classpath.value; this->bootimage = bootimage.value; this->codeimage = codeimage.value; + this->hostvm = hostvm.value; this->useLZMA = useLZMA.value != 0; if (entry.value) { @@ -2100,6 +2169,7 @@ class Arguments { "classpath = %s\n" "bootimage = %s\n" "codeimage = %s\n" + "hostvm = %s\n" "entryClass = %s\n" "entryMethod = %s\n" "entrySpec = %s\n" @@ -2110,6 +2180,7 @@ class Arguments { classpath, bootimage, codeimage, + hostvm, entryClass, entryMethod, entrySpec, @@ -2168,10 +2239,66 @@ int main(int ac, const char** av) return -1; } + JavaVM* hostVM = 0; + System::Library* hostVMLibrary = 0; + if (args.hostvm) { + if (s->success(s->load(&hostVMLibrary, args.hostvm))) { + typedef jint(JNICALL * CreateVM)(Machine**, Thread**, void*); + const char* name = "JNI_CreateJavaVM"; + CreateVM createVM + = reinterpret_cast(hostVMLibrary->resolve(name)); + + if (createVM) { + JavaVMInitArgs vmArgs; + vmArgs.version = JNI_VERSION_1_6; + vmArgs.nOptions = 2; + vmArgs.ignoreUnrecognized = JNI_TRUE; + +#define CLASSPATH_PROPERTY "-Xbootclasspath:" + + const char* classpath = args.classpath; + size_t classpathSize = strlen(classpath); + size_t classpathPropertyBufferSize = sizeof(CLASSPATH_PROPERTY) + + classpathSize; + + RUNTIME_ARRAY( + char, classpathPropertyBuffer, classpathPropertyBufferSize); + memcpy(RUNTIME_ARRAY_BODY(classpathPropertyBuffer), + CLASSPATH_PROPERTY, + sizeof(CLASSPATH_PROPERTY) - 1); + memcpy(RUNTIME_ARRAY_BODY(classpathPropertyBuffer) + + sizeof(CLASSPATH_PROPERTY) - 1, + classpath, + classpathSize + 1); + + JavaVMOption options[2]; + options[0].optionString = RUNTIME_ARRAY_BODY(classpathPropertyBuffer); + options[1].optionString = const_cast("-Davian.reentrant=true"); + + vmArgs.options = options; + + Thread* dummy; + if (JNI_OK != createVM(&hostVM, &dummy, &vmArgs)) { + fprintf(stderr, "unable to initialize host VM\n"); + hostVMLibrary->disposeAll(); + return -1; + } + } else { + fprintf(stderr, "unable to find %s in %s\n", name, args.hostvm); + hostVMLibrary->disposeAll(); + return -1; + } + } else { + fprintf(stderr, "unable to open %s\n", args.hostvm); + return -1; + } + } + uintptr_t arguments[] = {reinterpret_cast(&bootimageOutput), reinterpret_cast(&codeOutput), reinterpret_cast(&image), reinterpret_cast(code.begin()), + reinterpret_cast(hostVM), reinterpret_cast(args.entryClass), reinterpret_cast(args.entryMethod), reinterpret_cast(args.entrySpec), @@ -2183,6 +2310,11 @@ int main(int ac, const char** av) run(t, writeBootImage, arguments); + if (hostVM) { + hostVM->vtable->DestroyJavaVM(hostVM); + hostVMLibrary->disposeAll(); + } + if (t->exception) { printTrace(t, t->exception); return -1; diff --git a/test/BufferedInputStreamTest.java b/test/BufferedInputStreamTest.java new file mode 100644 index 0000000000..ba978cd5ef --- /dev/null +++ b/test/BufferedInputStreamTest.java @@ -0,0 +1,80 @@ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.BufferedInputStream; + +/** + * Checks that BufferedInputStream does not block if data is available in it's internal buffer. + */ +public class BufferedInputStreamTest +{ + public static void main(String[] args) throws IOException + { + MyByteArrayStream in = new MyByteArrayStream(new byte[100]); + + BufferedInputStream bin = new BufferedInputStream(in); + //read a single byte to fill the buffer + int b = bin.read(); + byte[] buf = new byte[10]; + //now try to read 10 bytes. this should a least return the content of the buffer. On OpenJDK this are + //4 bytes (the rest of the buffer returned by MyByteArrayStream in the first call). + //It should definately NOT block. + int count = bin.read(buf); + System.out.println("Read bytes: " + count); + } + + /** + * Internal Stream used to show the BufferedInputStream behaviour. + */ + static class MyByteArrayStream extends ByteArrayInputStream + { + boolean stopReading = false; + + /** + * @param buf + */ + public MyByteArrayStream(byte[] buf) + { + super(buf); + } + + /* (non-Javadoc) + * @see java.io.ByteArrayInputStream#read(byte[], int, int) + */ + @Override + public synchronized int read(byte[] b, int off, int len) + { + if(stopReading == false) + { //On the first call 5 bytes are returned. + stopReading = true; + return super.read(b, off, 5); + } + //on all following calls block. The spec says, that a least one byte is returned, if the + //stream is not at EOF. + while(available() == 0) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + } + } + return 0; + } + + /* (non-Javadoc) + * @see java.io.ByteArrayInputStream#available() + */ + @Override + public synchronized int available() + { + if(stopReading) + { + return 0; + } + return super.available(); + } + } +} diff --git a/test/Misc.java b/test/Misc.java index ec3bd0bd25..42d452a37a 100644 --- a/test/Misc.java +++ b/test/Misc.java @@ -291,9 +291,18 @@ public class Misc { test = true; } } - expect(count == 2); - expect(test); - expect(extraDir); + // This test is only relevant if multi-classpath-test.txt + // actually exists in somewhere under the classpath from which + // Misc.class was loaded. Since we run this test from an + // AOT-compiled boot image as well as straight from the + // filesystem, and the boot image does not contain + // multi-classpath-test.txt, we'll skip the test if it's not + // present. + if (count != 0) { + expect(count == 2); + expect(test); + expect(extraDir); + } } catch (IOException e) { throw new RuntimeException(e); } diff --git a/test/ci.sh b/test/ci.sh index 69c89d273f..c52ca7cf01 100755 --- a/test/ci.sh +++ b/test/ci.sh @@ -97,6 +97,7 @@ else if has_flag openjdk-src || ! has_flag openjdk; then run make ${flags} mode=debug bootimage=true ${make_target} run make ${flags} bootimage=true ${make_target} + run make ${flags} bootimage=true bootimage-test=true ${make_target} fi if ! has_flag openjdk && ! has_flag android && ! has_flag arch; then