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 invokevirtual;
  26 
  27 import java.lang.reflect.Method;
  28 import java.lang.reflect.Modifier;
  29 
  30 
  31 public class Checker extends shared.Checker {
  32     public Checker(Class staticTargetClass, Class dynamicTargetClass) {
  33         super(staticTargetClass, dynamicTargetClass);
  34     }
  35 
  36     public String check (Class callerClass) {
  37         Method m;
  38         try {
  39             // May cause java.lang.VerifyError
  40             m = getOverriddenMethod();
  41         } catch (Throwable e) {
  42             return e.getClass().getName();
  43         }
  44 
  45         // Check method accessibility (it's a static property, according to JLS #6.6: Access Control)
  46         if (m != null) {
  47             Method staticTargetMethod = getDeclaredMethod(staticTargetClass);
  48 
  49             if (checkAccess(staticTargetMethod, callerClass)) {
  50                 // Can't invoke abstract method
  51                 if ( Modifier.isAbstract(m.getModifiers())) {
  52                     return "java.lang.AbstractMethodError";
  53                 }
  54 
  55                 return String.format("%s.%s"
  56                         , m.getDeclaringClass().getSimpleName()
  57                         , methodName
  58                         );
  59             } else {
  60                 // if method isn't accessible, IllegalAccessError is thrown
  61                 return "java.lang.IllegalAccessError";
  62             }
  63         } else {
  64             // if method == null, NoSuchMethodError is thrown
  65             return "java.lang.NoSuchMethodError";
  66         }
  67     }
  68 
  69     public Method getOverriddenMethod() {
  70         return getOverriddenMethod(staticTargetClass, dynamicTargetClass);
  71     }
  72 
  73     public Method getOverriddenMethod(Class staticTarget, Class dynamicTarget) {
  74         // Assertion #1. C is a subclass of A
  75         if (!staticTarget.isAssignableFrom(dynamicTarget)) {
  76             return null;
  77         }
  78 
  79         Method staticTargetMethod = getDeclaredMethod(staticTarget);
  80         Method dynamicTargetMethod = getDeclaredMethod(dynamicTarget);
  81 
  82         if (staticTarget.equals(dynamicTarget)) {
  83             return staticTargetMethod;
  84         }
  85 
  86         // TODO: ? need to find out the right behavior
  87         if (staticTargetMethod == null) {
  88             return null;
  89         }
  90 
  91         // Dynamic target doesn't have desired method, so check it's superclass
  92         if (dynamicTargetMethod == null) {
  93             return getOverriddenMethod(staticTarget, dynamicTarget.getSuperclass());
  94         } else {
  95             // Private method can't override anything
  96             if (Modifier.isPrivate(dynamicTargetMethod.getModifiers())) {
  97                 return getOverriddenMethod(staticTarget, dynamicTarget.getSuperclass());
  98             }
  99         }
 100 
 101         // TODO: abstract methods
 102 
 103         //Assertion #3.a: A.m2 is PUB || PROT || (PP && PKG(A) == PKG(C))
 104         int staticTargetModifiers = staticTargetMethod.getModifiers();
 105         {
 106             boolean isPublic = Modifier.isPublic(staticTargetModifiers);
 107             boolean isProtected = Modifier.isProtected(staticTargetModifiers);
 108             boolean isPrivate = Modifier.isPrivate(staticTargetModifiers) ;
 109             String staticTargetPkg = getClassPackageName(staticTarget);
 110             String dynamicTargetPkg = getClassPackageName(dynamicTarget);
 111 
 112             if ( isPublic || isProtected
 113                  || ( !isPublic && !isProtected && !isPrivate
 114                       && staticTargetPkg.equals(dynamicTargetPkg)
 115                     ))
 116             {
 117                 return dynamicTargetMethod;
 118             }
 119         }
 120         // OR
 121         //Assertion #3.b: exists m3: C.m1 != B.m3, A.m2 != B.m3, B.m3 overrides A.m2, C.m1 overrides B.m3
 122         Class ancestor = dynamicTarget.getSuperclass();
 123         while (ancestor != staticTarget) {
 124             Method OverriddenM2 = getOverriddenMethod(staticTarget, ancestor);
 125             Method m3 = getDeclaredMethod(ancestor);
 126             Method m1 = getOverriddenMethod(ancestor, dynamicTarget);
 127 
 128             if (m1 != null && m3 != null) {
 129                 if (m1.equals(dynamicTargetMethod) && m3.equals(OverriddenM2)) {
 130                     return dynamicTargetMethod;
 131                 }
 132             } else {
 133                 if (m1 == null && dynamicTargetMethod == null
 134                     && m3 == null && OverriddenM2 == null)
 135                 {
 136                     return null;
 137                 }
 138             }
 139 
 140             ancestor = ancestor.getSuperclass();
 141         }
 142 
 143         return getOverriddenMethod(staticTarget, dynamicTarget.getSuperclass());
 144     }
 145 }