1 /*
  2  * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.internal.reflect;
 27 
 28 import java.lang.invoke.WrongMethodTypeException;
 29 import java.util.Set;
 30 
 31 /**
 32  * Utility methods used by DirectMethodHandleAccessor and DirectConstructorHandleAccessor
 33  */
 34 public class AccessorUtils {
 35     /**
 36      * Determines if the given exception thrown by MethodHandle::invokeExact
 37      * is caused by an illegal argument passed to Method::invoke or
 38      * Constructor::newInstance.  This method inspects the stack trace of
 39      * the exception to detect if it is thrown by the method handle core
 40      * implementation or the implementation of the reflected method or constructor.
 41      *
 42      * MethodHandle::invoke throws ClassCastException if the receiver object
 43      * is not an instance of the declaring class of the method if the method
 44      * is an instance method, or if a parameter value cannot be converted
 45      * to the corresponding formal parameter type.  It throws
 46      * NullPointerException if the receiver object is null if the method
 47      * is an instance method, or if unboxing operation of a parameter fails
 48      * because the parameter value is null.  It throws WrongMethodTypeException
 49      * if the method type mismatches.
 50      *
 51      * @param accessorType the accessor class that does the method handle invocation
 52      * @param e ClassCastException, NullPointerException or WrongMethodTypeException
 53      */
 54     static boolean isIllegalArgument(Class<?> accessorType, RuntimeException e) {
 55         assert(e instanceof ClassCastException || e instanceof NullPointerException ||
 56                e instanceof WrongMethodTypeException);
 57 
 58         StackTraceElement[] stackTrace = e.getStackTrace();
 59         if (stackTrace.length == 0) {
 60             return false;       // would this happen?
 61         }
 62 
 63         int i = 0;
 64         StackTraceElement frame = stackTrace[0];
 65         // Class::cast and Objects::requiresNonNull may be thrown by the implementation
 66         // of the reflected method/constructor.  Skip them and continue.
 67         if ((frame.getClassName().equals("java.lang.Class") && frame.getMethodName().equals("cast"))
 68                 || (frame.getClassName().equals("java.util.Objects") && frame.getMethodName().equals("requiresNonNull"))) {
 69             i++;
 70         }
 71         for (; i < stackTrace.length; i++) {
 72             frame = stackTrace[i];
 73             String cname = frame.getClassName();
 74             // it's illegal argument if this exception is thrown from accessorType
 75             if (cname.equals(accessorType.getName())) {
 76                 return true;
 77             }
 78             // if this exception is thrown from an unnamed module or not from java.base
 79             // then i.e. not from method handle core implementation
 80             if (frame.getModuleName() == null || !frame.getModuleName().equals("java.base")) {
 81                 return false;
 82             }
 83             int index = cname.lastIndexOf(".");
 84             String pn = index > 0 ? cname.substring(0, index) : "";
 85             // exception thrown from java.base but not from core reflection/method handle internals
 86             if (!IMPL_PACKAGES.contains(pn)) {
 87                 return false;
 88             }
 89             // If Constructor::newInstance is invoked by Method::invoke or vice versa,
 90             // so the exception is thrown from the implementation body of the reflected
 91             // method or constructor
 92             if ((accessorType == DirectMethodHandleAccessor.class
 93                     && cname.startsWith(DirectConstructorHandleAccessor.class.getName()))
 94                 || (accessorType == DirectConstructorHandleAccessor.class
 95                         && cname.startsWith(DirectMethodHandleAccessor.class.getName()))) {
 96                 // thrown from another reflection accessor impl class
 97                 return false;
 98             }
 99         }
100         return false;
101     }
102 
103     private static final Set<String> IMPL_PACKAGES = Set.of(
104             "java.lang.reflect",
105             "java.lang.invoke",
106             "jdk.internal.reflect",
107             "sun.invoke.util"
108     );
109 }