8275063: Implementation of Foreign Function & Memory API (Second incubator)

Reviewed-by: erikj, psandoz, jvernee, darcy
This commit is contained in:
Maurizio Cimadamore 2021-11-24 11:51:16 +00:00
parent 17e68caad7
commit 96e36071b6
191 changed files with 9463 additions and 7631 deletions

View file

@ -68,6 +68,7 @@ import jdk.internal.org.objectweb.asm.ModuleVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.Stable;
import sun.security.util.SecurityConstants;
/**
@ -110,7 +111,8 @@ public final class Module implements AnnotatedElement {
private final ModuleDescriptor descriptor;
// true, if this module allows restricted native access
private volatile boolean enableNativeAccess;
@Stable
private boolean enableNativeAccess;
/**
* Creates a new named Module. The resulting Module will be defined to the

View file

@ -1667,6 +1667,10 @@ abstract class MethodHandleImpl {
return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData);
}
@Override
public Class<?>[] exceptionTypes(MethodHandle handle) {
return VarHandles.exceptionTypes(handle);
}
});
}
@ -2273,15 +2277,16 @@ abstract class MethodHandleImpl {
// Indexes into constant method handles:
static final int
MH_cast = 0,
MH_selectAlternative = 1,
MH_countedLoopPred = 2,
MH_countedLoopStep = 3,
MH_initIterator = 4,
MH_iteratePred = 5,
MH_iterateNext = 6,
MH_Array_newInstance = 7,
MH_LIMIT = 8;
MH_cast = 0,
MH_selectAlternative = 1,
MH_countedLoopPred = 2,
MH_countedLoopStep = 3,
MH_initIterator = 4,
MH_iteratePred = 5,
MH_iterateNext = 6,
MH_Array_newInstance = 7,
MH_VarHandles_handleCheckedExceptions = 8,
MH_LIMIT = 9;
static MethodHandle getConstantHandle(int idx) {
MethodHandle handle = HANDLES[idx];
@ -2331,6 +2336,9 @@ abstract class MethodHandleImpl {
case MH_Array_newInstance:
return IMPL_LOOKUP.findStatic(Array.class, "newInstance",
MethodType.methodType(Object.class, Class.class, int.class));
case MH_VarHandles_handleCheckedExceptions:
return IMPL_LOOKUP.findStatic(VarHandles.class, "handleCheckedExceptions",
MethodType.methodType(void.class, Throwable.class));
}
} catch (ReflectiveOperationException ex) {
throw newInternalError(ex);

View file

@ -31,12 +31,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@ -45,8 +42,6 @@ import java.util.stream.Stream;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.VAR_HANDLE_IDENTITY_ADAPT;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
final class VarHandles {
@ -359,13 +354,13 @@ final class VarHandles {
return target;
}
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
public static VarHandle filterValue(VarHandle target, MethodHandle pFilterToTarget, MethodHandle pFilterFromTarget) {
Objects.requireNonNull(target);
Objects.requireNonNull(filterToTarget);
Objects.requireNonNull(filterFromTarget);
Objects.requireNonNull(pFilterToTarget);
Objects.requireNonNull(pFilterFromTarget);
//check that from/to filters do not throw checked exceptions
noCheckedExceptions(filterToTarget);
noCheckedExceptions(filterFromTarget);
MethodHandle filterToTarget = adaptForCheckedExceptions(pFilterToTarget);
MethodHandle filterFromTarget = adaptForCheckedExceptions(pFilterFromTarget);
List<Class<?>> newCoordinates = new ArrayList<>();
List<Class<?>> additionalCoordinates = new ArrayList<>();
@ -473,8 +468,9 @@ final class VarHandles {
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
for (int i = 0 ; i < filters.length ; i++) {
noCheckedExceptions(filters[i]);
MethodType filterType = filters[i].type();
MethodHandle filter = Objects.requireNonNull(filters[i]);
filter = adaptForCheckedExceptions(filter);
MethodType filterType = filter.type();
if (filterType.parameterCount() != 1) {
throw newIllegalArgumentException("Invalid filter type " + filterType);
} else if (newCoordinates.get(pos + i) != filterType.returnType()) {
@ -564,10 +560,10 @@ final class VarHandles {
return adjustedType;
}
public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) {
public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle pFilter) {
Objects.requireNonNull(target);
Objects.requireNonNull(filter);
noCheckedExceptions(filter);
Objects.requireNonNull(pFilter);
MethodHandle filter = adaptForCheckedExceptions(pFilter);
List<Class<?>> targetCoordinates = target.coordinateTypes();
if (pos < 0 || pos >= targetCoordinates.size()) {
@ -604,42 +600,55 @@ final class VarHandles {
(mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes));
}
private static void noCheckedExceptions(MethodHandle handle) {
private static MethodHandle adaptForCheckedExceptions(MethodHandle target) {
Class<?>[] exceptionTypes = exceptionTypes(target);
if (exceptionTypes != null) { // exceptions known
if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) {
throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions");
}
return target; // no adaptation needed
} else {
MethodHandle handler = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_VarHandles_handleCheckedExceptions);
MethodHandle zero = MethodHandles.zero(target.type().returnType()); // dead branch
handler = MethodHandles.collectArguments(zero, 0, handler);
return MethodHandles.catchException(target, Throwable.class, handler);
}
}
static void handleCheckedExceptions(Throwable throwable) throws Throwable {
if (isCheckedException(throwable.getClass())) {
throw new IllegalStateException("Adapter handle threw checked exception", throwable);
}
throw throwable;
}
static Class<?>[] exceptionTypes(MethodHandle handle) {
if (handle instanceof DirectMethodHandle directHandle) {
byte refKind = directHandle.member.getReferenceKind();
MethodHandleInfo info = new InfoFromMemberName(
MethodHandles.Lookup.IMPL_LOOKUP,
directHandle.member,
refKind);
final Class<?>[] exceptionTypes;
if (MethodHandleNatives.refKindIsMethod(refKind)) {
exceptionTypes = info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
return info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
.getExceptionTypes();
} else if (MethodHandleNatives.refKindIsField(refKind)) {
exceptionTypes = null;
return new Class<?>[0];
} else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
exceptionTypes = info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
return info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
.getExceptionTypes();
} else {
throw new AssertionError("Cannot get here");
}
if (exceptionTypes != null) {
if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) {
throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions");
}
}
} else if (handle instanceof DelegatingMethodHandle) {
noCheckedExceptions(((DelegatingMethodHandle)handle).getTarget());
} else {
//bound
BoundMethodHandle boundHandle = (BoundMethodHandle)handle;
for (int i = 0 ; i < boundHandle.fieldCount() ; i++) {
Object arg = boundHandle.arg(i);
if (arg instanceof MethodHandle){
noCheckedExceptions((MethodHandle) arg);
}
}
return exceptionTypes(((DelegatingMethodHandle)handle).getTarget());
} else if (handle instanceof NativeMethodHandle) {
return new Class<?>[0];
}
assert handle instanceof BoundMethodHandle : "Unexpected handle type: " + handle;
// unknown
return null;
}
private static boolean isCheckedException(Class<?> clazz) {

View file

@ -820,7 +820,7 @@ public abstract class Buffer {
}
@Override
public Scope.Handle acquireScope(Buffer buffer, boolean async) {
public Runnable acquireScope(Buffer buffer, boolean async) {
var scope = buffer.scope();
if (scope == null) {
return null;
@ -828,7 +828,8 @@ public abstract class Buffer {
if (async && scope.ownerThread() != null) {
throw new IllegalStateException("Confined scope not supported");
}
return scope.acquire();
scope.acquire0();
return scope::release0;
}
@Override

View file

@ -181,4 +181,11 @@ public interface JavaLangInvokeAccess {
* The given bytes is trusted.
*/
Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize);
/**
* A best-effort method that tries to find any exceptions thrown by the given method handle.
* @param handle the handle to check
* @return an array of exceptions, or {@code null}.
*/
Class<?>[] exceptionTypes(MethodHandle handle);
}

View file

@ -92,7 +92,7 @@ public interface JavaNioAccess {
* scope handle. Null is returned if the buffer has no scope, or
* acquiring is not required to guarantee safety.
*/
Scope.Handle acquireScope(Buffer buffer, boolean async);
Runnable acquireScope(Buffer buffer, boolean async);
/**
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views.

View file

@ -103,19 +103,13 @@ public class ScopedMemoryAccess {
*/
public interface Scope {
interface Handle {
Scope scope();
}
void checkValidState();
Thread ownerThread();
boolean isImplicit();
void acquire0();
Handle acquire();
void release(Handle handle);
void release0();
/**
* Error thrown when memory access fails because the memory has already been released.

View file

@ -32,6 +32,7 @@ import java.util.Objects;
import java.util.Set;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
/** Common utility routines used by both java.lang and
@ -106,6 +107,7 @@ public class Reflection {
}
}
@ForceInline
public static void ensureNativeAccess(Class<?> currentClass) {
Module module = currentClass.getModule();
if (!SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module)) {

View file

@ -475,15 +475,15 @@ public class IOUtil {
private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
static Scope.Handle acquireScope(ByteBuffer bb, boolean async) {
static Runnable acquireScope(ByteBuffer bb, boolean async) {
return NIO_ACCESS.acquireScope(bb, async);
}
private static void releaseScope(Scope.Handle handle) {
private static void releaseScope(Runnable handle) {
if (handle == null)
return;
try {
handle.scope().release(handle);
handle.run();
} catch (Exception e) {
throw new IllegalStateException(e);
}
@ -535,11 +535,11 @@ public class IOUtil {
}
}
static record Releaser(Scope.Handle handle) implements Runnable {
static record Releaser(Runnable handle) implements Runnable {
Releaser { Objects.requireNonNull(handle) ; }
@Override public void run() { releaseScope(handle); }
static Runnable of(Scope.Handle handle) { return new Releaser(handle); }
static Runnable ofNullable(Scope.Handle handle) {
static Runnable of(Runnable handle) { return new Releaser(handle); }
static Runnable ofNullable(Runnable handle) {
if (handle == null)
return () -> { };
return new Releaser(handle);