From 82fd3a8dcb6e4ba1e1f28e39afc87bcc5fe65ed6 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 18 Jul 2015 08:44:15 -0600 Subject: [PATCH 1/8] fix #447: leak in compile.cpp (thanks, @mretallack!) --- src/compile.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compile.cpp b/src/compile.cpp index 18fc34b797..276481580c 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -7030,9 +7030,9 @@ void compile(MyThread* t, Context* context) c->restoreState(state); - ir::Type* stackMap = (ir::Type*)malloc( + ir::Type* stackMap2 = (ir::Type*)malloc( sizeof(ir::Type) * context->method->code()->maxStack()); - Frame frame2(&frame, stackMap); + Frame frame2(&frame, stackMap2); unsigned end = duplicatedBaseIp + exceptionHandlerEnd(eh); if (exceptionHandlerIp(eh) >= static_cast(start) @@ -7053,6 +7053,7 @@ void compile(MyThread* t, Context* context) context->eventLog.append(PopContextEvent); eventIndex = calculateFrameMaps(t, context, 0, eventIndex, 0); + free(stackMap2); } } } From 4093036e6f86c28a80474efff2e2272a71dc96ba Mon Sep 17 00:00:00 2001 From: BCG Date: Tue, 28 Jul 2015 22:38:59 -0400 Subject: [PATCH 2/8] Closeable extends AutoCloseable; RandomAccessFile implements DataInput --- classpath/java/io/Closeable.java | 2 +- classpath/java/io/RandomAccessFile.java | 126 +++++++++++++++++++----- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/classpath/java/io/Closeable.java b/classpath/java/io/Closeable.java index 3e7e2a427a..5a04d7db63 100644 --- a/classpath/java/io/Closeable.java +++ b/classpath/java/io/Closeable.java @@ -10,7 +10,7 @@ package java.io; -public interface Closeable { +public interface Closeable extends AutoCloseable { void close() throws IOException; } diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index 9ba9134371..4739b9ea78 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -14,7 +14,8 @@ import java.lang.IllegalArgumentException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; -public class RandomAccessFile { +public class RandomAccessFile implements DataInput, Closeable { + private long peer; private File file; private long position = 0; @@ -77,44 +78,44 @@ public class RandomAccessFile { public int read(byte b[], int off, int len) throws IOException { if(b == null) - throw new IllegalArgumentException(); + throw new IllegalArgumentException(); if (peer == 0) - throw new IOException(); - if(len == 0) - return 0; - if (position + len > this.length) + throw new IOException(); + if(len == 0) + return 0; + if (position + len > this.length) throw new EOFException(); - if (off < 0 || off + len > b.length) + if (off < 0 || off + len > b.length) throw new ArrayIndexOutOfBoundsException(); int bytesRead = readBytes(peer, position, b, off, len); - position += bytesRead; - return bytesRead; + position += bytesRead; + return bytesRead; } public int read(byte b[]) throws IOException { if(b == null) - throw new IllegalArgumentException(); + throw new IllegalArgumentException(); if (peer == 0) - throw new IOException(); - if(b.length == 0) - return 0; - if (position + b.length > this.length) + throw new IOException(); + if(b.length == 0) + return 0; + if (position + b.length > this.length) throw new EOFException(); int bytesRead = readBytes(peer, position, b, 0, b.length); - position += bytesRead; - return bytesRead; + position += bytesRead; + return bytesRead; } public void readFully(byte b[], int off, int len) throws IOException { - if(b == null) - throw new IllegalArgumentException(); + if (b == null) + throw new IllegalArgumentException(); if (peer == 0) - throw new IOException(); - if(len == 0) - return; - if (position + len > this.length) + throw new IOException(); + if(len == 0) + return; + if (position + len > this.length) throw new EOFException(); - if (off < 0 || off + len > b.length) + if (off < 0 || off + len > b.length) throw new ArrayIndexOutOfBoundsException(); int n = 0; do { @@ -133,6 +134,85 @@ public class RandomAccessFile { private static native int readBytes(long peer, long position, byte[] buffer, int offset, int length); + public boolean readBoolean() throws IOException { + return readByte() != 0; + } + + public int read() throws IOException { + try { + return readByte() & 0xff; + } catch (final EOFException e) { + return -1; + } + } + + public byte readByte() throws IOException { + final byte[] buffer = new byte[1]; + readFully(buffer); + return buffer[0]; + } + + public short readShort() throws IOException { + final byte[] buffer = new byte[2]; + readFully(buffer); + return (short)((buffer[0] << 8) | buffer[1]); + } + + public int readInt() throws IOException { + byte[] buf = new byte[4]; + readFully(buf); + return ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]); + } + + public float readFloat() throws IOException { + return Float.floatToIntBits(readInt()); + } + + public double readDouble() throws IOException { + return Double.doubleToLongBits(readLong()); + } + + public long readLong() throws IOException { + return ((readInt() & 0xffffffffl) << 32) | (readInt() & 0xffffffffl); + } + + public char readChar() throws IOException { + return (char)readShort(); + } + + public int readUnsignedByte() throws IOException { + return readByte() & 0xff; + } + + public int readUnsignedShort() throws IOException { + return readShort() & 0xffff; + } + + public String readUTF() throws IOException { + int length = readUnsignedShort(); + byte[] bytes = new byte[length]; + readFully(bytes); + return new String(bytes, "UTF-8"); + } + + @Deprecated + public String readLine() throws IOException { + int c = read(); + if (c < 0) { + return null; + } else if (c == '\n') { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (;;) { + builder.append((char)c); + c = read(); + if (c < 0 || c == '\n') { + return builder.toString(); + } + } + } + public void write(int b) throws IOException { int count = writeBytes(peer, position, new byte[] { (byte)b }, 0, 1); if (count > 0) position += count; From 792684b9358be7f3c8dec73db6086f096be006ba Mon Sep 17 00:00:00 2001 From: "joshuawarner32@gmail.com" Date: Sun, 3 May 2015 20:57:38 -0600 Subject: [PATCH 3/8] first pass at minimal invokedynamic support for Java 8 lambdas This is a bunch of commits squashed into one per Josh's request. add dynamicTable field add invokedynamic instruction add defaultDynamic bootimage field add dummy invokedynamic support in bootimage-generator add defaultDynamic thunk check dynamicTable offset comment defaultDynamicThunk to fix unused function comment defaultDynamicThunk to fix unused function add dynamicTable / dynamicIndex stuff comment dynamicIndex and dynamicTable add invokedynamic instruction impl stub out addDynamic unstub addDynamic don't allow tail calls in invokedynamic implement stub JVM_GetTemporaryDirectory method (build broken) begin add InvokeDynamicTest Revert "(build broken) begin add InvokeDynamicTest" This reverts commit 77f9c54e32ac66d0803eeab93e4a10d3541987a8. add InternalError add URLClassPath.c for openjdk-src builds implement stub JVM_KnownToNotExist and JVM_GetResourceLookupCache methods intercept open0 / open for openjdk add basic java/lang/invoke stubs remove non-public java/lang/invoke classes fix invokedynamic example building --- classpath/java/lang/InternalError.java | 21 ++ .../java/lang/TypeNotPresentException.java | 21 ++ classpath/java/lang/invoke/CallSite.java | 4 + .../invoke/LambdaConversionException.java | 9 + .../java/lang/invoke/LambdaMetafactory.java | 5 + classpath/java/lang/invoke/MethodHandle.java | 3 + classpath/java/lang/invoke/MethodHandles.java | 5 + classpath/java/lang/invoke/MethodType.java | 3 + invoke.sh | 43 +++ makefile | 6 +- openjdk-src.mk | 2 + src/avian/constants.h | 1 + src/avian/target-fields.h | 6 +- src/bootimage-fields.cpp | 1 + src/classpath-openjdk.cpp | 21 +- src/compile.cpp | 325 ++++++++++++++---- src/jnienv.cpp | 19 + src/tools/bootimage-generator/main.cpp | 21 +- src/types.def | 6 +- test/InvokeDynamic.java | 7 + 20 files changed, 442 insertions(+), 87 deletions(-) create mode 100644 classpath/java/lang/InternalError.java create mode 100644 classpath/java/lang/TypeNotPresentException.java create mode 100644 classpath/java/lang/invoke/CallSite.java create mode 100644 classpath/java/lang/invoke/LambdaConversionException.java create mode 100644 classpath/java/lang/invoke/LambdaMetafactory.java create mode 100644 classpath/java/lang/invoke/MethodHandle.java create mode 100644 classpath/java/lang/invoke/MethodHandles.java create mode 100644 classpath/java/lang/invoke/MethodType.java create mode 100644 invoke.sh create mode 100644 test/InvokeDynamic.java diff --git a/classpath/java/lang/InternalError.java b/classpath/java/lang/InternalError.java new file mode 100644 index 0000000000..69ac254db4 --- /dev/null +++ b/classpath/java/lang/InternalError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class InternalError extends VirtualMachineError { + public InternalError(String message) { + super(message); + } + + public InternalError() { + this(null); + } +} diff --git a/classpath/java/lang/TypeNotPresentException.java b/classpath/java/lang/TypeNotPresentException.java new file mode 100644 index 0000000000..eb1f31ed73 --- /dev/null +++ b/classpath/java/lang/TypeNotPresentException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class TypeNotPresentException extends Exception { + public TypeNotPresentException(String message) { + super(message); + } + + public TypeNotPresentException() { + this(null); + } +} diff --git a/classpath/java/lang/invoke/CallSite.java b/classpath/java/lang/invoke/CallSite.java new file mode 100644 index 0000000000..a6c221e8de --- /dev/null +++ b/classpath/java/lang/invoke/CallSite.java @@ -0,0 +1,4 @@ +package java.lang.invoke; + +public abstract class CallSite { +} diff --git a/classpath/java/lang/invoke/LambdaConversionException.java b/classpath/java/lang/invoke/LambdaConversionException.java new file mode 100644 index 0000000000..4502b1c7d8 --- /dev/null +++ b/classpath/java/lang/invoke/LambdaConversionException.java @@ -0,0 +1,9 @@ +package java.lang.invoke; + +public class LambdaConversionException extends java.lang.Exception { + public LambdaConversionException() { throw new RuntimeException(); } + public LambdaConversionException(String s) { throw new RuntimeException(); } + public LambdaConversionException(String s, Throwable th) { throw new RuntimeException(); } + public LambdaConversionException(Throwable th) { throw new RuntimeException(); } + public LambdaConversionException(String s, Throwable th, boolean b, boolean b2) { throw new RuntimeException(); } +} diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java new file mode 100644 index 0000000000..88c4b9129f --- /dev/null +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -0,0 +1,5 @@ +package java.lang.invoke; + +public class LambdaMetafactory { + public static CallSite metafactory(MethodHandles.Lookup l, String s, MethodType mt, MethodType mt2, MethodHandle mh, MethodType mt3) throws LambdaConversionException { throw new RuntimeException(); } +} diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java new file mode 100644 index 0000000000..c0505c07fd --- /dev/null +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -0,0 +1,3 @@ +package java.lang.invoke; +public abstract class MethodHandle { +} diff --git a/classpath/java/lang/invoke/MethodHandles.java b/classpath/java/lang/invoke/MethodHandles.java new file mode 100644 index 0000000000..bbe782dd3a --- /dev/null +++ b/classpath/java/lang/invoke/MethodHandles.java @@ -0,0 +1,5 @@ +package java.lang.invoke; +public class MethodHandles { + public static class Lookup { + } +} diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java new file mode 100644 index 0000000000..58e59022db --- /dev/null +++ b/classpath/java/lang/invoke/MethodType.java @@ -0,0 +1,3 @@ +package java.lang.invoke; +public final class MethodType implements java.io.Serializable { +} diff --git a/invoke.sh b/invoke.sh new file mode 100644 index 0000000000..30c76e186c --- /dev/null +++ b/invoke.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +unzip -l /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/rt.jar | \ + grep java/lang/invoke | \ + grep -E '\$' | \ + awk '{print $4}' | \ + sed -E -e 's/\.class//g' | \ + while read path; do + echo $path + name=$(echo $path | sed -e 's|/|.|g') + + if javap -public $name | grep -q "public.*interface"; then + javap -public $name | grep -v "Compiled from" | \ + sed -E \ + -e 's/java.lang.invoke.//g' \ + -e 's/java.lang.Object/Object o/g' \ + -e 's/java.lang.reflect.Method/Method o/g' \ + -e 's/java.lang.String/String s/g' \ + -e 's/java.lang.Throwable/Throwable/g' \ + -e 's/int/int i/g' \ + -e 's/byte/byte b/g' \ + -e 's/boolean/boolean b/g' \ + -e 's/MethodType/MethodType mt/g' \ + -e 's/MethodHandle/MethodHandle mh/g' \ + -e 's/java.lang.Class\<\?\>/Class\<\?\> c/g' \ + -e 's/;/ { throw new RuntimeException(); }/g' \ + -e 's/public String s/public String/g' \ + -e 's/public static String s/public static String/g' \ + -e 's/public abstract MethodHandle mh/public abstract MethodHandle/g' \ + -e 's/public abstract MethodHandle mh/public abstract MethodHandle/g' \ + -e 's/public boolean b/public boolean/g' \ + -e 's/public int i/public int/g' \ + -e 's/public static final int i/public static final int/g' \ + -e 's/public byte b/public byte/g' \ + -e 's/public Object o/public Object/g' \ + -e 's/public MethodType mt/public MethodType/g' \ + -e 's/public MethodHandle mh/public MethodHandle/g' \ + -e 's/Object o\.\.\./Object... o/g' \ + -e 's/public final native Object o/public final native Object/g' \ + -e 's/public Class c/public Class/g' \ + > classpath/${path}.java + fi + done diff --git a/makefile b/makefile index 906fafb89c..037724ad2e 100755 --- a/makefile +++ b/makefile @@ -1689,7 +1689,7 @@ $(classpath-dep): $(classpath-sources) $(classpath-jar-dep) classes="$(shell $(MAKE) -s --no-print-directory build=$(build) \ $(classpath-classes) arch=$(build-arch) platform=$(bootimage-platform))"; \ if [ -n "$${classes}" ]; then \ - $(javac) -source 1.6 -target 1.6 \ + $(javac) -source 1.8 -target 1.8 \ -d $(classpath-build) -bootclasspath $(boot-classpath) \ $${classes}; fi @touch $(@) @@ -1765,7 +1765,7 @@ $(test-dep): $(test-sources) $(test-library) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \ if test -n "$${files}"; then \ - $(javac) -source 1.6 -target 1.6 \ + $(javac) -source 1.8 -target 1.8 \ -classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ @@ -1777,7 +1777,7 @@ $(test-extra-dep): $(test-extra-sources) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \ if test -n "$${files}"; then \ - $(javac) -source 1.6 -target 1.6 \ + $(javac) -source 1.8 -target 1.8 \ -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi @touch $(@) diff --git a/openjdk-src.mk b/openjdk-src.mk index 4e1d1bdf5e..f357df6520 100644 --- a/openjdk-src.mk +++ b/openjdk-src.mk @@ -49,6 +49,7 @@ openjdk-sources = \ $(openjdk-src)/share/native/java/util/zip/zip_util.c \ $(openjdk-src)/share/native/sun/management/VMManagementImpl.c \ $(openjdk-src)/share/native/sun/misc/GC.c \ + $(openjdk-src)/share/native/sun/misc/URLClassPath.c \ $(openjdk-src)/share/native/sun/misc/MessageUtils.c \ $(openjdk-src)/share/native/sun/misc/NativeSignalHandler.c \ $(openjdk-src)/share/native/sun/misc/Signal.c \ @@ -121,6 +122,7 @@ openjdk-headers-classes = \ sun.misc.VM \ sun.misc.VMSupport \ sun.misc.Version \ + sun.misc.URLClassPath \ sun.net.spi.DefaultProxySelector \ sun.nio.ch.FileKey \ sun.nio.ch.FileChannelImpl \ diff --git a/src/avian/constants.h b/src/avian/constants.h index 7939a2ec68..d40d58b055 100644 --- a/src/avian/constants.h +++ b/src/avian/constants.h @@ -146,6 +146,7 @@ enum OpCode { imul = 0x68, ineg = 0x74, instanceof = 0xc1, + invokedynamic = 0xba, invokeinterface = 0xb9, invokespecial = 0xb7, invokestatic = 0xb8, diff --git a/src/avian/target-fields.h b/src/avian/target-fields.h index 10421f5ce0..ac4402ece1 100644 --- a/src/avian/target-fields.h +++ b/src/avian/target-fields.h @@ -30,7 +30,8 @@ #define TARGET_THREAD_HEAPIMAGE 2312 #define TARGET_THREAD_CODEIMAGE 2320 #define TARGET_THREAD_THUNKTABLE 2328 -#define TARGET_THREAD_STACKLIMIT 2376 +#define TARGET_THREAD_DYNAMICTABLE 2336 +#define TARGET_THREAD_STACKLIMIT 2384 #elif(TARGET_BYTES_PER_WORD == 4) @@ -50,7 +51,8 @@ #define TARGET_THREAD_HEAPIMAGE 2192 #define TARGET_THREAD_CODEIMAGE 2196 #define TARGET_THREAD_THUNKTABLE 2200 -#define TARGET_THREAD_STACKLIMIT 2224 +#define TARGET_THREAD_DYNAMICTABLE 2204 +#define TARGET_THREAD_STACKLIMIT 2228 #else #error diff --git a/src/bootimage-fields.cpp b/src/bootimage-fields.cpp index 761eef88e5..1cd72c73ea 100644 --- a/src/bootimage-fields.cpp +++ b/src/bootimage-fields.cpp @@ -34,6 +34,7 @@ FIELD(virtualThunks) THUNK_FIELD(default_); THUNK_FIELD(defaultVirtual); +THUNK_FIELD(defaultDynamic); THUNK_FIELD(native); THUNK_FIELD(aioob); THUNK_FIELD(stackOverflow); diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 10a3f89204..946528aaca 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -2091,12 +2091,21 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData) if (fileInputStreamFdField) { cp->fileInputStreamFdField = fileInputStreamFdField->offset(); - intercept(t, - fileInputStreamClass, - "open", - "(Ljava/lang/String;)V", - voidPointer(openFile), - updateRuntimeData); + if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) { + intercept(t, + fileInputStreamClass, + "open0", + "(Ljava/lang/String;)V", + voidPointer(openFile), + updateRuntimeData); + } else { + intercept(t, + fileInputStreamClass, + "open", + "(Ljava/lang/String;)V", + voidPointer(openFile), + updateRuntimeData); + } if (findMethodOrNull(t, fileInputStreamClass, "read0", "()I") != 0) { intercept(t, diff --git a/src/compile.cpp b/src/compile.cpp index 276481580c..be2cc48d4e 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -58,7 +58,7 @@ namespace { namespace local { -const bool DebugCompile = false; +const bool DebugCompile = true; const bool DebugNatives = false; const bool DebugCallTable = false; const bool DebugMethodTree = false; @@ -83,6 +83,7 @@ const unsigned InitialZoneCapacityInBytes = 64 * 1024; enum ThunkIndex { compileMethodIndex, compileVirtualMethodIndex, + linkDynamicMethodIndex, invokeNativeIndex, throwArrayIndexOutOfBoundsIndex, throwStackOverflowIndex, @@ -295,6 +296,7 @@ class MyThread : public Thread { uintptr_t* heapImage; uint8_t* codeImage; void** thunkTable; + void** dynamicTable; CallTrace* trace; Reference* reference; avian::codegen::Architecture* arch; @@ -1278,6 +1280,93 @@ class Context { Slice argumentBuffer; }; +unsigned& dynamicIndex(MyThread* t); + +void**& dynamicTable(MyThread* t); + +void updateDynamicTable(MyThread* t, MyThread* o) +{ + o->dynamicTable = dynamicTable(t); + if (t->peer) + updateDynamicTable(static_cast(t->peer), o); + if (t->child) + updateDynamicTable(static_cast(t->child), o); +} + +uintptr_t defaultDynamicThunk(MyThread* t); + +uintptr_t compileVirtualThunk(MyThread* t, + unsigned index, + unsigned* size, + uintptr_t thunk, + const char* baseName); + + +unsigned addDynamic(MyThread* t, GcInvocation* invocation) +{ + ACQUIRE(t, t->m->classLock); + + int index = invocation->index(); + if (index == -1) { + index = dynamicIndex(t)++; + invocation->index() = index; + + unsigned oldCapacity = roots(t)->invocations() + ? roots(t)->invocations()->length() + : 0; + + if (static_cast(index) >= oldCapacity) { + unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096; + + void** newTable = static_cast( + t->m->heap->allocate(newCapacity * BytesPerWord)); + + GcArray* newData = makeArray(t, newCapacity); + PROTECT(t, newData); + + GcWordArray* newThunks = makeWordArray(t, newCapacity * 2); + PROTECT(t, newThunks); + + if (dynamicTable(t)) { + memcpy(newTable, dynamicTable(t), oldCapacity * BytesPerWord); + + for(size_t i = 0; i < oldCapacity; i++) { + newData->setBodyElement(t, i, + roots(t)->invocations()->body()[i]); + } + + + mark(t, newData, ArrayBody, oldCapacity); + + memcpy(newThunks->body().begin(), + compileRoots(t)->dynamicThunks()->body().begin(), + compileRoots(t)->dynamicThunks()->length() * BytesPerWord); + } + + ENTER(t, Thread::ExclusiveState); + + dynamicTable(t) = newTable; + roots(t)->setInvocations(t, newData); + + updateDynamicTable(static_cast(t->m->rootThread), t); + + compileRoots(t)->setDynamicThunks(t, newThunks); + } + + unsigned size; + uintptr_t thunk = compileVirtualThunk( + t, index, &size, defaultDynamicThunk(t), "dynamicThunk"); + compileRoots(t)->dynamicThunks()->body()[index * 2] = thunk; + compileRoots(t)->dynamicThunks()->body()[(index * 2) + 1] = size; + + t->dynamicTable[index] = reinterpret_cast(thunk); + + roots(t)->invocations()->setBodyElement(t, index, invocation); + } + + return index; +} + unsigned translateLocalIndex(Context* context, unsigned footprint, unsigned index) @@ -4944,6 +5033,53 @@ loop: args(c->threadRegister(), frame->append(argument), instance))); } break; + case invokedynamic: { + context->leaf = false; + + uint16_t poolIndex = codeReadInt16(t, code, ip); + ip += 2; + + GcInvocation* invocation = cast( + t, + singletonObject(t, context->method->code()->pool(), poolIndex - 1)); + + PROTECT(t, invocation); + + invocation->setClass(t, context->method->class_()); + + 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; + case invokeinterface: { context->leaf = false; @@ -6914,13 +7050,13 @@ void finish(MyThread* t, FixedAllocator* allocator, Context* context) reinterpret_cast(context->method->spec()->body().begin())); // for debugging: - if (false + if (true and ::strcmp(reinterpret_cast( context->method->class_()->name()->body().begin()), - "java/lang/System") == 0 + "InvokeDynamic") == 0 and ::strcmp(reinterpret_cast( context->method->name()->body().begin()), - "") == 0) { + "main") == 0) { trap(); } syncInstructionCache(start, codeSize); @@ -8317,6 +8453,7 @@ class MyProcessor : public Processor { public: Thunk default_; Thunk defaultVirtual; + Thunk defaultDynamic; Thunk native; Thunk aioob; Thunk stackOverflow; @@ -8342,8 +8479,10 @@ class MyProcessor : public Processor { GcArithmeticException::FixedSize), codeAllocator(s, Slice(0, 0)), callTableSize(0), + dynamicIndex(0), useNativeFeatures(useNativeFeatures), - compilationHandlers(0) + compilationHandlers(0), + dynamicTable(0) { thunkTable[compileMethodIndex] = voidPointer(local::compileMethod); thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod); @@ -8429,6 +8568,10 @@ class MyProcessor : public Processor { TARGET_THREAD_THUNKTABLE, &MyThread::thunkTable, "TARGET_THREAD_THUNKTABLE") + + checkConstant(t, + TARGET_THREAD_DYNAMICTABLE, + &MyThread::dynamicTable, + "TARGET_THREAD_DYNAMICTABLE") + checkConstant(t, TARGET_THREAD_STACKLIMIT, &MyThread::stackLimit, @@ -8993,7 +9136,7 @@ class MyProcessor : public Processor { if (image and code) { local::boot(static_cast(t), image, code); } else { - roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0); + roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); { GcArray* ct = makeArray(t, 128); @@ -9096,11 +9239,23 @@ class MyProcessor : public Processor { ThunkCollection thunks; ThunkCollection bootThunks; unsigned callTableSize; + unsigned dynamicIndex; bool useNativeFeatures; void* thunkTable[dummyIndex + 1]; CompilationHandlerList* compilationHandlers; + void** dynamicTable; }; +unsigned& dynamicIndex(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicIndex; +} + +void**& dynamicTable(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicTable; +} + const char* stringOrNull(const char* str) { if (str) { @@ -9240,15 +9395,16 @@ bool isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip) bool isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip) { - const unsigned NamedThunkCount = 5; + const unsigned NamedThunkCount = 6; MyProcessor::Thunk table[NamedThunkCount + ThunkCount]; table[0] = thunks->default_; table[1] = thunks->defaultVirtual; - table[2] = thunks->native; - table[3] = thunks->aioob; - table[4] = thunks->stackOverflow; + table[2] = thunks->defaultDynamic; + table[3] = thunks->native; + table[4] = thunks->aioob; + table[5] = thunks->stackOverflow; for (unsigned i = 0; i < ThunkCount; ++i) { new (table + NamedThunkCount + i) @@ -9598,8 +9754,8 @@ void findThunks(MyThread* t, BootImage* image, uint8_t* code) MyProcessor* p = processor(t); p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code); - p->bootThunks.defaultVirtual - = thunkToThunk(image->thunks.defaultVirtual, code); + p->bootThunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code); + p->bootThunks.defaultDynamic = thunkToThunk(image->thunks.defaultDynamic, code); p->bootThunks.native = thunkToThunk(image->thunks.native, code); p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code); p->bootThunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, code); @@ -9807,6 +9963,68 @@ void compileCall(MyThread* t, Context* c, ThunkIndex index, bool call = true) } } +void compileDefaultThunk(MyThread* t, + FixedAllocator* allocator, + MyProcessor::Thunk* thunk, + const char* name, + ThunkIndex thunkIndex, + bool hasTarget) +{ + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + if(hasTarget) { + lir::RegisterPair class_(t->arch->virtualCallTarget()); + lir::Memory virtualCallTargetSrc( + t->arch->stack(), + (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) + * TargetBytesPerWord); + + a->apply(lir::Move, + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_)); + + lir::Memory virtualCallTargetDst(t->arch->thread(), + TARGET_THREAD_VIRTUALCALLTARGET); + + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_), + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst)); + } + + lir::RegisterPair index(t->arch->virtualCallIndex()); + lir::Memory virtualCallIndex(t->arch->thread(), + TARGET_THREAD_VIRTUALCALLINDEX); + + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex)); + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + thunk->frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, thunkIndex); + + a->popFrame(t->arch->alignFrameSize(1)); + + lir::RegisterPair result(t->arch->returnLow()); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result)); + + thunk->length = a->endBlock(false)->resolve(0, 0); + + thunk->start = finish( + t, allocator, a, name, thunk->length); +} + void compileThunks(MyThread* t, FixedAllocator* allocator) { MyProcessor* p = processor(t); @@ -9836,59 +10054,13 @@ void compileThunks(MyThread* t, FixedAllocator* allocator) = finish(t, allocator, a, "default", p->thunks.default_.length); } - { - Context context(t); - avian::codegen::Assembler* a = context.assembler; + compileDefaultThunk + (t, allocator, &(p->thunks.defaultVirtual), "defaultVirtual", + compileVirtualMethodIndex, true); - lir::RegisterPair class_(t->arch->virtualCallTarget()); - lir::Memory virtualCallTargetSrc( - t->arch->stack(), - (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) - * TargetBytesPerWord); - - a->apply(lir::Move, - OperandInfo( - TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc), - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_)); - - lir::Memory virtualCallTargetDst(t->arch->thread(), - TARGET_THREAD_VIRTUALCALLTARGET); - - a->apply( - lir::Move, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_), - OperandInfo( - TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst)); - - lir::RegisterPair index(t->arch->virtualCallIndex()); - lir::Memory virtualCallIndex(t->arch->thread(), - TARGET_THREAD_VIRTUALCALLINDEX); - - a->apply( - lir::Move, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index), - OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex)); - - a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); - - p->thunks.defaultVirtual.frameSavedOffset = a->length(); - - lir::RegisterPair thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); - - compileCall(t, &context, compileVirtualMethodIndex); - - a->popFrame(t->arch->alignFrameSize(1)); - - lir::RegisterPair result(t->arch->returnLow()); - a->apply(lir::Jump, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result)); - - p->thunks.defaultVirtual.length = a->endBlock(false)->resolve(0, 0); - - p->thunks.defaultVirtual.start = finish( - t, allocator, a, "defaultVirtual", p->thunks.defaultVirtual.length); - } + compileDefaultThunk + (t, allocator, &(p->thunks.defaultDynamic), "defaultDynamic", + linkDynamicMethodIndex, false); { Context context(t); @@ -10040,6 +10212,11 @@ uintptr_t defaultVirtualThunk(MyThread* t) return reinterpret_cast(processor(t)->thunks.defaultVirtual.start); } +uintptr_t defaultDynamicThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.defaultDynamic.start); +} + uintptr_t nativeThunk(MyThread* t) { return reinterpret_cast(processor(t)->thunks.native.start); @@ -10056,7 +10233,11 @@ bool unresolved(MyThread* t, uintptr_t methodAddress) or methodAddress == bootDefaultThunk(t); } -uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) +uintptr_t compileVirtualThunk(MyThread* t, + unsigned index, + unsigned* size, + uintptr_t thunk, + const char* baseName) { Context context(t); avian::codegen::Assembler* a = context.assembler; @@ -10069,11 +10250,10 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &indexConstant), OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &indexRegister)); - avian::codegen::ResolvedPromise defaultVirtualThunkPromise( - defaultVirtualThunk(t)); - lir::Constant thunk(&defaultVirtualThunkPromise); + avian::codegen::ResolvedPromise promise(thunk); + lir::Constant target(&promise); a->apply(lir::Jump, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &thunk)); + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &target)); *size = a->endBlock(false)->resolve(0, 0); @@ -10083,8 +10263,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) a->setDestination(start); a->write(); - const char* const virtualThunkBaseName = "virtualThunk"; - const size_t virtualThunkBaseNameLength = strlen(virtualThunkBaseName); + const size_t virtualThunkBaseNameLength = strlen(baseName); const size_t maxIntStringLength = 10; THREAD_RUNTIME_ARRAY(t, @@ -10094,7 +10273,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) sprintf(RUNTIME_ARRAY_BODY(virtualThunkName), "%s%d", - virtualThunkBaseName, + baseName, index); logCompile(t, start, *size, 0, RUNTIME_ARRAY_BODY(virtualThunkName), 0); @@ -10120,7 +10299,7 @@ uintptr_t virtualThunk(MyThread* t, unsigned index) if (oldArray->body()[index * 2] == 0) { unsigned size; - uintptr_t thunk = compileVirtualThunk(t, index, &size); + uintptr_t thunk = compileVirtualThunk(t, index, &size, defaultVirtualThunk(t), "virtualThunk"); oldArray->body()[index * 2] = thunk; oldArray->body()[(index * 2) + 1] = size; } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 5a7fc27d66..fdfe120347 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3784,3 +3784,22 @@ extern "C" AVIAN_EXPORT jint JNICALL return run(*t, local::boot, 0) ? 0 : -1; } + +extern "C" AVIAN_EXPORT jstring JNICALL JVM_GetTemporaryDirectory(JNIEnv* e UNUSED) +{ + // Unimplemented + // This is used in newer builds of openjdk8, as a place to store statistics or something... + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL JVM_KnownToNotExist(JNIEnv* e UNUSED, jobject loader UNUSED, jstring classname UNUSED) +{ + // Unimplemented + abort(); +} + +extern "C" AVIAN_EXPORT jintArray JNICALL JVM_GetResourceLookupCache(JNIEnv* e UNUSED, jobject loader UNUSED, jstring resourcename UNUSED) +{ + // Unimplemented + abort(); +} diff --git a/src/tools/bootimage-generator/main.cpp b/src/tools/bootimage-generator/main.cpp index 3a9736dc8e..9d5ab08769 100644 --- a/src/tools/bootimage-generator/main.cpp +++ b/src/tools/bootimage-generator/main.cpp @@ -398,7 +398,8 @@ GcTriple* makeCodeImage(Thread* t, RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; for (unsigned i = 2; i < count + 2; ++i) { - switch (s.read1()) { + unsigned constType = s.read1(); + switch (constType) { case CONSTANT_Class: case CONSTANT_String: RUNTIME_ARRAY_BODY(types)[i] = Type_object; @@ -436,7 +437,25 @@ GcTriple* makeCodeImage(Thread* t, 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); } } diff --git a/src/types.def b/src/types.def index a140503eba..3e2a341955 100644 --- a/src/types.def +++ b/src/types.def @@ -78,7 +78,7 @@ (int32_t index) (object class) (object pool) - (object template) + (method template) (object site)) (type triple @@ -406,7 +406,8 @@ (finder virtualFileFinders) (field array virtualFiles) (field array arrayInterfaceTable) - (object threadTerminated)) + (object threadTerminated) + (field array invocations)) (type compileRoots (field array callTable) @@ -415,6 +416,7 @@ (object objectPools) (object staticTableArray) (wordArray virtualThunks) + (wordArray dynamicThunks) (method receiveMethod) (method windMethod) (method rewindMethod)) diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java new file mode 100644 index 0000000000..6a222700ee --- /dev/null +++ b/test/InvokeDynamic.java @@ -0,0 +1,7 @@ +public class InvokeDynamic { + + public static void main(String[] args) { + Runnable r = () -> System.out.println("success"); + r.run(); + } +} From 246545907978bf29c0906b161b863089792936d7 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 5 Aug 2015 15:55:52 -0600 Subject: [PATCH 4/8] implement basic Java 8 lambda support The two big pieces here are basic invokedynamic support and a working version of LambdaMetaFactory.metafactory. The latter works by dynamically building a synthetic class with three methods: a static factory method, a constructor for the factory method to call, and a method to satisfy the requested interface which defers to the specified MethodHandle. This work relies heavily on Avian's specific MethodType and MethodHandle implementations, which provide extra, non-standard features to make code generation easier. That means we'll probably need to use Avian's versions of java.lang.invoke.* even when building with the OpenJDK or Android class libraries. --- classpath/avian/Assembler.java | 21 +- classpath/avian/ClassAddendum.java | 2 + classpath/avian/Classes.java | 6 +- classpath/java/lang/invoke/CallSite.java | 7 +- .../java/lang/invoke/LambdaMetafactory.java | 272 +++++++++++++++++- classpath/java/lang/invoke/MethodHandle.java | 22 +- classpath/java/lang/invoke/MethodHandles.java | 13 + classpath/java/lang/invoke/MethodType.java | 258 +++++++++++++++++ classpath/java/lang/reflect/Method.java | 14 +- classpath/java/lang/reflect/Proxy.java | 2 + src/avian/machine.h | 13 +- src/compile.cpp | 215 +++++++++++++- src/machine.cpp | 21 +- src/types.def | 16 +- test/InvokeDynamic.java | 12 +- test/Subroutine.java | 3 +- 16 files changed, 870 insertions(+), 27 deletions(-) diff --git a/classpath/avian/Assembler.java b/classpath/avian/Assembler.java index 5b69314a91..64a06fdf81 100644 --- a/classpath/avian/Assembler.java +++ b/classpath/avian/Assembler.java @@ -60,6 +60,7 @@ public class Assembler { int name, int super_, int[] interfaces, + FieldData[] fields, MethodData[] methods) throws IOException { @@ -83,7 +84,13 @@ public class Assembler { write2(out, i + 1); } - write2(out, 0); // field count + write2(out, fields.length); + for (FieldData f: fields) { + write2(out, f.flags); + write2(out, f.nameIndex + 1); + write2(out, f.specIndex + 1); + write2(out, 0); // attribute count + } write2(out, methods.length); for (MethodData m: methods) { @@ -113,4 +120,16 @@ public class Assembler { this.code = code; } } + + public static class FieldData { + public final int flags; + public final int nameIndex; + public final int specIndex; + + public FieldData(int flags, int nameIndex, int specIndex) { + this.flags = flags; + this.nameIndex = nameIndex; + this.specIndex = specIndex; + } + } } diff --git a/classpath/avian/ClassAddendum.java b/classpath/avian/ClassAddendum.java index 94a9f09ab1..156c4df4a1 100644 --- a/classpath/avian/ClassAddendum.java +++ b/classpath/avian/ClassAddendum.java @@ -25,4 +25,6 @@ public class ClassAddendum extends Addendum { public byte[] enclosingClass; public Pair enclosingMethod; + + public VMMethod[] bootstrapMethodTable; } diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 2b84f1cbd4..def0047be5 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -48,8 +48,8 @@ public class Classes { private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec) throws ClassNotFoundException; - private static VMClass loadVMClass(ClassLoader loader, - byte[] nameBytes, int offset, int length) + public static VMClass loadVMClass(ClassLoader loader, + byte[] nameBytes, int offset, int length) { byte[] spec = new byte[length + 1]; System.arraycopy(nameBytes, offset, spec, 0, length); @@ -576,6 +576,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/classpath/java/lang/invoke/CallSite.java b/classpath/java/lang/invoke/CallSite.java index a6c221e8de..a78ed5dcd2 100644 --- a/classpath/java/lang/invoke/CallSite.java +++ b/classpath/java/lang/invoke/CallSite.java @@ -1,4 +1,9 @@ package java.lang.invoke; -public abstract class CallSite { +public class CallSite { + private final MethodHandle target; + + CallSite(MethodHandle target) { + this.target = target; + } } diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java index 88c4b9129f..fc26c54fa9 100644 --- a/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -1,5 +1,275 @@ package java.lang.invoke; +import static avian.Stream.write1; +import static avian.Stream.write2; +import static avian.Stream.write4; +import static avian.Stream.set4; +import static avian.Assembler.*; + +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import avian.Classes; +import avian.ConstantPool; +import avian.Assembler; +import avian.ConstantPool.PoolEntry; +import avian.SystemClassLoader; + public class LambdaMetafactory { - public static CallSite metafactory(MethodHandles.Lookup l, String s, MethodType mt, MethodType mt2, MethodHandle mh, MethodType mt3) throws LambdaConversionException { throw new RuntimeException(); } + private static int nextNumber = 0; + + private static Class resolveReturnInterface(MethodType type) { + int index = 1; + byte[] s = type.spec; + + while (s[index] != ')') ++ index; + + if (s[++ index] != 'L') throw new AssertionError(); + + ++ index; + + int end = index + 1; + while (s[end] != ';') ++ end; + + Class c = SystemClassLoader.getClass + (Classes.loadVMClass(type.loader, s, index, end - index)); + + if (! c.isInterface()) throw new AssertionError(); + + return c; + } + + private static int indexOf(int c, byte[] array) { + int i = 0; + while (array[i] != c) ++i; + return i; + } + + private static String constructorSpec(MethodType type) { + return Classes.makeString(type.spec, 0, indexOf(')', type.spec) + 1) + "V"; + } + + private static byte[] makeFactoryCode(List pool, + String className, + String constructorSpec, + MethodType type) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, type.footprint() + 2); // max stack + write2(out, type.footprint()); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, new_); + write2(out, ConstantPool.addClass(pool, className) + 1); + write1(out, dup); + + for (MethodType.Parameter p: type.parameters()) { + write1(out, p.load()); + write1(out, p.position()); + } + + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, className, "", constructorSpec) + 1); + + write1(out, areturn); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeConstructorCode(List pool, + String className, + MethodType type) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, 3); // max stack + write2(out, type.footprint() + 1); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Object", "", "()V") + 1); + + for (MethodType.Parameter p: type.parameters()) { + write1(out, aload_0); + write1(out, p.load()); + write1(out, p.position() + 1); + write1(out, putfield); + write2(out, ConstantPool.addFieldRef + (pool, className, "field" + p.index(), p.spec()) + 1); + } + + write1(out, return_); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeInvocationCode(List pool, + String className, + String constructorSpec, + MethodType fieldType, + MethodType localType, + MethodHandle implementation) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, fieldType.footprint() + + localType.footprint() + 2); // max stack + write2(out, localType.footprint() + 1); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + + 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); + } + + for (MethodType.Parameter p: localType.parameters()) { + write1(out, p.load()); + write1(out, p.position() + 1); + } + + write1(out, invokestatic); + 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_()); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + public static CallSite metafactory(MethodHandles.Lookup caller, + String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation, + MethodType instantiatedMethodType) + throws LambdaConversionException + { + String className; + { int number; + synchronized (LambdaMetafactory.class) { + number = nextNumber++; + } + className = "Lambda-" + number; + } + + List pool = new ArrayList(); + + int interfaceIndex = ConstantPool.addClass + (pool, invokedType.returnType().getName().replace('.', '/')); + + List fieldTable = new ArrayList(); + + for (MethodType.Parameter p: invokedType.parameters()) { + fieldTable.add + (new FieldData(0, + ConstantPool.addUtf8(pool, "field" + p.index()), + ConstantPool.addUtf8(pool, p.spec()))); + } + + String constructorSpec = constructorSpec(invokedType); + + List methodTable = new ArrayList(); + + try { + methodTable.add + (new MethodData + (Modifier.STATIC, + ConstantPool.addUtf8(pool, "make"), + ConstantPool.addUtf8(pool, Classes.makeString + (invokedType.spec, 0, + invokedType.spec.length - 1)), + makeFactoryCode(pool, className, constructorSpec, invokedType))); + + methodTable.add + (new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, ""), + ConstantPool.addUtf8(pool, constructorSpec), + makeConstructorCode(pool, className, invokedType))); + + methodTable.add + (new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, invokedName), + ConstantPool.addUtf8(pool, Classes.makeString + (methodType.spec, 0, + methodType.spec.length - 1)), + makeInvocationCode(pool, className, constructorSpec, invokedType, + methodType, methodImplementation))); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + + int nameIndex = ConstantPool.addClass(pool, className); + int superIndex = ConstantPool.addClass(pool, "java/lang/Object"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Assembler.writeClass + (out, pool, nameIndex, superIndex, new int[] { interfaceIndex }, + fieldTable.toArray(new FieldData[fieldTable.size()]), + methodTable.toArray(new MethodData[methodTable.size()])); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + + byte[] classData = out.toByteArray(); + + try { + return new CallSite + (new MethodHandle + (invokedType.loader, avian.SystemClassLoader.getClass + (avian.Classes.defineVMClass + (invokedType.loader, classData, 0, classData.length)) + .getMethod("make", invokedType.parameterArray()).vmMethod)); + } catch (NoSuchMethodException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + } } diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java index c0505c07fd..386d2354d4 100644 --- a/classpath/java/lang/invoke/MethodHandle.java +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -1,3 +1,23 @@ package java.lang.invoke; -public abstract class MethodHandle { + +public class MethodHandle { + private final ClassLoader loader; + final avian.VMMethod method; + private volatile MethodType type; + + MethodHandle(ClassLoader loader, avian.VMMethod method) { + this.loader = loader; + this.method = method; + } + + public String toString() { + return new java.lang.reflect.Method(method).toString(); + } + + public MethodType type() { + if (type == null) { + type = new MethodType(loader, method.spec); + } + return type; + } } diff --git a/classpath/java/lang/invoke/MethodHandles.java b/classpath/java/lang/invoke/MethodHandles.java index bbe782dd3a..f17c05daab 100644 --- a/classpath/java/lang/invoke/MethodHandles.java +++ b/classpath/java/lang/invoke/MethodHandles.java @@ -1,5 +1,18 @@ package java.lang.invoke; + public class MethodHandles { public static class Lookup { + final avian.VMClass class_; + private final int modes; + + private Lookup(avian.VMClass class_, int modes) { + this.class_ = class_; + this.modes = modes; + } + + public String toString() { + return "lookup[" + avian.SystemClassLoader.getClass(class_) + ", " + + modes + "]"; + } } } diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java index 58e59022db..44dd79e3cc 100644 --- a/classpath/java/lang/invoke/MethodType.java +++ b/classpath/java/lang/invoke/MethodType.java @@ -1,3 +1,261 @@ package java.lang.invoke; + +import static avian.Assembler.*; + +import avian.Classes; + +import java.util.List; +import java.util.ArrayList; + public final class MethodType implements java.io.Serializable { + final ClassLoader loader; + final byte[] spec; + private volatile List parameters; + private volatile Result result; + private volatile int footprint; + + MethodType(ClassLoader loader, byte[] spec) { + this.loader = loader; + this.spec = spec; + } + + public String toString() { + return Classes.makeString(spec, 0, spec.length - 1); + } + + public int footprint() { + parameters(); // ensure spec is parsed + + return footprint; + } + + public Class returnType() { + parameters(); // ensure spec is parsed + + return result.type; + } + + public Class[] parameterArray() { + parameters(); // ensure spec is parsed + + Class[] array = new Class[parameters.size()]; + for (int i = 0; i < parameters.size(); ++i) { + array[i] = parameters.get(i).type; + } + + return array; + } + + public Iterable parameters() { + if (parameters == null) { + List list = new ArrayList(); + int i; + int index = 0; + int position = 0; + for (i = 1; spec[i] != ')'; ++i) { + switch (spec[i]) { + case 'L': { + int start = i; + ++ i; + while (spec[i] != ';') ++ i; + + list.add(new Parameter + (index, + position, + Classes.makeString(spec, start, (i - start) + 1), + aload)); + } break; + + case '[': { + int start = i; + ++ i; + while (spec[i] == '[') ++ i; + + switch (spec[i]) { + case 'L': + ++ i; + while (spec[i] != ';') ++ i; + break; + + default: + break; + } + + list.add(new Parameter + (index, + position, + Classes.makeString(spec, start, (i - start) + 1), + aload)); + } break; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + list.add(new Parameter + (index, + position, + Classes.makeString(spec, i, 1), + iload)); + break; + + case 'F': + list.add(new Parameter + (index, + position, + Classes.makeString(spec, i, 1), + fload)); + break; + + case 'J': + list.add(new Parameter + (index, + position, + Classes.makeString(spec, i, 1), + lload)); + + ++ position; + break; + + case 'D': + list.add(new Parameter + (index, + position, + Classes.makeString(spec, i, 1), + dload)); + + ++ position; + break; + + default: throw new AssertionError(); + } + + ++ index; + ++ position; + } + + footprint = position; + + ++ i; + + switch (spec[i]) { + case 'L': { + int start = i; + ++ i; + while (spec[i] != ';') ++ i; + + result = new Result + (Classes.makeString(spec, start, (i - start) + 1), areturn); + } break; + + case '[': { + int start = i; + ++ i; + while (spec[i] == '[') ++ i; + + switch (spec[i]) { + case 'L': + ++ i; + while (spec[i] != ';') ++ i; + break; + + default: + break; + } + + result = new Result(Classes.makeString(spec, start, (i - start) + 1), + areturn); + } break; + + case 'V': + result = new Result(Classes.makeString(spec, i, 1), return_); + break; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + result = new Result(Classes.makeString(spec, i, 1), ireturn); + break; + + case 'F': + result = new Result(Classes.makeString(spec, i, 1), freturn); + break; + + case 'J': + result = new Result(Classes.makeString(spec, i, 1), lreturn); + break; + + case 'D': + result = new Result(Classes.makeString(spec, i, 1), dreturn); + break; + + default: throw new AssertionError(); + } + + parameters = list; + } + + return parameters; + } + + public Result result() { + parameters(); // ensure spec has been parsed + + return result; + } + + public class Parameter { + private final int index; + private final int position; + private final String spec; + private final Class type; + private final int load; + + private Parameter(int index, + int position, + String spec, + int load) + { + this.index = index; + this.position = position; + this.spec = spec; + this.type = Classes.forCanonicalName(loader, spec); + this.load = load; + } + + public int index() { + return index; + } + + public int position() { + return position; + } + + public String spec() { + return spec; + } + + public int load() { + return load; + } + } + + public class Result { + private final String spec; + private final Class type; + private final int return_; + + public Result(String spec, int return_) { + this.spec = spec; + this.type = Classes.forCanonicalName(loader, spec); + this.return_ = return_; + } + + public int return_() { + return return_; // :) + } + } } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 98b545ac56..58d788d5dd 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -18,7 +18,7 @@ import avian.Classes; import java.lang.annotation.Annotation; public class Method extends AccessibleObject implements Member { - private final VMMethod vmMethod; + public final VMMethod vmMethod; private boolean accessible; public Method(VMMethod vmMethod) { @@ -59,6 +59,18 @@ public class Method extends AccessibleObject implements Member { return getSpec(vmMethod); } + public String toString() { + StringBuilder sb = new StringBuilder(); + if (vmMethod.class_ != null) { + sb.append(Classes.makeString(vmMethod.class_.name, 0, + vmMethod.class_.name.length - 1)); + sb.append("."); + } + sb.append(getName()); + sb.append(getSpec()); + return sb.toString(); + } + public static String getSpec(VMMethod vmMethod) { return Classes.makeString(vmMethod.spec, 0, vmMethod.spec.length - 1); } diff --git a/classpath/java/lang/reflect/Proxy.java b/classpath/java/lang/reflect/Proxy.java index 306a48d916..60bbedc7ec 100644 --- a/classpath/java/lang/reflect/Proxy.java +++ b/classpath/java/lang/reflect/Proxy.java @@ -23,6 +23,7 @@ import avian.ConstantPool; import avian.ConstantPool.PoolEntry; import avian.Assembler; +import avian.Assembler.FieldData; import avian.Assembler.MethodData; import java.util.List; @@ -402,6 +403,7 @@ public class Proxy { ByteArrayOutputStream out = new ByteArrayOutputStream(); Assembler.writeClass (out, pool, nameIndex, superIndex, interfaceIndexes, + new FieldData[0], methodTable.toArray(new MethodData[methodTable.size()])); byte[] classData = out.toByteArray(); diff --git a/src/avian/machine.h b/src/avian/machine.h index f56b9720ac..b028981c76 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -3408,18 +3408,18 @@ inline GcClass* resolveClassInPool(Thread* t, inline object resolve( Thread* t, GcClassLoader* loader, - GcMethod* method, + GcSingleton* pool, unsigned index, object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*), Gc::Type errorType, bool throw_ = true) { - object o = singletonObject(t, method->code()->pool(), index); + object o = singletonObject(t, pool, index); loadMemoryBarrier(); if (objectClass(t, o) == type(t, GcReference::Type)) { - PROTECT(t, method); + PROTECT(t, pool); GcReference* reference = cast(t, o); PROTECT(t, reference); @@ -3439,8 +3439,7 @@ inline object resolve( if (o) { storeStoreMemoryBarrier(); - method->code()->pool()->setBodyElement( - t, index, reinterpret_cast(o)); + pool->setBodyElement(t, index, reinterpret_cast(o)); } } else { o = 0; @@ -3459,7 +3458,7 @@ inline GcField* resolveField(Thread* t, return cast(t, resolve(t, loader, - method, + method->code()->pool(), index, findFieldInClass, GcNoSuchFieldError::Type, @@ -3562,7 +3561,7 @@ inline GcMethod* resolveMethod(Thread* t, return cast(t, resolve(t, loader, - method, + method->code()->pool(), index, findMethodInClass, GcNoSuchMethodError::Type, diff --git a/src/compile.cpp b/src/compile.cpp index be2cc48d4e..3825132c8e 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -1284,6 +1284,8 @@ unsigned& dynamicIndex(MyThread* t); void**& dynamicTable(MyThread* t); +unsigned& dynamicTableSize(MyThread* t); + void updateDynamicTable(MyThread* t, MyThread* o) { o->dynamicTable = dynamicTable(t); @@ -1301,6 +1303,7 @@ uintptr_t compileVirtualThunk(MyThread* t, uintptr_t thunk, const char* baseName); +Allocator* allocator(MyThread* t); unsigned addDynamic(MyThread* t, GcInvocation* invocation) { @@ -1319,7 +1322,7 @@ unsigned addDynamic(MyThread* t, GcInvocation* invocation) unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096; void** newTable = static_cast( - t->m->heap->allocate(newCapacity * BytesPerWord)); + allocator(t)->allocate(newCapacity * BytesPerWord)); GcArray* newData = makeArray(t, newCapacity); PROTECT(t, newData); @@ -1345,7 +1348,11 @@ unsigned addDynamic(MyThread* t, GcInvocation* invocation) ENTER(t, Thread::ExclusiveState); + if (dynamicTable(t)) { + allocator(t)->free(dynamicTable(t), dynamicTableSize(t)); + } dynamicTable(t) = newTable; + dynamicTableSize(t) = newCapacity * BytesPerWord; roots(t)->setInvocations(t, newData); updateDynamicTable(static_cast(t->m->rootThread), t); @@ -7050,13 +7057,13 @@ void finish(MyThread* t, FixedAllocator* allocator, Context* context) reinterpret_cast(context->method->spec()->body().begin())); // for debugging: - if (true + if (false and ::strcmp(reinterpret_cast( context->method->class_()->name()->body().begin()), - "InvokeDynamic") == 0 + "java/lang/System") == 0 and ::strcmp(reinterpret_cast( context->method->name()->body().begin()), - "main") == 0) { + "") == 0) { trap(); } syncInstructionCache(start, codeSize); @@ -7271,6 +7278,187 @@ uint64_t compileVirtualMethod(MyThread* t) return reinterpret_cast(compileVirtualMethod2(t, class_, index)); } +GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) +{ + PROTECT(t, invocation); + + GcClass* c = invocation->class_(); + PROTECT(t, c); + + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + + PROTECT(t, bootstrapArray); + + GcMethod* bootstrap = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[0], + findMethodInClass, + GcNoSuchMethodError::Type)); + PROTECT(t, bootstrap); + + assertT(t, bootstrap->parameterCount() == 2 + bootstrapArray->length()); + + GcLookup* lookup + = makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC); + PROTECT(t, lookup); + + GcByteArray* nameBytes = invocation->template_()->name(); + GcString* name + = t->m->classpath->makeString(t, nameBytes, 0, nameBytes->length() - 1); + PROTECT(t, name); + + GcMethodType* type = makeMethodType( + t, c->loader(), invocation->template_()->spec(), 0, 0, 0); + PROTECT(t, type); + + GcArray* array = makeArray(t, bootstrap->parameterCount()); + PROTECT(t, array); + + unsigned argument = 0; + array->setBodyElement(t, argument++, lookup); + array->setBodyElement(t, argument++, name); + array->setBodyElement(t, argument++, type); + + MethodSpecIterator it( + t, reinterpret_cast(bootstrap->spec()->body().begin())); + + for (unsigned i = 0; i < argument; ++i) + it.next(); + + unsigned i = 0; + while (it.hasNext()) { + const char* p = it.next(); + switch (*p) { + case 'L': { + const char* const methodType = "Ljava/lang/invoke/MethodType;"; + const char* const methodHandle = "Ljava/lang/invoke/MethodHandle;"; + if (strncmp(p, methodType, strlen(methodType)) == 0) { + GcMethodType* type = makeMethodType( + t, + c->loader(), + cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[i + 1])), + 0, + 0, + 0); + + array->setBodyElement(t, i + argument, type); + } else if (strncmp(p, methodHandle, strlen(methodHandle)) == 0) { + GcMethod* method = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[i + 1], + findMethodInClass, + GcNoSuchMethodError::Type)); + + GcMethodHandle* handle = makeMethodHandle(t, c->loader(), method, 0); + + array->setBodyElement(t, i + argument, handle); + } else { + abort(t); + } + } break; + + case 'I': + case 'F': { + GcInt* box = makeInt( + t, + singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1])); + + array->setBodyElement(t, i + argument, box); + } break; + + case 'J': + case 'D': { + uint64_t v; + memcpy( + &v, + &singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1]), + 8); + + GcLong* box = makeLong(t, v); + + array->setBodyElement(t, i + argument, box); + } break; + + default: + abort(t); + } + + ++i; + } + + GcMethodHandle* handle = (bootstrap->flags() & ACC_STATIC) + ? 0 + : makeMethodHandle(t, c->loader(), bootstrap, 0); + + return cast( + t, t->m->processor->invokeArray(t, bootstrap, handle, array)); +} + +void* linkDynamicMethod2(MyThread* t, unsigned index) +{ + GcInvocation* invocation + = cast(t, roots(t)->invocations()->body()[index]); + + GcCallSite* site = invocation->site(); + + loadMemoryBarrier(); + + if (site == 0) { + t->trace->targetMethod = invocation->template_(); + + THREAD_RESOURCE0(t, static_cast(t)->trace->targetMethod = 0;); + + PROTECT(t, invocation); + + site = resolveDynamic(t, invocation); + PROTECT(t, site); + + compile(t, codeAllocator(t), 0, site->target()->method()); + + ACQUIRE(t, t->m->classLock); + + if (invocation->site() == 0) { + void* address + = reinterpret_cast(methodAddress(t, site->target()->method())); + + if ((site->target()->method()->flags() & ACC_NATIVE) == 0) { + t->dynamicTable[index] = address; + } + } + + storeStoreMemoryBarrier(); + + invocation->setSite(t, site); + site->setInvocation(t, invocation); + } + + GcMethod* target = invocation->site()->target()->method(); + + if (target->flags() & ACC_NATIVE) { + t->trace->nativeMethod = target; + } + + return reinterpret_cast(methodAddress(t, target)); +} + +uint64_t linkDynamicMethod(MyThread* t) +{ + unsigned index = t->virtualCallIndex; + t->virtualCallIndex = 0; + + return reinterpret_cast(linkDynamicMethod2(t, index)); +} + uint64_t invokeNativeFast(MyThread* t, GcMethod* method, void* function) { FastNativeFunction f; @@ -8482,10 +8670,12 @@ class MyProcessor : public Processor { dynamicIndex(0), useNativeFeatures(useNativeFeatures), compilationHandlers(0), - dynamicTable(0) + dynamicTable(0), + dynamicTableSize(0) { thunkTable[compileMethodIndex] = voidPointer(local::compileMethod); thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod); + thunkTable[linkDynamicMethodIndex] = voidPointer(linkDynamicMethod); thunkTable[invokeNativeIndex] = voidPointer(invokeNative); thunkTable[throwArrayIndexOutOfBoundsIndex] = voidPointer(throwArrayIndexOutOfBounds); @@ -8956,6 +9146,10 @@ class MyProcessor : public Processor { signals.unregisterHandler(SignalRegistrar::DivideByZero); signals.setCrashDumpDirectory(0); + if (dynamicTable) { + allocator->free(dynamicTable, dynamicTableSize); + } + this->~MyProcessor(); allocator->free(this, sizeof(*this)); @@ -9244,6 +9438,7 @@ class MyProcessor : public Processor { void* thunkTable[dummyIndex + 1]; CompilationHandlerList* compilationHandlers; void** dynamicTable; + unsigned dynamicTableSize; }; unsigned& dynamicIndex(MyThread* t) @@ -9256,6 +9451,11 @@ void**& dynamicTable(MyThread* t) return static_cast(t->m->processor)->dynamicTable; } +unsigned& dynamicTableSize(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicTableSize; +} + const char* stringOrNull(const char* str) { if (str) { @@ -10433,6 +10633,11 @@ avian::util::FixedAllocator* codeAllocator(MyThread* t) return &(processor(t)->codeAllocator); } +Allocator* allocator(MyThread* t) +{ + return processor(t)->allocator; +} + } // namespace local } // namespace diff --git a/src/machine.cpp b/src/machine.cpp index 7e2c95c9be..1cd581f9ba 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -1220,7 +1220,7 @@ GcClassAddendum* getClassAddendum(Thread* t, GcClass* class_, GcSingleton* pool) if (addendum == 0) { PROTECT(t, class_); - addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0); + addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0, 0); setField(t, class_, ClassAddendum, addendum); } return addendum; @@ -2811,6 +2811,25 @@ void parseAttributeTable(Thread* t, GcClassAddendum* addendum = getClassAddendum(t, class_, pool); addendum->setAnnotationTable(t, body); + } else if (vm::strcmp(reinterpret_cast("BootstrapMethods"), + name->body().begin()) == 0) { + unsigned count = s.read2(); + GcArray* array = makeArray(t, count); + PROTECT(t, array); + + for (unsigned i = 0; i < count; ++i) { + unsigned reference = s.read2() - 1; + unsigned argumentCount = s.read2(); + GcCharArray* element = makeCharArray(t, 1 + argumentCount); + element->body()[0] = reference; + for (unsigned ai = 0; ai < argumentCount; ++ai) { + element->body()[1 + ai] = s.read2() - 1; + } + array->setBodyElement(t, i, element); + } + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setBootstrapMethodTable(t, array); } else if (vm::strcmp(reinterpret_cast("EnclosingMethod"), name->body().begin()) == 0) { int16_t enclosingClass = s.read2(); diff --git a/src/types.def b/src/types.def index 3e2a341955..da247c9d26 100644 --- a/src/types.def +++ b/src/types.def @@ -22,6 +22,16 @@ (type cloneable java/lang/Cloneable) +(type callSite java/lang/invoke/CallSite + (require invocation invocation)) + +(type methodHandle java/lang/invoke/MethodHandle + (alias method method vmtarget)) + +(type methodType java/lang/invoke/MethodType) + +(type lookup java/lang/invoke/MethodHandles$Lookup) + (type singleton avian/Singleton (array maybe_object body)) @@ -76,10 +86,10 @@ (type invocation (uint16_t bootstrap) (int32_t index) - (object class) - (object pool) + (class class) + (singleton pool) (method template) - (object site)) + (callSite site)) (type triple (object first) diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java index 6a222700ee..7f54990324 100644 --- a/test/InvokeDynamic.java +++ b/test/InvokeDynamic.java @@ -1,7 +1,15 @@ public class InvokeDynamic { + private interface Operation { + int operate(int a, int b); + } + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + public static void main(String[] args) { - Runnable r = () -> System.out.println("success"); - r.run(); + int c = 4; + Operation op = (a, b) -> a + b - c; + expect(op.operate(2, 3) == 1); } } diff --git a/test/Subroutine.java b/test/Subroutine.java index 5fb5bfa223..e03686a9f5 100644 --- a/test/Subroutine.java +++ b/test/Subroutine.java @@ -2,6 +2,7 @@ import avian.Stream; import avian.ConstantPool; import avian.ConstantPool.PoolEntry; import avian.Assembler; +import avian.Assembler.FieldData; import avian.Assembler.MethodData; import java.util.ArrayList; @@ -95,7 +96,7 @@ public class Subroutine { Assembler.writeClass (out, pool, ConstantPool.addClass(pool, name), ConstantPool.addClass(pool, "java/lang/Object"), - new int[0], new MethodData[] + new int[0], new FieldData[0], new MethodData[] { new MethodData(Assembler.ACC_STATIC | Assembler.ACC_PUBLIC, ConstantPool.addUtf8(pool, "test"), ConstantPool.addUtf8(pool, "()V"), From 8a7944d25c4daaeb40942bd08a125ec5e3729b39 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 6 Aug 2015 13:24:06 -0600 Subject: [PATCH 5/8] add support for openjdk=$JDK8_HOME All tests pass for the process=compile build. Next step: process=interpret. --- .travis.yml | 3 + classpath/avian/Classes.java | 2 + .../java/lang/invoke/LambdaMetafactory.java | 33 ++- classpath/java/lang/invoke/MethodHandle.java | 25 +- classpath/java/lang/invoke/MethodType.java | 254 ++++++++++-------- classpath/java/lang/reflect/Method.java | 12 - makefile | 7 +- src/avian/constants.h | 10 + src/builtin.cpp | 7 + src/classpath-openjdk.cpp | 1 + src/compile.cpp | 16 +- test/InvokeDynamic.java | 16 +- 12 files changed, 247 insertions(+), 139 deletions(-) diff --git a/.travis.yml b/.travis.yml index d18b9c7fd3..8a029d50da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: cpp cache: apt +jdk: + - oraclejdk8 + os: - linux - osx diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index def0047be5..7d428b28d4 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -45,6 +45,8 @@ 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; diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java index fc26c54fa9..5a814d1649 100644 --- a/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -155,7 +155,19 @@ public class LambdaMetafactory { write1(out, p.position() + 1); } - write1(out, invokestatic); + switch (implementation.kind) { + case MethodHandle.REF_invokeStatic: + write1(out, invokestatic); + break; + + case MethodHandle.REF_invokeSpecial: + write1(out, invokespecial); + 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"); + } + write2(out, ConstantPool.addMethodRef (pool, Classes.makeString(implementation.method.class_.name, 0, @@ -213,11 +225,9 @@ public class LambdaMetafactory { try { methodTable.add (new MethodData - (Modifier.STATIC, + (Modifier.PUBLIC | Modifier.STATIC, ConstantPool.addUtf8(pool, "make"), - ConstantPool.addUtf8(pool, Classes.makeString - (invokedType.spec, 0, - invokedType.spec.length - 1)), + ConstantPool.addUtf8(pool, invokedType.toMethodDescriptorString()), makeFactoryCode(pool, className, constructorSpec, invokedType))); methodTable.add @@ -231,9 +241,7 @@ public class LambdaMetafactory { (new MethodData (Modifier.PUBLIC, ConstantPool.addUtf8(pool, invokedName), - ConstantPool.addUtf8(pool, Classes.makeString - (methodType.spec, 0, - methodType.spec.length - 1)), + ConstantPool.addUtf8(pool, methodType.toMethodDescriptorString()), makeInvocationCode(pool, className, constructorSpec, invokedType, methodType, methodImplementation))); } catch (IOException e) { @@ -262,10 +270,11 @@ public class LambdaMetafactory { try { return new CallSite (new MethodHandle - (invokedType.loader, avian.SystemClassLoader.getClass - (avian.Classes.defineVMClass - (invokedType.loader, classData, 0, classData.length)) - .getMethod("make", invokedType.parameterArray()).vmMethod)); + (MethodHandle.REF_invokeStatic, invokedType.loader, Classes.toVMMethod + (avian.SystemClassLoader.getClass + (avian.Classes.defineVMClass + (invokedType.loader, classData, 0, classData.length)) + .getMethod("make", invokedType.parameterArray())))); } catch (NoSuchMethodException e) { AssertionError error = new AssertionError(); error.initCause(e); diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java index 386d2354d4..4efbfbabd6 100644 --- a/classpath/java/lang/invoke/MethodHandle.java +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -1,18 +1,35 @@ package java.lang.invoke; +import avian.Classes; + public class MethodHandle { + static final int REF_invokeStatic = 6; + static final int REF_invokeSpecial = 7; + + final int kind; private final ClassLoader loader; final avian.VMMethod method; private volatile MethodType type; - MethodHandle(ClassLoader loader, avian.VMMethod method) { + MethodHandle(int kind, ClassLoader loader, avian.VMMethod method) { + this.kind = kind; this.loader = loader; this.method = method; } - + public String toString() { - return new java.lang.reflect.Method(method).toString(); - } + StringBuilder sb = new StringBuilder(); + if (method.class_ != null) { + sb.append(Classes.makeString(method.class_.name, 0, + method.class_.name.length - 1)); + sb.append("."); + } + sb.append(Classes.makeString(method.name, 0, + method.name.length - 1)); + sb.append(Classes.makeString(method.spec, 0, + method.spec.length - 1)); + return sb.toString(); + } public MethodType type() { if (type == null) { diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java index 44dd79e3cc..c8bf5bce62 100644 --- a/classpath/java/lang/invoke/MethodType.java +++ b/classpath/java/lang/invoke/MethodType.java @@ -2,12 +2,18 @@ package java.lang.invoke; import static avian.Assembler.*; +import avian.Assembler; import avian.Classes; +import avian.VMClass; import java.util.List; import java.util.ArrayList; public final class MethodType implements java.io.Serializable { + private static final char[] Primitives = new char[] { + 'V', 'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D' + }; + final ClassLoader loader; final byte[] spec; private volatile List parameters; @@ -19,6 +25,77 @@ public final class MethodType implements java.io.Serializable { this.spec = spec; } + public String toMethodDescriptorString() { + return Classes.makeString(spec, 0, spec.length - 1); + } + + private static String spec(Class c) { + if (c.isPrimitive()) { + VMClass vmc = Classes.toVMClass(c); + for (char p: Primitives) { + if (vmc == Classes.primitiveClass(p)) { + return String.valueOf(p); + } + } + throw new AssertionError(); + } else if (c.isArray()) { + return "[" + spec(c.getComponentType()); + } else { + return "L" + c.getName().replace('.', '/') + ";"; + } + } + + private MethodType(Class rtype, + Class ... ptypes) + { + loader = rtype.getClassLoader(); + + StringBuilder sb = new StringBuilder(); + sb.append('('); + parameters = new ArrayList(ptypes.length); + int position = 0; + for (int i = 0; i < ptypes.length; ++i) { + String spec = spec(ptypes[i]); + sb.append(spec); + + Type type = type(spec); + + parameters.add(new Parameter(i, + position, + spec, + ptypes[i], + type.load)); + + position += type.size; + } + sb.append(')'); + + footprint = position; + + String spec = spec(rtype); + sb.append(spec); + + result = new Result(spec, rtype, type(spec).return_); + + this.spec = sb.toString().getBytes(); + } + + public static MethodType methodType(Class rtype, + Class ptype0, + Class ... ptypes) + { + Class[] array = new Class[ptypes.length + 1]; + array[0] = ptype0; + System.arraycopy(ptypes, 0, array, 1, ptypes.length); + return methodType(rtype, array); + } + + public static MethodType methodType(Class rtype, + Class ... ptypes) + { + return new MethodType(rtype, ptypes); + } + public String toString() { return Classes.makeString(spec, 0, spec.length - 1); } @@ -53,21 +130,14 @@ public final class MethodType implements java.io.Serializable { int index = 0; int position = 0; for (i = 1; spec[i] != ')'; ++i) { + int start = i; switch (spec[i]) { case 'L': { - int start = i; ++ i; while (spec[i] != ';') ++ i; - - list.add(new Parameter - (index, - position, - Classes.makeString(spec, start, (i - start) + 1), - aload)); } break; case '[': { - int start = i; ++ i; while (spec[i] == '[') ++ i; @@ -80,12 +150,6 @@ public final class MethodType implements java.io.Serializable { default: break; } - - list.add(new Parameter - (index, - position, - Classes.makeString(spec, start, (i - start) + 1), - aload)); } break; case 'Z': @@ -93,107 +157,38 @@ public final class MethodType implements java.io.Serializable { case 'S': case 'C': case 'I': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - iload)); - break; - case 'F': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - fload)); - break; - case 'J': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - lload)); - - ++ position; - break; - case 'D': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - dload)); - - ++ position; break; default: throw new AssertionError(); } + String paramSpec = Classes.makeString(spec, start, (i - start) + 1); + Type type = type(paramSpec); + + list.add(new Parameter + (index, + position, + paramSpec, + Classes.forCanonicalName(loader, paramSpec), + type.load)); + ++ index; - ++ position; + position += type.size; } footprint = position; ++ i; - switch (spec[i]) { - case 'L': { - int start = i; - ++ i; - while (spec[i] != ';') ++ i; - - result = new Result - (Classes.makeString(spec, start, (i - start) + 1), areturn); - } break; - - case '[': { - int start = i; - ++ i; - while (spec[i] == '[') ++ i; - - switch (spec[i]) { - case 'L': - ++ i; - while (spec[i] != ';') ++ i; - break; - - default: - break; - } - - result = new Result(Classes.makeString(spec, start, (i - start) + 1), - areturn); - } break; - - case 'V': - result = new Result(Classes.makeString(spec, i, 1), return_); - break; - - case 'Z': - case 'B': - case 'S': - case 'C': - case 'I': - result = new Result(Classes.makeString(spec, i, 1), ireturn); - break; - - case 'F': - result = new Result(Classes.makeString(spec, i, 1), freturn); - break; - - case 'J': - result = new Result(Classes.makeString(spec, i, 1), lreturn); - break; - - case 'D': - result = new Result(Classes.makeString(spec, i, 1), dreturn); - break; - - default: throw new AssertionError(); - } + String paramSpec = Classes.makeString(spec, i, spec.length - i - 1); + Type type = type(paramSpec); + + result = new Result(paramSpec, + Classes.forCanonicalName(loader, paramSpec), + type.return_); parameters = list; } @@ -207,7 +202,55 @@ public final class MethodType implements java.io.Serializable { return result; } - public class Parameter { + private static Type type(String spec) { + switch (spec.charAt(0)) { + case 'L': + case '[': + return Type.ObjectType; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + return Type.IntegerType; + + case 'F': + return Type.FloatType; + + case 'J': + return Type.LongType; + + case 'D': + return Type.DoubleType; + + case 'V': + return Type.VoidType; + + default: throw new AssertionError(); + } + } + + private static enum Type { + ObjectType(aload, areturn, 1), + IntegerType(iload, ireturn, 1), + FloatType(fload, freturn, 1), + LongType(lload, lreturn, 1), + DoubleType(dload, dreturn, 1), + VoidType(-1, Assembler.return_, -1); + + public final int load; + public final int return_; + public final int size; + + private Type(int load, int return_, int size) { + this.load = load; + this.return_ = return_; + this.size = size; + } + } + + public static class Parameter { private final int index; private final int position; private final String spec; @@ -217,12 +260,13 @@ public final class MethodType implements java.io.Serializable { private Parameter(int index, int position, String spec, + Class type, int load) { this.index = index; this.position = position; this.spec = spec; - this.type = Classes.forCanonicalName(loader, spec); + this.type = type; this.load = load; } @@ -243,14 +287,14 @@ public final class MethodType implements java.io.Serializable { } } - public class Result { + public static class Result { private final String spec; private final Class type; private final int return_; - public Result(String spec, int return_) { + public Result(String spec, Class type, int return_) { this.spec = spec; - this.type = Classes.forCanonicalName(loader, spec); + this.type = type; this.return_ = return_; } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 58d788d5dd..705fc8ec16 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -58,18 +58,6 @@ public class Method extends AccessibleObject implements Member { private String getSpec() { return getSpec(vmMethod); } - - public String toString() { - StringBuilder sb = new StringBuilder(); - if (vmMethod.class_ != null) { - sb.append(Classes.makeString(vmMethod.class_.name, 0, - vmMethod.class_.name.length - 1)); - sb.append("."); - } - sb.append(getName()); - sb.append(getSpec()); - return sb.toString(); - } public static String getSpec(VMMethod vmMethod) { return Classes.makeString(vmMethod.spec, 0, vmMethod.spec.length - 1); diff --git a/makefile b/makefile index 037724ad2e..d102e06bdb 100755 --- a/makefile +++ b/makefile @@ -1473,7 +1473,12 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/VMField.java \ $(classpath-src)/avian/VMMethod.java \ $(classpath-src)/avian/avianvmresource/Handler.java \ - $(classpath-src)/avian/file/Handler.java + $(classpath-src)/avian/file/Handler.java \ + $(classpath-src)/java/lang/invoke/MethodHandle.java \ + $(classpath-src)/java/lang/invoke/MethodHandles.java \ + $(classpath-src)/java/lang/invoke/MethodType.java \ + $(classpath-src)/java/lang/invoke/LambdaMetafactory.java \ + $(classpath-src)/java/lang/invoke/CallSite.java ifeq ($(openjdk),) classpath-sources := $(classpath-sources) \ diff --git a/src/avian/constants.h b/src/avian/constants.h index d40d58b055..ef2bc24364 100644 --- a/src/avian/constants.h +++ b/src/avian/constants.h @@ -263,6 +263,16 @@ const unsigned ACC_INTERFACE = 1 << 9; const unsigned ACC_ABSTRACT = 1 << 10; const unsigned ACC_STRICT = 1 << 11; +const unsigned REF_getField = 1; +const unsigned REF_getStatic = 2; +const unsigned REF_putField = 3; +const unsigned REF_putStatic = 4; +const unsigned REF_invokeVirtual = 5; +const unsigned REF_invokeStatic = 6; +const unsigned REF_invokeSpecial = 7; +const unsigned REF_newInvokeSpecial = 8; +const unsigned REF_invokeInterface = 9; + const int AVIAN_JNI_COMMIT = 1; const int AVIAN_JNI_ABORT = 2; diff --git a/src/builtin.cpp b/src/builtin.cpp index 979b52f6e1..8cecd382ad 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -113,6 +113,13 @@ extern "C" AVIAN_EXPORT int64_t JNICALL cast(t, reinterpret_cast(arguments[0]))->vmClass()); } +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_toVMMethod(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(t->m->classpath->getVMMethod( + t, cast(t, reinterpret_cast(arguments[0])))); +} + extern "C" AVIAN_EXPORT void JNICALL Avian_avian_Classes_initialize(Thread* t, object, uintptr_t* arguments) { diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 946528aaca..5cbed68a1a 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -484,6 +484,7 @@ class MyClasspath : public Classpath { } oarray = charArray; + offset = 0; } else { expect(t, objectClass(t, oarray) == type(t, GcCharArray::Type)); } diff --git a/src/compile.cpp b/src/compile.cpp index 3825132c8e..21b81af337 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -7351,6 +7351,12 @@ GcCallSite* resolveDynamic(MyThread* 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, resolve(t, c->loader(), @@ -7359,7 +7365,8 @@ GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) findMethodInClass, GcNoSuchMethodError::Type)); - GcMethodHandle* handle = makeMethodHandle(t, c->loader(), method, 0); + GcMethodHandle* handle + = makeMethodHandle(t, kind, c->loader(), method, 0); array->setBodyElement(t, i + argument, handle); } else { @@ -7396,9 +7403,10 @@ GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) ++i; } - GcMethodHandle* handle = (bootstrap->flags() & ACC_STATIC) - ? 0 - : makeMethodHandle(t, c->loader(), bootstrap, 0); + GcMethodHandle* handle + = (bootstrap->flags() & ACC_STATIC) + ? 0 + : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); return cast( t, t->m->processor->invokeArray(t, bootstrap, handle, array)); diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java index 7f54990324..86f1d1a412 100644 --- a/test/InvokeDynamic.java +++ b/test/InvokeDynamic.java @@ -1,4 +1,10 @@ public class InvokeDynamic { + private final int foo; + + private InvokeDynamic(int foo) { + this.foo = foo; + } + private interface Operation { int operate(int a, int b); } @@ -10,6 +16,14 @@ public class InvokeDynamic { public static void main(String[] args) { int c = 4; Operation op = (a, b) -> a + b - c; - expect(op.operate(2, 3) == 1); + expect(op.operate(2, 3) == (2 + 3) - 4); + + new InvokeDynamic(3).test(); + } + + private void test() { + int c = 2; + Operation op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); } } From 66712a8cfff98bafa7e0dcfe4634cd80f3393d65 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 6 Aug 2015 17:22:14 -0600 Subject: [PATCH 6/8] add invokedynamic support to interpreter --- src/avian/machine.h | 2 + src/compile.cpp | 136 +--------------------------------------- src/interpret.cpp | 28 +++++++++ src/machine.cpp | 134 +++++++++++++++++++++++++++++++++++++++ test/InvokeDynamic.java | 4 +- 5 files changed, 168 insertions(+), 136 deletions(-) diff --git a/src/avian/machine.h b/src/avian/machine.h index b028981c76..9f5654303d 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -2349,6 +2349,8 @@ GcClass* findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec); GcJclass* getDeclaringClass(Thread* t, GcClass* c); +GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation); + inline bool emptyMethod(Thread* t UNUSED, GcMethod* method) { return ((method->flags() & ACC_NATIVE) == 0) diff --git a/src/compile.cpp b/src/compile.cpp index 21b81af337..74605cc9b5 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -58,7 +58,7 @@ namespace { namespace local { -const bool DebugCompile = true; +const bool DebugCompile = false; const bool DebugNatives = false; const bool DebugCallTable = false; const bool DebugMethodTree = false; @@ -7278,140 +7278,6 @@ uint64_t compileVirtualMethod(MyThread* t) return reinterpret_cast(compileVirtualMethod2(t, class_, index)); } -GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) -{ - PROTECT(t, invocation); - - GcClass* c = invocation->class_(); - PROTECT(t, c); - - GcCharArray* bootstrapArray = cast( - t, - cast(t, c->addendum()->bootstrapMethodTable()) - ->body()[invocation->bootstrap()]); - - PROTECT(t, bootstrapArray); - - GcMethod* bootstrap = cast(t, - resolve(t, - c->loader(), - invocation->pool(), - bootstrapArray->body()[0], - findMethodInClass, - GcNoSuchMethodError::Type)); - PROTECT(t, bootstrap); - - assertT(t, bootstrap->parameterCount() == 2 + bootstrapArray->length()); - - GcLookup* lookup - = makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC); - PROTECT(t, lookup); - - GcByteArray* nameBytes = invocation->template_()->name(); - GcString* name - = t->m->classpath->makeString(t, nameBytes, 0, nameBytes->length() - 1); - PROTECT(t, name); - - GcMethodType* type = makeMethodType( - t, c->loader(), invocation->template_()->spec(), 0, 0, 0); - PROTECT(t, type); - - GcArray* array = makeArray(t, bootstrap->parameterCount()); - PROTECT(t, array); - - unsigned argument = 0; - array->setBodyElement(t, argument++, lookup); - array->setBodyElement(t, argument++, name); - array->setBodyElement(t, argument++, type); - - MethodSpecIterator it( - t, reinterpret_cast(bootstrap->spec()->body().begin())); - - for (unsigned i = 0; i < argument; ++i) - it.next(); - - unsigned i = 0; - while (it.hasNext()) { - const char* p = it.next(); - switch (*p) { - case 'L': { - const char* const methodType = "Ljava/lang/invoke/MethodType;"; - const char* const methodHandle = "Ljava/lang/invoke/MethodHandle;"; - if (strncmp(p, methodType, strlen(methodType)) == 0) { - GcMethodType* type = makeMethodType( - t, - c->loader(), - cast( - t, - singletonObject( - t, invocation->pool(), bootstrapArray->body()[i + 1])), - 0, - 0, - 0); - - 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, - resolve(t, - c->loader(), - invocation->pool(), - bootstrapArray->body()[i + 1], - findMethodInClass, - GcNoSuchMethodError::Type)); - - GcMethodHandle* handle - = makeMethodHandle(t, kind, c->loader(), method, 0); - - array->setBodyElement(t, i + argument, handle); - } else { - abort(t); - } - } break; - - case 'I': - case 'F': { - GcInt* box = makeInt( - t, - singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1])); - - array->setBodyElement(t, i + argument, box); - } break; - - case 'J': - case 'D': { - uint64_t v; - memcpy( - &v, - &singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1]), - 8); - - GcLong* box = makeLong(t, v); - - array->setBodyElement(t, i + argument, box); - } break; - - default: - abort(t); - } - - ++i; - } - - GcMethodHandle* handle - = (bootstrap->flags() & ACC_STATIC) - ? 0 - : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); - - return cast( - t, t->m->processor->invokeArray(t, bootstrap, handle, array)); -} - void* linkDynamicMethod2(MyThread* t, unsigned index) { GcInvocation* invocation diff --git a/src/interpret.cpp b/src/interpret.cpp index 49507ffca5..67871b40d8 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -1981,6 +1981,34 @@ loop: } goto loop; + case invokedynamic: { + uint16_t index = codeReadInt16(t, code, ip); + + ip += 2; + + GcInvocation* invocation = cast(t, singletonObject(t, code->pool(), index - 1)); + + GcCallSite* site = invocation->site(); + + loadMemoryBarrier(); + + if (site == 0) { + PROTECT(t, invocation); + + invocation->setClass(t, frameMethod(t, frame)->class_()); + + site = resolveDynamic(t, invocation); + PROTECT(t, site); + + storeStoreMemoryBarrier(); + + invocation->setSite(t, site); + site->setInvocation(t, invocation); + } + + method = site->target()->method(); + } goto invoke; + case invokeinterface: { uint16_t index = codeReadInt16(t, code, ip); diff --git a/src/machine.cpp b/src/machine.cpp index 1cd581f9ba..87cdf5f626 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -6032,6 +6032,140 @@ GcJclass* getDeclaringClass(Thread* t, GcClass* c) return 0; } +GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) +{ + PROTECT(t, invocation); + + GcClass* c = invocation->class_(); + PROTECT(t, c); + + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + + PROTECT(t, bootstrapArray); + + GcMethod* bootstrap = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[0], + findMethodInClass, + GcNoSuchMethodError::Type)); + PROTECT(t, bootstrap); + + assertT(t, bootstrap->parameterCount() == 2 + bootstrapArray->length()); + + GcLookup* lookup + = makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC); + PROTECT(t, lookup); + + GcByteArray* nameBytes = invocation->template_()->name(); + GcString* name + = t->m->classpath->makeString(t, nameBytes, 0, nameBytes->length() - 1); + PROTECT(t, name); + + GcMethodType* type = makeMethodType( + t, c->loader(), invocation->template_()->spec(), 0, 0, 0); + PROTECT(t, type); + + GcArray* array = makeArray(t, bootstrap->parameterCount()); + PROTECT(t, array); + + unsigned argument = 0; + array->setBodyElement(t, argument++, lookup); + array->setBodyElement(t, argument++, name); + array->setBodyElement(t, argument++, type); + + MethodSpecIterator it( + t, reinterpret_cast(bootstrap->spec()->body().begin())); + + for (unsigned i = 0; i < argument; ++i) + it.next(); + + unsigned i = 0; + while (it.hasNext()) { + const char* p = it.next(); + switch (*p) { + case 'L': { + const char* const methodType = "Ljava/lang/invoke/MethodType;"; + const char* const methodHandle = "Ljava/lang/invoke/MethodHandle;"; + if (strncmp(p, methodType, strlen(methodType)) == 0) { + GcMethodType* type = makeMethodType( + t, + c->loader(), + cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[i + 1])), + 0, + 0, + 0); + + 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, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[i + 1], + findMethodInClass, + GcNoSuchMethodError::Type)); + + GcMethodHandle* handle + = makeMethodHandle(t, kind, c->loader(), method, 0); + + array->setBodyElement(t, i + argument, handle); + } else { + abort(t); + } + } break; + + case 'I': + case 'F': { + GcInt* box = makeInt( + t, + singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1])); + + array->setBodyElement(t, i + argument, box); + } break; + + case 'J': + case 'D': { + uint64_t v; + memcpy( + &v, + &singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1]), + 8); + + GcLong* box = makeLong(t, v); + + array->setBodyElement(t, i + argument, box); + } break; + + default: + abort(t); + } + + ++i; + } + + GcMethodHandle* handle + = (bootstrap->flags() & ACC_STATIC) + ? 0 + : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); + + return cast( + t, t->m->processor->invokeArray(t, bootstrap, handle, array)); +} + void noop() { } diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java index 86f1d1a412..90d88d6fc6 100644 --- a/test/InvokeDynamic.java +++ b/test/InvokeDynamic.java @@ -18,7 +18,9 @@ public class InvokeDynamic { Operation op = (a, b) -> a + b - c; expect(op.operate(2, 3) == (2 + 3) - 4); - new InvokeDynamic(3).test(); + for (int i = 0; i < 4; ++i) { + new InvokeDynamic(i).test(); + } } private void test() { From 549810deba59cf0dc0761b7c9283cf79e2c243ac Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 6 Aug 2015 17:23:14 -0600 Subject: [PATCH 7/8] allow building with Java 7 --- .travis.yml | 3 --- makefile | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a029d50da..d18b9c7fd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ language: cpp cache: apt -jdk: - - oraclejdk8 - os: - linux - osx diff --git a/makefile b/makefile index d102e06bdb..ddf9a55bd3 100755 --- a/makefile +++ b/makefile @@ -3,6 +3,10 @@ MAKEFLAGS = -s name = avian version := $(shell grep version gradle.properties | cut -d'=' -f2) +java-version := $(shell $(JAVA_HOME)/bin/java -version 2>&1 \ + | head -n 1 \ + | sed 's/.*version "1.\([^.]*\).*/\1/') + build-arch := $(shell uname -m \ | sed 's/^i.86$$/i386/' \ | sed 's/^x86pc$$/i386/' \ @@ -1478,6 +1482,7 @@ ifneq ($(classpath),avian) $(classpath-src)/java/lang/invoke/MethodHandles.java \ $(classpath-src)/java/lang/invoke/MethodType.java \ $(classpath-src)/java/lang/invoke/LambdaMetafactory.java \ + $(classpath-src)/java/lang/invoke/LambdaConversionException.java \ $(classpath-src)/java/lang/invoke/CallSite.java ifeq ($(openjdk),) @@ -1514,7 +1519,10 @@ vm-classes = \ avian/resource/*.class test-support-sources = $(shell find $(test)/avian/ -name '*.java') -test-sources = $(wildcard $(test)/*.java) +test-sources := $(wildcard $(test)/*.java) +ifeq (7,$(java-version)) + test-sources := $(subst $(test)/InvokeDynamic.java,,$(test-sources)) +endif test-cpp-sources = $(wildcard $(test)/*.cpp) test-sources += $(test-support-sources) test-support-classes = $(call java-classes, $(test-support-sources),$(test),$(test-build)) @@ -1694,7 +1702,7 @@ $(classpath-dep): $(classpath-sources) $(classpath-jar-dep) classes="$(shell $(MAKE) -s --no-print-directory build=$(build) \ $(classpath-classes) arch=$(build-arch) platform=$(bootimage-platform))"; \ if [ -n "$${classes}" ]; then \ - $(javac) -source 1.8 -target 1.8 \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ -d $(classpath-build) -bootclasspath $(boot-classpath) \ $${classes}; fi @touch $(@) @@ -1770,7 +1778,7 @@ $(test-dep): $(test-sources) $(test-library) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \ if test -n "$${files}"; then \ - $(javac) -source 1.8 -target 1.8 \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ -classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ @@ -1782,7 +1790,7 @@ $(test-extra-dep): $(test-extra-sources) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \ if test -n "$${files}"; then \ - $(javac) -source 1.8 -target 1.8 \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi @touch $(@) From 03cf4f98a1abb40af255be8c390a95b4c2a4aa8a Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 7 Aug 2015 20:49:50 -0600 Subject: [PATCH 8/8] remove invoke.sh This wasn't meant to be checked in long-term. --- invoke.sh | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 invoke.sh diff --git a/invoke.sh b/invoke.sh deleted file mode 100644 index 30c76e186c..0000000000 --- a/invoke.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -unzip -l /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/rt.jar | \ - grep java/lang/invoke | \ - grep -E '\$' | \ - awk '{print $4}' | \ - sed -E -e 's/\.class//g' | \ - while read path; do - echo $path - name=$(echo $path | sed -e 's|/|.|g') - - if javap -public $name | grep -q "public.*interface"; then - javap -public $name | grep -v "Compiled from" | \ - sed -E \ - -e 's/java.lang.invoke.//g' \ - -e 's/java.lang.Object/Object o/g' \ - -e 's/java.lang.reflect.Method/Method o/g' \ - -e 's/java.lang.String/String s/g' \ - -e 's/java.lang.Throwable/Throwable/g' \ - -e 's/int/int i/g' \ - -e 's/byte/byte b/g' \ - -e 's/boolean/boolean b/g' \ - -e 's/MethodType/MethodType mt/g' \ - -e 's/MethodHandle/MethodHandle mh/g' \ - -e 's/java.lang.Class\<\?\>/Class\<\?\> c/g' \ - -e 's/;/ { throw new RuntimeException(); }/g' \ - -e 's/public String s/public String/g' \ - -e 's/public static String s/public static String/g' \ - -e 's/public abstract MethodHandle mh/public abstract MethodHandle/g' \ - -e 's/public abstract MethodHandle mh/public abstract MethodHandle/g' \ - -e 's/public boolean b/public boolean/g' \ - -e 's/public int i/public int/g' \ - -e 's/public static final int i/public static final int/g' \ - -e 's/public byte b/public byte/g' \ - -e 's/public Object o/public Object/g' \ - -e 's/public MethodType mt/public MethodType/g' \ - -e 's/public MethodHandle mh/public MethodHandle/g' \ - -e 's/Object o\.\.\./Object... o/g' \ - -e 's/public final native Object o/public final native Object/g' \ - -e 's/public Class c/public Class/g' \ - > classpath/${path}.java - fi - done