From 9411c223af2e527983558d1ec27fec3faef8846b Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 10 Jul 2017 17:01:56 +0100 Subject: [PATCH] Squashed commit of the following: (#15) commit fabf4dd0cca19118c59c925f572a655d1d9c4092 Author: Joel Dice Date: Sun Jul 9 17:36:06 2017 -0600 reserve a little more stack space for worst-case lambda expressions commit 24b95016dd6f62fa4a82312310fcb5c55cc22f93 Author: Joel Dice Date: Sun Jul 9 17:33:39 2017 -0600 support constructor references commit a329416f4f37840d2a5d7976874585413cd4c6af Author: Joel Dice Date: Sun Jul 9 17:11:19 2017 -0600 normalize class name in Classes.forName This fixes a Tomcat classloading issue when using the OpenJDK class library. commit ecafdf40baf2f3cde4835620e9af4380b1081fe7 Author: Joel Dice Date: Sun Jul 9 16:58:04 2017 -0600 fix some lambda bugs commit 552dfd779baf077cc6445b4042588e4b6e34f3d2 Merge: 63dda56 aa2f25d Author: Joel Dice Date: Sat Jul 8 10:57:59 2017 -0600 Merge remote-tracking branch 'corda/chrisr3-invoking-extension' commit 63dda560f8648a793bdb609879076ab729b17e19 Merge: 3ec983d e589f10 Author: Joel Dice Date: Sat Jul 8 10:54:53 2017 -0600 Merge pull request #538 from dicej/master avoid definePackage infinite recursion with OpenJDK class library commit aa2f25da404b2fc168a672392df939b52ea250ab Author: Chris Rankin Date: Mon Jul 3 17:26:41 2017 +0100 Add support for "invokevirtual" and "invokeinterface". The "invokeinterface" support seems to be broken for lambdas that require native types to be boxed/unboxed. commit e589f105741ea36d07bba1e300c7ed69c33a22d1 Author: Joel Dice Date: Sun Jul 2 19:43:36 2017 -0600 avoid definePackage infinite recursion with OpenJDK class library commit 3ec983dd82e01b36fb96411df345a54119a68181 Merge: 6f6bdd7 78881d4 Author: Joel Dice Date: Tue Jun 13 09:50:49 2017 -0600 Merge pull request #534 from lwahlmeier/fileGetParentFile fixed File.getParent when getting parent returns root commit 78881d427cd474c14f5ce94484f5253de564074b Author: Luke Wahlmeier Date: Tue May 30 13:42:56 2017 -0600 fixed File.getParent when getting parent returns root commit 6f6bdd7d5c171e0c4c807d0a4f3aa676461307de Merge: 8b694f9 b22343e Author: Joel Dice Date: Mon May 22 12:58:09 2017 -0600 Merge pull request #529 from mikehearn/proguard-fixes Some fixes to the ProGuard files commit 8b694f961496f2d47a377254f61153b0338335fd Merge: dfae414 61c82e1 Author: Joel Dice Date: Mon May 22 12:57:54 2017 -0600 Merge pull request #531 from mikehearn/fileinputstream-compat FileInputStream has wrapped some more native methods commit dfae414da8da57049076c16c43472cc32bdeb07c Merge: 16dd804 d7a6f68 Author: Joel Dice Date: Mon May 22 12:57:38 2017 -0600 Merge pull request #532 from mikehearn/use-symlinks Symlink OpenJDK files rather than hardlink commit d7a6f68235b2d42c03aba36f1bb48d173adb4fa6 Author: Mike Hearn Date: Sun May 14 21:42:06 2017 +0200 Symlink OpenJDK files rather than hardlink commit 61c82e1070d857bcdc7a4f086d13836d9608da00 Author: Mike Hearn Date: Sun May 14 21:40:52 2017 +0200 FileInputStream has wrapped some more native methods in recent OpenJDK releases commit b22343eb6731f1df2631635e2fcf9d51817273fe Author: Mike Hearn Date: Sun May 14 21:38:09 2017 +0200 Some fixes to the ProGuard files commit 16dd804f392168497fa17ab682978f938e291bfb Merge: e2d3270 19deadd Author: Joel Dice Date: Wed Mar 22 16:00:30 2017 -0600 Merge pull request #526 from MaartenR/arrays-binsearch Added integer array binary search methods to Arrays class commit e2d3270fe8e92203b6107b95f862ff5bd245c37a Merge: dee99d6 545b9c8 Author: Joel Dice Date: Wed Mar 22 16:00:20 2017 -0600 Merge pull request #527 from MaartenR/integer-nolz Added numberOfLeadingZeros method to Integer class commit 545b9c8732a6ea026285b1edf976a79eb541ef2e Author: Maarten Raaphorst Date: Tue Mar 14 11:23:36 2017 +0100 Tested more negative numbers commit b45bcf09535daef99ed31451ae55db8bec83164c Author: Maarten Raaphorst Date: Tue Mar 14 10:47:27 2017 +0100 Added unit test for numberOfLeadingZeros method of the Integer class commit 19deadd36bbfc02a8c13e6d91963e41f5125057f Author: Maarten Raaphorst Date: Mon Mar 13 12:19:17 2017 +0100 Fixed mistake with end index being exclusive commit 7271c0b7077ae3bef473e036aa0f0298a9eb0601 Author: Maarten Raaphorst Date: Fri Mar 10 11:17:13 2017 +0100 Added numberOfLeadingZeros method to Integer class commit 023bb69acd071348f29cd43ff954d3dcf2856c7c Author: Maarten Raaphorst Date: Thu Mar 9 13:43:03 2017 +0100 Added integer array binary search methods to Arrays class commit dee99d6dd60b743a870d1a2c58183649c8a6449c Merge: f7a651d 6c90953 Author: Joel Dice Date: Wed Mar 1 08:39:32 2017 -0700 Merge pull request #525 from MaartenR/master Added functionality to HttpURLConnection for obtaining content length commit 6c90953745be5451ac9a028639f94e3fcc43d7f3 Author: Maarten Raaphorst Date: Wed Mar 1 14:19:51 2017 +0100 Made http headers case insensitive commit 2c3a82d2b387251a8932ddb6575e718c2c1785a7 Author: Maarten Raaphorst Date: Fri Feb 3 05:42:56 2017 -0800 Added functionality to HttpURLConnection for obtaining content length --- sgx-jvm/avian/.gitignore | 1 + sgx-jvm/avian/classpath/avian/Classes.java | 8 +- .../avian/classpath/avian/ConstantPool.java | 37 +++ .../classpath/avian/SystemClassLoader.java | 15 -- .../avian/classpath/avian/http/Handler.java | 34 ++- sgx-jvm/avian/classpath/java/io/File.java | 6 +- .../classpath/java/lang/ClassLoader.java | 32 ++- .../avian/classpath/java/lang/Integer.java | 10 + .../java/lang/invoke/LambdaMetafactory.java | 211 +++++++++++++++++- .../java/lang/invoke/MethodHandle.java | 3 + .../java/lang/invoke/MethodType.java | 22 +- .../classpath/java/net/URLConnection.java | 38 ++++ sgx-jvm/avian/classpath/java/util/Arrays.java | 26 +++ sgx-jvm/avian/makefile | 8 +- sgx-jvm/avian/openjdk.pro | 16 ++ sgx-jvm/avian/src/avian/machine.h | 9 +- sgx-jvm/avian/src/classpath-openjdk.cpp | 44 +++- sgx-jvm/avian/src/machine.cpp | 16 +- sgx-jvm/avian/test/ArraysTest.java | 32 +++ sgx-jvm/avian/test/Files.java | 10 + sgx-jvm/avian/test/Integers.java | 46 ++++ sgx-jvm/avian/test/InvokeDynamic.java | 89 +++++++- sgx-jvm/avian/test/Reflection.java | 19 +- sgx-jvm/avian/vm.pro | 11 +- 24 files changed, 653 insertions(+), 90 deletions(-) diff --git a/sgx-jvm/avian/.gitignore b/sgx-jvm/avian/.gitignore index 989c2222e8..6767701eae 100644 --- a/sgx-jvm/avian/.gitignore +++ b/sgx-jvm/avian/.gitignore @@ -15,3 +15,4 @@ workspace/ src/.cproject /cmake-build /cmake-build +.idea/* diff --git a/sgx-jvm/avian/classpath/avian/Classes.java b/sgx-jvm/avian/classpath/avian/Classes.java index b55408834c..0d414184dc 100644 --- a/sgx-jvm/avian/classpath/avian/Classes.java +++ b/sgx-jvm/avian/classpath/avian/Classes.java @@ -46,7 +46,7 @@ public class Classes { public static native VMClass toVMClass(Class c); public static native VMMethod toVMMethod(Method m); - + private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec) throws ClassNotFoundException; @@ -295,7 +295,7 @@ public class Classes { if (loader == null) { loader = Class.class.getClassLoader(); } - Class c = loader.loadClass(name); + Class c = loader.loadClass(name.replace('/', '.')); VMClass vmc = SystemClassLoader.vmClass(c); link(vmc, loader); if (initialize) { @@ -443,7 +443,7 @@ public class Classes { } return null; } - + public static int findMethod(VMClass vmClass, String name, Class[] parameterTypes) { @@ -599,6 +599,6 @@ public class Classes { private static native void acquireClassLock(); private static native void releaseClassLock(); - + public static native String makeString(byte[] array, int offset, int length); } diff --git a/sgx-jvm/avian/classpath/avian/ConstantPool.java b/sgx-jvm/avian/classpath/avian/ConstantPool.java index f795c19e7e..94a0b5ff5d 100644 --- a/sgx-jvm/avian/classpath/avian/ConstantPool.java +++ b/sgx-jvm/avian/classpath/avian/ConstantPool.java @@ -26,6 +26,7 @@ public class ConstantPool { private static final int CONSTANT_NameAndType = 12; private static final int CONSTANT_Fieldref = 9; private static final int CONSTANT_Methodref = 10; + private static final int CONSTANT_InterfaceMethodref = 11; public static int add(List pool, PoolEntry e) { int i = 0; @@ -85,6 +86,16 @@ public class ConstantPool { addNameAndType(pool, name, spec))); } + public static int addInterfaceMethodRef(List pool, + String interfaceName, + String name, + String spec) + { + return add(pool, new InterfaceMethodRefPoolEntry + (addClass(pool, interfaceName), + addNameAndType(pool, name, spec))); + } + public interface PoolEntry { public void writeTo(OutputStream out) throws IOException; } @@ -239,4 +250,30 @@ public class ConstantPool { } } } + + private static class InterfaceMethodRefPoolEntry implements PoolEntry { + private final int classIndex; + private final int nameAndTypeIndex; + + public InterfaceMethodRefPoolEntry(int classIndex, int nameAndTypeIndex) { + this.classIndex = classIndex; + this.nameAndTypeIndex = nameAndTypeIndex; + } + + public void writeTo(OutputStream out) throws IOException { + write1(out, CONSTANT_InterfaceMethodref); + write2(out, classIndex + 1); + write2(out, nameAndTypeIndex + 1); + } + + public boolean equals(Object o) { + if (o instanceof InterfaceMethodRefPoolEntry) { + InterfaceMethodRefPoolEntry other = (InterfaceMethodRefPoolEntry) o; + return other.classIndex == classIndex + && other.nameAndTypeIndex == nameAndTypeIndex; + } else { + return false; + } + } + } } diff --git a/sgx-jvm/avian/classpath/avian/SystemClassLoader.java b/sgx-jvm/avian/classpath/avian/SystemClassLoader.java index 5dd2ee4352..71c3749f60 100644 --- a/sgx-jvm/avian/classpath/avian/SystemClassLoader.java +++ b/sgx-jvm/avian/classpath/avian/SystemClassLoader.java @@ -76,21 +76,6 @@ public class SystemClassLoader extends ClassLoader { return null; } - protected Package getPackage(String name) { - Package p = super.getPackage(name); - if (p == null) { - String source = getPackageSource(name); - 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); - } - } - - return super.getPackage(name); - } - protected static native String getPackageSource(String name); // OpenJDK's java.lang.ClassLoader.getResource makes use of diff --git a/sgx-jvm/avian/classpath/avian/http/Handler.java b/sgx-jvm/avian/classpath/avian/http/Handler.java index 28f36d4194..03eec5b2c8 100644 --- a/sgx-jvm/avian/classpath/avian/http/Handler.java +++ b/sgx-jvm/avian/classpath/avian/http/Handler.java @@ -23,8 +23,11 @@ import java.net.Socket; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; -import java.util.Map; +import java.util.List; +import java.util.Map; public class Handler extends URLStreamHandler { @@ -35,10 +38,12 @@ public class Handler extends URLStreamHandler class HttpURLConnection extends URLConnection { + private static final String HKEY_CONTENT_LENGTH = "content-length"; + Socket socket; private BufferedWriter writer; private InputStream bin; - private Map header = new HashMap(); + private Map> header = new HashMap>(); private int status; protected HttpURLConnection(URL url) @@ -97,7 +102,12 @@ public class Handler extends URLStreamHandler int i = line.indexOf(':'); if(i > 0) { - header.put(line.substring(0, i), line.substring(i + 1) .trim()); + String key = line.substring(0, i).toLowerCase(); + String value = line.substring(i + 1).trim(); + + List valueList = new ArrayList(); + valueList.add(value); + header.put(key, Collections.unmodifiableList(valueList)); } line = reader.readLine(); } @@ -116,5 +126,23 @@ public class Handler extends URLStreamHandler { throw new UnsupportedOperationException("Can' write to HTTP Connection"); } + + @Override + public int getContentLength() + { + return getHeaderFieldInt(HKEY_CONTENT_LENGTH, -1); + } + + @Override + public long getContentLengthLong() + { + return getHeaderFieldLong(HKEY_CONTENT_LENGTH, -1l); + } + + @Override + public Map> getHeaderFields() + { + return Collections.unmodifiableMap(header); + } } } diff --git a/sgx-jvm/avian/classpath/java/io/File.java b/sgx-jvm/avian/classpath/java/io/File.java index 732f2d050f..10c331498f 100644 --- a/sgx-jvm/avian/classpath/java/io/File.java +++ b/sgx-jvm/avian/classpath/java/io/File.java @@ -85,7 +85,7 @@ public class File implements Serializable { } private static String stripSeparators(String p) { - while (p.endsWith(FileSeparator)) { + while (p.length() > 1 && p.endsWith(FileSeparator)) { p = p.substring(0, p.length() - 1); } return p; @@ -173,8 +173,10 @@ public class File implements Serializable { public String getParent() { int index = path.lastIndexOf(FileSeparator); - if (index >= 0) { + if (index > 0) { return normalize(path.substring(0, index)); + } else if (index == 0) { + return normalize(path.substring(0, FileSeparator.length())); } else { return null; } diff --git a/sgx-jvm/avian/classpath/java/lang/ClassLoader.java b/sgx-jvm/avian/classpath/java/lang/ClassLoader.java index 85404fa924..8cc4329452 100644 --- a/sgx-jvm/avian/classpath/java/lang/ClassLoader.java +++ b/sgx-jvm/avian/classpath/java/lang/ClassLoader.java @@ -45,9 +45,31 @@ public abstract class ClassLoader { } protected Package getPackage(String name) { + Package p; synchronized (this) { - return packages().get(name); + p = packages().get(name); } + + if (parent != null) { + p = parent.getPackage(name); + } else { + // todo: load attributes from JAR manifest if available + p = definePackage(name, null, null, null, null, null, null, null); + } + + if (p != null) { + synchronized (this) { + Package p2 = packages().get(name); + if (p2 != null) { + p = p2; + } else { + packages().put(name, p); + } + } + } + + + return p; } protected Package[] getPackages() { @@ -145,7 +167,7 @@ public abstract class ClassLoader { public final ClassLoader getParent() { return parent; } - + protected URL findResource(String path) { return null; } @@ -183,11 +205,11 @@ public abstract class ClassLoader { public static InputStream getSystemResourceAsStream(String path) { return getSystemClassLoader().getResourceAsStream(path); } - + public static Enumeration getSystemResources(String name) throws IOException { return getSystemClassLoader().getResources(name); } - + public Enumeration getResources(String name) throws IOException { Collection resources = collectResources(name); @@ -209,5 +231,5 @@ public abstract class ClassLoader { static native Class getCaller(); - static native void load(String name, Class caller, boolean mapName); + static native void load(String name, Class caller, boolean mapName); } diff --git a/sgx-jvm/avian/classpath/java/lang/Integer.java b/sgx-jvm/avian/classpath/java/lang/Integer.java index 0dbf20ae21..3f48a91a9f 100644 --- a/sgx-jvm/avian/classpath/java/lang/Integer.java +++ b/sgx-jvm/avian/classpath/java/lang/Integer.java @@ -139,4 +139,14 @@ public final class Integer extends Number implements Comparable { } return new Integer(parseInt(string, 10)); } + + public static int numberOfLeadingZeros(int i) { + // See nlz5 at http://www.hackersdelight.org/hdcodetxt/nlz.c.txt + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return bitCount(~i); + } } diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java b/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java index 4eb5cf771a..269c288a1b 100644 --- a/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/sgx-jvm/avian/classpath/java/lang/invoke/LambdaMetafactory.java @@ -24,7 +24,9 @@ import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; import java.util.ArrayList; +import java.util.Iterator; import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.io.IOException; import avian.Classes; @@ -145,6 +147,127 @@ public class LambdaMetafactory { return result; } + private static void maybeBoxOrUnbox(ByteArrayOutputStream out, + List pool, + MethodType.TypeSpec from, + MethodType.TypeSpec to) + throws IOException + { + if (to.type().isPrimitive()) { + if (! from.type().isPrimitive()) { + write1(out, invokevirtual); + + try { + switch (to.spec()) { + case "Z": + writeMethodReference(out, pool, Classes.toVMMethod + (Boolean.class.getMethod("booleanValue"))); + break; + + case "B": + writeMethodReference(out, pool, Classes.toVMMethod + (Byte.class.getMethod("byteValue"))); + break; + + case "S": + writeMethodReference(out, pool, Classes.toVMMethod + (Short.class.getMethod("shortValue"))); + break; + + case "C": + writeMethodReference(out, pool, Classes.toVMMethod + (Character.class.getMethod("charValue"))); + break; + + case "I": + writeMethodReference(out, pool, Classes.toVMMethod + (Integer.class.getMethod("intValue"))); + break; + + case "F": + writeMethodReference(out, pool, Classes.toVMMethod + (Float.class.getMethod("floatValue"))); + break; + + case "J": + writeMethodReference(out, pool, Classes.toVMMethod + (Long.class.getMethod("longValue"))); + break; + + case "D": + writeMethodReference(out, pool, Classes.toVMMethod + (Double.class.getMethod("doubleValue"))); + break; + + default: + throw new AssertionError("don't know how to auto-unbox to " + to.spec()); + } + } catch (NoSuchMethodException e) { + throw new Error(e); + } + } + } else if (from.type().isPrimitive()) { + write1(out, invokestatic); + + try { + switch (from.spec()) { + case "Z": + writeMethodReference(out, pool, Classes.toVMMethod + (Boolean.class.getMethod + ("valueOf", Boolean.TYPE))); + break; + + case "B": + writeMethodReference(out, pool, Classes.toVMMethod + (Byte.class.getMethod + ("valueOf", Byte.TYPE))); + break; + + case "S": + writeMethodReference(out, pool, Classes.toVMMethod + (Short.class.getMethod + ("valueOf", Short.TYPE))); + break; + + case "C": + writeMethodReference(out, pool, Classes.toVMMethod + (Character.class.getMethod + ("valueOf", Character.TYPE))); + break; + + case "I": + writeMethodReference(out, pool, Classes.toVMMethod + (Integer.class.getMethod + ("valueOf", Integer.TYPE))); + break; + + case "F": + writeMethodReference(out, pool, Classes.toVMMethod + (Float.class.getMethod + ("valueOf", Float.TYPE))); + break; + + case "J": + writeMethodReference(out, pool, Classes.toVMMethod + (Long.class.getMethod + ("valueOf", Long.TYPE))); + break; + + case "D": + writeMethodReference(out, pool, Classes.toVMMethod + (Double.class.getMethod + ("valueOf", Double.TYPE))); + break; + + default: + throw new AssertionError("don't know how to autobox from " + from.spec()); + } + } catch (NoSuchMethodException e) { + throw new Error(e); + } + } + } + private static byte[] makeInvocationCode(List pool, String className, String constructorSpec, @@ -155,47 +278,81 @@ public class LambdaMetafactory { { ByteArrayOutputStream out = new ByteArrayOutputStream(); write2(out, fieldType.footprint() - + localType.footprint() + 2); // max stack + + localType.footprint() + 4); // max stack write2(out, localType.footprint() + 1); // max locals write4(out, 0); // length (we'll set the real value later) write1(out, aload_0); + Iterator dst = implementation.type().parameters().iterator(); + + boolean skip = implementation.kind != MethodHandle.REF_invokeStatic; + for (MethodType.Parameter p: fieldType.parameters()) { write1(out, aload_0); write1(out, getfield); write2(out, ConstantPool.addFieldRef (pool, className, "field" + p.index(), p.spec()) + 1); + if (skip) { + skip = false; + } else { + maybeBoxOrUnbox(out, pool, p, dst.next()); + } } for (MethodType.Parameter p: localType.parameters()) { write1(out, p.load()); write1(out, p.position() + 1); + if (skip) { + skip = false; + } else { + maybeBoxOrUnbox(out, pool, p, dst.next()); + } } switch (implementation.kind) { + case MethodHandle.REF_invokeVirtual: + write1(out, invokevirtual); + writeMethodReference(out, pool, implementation.method); + break; + case MethodHandle.REF_invokeStatic: write1(out, invokestatic); + writeMethodReference(out, pool, implementation.method); break; case MethodHandle.REF_invokeSpecial: write1(out, invokespecial); + writeMethodReference(out, pool, implementation.method); + break; + + case MethodHandle.REF_newInvokeSpecial: + write1(out, new_); + write2(out, ConstantPool.addClass + (pool, + Classes.makeString + (implementation.method.class_.name, 0, + implementation.method.class_.name.length - 1)) + 1); + write1(out, dup); + write1(out, invokespecial); + writeMethodReference(out, pool, implementation.method); + break; + + case MethodHandle.REF_invokeInterface: + write1(out, invokeinterface); + writeInterfaceMethodReference(out, pool, implementation.method); + write1(out, implementation.method.parameterFootprint); + write1(out, 0); break; default: throw new AssertionError - ("todo: implement per http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5"); + ("todo: implement '" + implementation.kind + "' per http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5"); } - write2(out, ConstantPool.addMethodRef - (pool, - Classes.makeString(implementation.method.class_.name, 0, - implementation.method.class_.name.length - 1), - Classes.makeString(implementation.method.name, 0, - implementation.method.name.length - 1), - Classes.makeString(implementation.method.spec, 0, - implementation.method.spec.length - 1)) + 1); - - write1(out, implementation.type().result().return_()); + if (implementation.kind != MethodHandle.REF_newInvokeSpecial) { + maybeBoxOrUnbox(out, pool, implementation.type().result(), localType.result()); + } + write1(out, localType.result().return_()); write2(out, 0); // exception handler table length write2(out, 0); // attribute count @@ -206,6 +363,36 @@ public class LambdaMetafactory { return result; } + private static void writeMethodReference(OutputStream out, + List pool, + avian.VMMethod method) + throws IOException + { + write2(out, ConstantPool.addMethodRef + (pool, + Classes.makeString(method.class_.name, 0, + method.class_.name.length - 1), + Classes.makeString(method.name, 0, + method.name.length - 1), + Classes.makeString(method.spec, 0, + method.spec.length - 1)) + 1); + } + + private static void writeInterfaceMethodReference(OutputStream out, + List pool, + avian.VMMethod method) + throws IOException + { + write2(out, ConstantPool.addInterfaceMethodRef + (pool, + Classes.makeString(method.class_.name, 0, + method.class_.name.length - 1), + Classes.makeString(method.name, 0, + method.name.length - 1), + Classes.makeString(method.spec, 0, + method.spec.length - 1)) + 1); + } + public static byte[] makeLambda(String invokedName, String invokedType, String methodType, diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java b/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java index bbb5cabdec..5e8b9e79c0 100644 --- a/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java +++ b/sgx-jvm/avian/classpath/java/lang/invoke/MethodHandle.java @@ -14,8 +14,11 @@ import avian.Classes; import avian.SystemClassLoader; public class MethodHandle { + static final int REF_invokeVirtual = 5; static final int REF_invokeStatic = 6; static final int REF_invokeSpecial = 7; + static final int REF_newInvokeSpecial = 8; + static final int REF_invokeInterface = 9; final int kind; private final ClassLoader loader; diff --git a/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java b/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java index 4ac62810f5..a624d83fed 100644 --- a/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java +++ b/sgx-jvm/avian/classpath/java/lang/invoke/MethodType.java @@ -267,7 +267,13 @@ public final class MethodType implements java.io.Serializable { } } - public static class Parameter { + public interface TypeSpec { + public Class type(); + + public String spec(); + } + + public static class Parameter implements TypeSpec { private final int index; private final int position; private final String spec; @@ -299,12 +305,16 @@ public final class MethodType implements java.io.Serializable { return spec; } + public Class type() { + return type; + } + public int load() { return load; } } - public static class Result { + public static class Result implements TypeSpec { private final String spec; private final Class type; private final int return_; @@ -318,5 +328,13 @@ public final class MethodType implements java.io.Serializable { public int return_() { return return_; // :) } + + public String spec() { + return spec; + } + + public Class type() { + return type; + } } } diff --git a/sgx-jvm/avian/classpath/java/net/URLConnection.java b/sgx-jvm/avian/classpath/java/net/URLConnection.java index 0a34889e42..59330e0bfe 100644 --- a/sgx-jvm/avian/classpath/java/net/URLConnection.java +++ b/sgx-jvm/avian/classpath/java/net/URLConnection.java @@ -13,6 +13,9 @@ package java.net; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Collections; +import java.util.List; +import java.util.Map; public abstract class URLConnection { protected final URL url; @@ -32,6 +35,10 @@ public abstract class URLConnection { return -1; } + public long getContentLengthLong() { + return -1l; + } + public abstract void connect() throws IOException; public InputStream getInputStream() throws IOException { @@ -61,4 +68,35 @@ public abstract class URLConnection { public void setUseCaches(boolean v) { useCaches = v; } + + public String getHeaderField(String name) { + String result = null; + if(name != null) { + List values = getHeaderFields().get(name.toLowerCase()); + if (values != null && values.size() > 0) { + result = values.get(0); + } + } + + return result; + } + + public int getHeaderFieldInt(String name, int Default) { + return (int) getHeaderFieldLong(name, Default); + } + + public long getHeaderFieldLong(String name, long Default) { + long result = Default; + try { + result = Long.parseLong(getHeaderField(name)); + } catch(Exception e) { + // Do nothing, default will be returned + } + + return result; + } + + public Map> getHeaderFields() { + return Collections.emptyMap(); + } } diff --git a/sgx-jvm/avian/classpath/java/util/Arrays.java b/sgx-jvm/avian/classpath/java/util/Arrays.java index 0c2de7dba2..7fb756cefd 100644 --- a/sgx-jvm/avian/classpath/java/util/Arrays.java +++ b/sgx-jvm/avian/classpath/java/util/Arrays.java @@ -686,4 +686,30 @@ public class Arrays { System.arraycopy(array, 0, result, 0, length); return result; } + + public static int binarySearch(int[] a, int key) { + return binarySearch(a, 0, a.length - 1, key); + } + + public static int binarySearch(int[] a, int fromIndex, int toIndex, int key) { + checkRange(a.length, fromIndex, toIndex); + + // Assume array is already sorted + int left = fromIndex; + int right = toIndex - 1; + int mid; + while(left <= right) { + mid = (left + right) / 2; + if(a[mid] < key) { + left = mid + 1; + } else if(a[mid] > key) { + right = mid - 1; + } else { + return mid; + } + } + + return -left - 1; + } + } diff --git a/sgx-jvm/avian/makefile b/sgx-jvm/avian/makefile index fa2c522830..0c8406bf06 100755 --- a/sgx-jvm/avian/makefile +++ b/sgx-jvm/avian/makefile @@ -2345,21 +2345,21 @@ ifeq ($(kernel),darwin) $(header-sysroot)/usr/include/netinet/icmp6.h \ $(header-sysroot)/usr/include/netinet/ip_var.h; do \ if [ ! -f "$(build)/openjdk/netinet/$$(basename $${file})" ]; then \ - ln "$${file}" "$(build)/openjdk/netinet/$$(basename $${file})"; \ + ln -s "$${file}" "$(build)/openjdk/netinet/$$(basename $${file})"; \ fi; \ done mkdir -p $(build)/openjdk/netinet6 for file in \ $(header-sysroot)/usr/include/netinet6/in6_var.h; do \ if [ ! -f "$(build)/openjdk/netinet6/$$(basename $${file})" ]; then \ - ln "$${file}" "$(build)/openjdk/netinet6/$$(basename $${file})"; \ + ln -s "$${file}" "$(build)/openjdk/netinet6/$$(basename $${file})"; \ fi; \ done mkdir -p $(build)/openjdk/net for file in \ $(header-sysroot)/usr/include/net/if_arp.h; do \ if [ ! -f "$(build)/openjdk/net/$$(basename $${file})" ]; then \ - ln "$${file}" "$(build)/openjdk/net/$$(basename $${file})"; \ + ln -s "$${file}" "$(build)/openjdk/net/$$(basename $${file})"; \ fi; \ done mkdir -p $(build)/openjdk/sys @@ -2367,7 +2367,7 @@ ifeq ($(kernel),darwin) $(header-sysroot)/usr/include/sys/kern_event.h \ $(header-sysroot)/usr/include/sys/sys_domain.h; do \ if [ ! -f "$(build)/openjdk/sys/$$(basename $${file})" ]; then \ - ln "$${file}" "$(build)/openjdk/sys/$$(basename $${file})"; \ + ln -s "$${file}" "$(build)/openjdk/sys/$$(basename $${file})"; \ fi; \ done endif diff --git a/sgx-jvm/avian/openjdk.pro b/sgx-jvm/avian/openjdk.pro index 2393930fff..8054c52812 100644 --- a/sgx-jvm/avian/openjdk.pro +++ b/sgx-jvm/avian/openjdk.pro @@ -332,3 +332,19 @@ ; } +# Needed to create temporary file paths, amongst other things. +-keep class sun.security.provider.NativePRNG +-keep class sun.security.provider.SHA + +# Character set codecs +-keep class sun.nio.cs.ISO_8859_1 +-keep class sun.nio.cs.UTF_8 +-keep class sun.nio.cs.UTF_16 +-keep class sun.nio.cs.UTF_16BE +-keep class sun.nio.cs.UTF_16LE +-keep class sun.nio.cs.US_ASCII + +# Accessed from native code +-keep class java.util.zip.Deflater { + ; +} \ No newline at end of file diff --git a/sgx-jvm/avian/src/avian/machine.h b/sgx-jvm/avian/src/avian/machine.h index 8b59bb85a2..7bb8f1f095 100644 --- a/sgx-jvm/avian/src/avian/machine.h +++ b/sgx-jvm/avian/src/avian/machine.h @@ -3455,6 +3455,7 @@ inline object resolve( loadMemoryBarrier(); if (objectClass(t, o) == type(t, GcReference::Type)) { + PROTECT(t, loader); PROTECT(t, pool); GcReference* reference = cast(t, o); @@ -3475,6 +3476,10 @@ inline object resolve( if (o) { storeStoreMemoryBarrier(); + if (objectClass(t, o) == type(t, GcMethod::Type)) { + o = makeMethodHandle(t, reference->kind(), loader, cast(t, o), 0); + } + pool->setBodyElement(t, index, reinterpret_cast(o)); } } else { @@ -3594,7 +3599,7 @@ inline GcMethod* resolveMethod(Thread* t, unsigned index, bool throw_ = true) { - return cast(t, + GcMethodHandle* handle = cast(t, resolve(t, loader, method->code()->pool(), @@ -3602,6 +3607,8 @@ inline GcMethod* resolveMethod(Thread* t, findMethodInClass, GcNoSuchMethodError::Type, throw_)); + + return handle ? handle->method() : 0; } inline GcMethod* resolveMethod(Thread* t, diff --git a/sgx-jvm/avian/src/classpath-openjdk.cpp b/sgx-jvm/avian/src/classpath-openjdk.cpp index dfc8464c2e..ed2a9a2131 100644 --- a/sgx-jvm/avian/src/classpath-openjdk.cpp +++ b/sgx-jvm/avian/src/classpath-openjdk.cpp @@ -2102,6 +2102,8 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData) if (fileInputStreamFdField) { cp->fileInputStreamFdField = fileInputStreamFdField->offset(); + // Some OpenJDK versions wrap the native call in a simple forwarder method, others don't. + if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) { intercept(t, fileInputStreamClass, @@ -2141,19 +2143,37 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData) voidPointer(readBytesFromFile), updateRuntimeData); - intercept(t, - fileInputStreamClass, - "skip", - "(J)J", - voidPointer(skipBytesInFile), - updateRuntimeData); + if (findMethodOrNull(t, fileInputStreamClass, "skip0", "()I") != 0) { + intercept(t, + fileInputStreamClass, + "skip0", + "(J)J", + voidPointer(skipBytesInFile), + updateRuntimeData); + } else { + intercept(t, + fileInputStreamClass, + "skip", + "(J)J", + voidPointer(skipBytesInFile), + updateRuntimeData); + } - intercept(t, - fileInputStreamClass, - "available", - "()I", - voidPointer(availableBytesInFile), - updateRuntimeData); + if (findMethodOrNull(t, fileInputStreamClass, "available0", "()I") != 0) { + intercept(t, + fileInputStreamClass, + "available0", + "()I", + voidPointer(availableBytesInFile), + updateRuntimeData); + } else { + intercept(t, + fileInputStreamClass, + "available", + "()I", + voidPointer(availableBytesInFile), + updateRuntimeData); + } intercept(t, fileInputStreamClass, diff --git a/sgx-jvm/avian/src/machine.cpp b/sgx-jvm/avian/src/machine.cpp index 50768edc2e..84cc9ca599 100644 --- a/sgx-jvm/avian/src/machine.cpp +++ b/sgx-jvm/avian/src/machine.cpp @@ -6094,13 +6094,13 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) PROTECT(t, bootstrapArray); // Resolve the bootstrap method itself. - GcMethod* bootstrap = cast(t, + GcMethod* bootstrap = cast(t, resolve(t, c->loader(), invocation->pool(), bootstrapArray->body()[0], findMethodInClass, - GcNoSuchMethodError::Type)); + GcNoSuchMethodError::Type))->method(); PROTECT(t, bootstrap); // Caller context info to be passed to the bootstrap method. @@ -6219,13 +6219,7 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) array->setBodyElement(t, i + argument, type); } else if (strncmp(p, methodHandle, strlen(methodHandle)) == 0) { - GcReference* reference = cast( - t, - singletonObject( - t, invocation->pool(), bootstrapArray->body()[i + 1])); - int kind = reference->kind(); - - GcMethod* method = cast(t, + GcMethodHandle* handle = cast(t, resolve(t, c->loader(), invocation->pool(), @@ -6233,9 +6227,6 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) findMethodInClass, GcNoSuchMethodError::Type)); - GcMethodHandle* handle - = makeMethodHandle(t, kind, c->loader(), method, 0); - array->setBodyElement(t, i + argument, handle); } else { abort(t); @@ -6265,6 +6256,7 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) } break; default: + fprintf(stderr, "todo: unsupported bootstrap argument type: %s", p); abort(t); } diff --git a/sgx-jvm/avian/test/ArraysTest.java b/sgx-jvm/avian/test/ArraysTest.java index cd008065b6..6a9d478f5c 100644 --- a/sgx-jvm/avian/test/ArraysTest.java +++ b/sgx-jvm/avian/test/ArraysTest.java @@ -40,6 +40,37 @@ public class ArraysTest { } } + public static void testBinarySearch() { + int[] a = new int[]{ 1, 5, 7, 8, 10, 13, 17, 18, 21, 26 }; + + int i = Arrays.binarySearch(a, 5); + expect(i == 1); + i = Arrays.binarySearch(a, 17); + expect(i == 6); + i = Arrays.binarySearch(a, 6); + expect(i == -3); + i = Arrays.binarySearch(a, 1, 4, 8); + expect(i == 3); + i = Arrays.binarySearch(a, 1, 4, 10); + expect(i == -5); + + Exception exception = null; + try { + Arrays.binarySearch(a, -1, a.length, 4); + } catch (ArrayIndexOutOfBoundsException e) { + exception = e; + } + expect(exception != null); + + exception = null; + try { + Arrays.binarySearch(a, 0, a.length + 1, 4); + } catch (ArrayIndexOutOfBoundsException e) { + exception = e; + } + expect(exception != null); + } + public static void main(String[] args) { { int[] array = new int[0]; Exception exception = null; @@ -170,5 +201,6 @@ public class ArraysTest { } testSort(); + testBinarySearch(); } } diff --git a/sgx-jvm/avian/test/Files.java b/sgx-jvm/avian/test/Files.java index 21bd5cf07c..05045be184 100644 --- a/sgx-jvm/avian/test/Files.java +++ b/sgx-jvm/avian/test/Files.java @@ -26,6 +26,15 @@ public class Files { } + private static void isRootParent() { + if(!IsWindows) { + File f = new File("/root"); + File f2 = f.getParentFile(); + System.out.println("------------"+f2); + expect(f2.getPath().equals("/")); + } + } + private static void setExecutableTestWithPermissions(boolean executable) throws Exception { @@ -49,6 +58,7 @@ public class Files { isAbsoluteTest(false); setExecutableTestWithPermissions(true); setExecutableTestWithPermissions(false); + isRootParent(); { File f = new File("test.txt"); f.createNewFile(); diff --git a/sgx-jvm/avian/test/Integers.java b/sgx-jvm/avian/test/Integers.java index 7c977d6136..d980802e0e 100644 --- a/sgx-jvm/avian/test/Integers.java +++ b/sgx-jvm/avian/test/Integers.java @@ -20,6 +20,50 @@ public class Integers { return m; } + private static void testNumberOfLeadingZeros() { + expect(Integer.numberOfLeadingZeros(Integer.MAX_VALUE) == 1); + expect(Integer.numberOfLeadingZeros(0) == 32); + + int positive = 1; + int negative = Integer.MIN_VALUE; + for(int i = 0; i < 32; i++) { + expect(Integer.numberOfLeadingZeros(positive) == 32 - i - 1); + positive <<= 1; + + expect(Integer.numberOfLeadingZeros(negative) == 0); + negative += ((int)Math.pow(2, i)); + } + + for(int i = 0; i < 99999; i++) { + String binary = ""; + int nolz = 0; + boolean nolzFound = false; + for(int j = 0; j < 31; j++) { + double r = Math.random(); + if(r < 0.5) { + binary += "0"; + if(!nolzFound) { + nolz++; + } + } else { + binary += "1"; + nolzFound = true; + } + } + + double r = Math.random(); + if(r < 0.5) { + // positive + binary = "0" + binary; + nolz++; + expect(Integer.numberOfLeadingZeros(Integer.parseInt(binary, 2)) == nolz); + } else { + // negative + expect(Integer.numberOfLeadingZeros(-Integer.parseInt(binary, 2)) == 0); + } + } + } + public static void main(String[] args) throws Exception { { int foo = 1028; foo -= 1023; @@ -320,5 +364,7 @@ public class Integers { expect(-83 == Integer.decode("-0123").intValue()); expect(-291 == Integer.decode("-0x123").intValue()); expect(291 == Integer.decode("#123").intValue()); + + testNumberOfLeadingZeros(); } } diff --git a/sgx-jvm/avian/test/InvokeDynamic.java b/sgx-jvm/avian/test/InvokeDynamic.java index 6d6e2842aa..cd65c22199 100644 --- a/sgx-jvm/avian/test/InvokeDynamic.java +++ b/sgx-jvm/avian/test/InvokeDynamic.java @@ -1,3 +1,5 @@ +import java.util.*; + public class InvokeDynamic { private final int foo; @@ -27,6 +29,40 @@ public class InvokeDynamic { T get(); } + private interface Consumer { + void accept(T obj); + } + + private interface Function { + R apply(T obj); + } + + private interface BiFunction { + R apply(T t, U u); + } + + private interface GetLong { + long get(long l); + } + + private interface GetDouble { + double get(double d); + } + + private static class LongHolder implements GetLong { + @Override + public long get(long l) { + return l; + } + } + + private static class DoubleHolder implements GetDouble { + @Override + public double get(double d) { + return d; + } + } + private static void expect(boolean v) { if (! v) throw new RuntimeException(); } @@ -93,19 +129,56 @@ public class InvokeDynamic { s.someFunction(1, 2, ""); } + { Consumer c = System.out::println; + c.accept("invoke virtual"); + } + + { Function f = CharSequence::toString; + expect(f.apply("invoke interface") == "invoke interface"); + } + + { Function f = CharSequence::length; + expect(f.apply("invoke interface") == 16); + } + + { BiFunction f = CharSequence::charAt; + String data = "0123456789"; + for (int i = 0; i < data.length(); ++i) { + expect(f.apply(data, i) == data.charAt(i)); + } + } + + { Function, Iterator> f = java.util.List::iterator; + Iterator iter = f.apply(Arrays.asList("1", "22", "333")); + expect(iter.next() == "1"); + expect(iter.next() == "22"); + expect(iter.next() == "333"); + expect(! iter.hasNext()); + } + + { BiFunction f = GetLong::get; + expect(f.apply(new LongHolder(), 20L) == 20L); + } + + { BiFunction f = GetDouble::get; + expect(f.apply(new DoubleHolder(), 20d) == 20d); + } + // This abort()s in machine.cpp // { Foo s = (Foo & Marker) this::requiresBridge; // s.someFunction(1, 2, ""); // } - // NPE - // { UnboxedSerializable s = InvokeDynamic::addBoxed; - // expect(s.add(1, 2) == 3); - // } + { UnboxedSerializable s = InvokeDynamic::addBoxed; + expect(s.add(1, 2) == 3); + } - // NPE - // { Unboxed s = InvokeDynamic::addBoxed; - // expect(s.add(1, 2) == 3); - // } + { Unboxed s = InvokeDynamic::addBoxed; + expect(s.add(1, 2) == 3); + } + + { Supplier> s = java.util.ArrayList::new; + java.util.List list = s.get(); + } } } diff --git a/sgx-jvm/avian/test/Reflection.java b/sgx-jvm/avian/test/Reflection.java index 39dc5c85a8..1280a2c2b0 100644 --- a/sgx-jvm/avian/test/Reflection.java +++ b/sgx-jvm/avian/test/Reflection.java @@ -49,9 +49,10 @@ public class Reflection { private static void innerClasses() throws Exception { Class c = Reflection.class; Class[] inner = c.getDeclaredClasses(); - expect(2 == inner.length); + expect(3 == inner.length); expect(Hello.class == inner[0] - || Hello.class == inner[1]); + || Hello.class == inner[1] + || Hello.class == inner[2]); } private int egads; @@ -98,7 +99,7 @@ public class Reflection { expect(1 == args.length); expect(args[0] == String.class); } - + public static void throwOOME() { throw new OutOfMemoryError(); } @@ -110,15 +111,23 @@ public class Reflection { expect(!Reflection.class.isMemberClass()); expect(Reflection.Hello.class.isMemberClass()); - + Cloneable anonymousLocal = new Cloneable() {}; expect(anonymousLocal.getClass().isAnonymousClass()); - + class NamedLocal {} expect(NamedLocal.class.isLocalClass()); } + private static class MyClassLoader extends ClassLoader { + public Package definePackage1(String name) { + return definePackage(name, null, null, null, null, null, null, null); + } + } + public static void main(String[] args) throws Exception { + expect(new MyClassLoader().definePackage1("foo").getName().equals("foo")); + innerClasses(); annotations(); genericType(); diff --git a/sgx-jvm/avian/vm.pro b/sgx-jvm/avian/vm.pro index ebc6f7c7e0..6ead35b18b 100644 --- a/sgx-jvm/avian/vm.pro +++ b/sgx-jvm/avian/vm.pro @@ -1,10 +1,11 @@ # proguard include file (http://proguard.sourceforge.net) -# we call the values method reflectively in Enum.valueOf(): - --keepclassmembers public class * extends java.lang.Enum { - public static *** values(); - } +# Enums have methods and members that are called reflectively in both Avian and OpenJDK. +-keepclassmembers enum * { + **[] $VALUES; + public *; + public static **[] values(); +} # the VM depends on the fixed layout of the following classes: