< prev index next >

src/java.base/share/classes/java/lang/invoke/VarHandles.java

Print this page
*** 29,26 ***
  
  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;
  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 {
  
      static ClassValue<ConcurrentMap<Integer, MethodHandle>> ADDRESS_FACTORIES = new ClassValue<>() {
          @Override
--- 29,21 ---

*** 357,17 ***
              target = filterCoordinates(target, i, MethodHandles.identity(mtype.parameterType(i)));
          }
          return target;
      }
  
!     public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
          Objects.requireNonNull(target);
!         Objects.requireNonNull(filterToTarget);
!         Objects.requireNonNull(filterFromTarget);
          //check that from/to filters do not throw checked exceptions
!         noCheckedExceptions(filterToTarget);
!         noCheckedExceptions(filterFromTarget);
  
          List<Class<?>> newCoordinates = new ArrayList<>();
          List<Class<?>> additionalCoordinates = new ArrayList<>();
          newCoordinates.addAll(target.coordinateTypes());
  
--- 352,17 ---
              target = filterCoordinates(target, i, MethodHandles.identity(mtype.parameterType(i)));
          }
          return target;
      }
  
!     public static VarHandle filterValue(VarHandle target, MethodHandle pFilterToTarget, MethodHandle pFilterFromTarget) {
          Objects.requireNonNull(target);
!         Objects.requireNonNull(pFilterToTarget);
!         Objects.requireNonNull(pFilterFromTarget);
          //check that from/to filters do not throw checked exceptions
!         MethodHandle filterToTarget = adaptForCheckedExceptions(pFilterToTarget);
!         MethodHandle filterFromTarget = adaptForCheckedExceptions(pFilterFromTarget);
  
          List<Class<?>> newCoordinates = new ArrayList<>();
          List<Class<?>> additionalCoordinates = new ArrayList<>();
          newCoordinates.addAll(target.coordinateTypes());
  

*** 471,12 ***
  
          if (filters.length == 0) return target;
  
          List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
          for (int i = 0 ; i < filters.length ; i++) {
!             noCheckedExceptions(filters[i]);
!             MethodType filterType = filters[i].type();
              if (filterType.parameterCount() != 1) {
                  throw newIllegalArgumentException("Invalid filter type " + filterType);
              } else if (newCoordinates.get(pos + i) != filterType.returnType()) {
                  throw newIllegalArgumentException("Invalid filter type " + filterType + " for coordinate type " + newCoordinates.get(i));
              }
--- 466,13 ---
  
          if (filters.length == 0) return target;
  
          List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
          for (int i = 0 ; i < filters.length ; i++) {
!             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()) {
                  throw newIllegalArgumentException("Invalid filter type " + filterType + " for coordinate type " + newCoordinates.get(i));
              }

*** 562,14 ***
              adjustedType = adjustedType.appendParameterTypes(oldType.parameterType(1 + oldCoordinates.size() + i));
          }
          return adjustedType;
      }
  
!     public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) {
          Objects.requireNonNull(target);
!         Objects.requireNonNull(filter);
!         noCheckedExceptions(filter);
  
          List<Class<?>> targetCoordinates = target.coordinateTypes();
          if (pos < 0 || pos >= targetCoordinates.size()) {
              throw newIllegalArgumentException("Invalid position " + pos + " for coordinate types", targetCoordinates);
          } else if (filter.type().returnType() == void.class) {
--- 558,14 ---
              adjustedType = adjustedType.appendParameterTypes(oldType.parameterType(1 + oldCoordinates.size() + i));
          }
          return adjustedType;
      }
  
!     public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle pFilter) {
          Objects.requireNonNull(target);
!         Objects.requireNonNull(pFilter);
!         MethodHandle filter = adaptForCheckedExceptions(pFilter);
  
          List<Class<?>> targetCoordinates = target.coordinateTypes();
          if (pos < 0 || pos >= targetCoordinates.size()) {
              throw newIllegalArgumentException("Invalid position " + pos + " for coordinate types", targetCoordinates);
          } else if (filter.type().returnType() == void.class) {

*** 602,46 ***
  
          return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
                  (mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes));
      }
  
!     private static void noCheckedExceptions(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)
                          .getExceptionTypes();
              } else if (MethodHandleNatives.refKindIsField(refKind)) {
!                 exceptionTypes = null;
              } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
!                 exceptionTypes = 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);
-                 }
-             }
          }
      }
  
      private static boolean isCheckedException(Class<?> clazz) {
          return Throwable.class.isAssignableFrom(clazz) &&
                  !RuntimeException.class.isAssignableFrom(clazz) &&
--- 598,59 ---
  
          return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
                  (mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes));
      }
  
!     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);
              if (MethodHandleNatives.refKindIsMethod(refKind)) {
!                 return info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
                          .getExceptionTypes();
              } else if (MethodHandleNatives.refKindIsField(refKind)) {
!                 return new Class<?>[0];
              } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
!                 return info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
                          .getExceptionTypes();
              } else {
                  throw new AssertionError("Cannot get here");
              }
          } else if (handle instanceof DelegatingMethodHandle) {
!             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) {
          return Throwable.class.isAssignableFrom(clazz) &&
                  !RuntimeException.class.isAssignableFrom(clazz) &&
< prev index next >