Squashed commit of the following: (#15)

commit fabf4dd0cca19118c59c925f572a655d1d9c4092
Author: Joel Dice <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
Date:   Sun Jul 9 17:33:39 2017 -0600

    support constructor references

commit a329416f4f37840d2a5d7976874585413cd4c6af
Author: Joel Dice <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
Date:   Sun Jul 9 16:58:04 2017 -0600

    fix some lambda bugs

commit 552dfd779baf077cc6445b4042588e4b6e34f3d2
Merge: 63dda56 aa2f25d
Author: Joel Dice <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
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 <chris.rankin@r3.com>
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 <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
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 <lwahlmeier@gmail.com>
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 <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
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 <mike@plan99.net>
Date:   Sun May 14 21:42:06 2017 +0200

    Symlink OpenJDK files rather than hardlink

commit 61c82e1070d857bcdc7a4f086d13836d9608da00
Author: Mike Hearn <mike@plan99.net>
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 <mike@plan99.net>
Date:   Sun May 14 21:38:09 2017 +0200

    Some fixes to the ProGuard files

commit 16dd804f392168497fa17ab682978f938e291bfb
Merge: e2d3270 19deadd
Author: Joel Dice <joel.dice@gmail.com>
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 <joel.dice@gmail.com>
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 <mwraaphorst@gmail.com>
Date:   Tue Mar 14 11:23:36 2017 +0100

    Tested more negative numbers

commit b45bcf09535daef99ed31451ae55db8bec83164c
Author: Maarten Raaphorst <mwraaphorst@gmail.com>
Date:   Tue Mar 14 10:47:27 2017 +0100

    Added unit test for numberOfLeadingZeros method of the Integer class

commit 19deadd36bbfc02a8c13e6d91963e41f5125057f
Author: Maarten Raaphorst <mwraaphorst@gmail.com>
Date:   Mon Mar 13 12:19:17 2017 +0100

    Fixed mistake with end index being exclusive

commit 7271c0b7077ae3bef473e036aa0f0298a9eb0601
Author: Maarten Raaphorst <mwraaphorst@gmail.com>
Date:   Fri Mar 10 11:17:13 2017 +0100

    Added numberOfLeadingZeros method to Integer class

commit 023bb69acd071348f29cd43ff954d3dcf2856c7c
Author: Maarten Raaphorst <mwraaphorst@gmail.com>
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 <joel.dice@gmail.com>
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 <raaphorst@serviceplanet.nl>
Date:   Wed Mar 1 14:19:51 2017 +0100

    Made http headers case insensitive

commit 2c3a82d2b387251a8932ddb6575e718c2c1785a7
Author: Maarten Raaphorst <mwraaphorst@gmail.com>
Date:   Fri Feb 3 05:42:56 2017 -0800

    Added functionality to HttpURLConnection for obtaining content length
This commit is contained in:
Chris Rankin 2017-07-10 17:01:56 +01:00 committed by GitHub
parent b40533a14f
commit 9411c223af
24 changed files with 653 additions and 90 deletions

View File

@ -15,3 +15,4 @@ workspace/
src/.cproject src/.cproject
/cmake-build /cmake-build
/cmake-build /cmake-build
.idea/*

View File

@ -46,7 +46,7 @@ public class Classes {
public static native VMClass toVMClass(Class c); public static native VMClass toVMClass(Class c);
public static native VMMethod toVMMethod(Method m); public static native VMMethod toVMMethod(Method m);
private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec) private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec)
throws ClassNotFoundException; throws ClassNotFoundException;
@ -295,7 +295,7 @@ public class Classes {
if (loader == null) { if (loader == null) {
loader = Class.class.getClassLoader(); loader = Class.class.getClassLoader();
} }
Class c = loader.loadClass(name); Class c = loader.loadClass(name.replace('/', '.'));
VMClass vmc = SystemClassLoader.vmClass(c); VMClass vmc = SystemClassLoader.vmClass(c);
link(vmc, loader); link(vmc, loader);
if (initialize) { if (initialize) {
@ -443,7 +443,7 @@ public class Classes {
} }
return null; return null;
} }
public static int findMethod(VMClass vmClass, String name, public static int findMethod(VMClass vmClass, String name,
Class[] parameterTypes) Class[] parameterTypes)
{ {
@ -599,6 +599,6 @@ public class Classes {
private static native void acquireClassLock(); private static native void acquireClassLock();
private static native void releaseClassLock(); private static native void releaseClassLock();
public static native String makeString(byte[] array, int offset, int length); public static native String makeString(byte[] array, int offset, int length);
} }

View File

@ -26,6 +26,7 @@ public class ConstantPool {
private static final int CONSTANT_NameAndType = 12; private static final int CONSTANT_NameAndType = 12;
private static final int CONSTANT_Fieldref = 9; private static final int CONSTANT_Fieldref = 9;
private static final int CONSTANT_Methodref = 10; private static final int CONSTANT_Methodref = 10;
private static final int CONSTANT_InterfaceMethodref = 11;
public static int add(List<PoolEntry> pool, PoolEntry e) { public static int add(List<PoolEntry> pool, PoolEntry e) {
int i = 0; int i = 0;
@ -85,6 +86,16 @@ public class ConstantPool {
addNameAndType(pool, name, spec))); addNameAndType(pool, name, spec)));
} }
public static int addInterfaceMethodRef(List<PoolEntry> pool,
String interfaceName,
String name,
String spec)
{
return add(pool, new InterfaceMethodRefPoolEntry
(addClass(pool, interfaceName),
addNameAndType(pool, name, spec)));
}
public interface PoolEntry { public interface PoolEntry {
public void writeTo(OutputStream out) throws IOException; 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;
}
}
}
} }

View File

@ -76,21 +76,6 @@ public class SystemClassLoader extends ClassLoader {
return null; 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); protected static native String getPackageSource(String name);
// OpenJDK's java.lang.ClassLoader.getResource makes use of // OpenJDK's java.lang.ClassLoader.getResource makes use of

View File

@ -23,8 +23,11 @@ import java.net.Socket;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.net.URLStreamHandler; import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.List;
import java.util.Map;
public class Handler extends URLStreamHandler public class Handler extends URLStreamHandler
{ {
@ -35,10 +38,12 @@ public class Handler extends URLStreamHandler
class HttpURLConnection extends URLConnection class HttpURLConnection extends URLConnection
{ {
private static final String HKEY_CONTENT_LENGTH = "content-length";
Socket socket; Socket socket;
private BufferedWriter writer; private BufferedWriter writer;
private InputStream bin; private InputStream bin;
private Map<String,String> header = new HashMap<String, String>(); private Map<String, List<String>> header = new HashMap<String, List<String>>();
private int status; private int status;
protected HttpURLConnection(URL url) protected HttpURLConnection(URL url)
@ -97,7 +102,12 @@ public class Handler extends URLStreamHandler
int i = line.indexOf(':'); int i = line.indexOf(':');
if(i > 0) 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<String> valueList = new ArrayList<String>();
valueList.add(value);
header.put(key, Collections.unmodifiableList(valueList));
} }
line = reader.readLine(); line = reader.readLine();
} }
@ -116,5 +126,23 @@ public class Handler extends URLStreamHandler
{ {
throw new UnsupportedOperationException("Can' write to HTTP Connection"); 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<String,List<String>> getHeaderFields()
{
return Collections.unmodifiableMap(header);
}
} }
} }

View File

@ -85,7 +85,7 @@ public class File implements Serializable {
} }
private static String stripSeparators(String p) { private static String stripSeparators(String p) {
while (p.endsWith(FileSeparator)) { while (p.length() > 1 && p.endsWith(FileSeparator)) {
p = p.substring(0, p.length() - 1); p = p.substring(0, p.length() - 1);
} }
return p; return p;
@ -173,8 +173,10 @@ public class File implements Serializable {
public String getParent() { public String getParent() {
int index = path.lastIndexOf(FileSeparator); int index = path.lastIndexOf(FileSeparator);
if (index >= 0) { if (index > 0) {
return normalize(path.substring(0, index)); return normalize(path.substring(0, index));
} else if (index == 0) {
return normalize(path.substring(0, FileSeparator.length()));
} else { } else {
return null; return null;
} }

View File

@ -45,9 +45,31 @@ public abstract class ClassLoader {
} }
protected Package getPackage(String name) { protected Package getPackage(String name) {
Package p;
synchronized (this) { 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() { protected Package[] getPackages() {
@ -145,7 +167,7 @@ public abstract class ClassLoader {
public final ClassLoader getParent() { public final ClassLoader getParent() {
return parent; return parent;
} }
protected URL findResource(String path) { protected URL findResource(String path) {
return null; return null;
} }
@ -183,11 +205,11 @@ public abstract class ClassLoader {
public static InputStream getSystemResourceAsStream(String path) { public static InputStream getSystemResourceAsStream(String path) {
return getSystemClassLoader().getResourceAsStream(path); return getSystemClassLoader().getResourceAsStream(path);
} }
public static Enumeration<URL> getSystemResources(String name) throws IOException { public static Enumeration<URL> getSystemResources(String name) throws IOException {
return getSystemClassLoader().getResources(name); return getSystemClassLoader().getResources(name);
} }
public Enumeration<URL> getResources(String name) public Enumeration<URL> getResources(String name)
throws IOException { throws IOException {
Collection<URL> resources = collectResources(name); Collection<URL> resources = collectResources(name);
@ -209,5 +231,5 @@ public abstract class ClassLoader {
static native Class getCaller(); static native Class getCaller();
static native void load(String name, Class caller, boolean mapName); static native void load(String name, Class caller, boolean mapName);
} }

View File

@ -139,4 +139,14 @@ public final class Integer extends Number implements Comparable<Integer> {
} }
return new Integer(parseInt(string, 10)); 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);
}
} }

View File

@ -24,7 +24,9 @@ import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.IOException; import java.io.IOException;
import avian.Classes; import avian.Classes;
@ -145,6 +147,127 @@ public class LambdaMetafactory {
return result; return result;
} }
private static void maybeBoxOrUnbox(ByteArrayOutputStream out,
List<PoolEntry> 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<PoolEntry> pool, private static byte[] makeInvocationCode(List<PoolEntry> pool,
String className, String className,
String constructorSpec, String constructorSpec,
@ -155,47 +278,81 @@ public class LambdaMetafactory {
{ {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
write2(out, fieldType.footprint() write2(out, fieldType.footprint()
+ localType.footprint() + 2); // max stack + localType.footprint() + 4); // max stack
write2(out, localType.footprint() + 1); // max locals write2(out, localType.footprint() + 1); // max locals
write4(out, 0); // length (we'll set the real value later) write4(out, 0); // length (we'll set the real value later)
write1(out, aload_0); write1(out, aload_0);
Iterator<MethodType.Parameter> dst = implementation.type().parameters().iterator();
boolean skip = implementation.kind != MethodHandle.REF_invokeStatic;
for (MethodType.Parameter p: fieldType.parameters()) { for (MethodType.Parameter p: fieldType.parameters()) {
write1(out, aload_0); write1(out, aload_0);
write1(out, getfield); write1(out, getfield);
write2(out, ConstantPool.addFieldRef write2(out, ConstantPool.addFieldRef
(pool, className, "field" + p.index(), p.spec()) + 1); (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()) { for (MethodType.Parameter p: localType.parameters()) {
write1(out, p.load()); write1(out, p.load());
write1(out, p.position() + 1); write1(out, p.position() + 1);
if (skip) {
skip = false;
} else {
maybeBoxOrUnbox(out, pool, p, dst.next());
}
} }
switch (implementation.kind) { switch (implementation.kind) {
case MethodHandle.REF_invokeVirtual:
write1(out, invokevirtual);
writeMethodReference(out, pool, implementation.method);
break;
case MethodHandle.REF_invokeStatic: case MethodHandle.REF_invokeStatic:
write1(out, invokestatic); write1(out, invokestatic);
writeMethodReference(out, pool, implementation.method);
break; break;
case MethodHandle.REF_invokeSpecial: case MethodHandle.REF_invokeSpecial:
write1(out, 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; break;
default: throw new AssertionError 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 if (implementation.kind != MethodHandle.REF_newInvokeSpecial) {
(pool, maybeBoxOrUnbox(out, pool, implementation.type().result(), localType.result());
Classes.makeString(implementation.method.class_.name, 0, }
implementation.method.class_.name.length - 1), write1(out, localType.result().return_());
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_());
write2(out, 0); // exception handler table length write2(out, 0); // exception handler table length
write2(out, 0); // attribute count write2(out, 0); // attribute count
@ -206,6 +363,36 @@ public class LambdaMetafactory {
return result; return result;
} }
private static void writeMethodReference(OutputStream out,
List<PoolEntry> 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<PoolEntry> 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, public static byte[] makeLambda(String invokedName,
String invokedType, String invokedType,
String methodType, String methodType,

View File

@ -14,8 +14,11 @@ import avian.Classes;
import avian.SystemClassLoader; import avian.SystemClassLoader;
public class MethodHandle { public class MethodHandle {
static final int REF_invokeVirtual = 5;
static final int REF_invokeStatic = 6; static final int REF_invokeStatic = 6;
static final int REF_invokeSpecial = 7; static final int REF_invokeSpecial = 7;
static final int REF_newInvokeSpecial = 8;
static final int REF_invokeInterface = 9;
final int kind; final int kind;
private final ClassLoader loader; private final ClassLoader loader;

View File

@ -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 index;
private final int position; private final int position;
private final String spec; private final String spec;
@ -299,12 +305,16 @@ public final class MethodType implements java.io.Serializable {
return spec; return spec;
} }
public Class type() {
return type;
}
public int load() { public int load() {
return load; return load;
} }
} }
public static class Result { public static class Result implements TypeSpec {
private final String spec; private final String spec;
private final Class type; private final Class type;
private final int return_; private final int return_;
@ -318,5 +328,13 @@ public final class MethodType implements java.io.Serializable {
public int return_() { public int return_() {
return return_; // :) return return_; // :)
} }
public String spec() {
return spec;
}
public Class type() {
return type;
}
} }
} }

View File

@ -13,6 +13,9 @@ package java.net;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public abstract class URLConnection { public abstract class URLConnection {
protected final URL url; protected final URL url;
@ -32,6 +35,10 @@ public abstract class URLConnection {
return -1; return -1;
} }
public long getContentLengthLong() {
return -1l;
}
public abstract void connect() throws IOException; public abstract void connect() throws IOException;
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
@ -61,4 +68,35 @@ public abstract class URLConnection {
public void setUseCaches(boolean v) { public void setUseCaches(boolean v) {
useCaches = v; useCaches = v;
} }
public String getHeaderField(String name) {
String result = null;
if(name != null) {
List<String> 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<String,List<String>> getHeaderFields() {
return Collections.emptyMap();
}
} }

View File

@ -686,4 +686,30 @@ public class Arrays {
System.arraycopy(array, 0, result, 0, length); System.arraycopy(array, 0, result, 0, length);
return result; 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;
}
} }

View File

@ -2345,21 +2345,21 @@ ifeq ($(kernel),darwin)
$(header-sysroot)/usr/include/netinet/icmp6.h \ $(header-sysroot)/usr/include/netinet/icmp6.h \
$(header-sysroot)/usr/include/netinet/ip_var.h; do \ $(header-sysroot)/usr/include/netinet/ip_var.h; do \
if [ ! -f "$(build)/openjdk/netinet/$$(basename $${file})" ]; then \ if [ ! -f "$(build)/openjdk/netinet/$$(basename $${file})" ]; then \
ln "$${file}" "$(build)/openjdk/netinet/$$(basename $${file})"; \ ln -s "$${file}" "$(build)/openjdk/netinet/$$(basename $${file})"; \
fi; \ fi; \
done done
mkdir -p $(build)/openjdk/netinet6 mkdir -p $(build)/openjdk/netinet6
for file in \ for file in \
$(header-sysroot)/usr/include/netinet6/in6_var.h; do \ $(header-sysroot)/usr/include/netinet6/in6_var.h; do \
if [ ! -f "$(build)/openjdk/netinet6/$$(basename $${file})" ]; then \ if [ ! -f "$(build)/openjdk/netinet6/$$(basename $${file})" ]; then \
ln "$${file}" "$(build)/openjdk/netinet6/$$(basename $${file})"; \ ln -s "$${file}" "$(build)/openjdk/netinet6/$$(basename $${file})"; \
fi; \ fi; \
done done
mkdir -p $(build)/openjdk/net mkdir -p $(build)/openjdk/net
for file in \ for file in \
$(header-sysroot)/usr/include/net/if_arp.h; do \ $(header-sysroot)/usr/include/net/if_arp.h; do \
if [ ! -f "$(build)/openjdk/net/$$(basename $${file})" ]; then \ if [ ! -f "$(build)/openjdk/net/$$(basename $${file})" ]; then \
ln "$${file}" "$(build)/openjdk/net/$$(basename $${file})"; \ ln -s "$${file}" "$(build)/openjdk/net/$$(basename $${file})"; \
fi; \ fi; \
done done
mkdir -p $(build)/openjdk/sys 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/kern_event.h \
$(header-sysroot)/usr/include/sys/sys_domain.h; do \ $(header-sysroot)/usr/include/sys/sys_domain.h; do \
if [ ! -f "$(build)/openjdk/sys/$$(basename $${file})" ]; then \ if [ ! -f "$(build)/openjdk/sys/$$(basename $${file})" ]; then \
ln "$${file}" "$(build)/openjdk/sys/$$(basename $${file})"; \ ln -s "$${file}" "$(build)/openjdk/sys/$$(basename $${file})"; \
fi; \ fi; \
done done
endif endif

View File

@ -332,3 +332,19 @@
<methods>; <methods>;
} }
# 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 {
<fields>;
}

View File

@ -3455,6 +3455,7 @@ inline object resolve(
loadMemoryBarrier(); loadMemoryBarrier();
if (objectClass(t, o) == type(t, GcReference::Type)) { if (objectClass(t, o) == type(t, GcReference::Type)) {
PROTECT(t, loader);
PROTECT(t, pool); PROTECT(t, pool);
GcReference* reference = cast<GcReference>(t, o); GcReference* reference = cast<GcReference>(t, o);
@ -3475,6 +3476,10 @@ inline object resolve(
if (o) { if (o) {
storeStoreMemoryBarrier(); storeStoreMemoryBarrier();
if (objectClass(t, o) == type(t, GcMethod::Type)) {
o = makeMethodHandle(t, reference->kind(), loader, cast<GcMethod>(t, o), 0);
}
pool->setBodyElement(t, index, reinterpret_cast<uintptr_t>(o)); pool->setBodyElement(t, index, reinterpret_cast<uintptr_t>(o));
} }
} else { } else {
@ -3594,7 +3599,7 @@ inline GcMethod* resolveMethod(Thread* t,
unsigned index, unsigned index,
bool throw_ = true) bool throw_ = true)
{ {
return cast<GcMethod>(t, GcMethodHandle* handle = cast<GcMethodHandle>(t,
resolve(t, resolve(t,
loader, loader,
method->code()->pool(), method->code()->pool(),
@ -3602,6 +3607,8 @@ inline GcMethod* resolveMethod(Thread* t,
findMethodInClass, findMethodInClass,
GcNoSuchMethodError::Type, GcNoSuchMethodError::Type,
throw_)); throw_));
return handle ? handle->method() : 0;
} }
inline GcMethod* resolveMethod(Thread* t, inline GcMethod* resolveMethod(Thread* t,

View File

@ -2102,6 +2102,8 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData)
if (fileInputStreamFdField) { if (fileInputStreamFdField) {
cp->fileInputStreamFdField = fileInputStreamFdField->offset(); 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) { if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) {
intercept(t, intercept(t,
fileInputStreamClass, fileInputStreamClass,
@ -2141,19 +2143,37 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData)
voidPointer(readBytesFromFile), voidPointer(readBytesFromFile),
updateRuntimeData); updateRuntimeData);
intercept(t, if (findMethodOrNull(t, fileInputStreamClass, "skip0", "()I") != 0) {
fileInputStreamClass, intercept(t,
"skip", fileInputStreamClass,
"(J)J", "skip0",
voidPointer(skipBytesInFile), "(J)J",
updateRuntimeData); voidPointer(skipBytesInFile),
updateRuntimeData);
} else {
intercept(t,
fileInputStreamClass,
"skip",
"(J)J",
voidPointer(skipBytesInFile),
updateRuntimeData);
}
intercept(t, if (findMethodOrNull(t, fileInputStreamClass, "available0", "()I") != 0) {
fileInputStreamClass, intercept(t,
"available", fileInputStreamClass,
"()I", "available0",
voidPointer(availableBytesInFile), "()I",
updateRuntimeData); voidPointer(availableBytesInFile),
updateRuntimeData);
} else {
intercept(t,
fileInputStreamClass,
"available",
"()I",
voidPointer(availableBytesInFile),
updateRuntimeData);
}
intercept(t, intercept(t,
fileInputStreamClass, fileInputStreamClass,

View File

@ -6094,13 +6094,13 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
PROTECT(t, bootstrapArray); PROTECT(t, bootstrapArray);
// Resolve the bootstrap method itself. // Resolve the bootstrap method itself.
GcMethod* bootstrap = cast<GcMethod>(t, GcMethod* bootstrap = cast<GcMethodHandle>(t,
resolve(t, resolve(t,
c->loader(), c->loader(),
invocation->pool(), invocation->pool(),
bootstrapArray->body()[0], bootstrapArray->body()[0],
findMethodInClass, findMethodInClass,
GcNoSuchMethodError::Type)); GcNoSuchMethodError::Type))->method();
PROTECT(t, bootstrap); PROTECT(t, bootstrap);
// Caller context info to be passed to the bootstrap method. // 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); array->setBodyElement(t, i + argument, type);
} else if (strncmp(p, methodHandle, strlen(methodHandle)) == 0) { } else if (strncmp(p, methodHandle, strlen(methodHandle)) == 0) {
GcReference* reference = cast<GcReference>( GcMethodHandle* handle = cast<GcMethodHandle>(t,
t,
singletonObject(
t, invocation->pool(), bootstrapArray->body()[i + 1]));
int kind = reference->kind();
GcMethod* method = cast<GcMethod>(t,
resolve(t, resolve(t,
c->loader(), c->loader(),
invocation->pool(), invocation->pool(),
@ -6233,9 +6227,6 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
findMethodInClass, findMethodInClass,
GcNoSuchMethodError::Type)); GcNoSuchMethodError::Type));
GcMethodHandle* handle
= makeMethodHandle(t, kind, c->loader(), method, 0);
array->setBodyElement(t, i + argument, handle); array->setBodyElement(t, i + argument, handle);
} else { } else {
abort(t); abort(t);
@ -6265,6 +6256,7 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
} break; } break;
default: default:
fprintf(stderr, "todo: unsupported bootstrap argument type: %s", p);
abort(t); abort(t);
} }

View File

@ -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) { public static void main(String[] args) {
{ int[] array = new int[0]; { int[] array = new int[0];
Exception exception = null; Exception exception = null;
@ -170,5 +201,6 @@ public class ArraysTest {
} }
testSort(); testSort();
testBinarySearch();
} }
} }

View File

@ -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) private static void setExecutableTestWithPermissions(boolean executable)
throws Exception throws Exception
{ {
@ -49,6 +58,7 @@ public class Files {
isAbsoluteTest(false); isAbsoluteTest(false);
setExecutableTestWithPermissions(true); setExecutableTestWithPermissions(true);
setExecutableTestWithPermissions(false); setExecutableTestWithPermissions(false);
isRootParent();
{ File f = new File("test.txt"); { File f = new File("test.txt");
f.createNewFile(); f.createNewFile();

View File

@ -20,6 +20,50 @@ public class Integers {
return m; 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 { public static void main(String[] args) throws Exception {
{ int foo = 1028; { int foo = 1028;
foo -= 1023; foo -= 1023;
@ -320,5 +364,7 @@ public class Integers {
expect(-83 == Integer.decode("-0123").intValue()); expect(-83 == Integer.decode("-0123").intValue());
expect(-291 == Integer.decode("-0x123").intValue()); expect(-291 == Integer.decode("-0x123").intValue());
expect(291 == Integer.decode("#123").intValue()); expect(291 == Integer.decode("#123").intValue());
testNumberOfLeadingZeros();
} }
} }

View File

@ -1,3 +1,5 @@
import java.util.*;
public class InvokeDynamic { public class InvokeDynamic {
private final int foo; private final int foo;
@ -27,6 +29,40 @@ public class InvokeDynamic {
T get(); T get();
} }
private interface Consumer<T> {
void accept(T obj);
}
private interface Function<T, R> {
R apply(T obj);
}
private interface BiFunction<T, U, R> {
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) { private static void expect(boolean v) {
if (! v) throw new RuntimeException(); if (! v) throw new RuntimeException();
} }
@ -93,19 +129,56 @@ public class InvokeDynamic {
s.someFunction(1, 2, ""); s.someFunction(1, 2, "");
} }
{ Consumer<String> c = System.out::println;
c.accept("invoke virtual");
}
{ Function<CharSequence, String> f = CharSequence::toString;
expect(f.apply("invoke interface") == "invoke interface");
}
{ Function<CharSequence, Integer> f = CharSequence::length;
expect(f.apply("invoke interface") == 16);
}
{ BiFunction<CharSequence, Integer, Character> f = CharSequence::charAt;
String data = "0123456789";
for (int i = 0; i < data.length(); ++i) {
expect(f.apply(data, i) == data.charAt(i));
}
}
{ Function<java.util.List<String>, Iterator<String>> f = java.util.List<String>::iterator;
Iterator<String> iter = f.apply(Arrays.asList("1", "22", "333"));
expect(iter.next() == "1");
expect(iter.next() == "22");
expect(iter.next() == "333");
expect(! iter.hasNext());
}
{ BiFunction<GetLong, Long, Long> f = GetLong::get;
expect(f.apply(new LongHolder(), 20L) == 20L);
}
{ BiFunction<GetDouble, Double, Double> f = GetDouble::get;
expect(f.apply(new DoubleHolder(), 20d) == 20d);
}
// This abort()s in machine.cpp // This abort()s in machine.cpp
// { Foo s = (Foo & Marker) this::requiresBridge; // { Foo s = (Foo & Marker) this::requiresBridge;
// s.someFunction(1, 2, ""); // s.someFunction(1, 2, "");
// } // }
// NPE { UnboxedSerializable s = InvokeDynamic::addBoxed;
// { UnboxedSerializable s = InvokeDynamic::addBoxed; expect(s.add(1, 2) == 3);
// expect(s.add(1, 2) == 3); }
// }
// NPE { Unboxed s = InvokeDynamic::addBoxed;
// { Unboxed s = InvokeDynamic::addBoxed; expect(s.add(1, 2) == 3);
// expect(s.add(1, 2) == 3); }
// }
{ Supplier<java.util.List<String>> s = java.util.ArrayList<String>::new;
java.util.List<String> list = s.get();
}
} }
} }

View File

@ -49,9 +49,10 @@ public class Reflection {
private static void innerClasses() throws Exception { private static void innerClasses() throws Exception {
Class c = Reflection.class; Class c = Reflection.class;
Class[] inner = c.getDeclaredClasses(); Class[] inner = c.getDeclaredClasses();
expect(2 == inner.length); expect(3 == inner.length);
expect(Hello.class == inner[0] expect(Hello.class == inner[0]
|| Hello.class == inner[1]); || Hello.class == inner[1]
|| Hello.class == inner[2]);
} }
private int egads; private int egads;
@ -98,7 +99,7 @@ public class Reflection {
expect(1 == args.length); expect(1 == args.length);
expect(args[0] == String.class); expect(args[0] == String.class);
} }
public static void throwOOME() { public static void throwOOME() {
throw new OutOfMemoryError(); throw new OutOfMemoryError();
} }
@ -110,15 +111,23 @@ public class Reflection {
expect(!Reflection.class.isMemberClass()); expect(!Reflection.class.isMemberClass());
expect(Reflection.Hello.class.isMemberClass()); expect(Reflection.Hello.class.isMemberClass());
Cloneable anonymousLocal = new Cloneable() {}; Cloneable anonymousLocal = new Cloneable() {};
expect(anonymousLocal.getClass().isAnonymousClass()); expect(anonymousLocal.getClass().isAnonymousClass());
class NamedLocal {} class NamedLocal {}
expect(NamedLocal.class.isLocalClass()); 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 { public static void main(String[] args) throws Exception {
expect(new MyClassLoader().definePackage1("foo").getName().equals("foo"));
innerClasses(); innerClasses();
annotations(); annotations();
genericType(); genericType();

View File

@ -1,10 +1,11 @@
# proguard include file (http://proguard.sourceforge.net) # proguard include file (http://proguard.sourceforge.net)
# we call the values method reflectively in Enum.valueOf(): # Enums have methods and members that are called reflectively in both Avian and OpenJDK.
-keepclassmembers enum * {
-keepclassmembers public class * extends java.lang.Enum { **[] $VALUES;
public static *** values(); public *;
} public static **[] values();
}
# the VM depends on the fixed layout of the following classes: # the VM depends on the fixed layout of the following classes: