1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.classfile.editor; 22 23 import proguard.classfile.*; 24 import proguard.classfile.constant.*; 25 import proguard.classfile.util.*; 26 import proguard.classfile.visitor.*; 27 28 /** 29 * This ClassVisitor fixes the access modifiers of all classes and class 30 * members that are referenced by the classes that it visits. 31 * 32 * @author Eric Lafortune 33 */ 34 public class AccessFixer 35 extends ReferencedClassVisitor 36 implements ClassVisitor 37 { 38 /** 39 * Creates a new AccessFixer. 40 */ AccessFixer()41 public AccessFixer() 42 { 43 // Unfortunately, the inner class must be static to be passed to the 44 // super constructor. We therefore can't let it refer to this class; 45 // we'll let this class refer to the inner class instead. 46 super(new MyAccessFixer()); 47 } 48 49 50 // Overridden methods for ClassVisitor. 51 visitProgramClass(ProgramClass programClass)52 public void visitProgramClass(ProgramClass programClass) 53 { 54 // Remember the referencing class. 55 ((MyAccessFixer)classVisitor).referencingClass = programClass; 56 57 // Start visiting and fixing the referenced classes and class members. 58 super.visitProgramClass(programClass); 59 } 60 61 visitLibraryClass(LibraryClass libraryClass)62 public void visitLibraryClass(LibraryClass libraryClass) 63 { 64 // Remember the referencing class. 65 ((MyAccessFixer)classVisitor).referencingClass = libraryClass; 66 67 // Start visiting and fixing the referenced classes and class members. 68 super.visitLibraryClass(libraryClass); 69 } 70 71 72 // Overridden methods for MemberVisitor. 73 visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)74 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 75 { 76 // Fix the referenced classes and class members. 77 super.visitProgramMember(programClass, programMethod); 78 79 // Fix overridden or implemented methods higher up the hierarchy. 80 // We can ignore private and static methods and initializers. 81 if ((programMethod.getAccessFlags() & (ClassConstants.ACC_PRIVATE | 82 ClassConstants.ACC_STATIC)) == 0 && 83 !ClassUtil.isInitializer(programMethod.getName(programClass))) 84 { 85 programClass.hierarchyAccept(false, true, false, false, 86 new NamedMethodVisitor(programMethod.getName(programClass), 87 programMethod.getDescriptor(programClass), 88 new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | 89 ClassConstants.ACC_STATIC, 90 (MemberVisitor)classVisitor))); 91 } 92 } 93 94 visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)95 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 96 { 97 // Fix the referenced classes and class members. 98 super.visitLibraryMember(libraryClass, libraryMethod); 99 100 // Fix overridden or implemented methods higher up the hierarchy. 101 // We can ignore private and static methods and initializers. 102 if ((libraryMethod.getAccessFlags() & (ClassConstants.ACC_PRIVATE | 103 ClassConstants.ACC_STATIC)) == 0 && 104 !ClassUtil.isInitializer(libraryMethod.getName(libraryClass))) 105 { 106 libraryClass.hierarchyAccept(false, true, false, false, 107 new NamedMethodVisitor(libraryMethod.getName(libraryClass), 108 libraryMethod.getDescriptor(libraryClass), 109 new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | 110 ClassConstants.ACC_STATIC, 111 (MemberVisitor)classVisitor))); 112 } 113 } 114 115 116 // Overridden methods for ConstantVisitor. 117 visitStringConstant(Clazz clazz, StringConstant stringConstant)118 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 119 { 120 // Fix the access flags of the referenced class, if any. 121 super.visitStringConstant(clazz, stringConstant); 122 123 // Fix the access flags of the referenced class member, if any. 124 stringConstant.referencedMemberAccept((MemberVisitor)classVisitor); 125 } 126 127 visitAnyRefConstant(Clazz clazz, RefConstant refConstant)128 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 129 { 130 // Fix the access flags of the referenced class. 131 super.visitAnyRefConstant(clazz, refConstant); 132 133 // Fix the access flags of the referenced class member. 134 refConstant.referencedMemberAccept((MemberVisitor)classVisitor); 135 } 136 137 138 /** 139 * This ClassVisitor and MemberVisitor fixes the access flags of the 140 * classes and class members that it visits, relative to the referencing 141 * class. 142 */ 143 private static class MyAccessFixer 144 extends SimplifiedVisitor 145 implements ClassVisitor, 146 MemberVisitor 147 { 148 private Clazz referencingClass; 149 150 151 // Implementations for ClassVisitor. 152 visitLibraryClass(LibraryClass libraryClass)153 public void visitLibraryClass(LibraryClass libraryClass) {} 154 155 visitProgramClass(ProgramClass programClass)156 public void visitProgramClass(ProgramClass programClass) 157 { 158 int currentAccessFlags = programClass.getAccessFlags(); 159 int currentAccessLevel = AccessUtil.accessLevel(currentAccessFlags); 160 161 // Compute the required access level. 162 int requiredAccessLevel = 163 inSamePackage(programClass, referencingClass) ? 164 AccessUtil.PACKAGE_VISIBLE : 165 AccessUtil.PUBLIC; 166 167 // Fix the class access flags if necessary. 168 if (currentAccessLevel < requiredAccessLevel) 169 { 170 programClass.u2accessFlags = 171 AccessUtil.replaceAccessFlags(currentAccessFlags, 172 AccessUtil.accessFlags(requiredAccessLevel)); 173 } 174 } 175 176 177 // Implementations for MemberVisitor. 178 visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)179 public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) {} 180 181 visitProgramMember(ProgramClass programClass, ProgramMember programMember)182 public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) 183 { 184 int currentAccessFlags = programMember.getAccessFlags(); 185 int currentAccessLevel = AccessUtil.accessLevel(currentAccessFlags); 186 187 // Compute the required access level. 188 int requiredAccessLevel = 189 programClass.equals(referencingClass) ? AccessUtil.PRIVATE : 190 inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE : 191 programClass.extends_(referencingClass) && 192 referencingClass.extends_(programClass) ? AccessUtil.PROTECTED : 193 AccessUtil.PUBLIC; 194 195 // Fix the class member access flags if necessary. 196 if (currentAccessLevel < requiredAccessLevel) 197 { 198 programMember.u2accessFlags = 199 AccessUtil.replaceAccessFlags(currentAccessFlags, 200 AccessUtil.accessFlags(requiredAccessLevel)); 201 } 202 } 203 204 205 // Small utility methods. 206 207 /** 208 * Returns whether the two given classes are in the same package. 209 */ inSamePackage(ProgramClass class1, Clazz class2)210 private boolean inSamePackage(ProgramClass class1, Clazz class2) 211 { 212 return ClassUtil.internalPackageName(class1.getName()).equals( 213 ClassUtil.internalPackageName(class2.getName())); 214 } 215 } 216 } 217