AccessFixer.java revision b72c5c2e5482cf10117b2b25f642f7616b2326c3
1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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 */
21package proguard.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.constant.*;
25import proguard.classfile.constant.visitor.ConstantVisitor;
26import proguard.classfile.util.*;
27import proguard.classfile.visitor.*;
28
29/**
30 * This ConstantVisitor fixes the access modifiers of all classes and class
31 * members that are referenced by the constants that it visits.
32 *
33 * @author Eric Lafortune
34 */
35public class AccessFixer
36extends      SimplifiedVisitor
37implements   ConstantVisitor,
38             ClassVisitor,
39             MemberVisitor
40{
41    private MyReferencedClassFinder referencedClassFinder = new MyReferencedClassFinder();
42
43    private Clazz referencingClass;
44    private Clazz referencedClass;
45
46
47    // Implementations for ConstantVisitor.
48
49    public void visitAnyConstant(Clazz clazz, Constant constant) {}
50
51
52    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
53    {
54        referencingClass = clazz;
55        referencedClass  = stringConstant.referencedClass;
56
57        // Make sure the access flags of the referenced class or class member,
58        // if any, are acceptable.
59        stringConstant.referencedClassAccept(this);
60        stringConstant.referencedMemberAccept(this);
61    }
62
63
64    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
65    {
66        referencingClass = clazz;
67
68        // Remember the specified class, since it might be different from
69        // the referenced class that acutally contains the class member.
70        clazz.constantPoolEntryAccept(refConstant.u2classIndex, referencedClassFinder);
71
72        // Make sure the access flags of the referenced class member are
73        // acceptable.
74        refConstant.referencedMemberAccept(this);
75    }
76
77
78    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
79    {
80        referencingClass = clazz;
81
82        // Make sure the access flags of the referenced class are acceptable.
83        classConstant.referencedClassAccept(this);
84    }
85
86
87    // Implementations for ClassVisitor.
88
89    public void visitLibraryClass(LibraryClass libraryClass) {}
90
91
92    public void visitProgramClass(ProgramClass programClass)
93    {
94        int currentAccessFlags  = programClass.getAccessFlags();
95        int currentAccessLevel  = AccessUtil.accessLevel(currentAccessFlags);
96
97        // Compute the required access level.
98        Clazz referencingClass = this.referencingClass;
99        int requiredAccessLevel =
100            inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE :
101                                                            AccessUtil.PUBLIC;
102
103        // Fix the class access flags if necessary.
104        if (currentAccessLevel < requiredAccessLevel)
105        {
106            programClass.u2accessFlags =
107                AccessUtil.replaceAccessFlags(currentAccessFlags,
108                                              AccessUtil.accessFlags(requiredAccessLevel));
109        }
110    }
111
112
113    // Implementations for MemberVisitor.
114
115    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) {}
116
117
118    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
119    {
120        int currentAccessFlags  = programMember.getAccessFlags();
121        int currentAccessLevel  = AccessUtil.accessLevel(currentAccessFlags);
122
123        // Compute the required access level.
124        int requiredAccessLevel =
125            programClass.equals(referencingClass)         ? AccessUtil.PRIVATE         :
126            inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE :
127            referencedClass.extends_(referencingClass) &&
128            referencingClass.extends_(programClass)       ? AccessUtil.PROTECTED       :
129                                                            AccessUtil.PUBLIC;
130
131        // Fix the class member access flags if necessary.
132        if (currentAccessLevel < requiredAccessLevel)
133        {
134            programMember.u2accessFlags =
135                AccessUtil.replaceAccessFlags(currentAccessFlags,
136                                              AccessUtil.accessFlags(requiredAccessLevel));
137        }
138    }
139
140
141    /**
142     * This ConstantVisitor returns the referenced class of the class constant
143     * that it visits.
144     */
145    private class MyReferencedClassFinder
146    extends       SimplifiedVisitor
147    implements    ConstantVisitor
148    {
149        // Implementations for ConstantVisitor.
150        public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
151        {
152            referencedClass = classConstant.referencedClass;
153        }
154    }
155
156
157    // Small utility methods.
158
159    private boolean inSamePackage(ProgramClass class1, Clazz class2)
160    {
161        return ClassUtil.internalPackageName(class1.getName()).equals(
162               ClassUtil.internalPackageName(class2.getName()));
163    }
164}
165