1 /*
   2  * Copyright (c) 2009, 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package invokespecial;
  26 
  27 import java.lang.reflect.Method;
  28 import java.lang.reflect.Modifier;
  29 
  30 public class Checker extends shared.Checker {
  31 
  32     public Checker(Class staticTargetClass, Class dynamicTargetClass) {
  33         super(staticTargetClass, dynamicTargetClass);
  34     }
  35 
  36     public String check (Class callerClass) {
  37         /*
  38          * If objectref is null, the invokespecial instruction throws a NullPointerException.
  39          */
  40         if (dynamicTargetClass == null) {
  41             return "java.lang.NullPointerException";
  42         }
  43 
  44         /*
  45          * TODO: find a citiation from spec for this case
  46          */
  47         Method resolvedMethod;
  48         try {
  49             // May throw VerifyError
  50             resolvedMethod = getMethodInHierarchy(staticTargetClass);
  51         } catch (Throwable e) {
  52             return e.getClass().getName();
  53         }
  54 
  55         if (resolvedMethod == null) {
  56             return "java.lang.NoSuchMethodError";
  57         }
  58 
  59        /*
  60         * If:
  61         *   - the resolved method is protected (4.7)
  62         *   - it is a member of a superclass of the current class
  63         *   - the method is not declared in the same run-time package (5.3) as the current class
  64         * then:
  65         *   the class of objectref must be either the current class or a subclass of the
  66         * current class.
  67         */
  68 
  69         if (Modifier.isProtected(resolvedMethod.getModifiers())) {
  70             Method methodInSuperclass = getMethodInHierarchy(resolvedMethod.getDeclaringClass().getSuperclass());
  71 
  72             if (methodInSuperclass != null) {
  73                 String resolvedMethodPkg = getClassPackageName(resolvedMethod.getDeclaringClass());
  74                 String methodInSuperclassPkg = getClassPackageName(methodInSuperclass.getDeclaringClass());
  75 
  76                 if (!resolvedMethodPkg.equals(methodInSuperclassPkg)) {
  77                     //TODO: clarify this
  78 //                    if (callerClass == methodInSuperclass.getDeclaringClass()) {
  79 //                        return "java.lang.IllegalAccessError";
  80 //                    }
  81                 }
  82             }
  83         }
  84 
  85        /*
  86         * The resolved method is selected for invocation unless all of
  87         * the following conditions are true:
  88         *     * TODO: The ACC_SUPER flag (see Table 4.1, "Class access and property
  89         *       modifiers") is set for the current class.
  90         *     * The class of the resolved method is a superclass of the
  91         *       current class - assumed by construction procedure
  92         *
  93         *     * The resolved method is not an instance initialization method (3.9).
  94         */
  95         if (!"<init>".equals(methodName)) {
  96            /*
  97             * Let C be the direct superclass of the current class:
  98             *    * If C contains a declaration for an instance method with the same
  99             *      name and descriptor as the resolved method, then this method will be
 100             *      invoked. The lookup procedure terminates.
 101             *    * Otherwise, if C has a superclass, this same lookup procedure is
 102             *      performed recursively using the direct superclass of C. The method to
 103             *      be invoked is the result of the recursive invocation of this lookup
 104             *      procedure.
 105             *    * Otherwise, an AbstractMethodError is raised.
 106             *      TODO: so far, sometimes NSME is thrown
 107             */
 108             Class klass = dynamicTargetClass.getSuperclass();
 109 
 110             while (klass != Object.class) {
 111                 Method method = getDeclaredMethod(klass);
 112 
 113                 if (method != null) {
 114                     /*
 115                      * If the resolved method is a class (static) method, the
 116                      * invokespecial instruction throws an IncompatibleClassChangeError.
 117                      */
 118                     if (Modifier.isStatic(method.getModifiers())) {
 119                         return "java.lang.IncompatibleClassChangeError";
 120                     }
 121 
 122                     // Check access rights
 123                     if ( checkAccess(method, callerClass)
 124 //                         && !(
 125 //                                 Modifier.isProtected(method.getModifiers())
 126 //                                 && (
 127 //                                     staticTargetClass.isAssignableFrom(callerClass)
 128 //                                     || getClassPackageName(staticTargetClass).equals(getClassPackageName(callerClass))
 129 //                                    )
 130 //
 131 //                            )
 132                         )
 133                     {
 134                         return String.format("%s.%s"
 135                                 , method.getDeclaringClass().getSimpleName()
 136                                 , methodName
 137                                 );
 138                     } else {
 139                         // IAE is thrown when located method can't be accessed from the call site
 140                         return "java.lang.IllegalAccessError";
 141                     }
 142                 }
 143 
 144                 klass = klass.getSuperclass();
 145             }
 146 
 147             return "java.lang.AbstractMethodError";
 148         } else {
 149             // The resolved method is an instance initialization method (3.9).
 150         }
 151 
 152         // TODO: change
 153         return "---";
 154     }
 155 }