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         // If objectref is null, the invokespecial instruction throws a NullPointerException.
  38         if (dynamicTargetClass == null) {
  39             return "java.lang.NullPointerException";
  40         }
  41 
  42         // TODO: find a citation from spec for this case
  43         Method resolvedMethod;
  44         try {
  45             // May throw VerifyError
  46             resolvedMethod = getMethodInHierarchy(staticTargetClass);
  47         } catch (Throwable e) {
  48             return e.getClass().getName();
  49         }
  50 
  51         if (resolvedMethod == null) {
  52             return "java.lang.NoSuchMethodError";
  53         }
  54 
  55        // If:
  56        //   - the resolved method is protected (4.7)
  57        //   - it is a member of a superclass of the current class
  58        //   - the method is not declared in the same run-time package (5.3) as the current class
  59        // then:
  60        //   the class of objectref must be either the current class or a subclass of the
  61        // current class.
  62 
  63         if (Modifier.isProtected(resolvedMethod.getModifiers())) {
  64             Method methodInSuperclass = getMethodInHierarchy(resolvedMethod.getDeclaringClass().getSuperclass());
  65 
  66             if (methodInSuperclass != null) {
  67                 String resolvedMethodPkg = getClassPackageName(resolvedMethod.getDeclaringClass());
  68                 String methodInSuperclassPkg = getClassPackageName(methodInSuperclass.getDeclaringClass());
  69 
  70                 if (!resolvedMethodPkg.equals(methodInSuperclassPkg)) {
  71                     //TODO: clarify this
  72 //                    if (callerClass == methodInSuperclass.getDeclaringClass()) {
  73 //                        return "java.lang.IllegalAccessError";
  74 //                    }
  75                 }
  76             }
  77         }
  78 
  79        /*
  80         * The resolved method is selected for invocation unless all of
  81         * the following conditions are true:
  82         *     * TODO: The ACC_SUPER flag (see Table 4.1, "Class access and property
  83         *       modifiers") is set for the current class.
  84         *     * The class of the resolved method is a superclass of the
  85         *       current class - assumed by construction procedure
  86         *
  87         *     * The resolved method is not an instance initialization method (3.9).
  88         */
  89         if (!"<init>".equals(methodName)) {
  90            /*
  91             * Let C be the direct superclass of the current class:
  92             *    * If C contains a declaration for an instance method with the same
  93             *      name and descriptor as the resolved method, then this method will be
  94             *      invoked. The lookup procedure terminates.
  95             *    * Otherwise, if C has a superclass, this same lookup procedure is
  96             *      performed recursively using the direct superclass of C. The method to
  97             *      be invoked is the result of the recursive invocation of this lookup
  98             *      procedure.
  99             *    * Otherwise, an AbstractMethodError is raised.
 100             *      TODO: so far, sometimes NSME is thrown
 101             */
 102             Class klass = dynamicTargetClass.getSuperclass();
 103 
 104             while (klass != Object.class) {
 105                 Method method = getDeclaredMethod(klass);
 106 
 107                 if (method != null) {
 108                     /*
 109                      * If the resolved method is a class (static) method, the
 110                      * invokespecial instruction throws an IncompatibleClassChangeError.
 111                      */
 112                     if (Modifier.isStatic(method.getModifiers())) {
 113                         return "java.lang.IncompatibleClassChangeError";
 114                     }
 115 
 116                     // Check access rights
 117                     if ( checkAccess(method, callerClass)
 118 //                         && !(
 119 //                                 Modifier.isProtected(method.getModifiers())
 120 //                                 && (
 121 //                                     staticTargetClass.isAssignableFrom(callerClass)
 122 //                                     || getClassPackageName(staticTargetClass).equals(getClassPackageName(callerClass))
 123 //                                    )
 124 //
 125 //                            )
 126                         )
 127                     {
 128                         return String.format("%s.%s"
 129                                 , method.getDeclaringClass().getSimpleName()
 130                                 , methodName
 131                                 );
 132                     } else {
 133                         // IAE is thrown when located method can't be accessed from the call site
 134                         return "java.lang.IllegalAccessError";
 135                     }
 136                 }
 137 
 138                 klass = klass.getSuperclass();
 139             }
 140 
 141             return "java.lang.AbstractMethodError";
 142         } else {
 143             // The resolved method is an instance initialization method (3.9).
 144         }
 145 
 146         // TODO: change
 147         return "---";
 148     }
 149 }