001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.optimize.info;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.util.*;
025: import proguard.classfile.visitor.ClassVisitor;
026: import proguard.optimize.KeepMarker;
027:
028: /**
029: * This ClassVisitor investigates all classes that it visits to see whether
030: * they have/are the sole (non-abstract) implementation of an interface.
031: * It may already modify the access of the single implementing class to match
032: * the access of the interface.
033: *
034: * @author Eric Lafortune
035: */
036: public class SingleImplementationMarker extends SimplifiedVisitor
037: implements ClassVisitor {
038: private static final boolean DEBUG = false;
039:
040: private final boolean allowAccessModification;
041: private final ClassVisitor extraClassVisitor;
042:
043: /**
044: * Creates a new SingleImplementationMarker.
045: * @param allowAccessModification indicates whether the access modifiers of
046: * a class can be changed in order to inline
047: * it.
048: */
049: public SingleImplementationMarker(boolean allowAccessModification) {
050: this (allowAccessModification, null);
051: }
052:
053: /**
054: * Creates a new SingleImplementationMarker.
055: * @param allowAccessModification indicates whether the access modifiers of
056: * a class can be changed in order to inline
057: * it.
058: * @param extraClassVisitor an optional extra visitor for all inlinable
059: * interfaces.
060: */
061: public SingleImplementationMarker(boolean allowAccessModification,
062: ClassVisitor extraClassVisitor) {
063: this .allowAccessModification = allowAccessModification;
064: this .extraClassVisitor = extraClassVisitor;
065: }
066:
067: // Implementations for ClassVisitor.
068:
069: public void visitProgramClass(ProgramClass programClass) {
070: // The program class must be an interface class that cannot be
071: // implemented again.
072: if ((programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) == 0
073: || KeepMarker.isKept(programClass)) {
074: return;
075: }
076:
077: // The interface class must have a single implementation.
078: Clazz[] subClasses = programClass.subClasses;
079: if (subClasses == null || subClasses.length != 1) {
080: return;
081: }
082:
083: // If the single implementation is an interface, check it recursively.
084: Clazz singleImplementationClass = subClasses[0];
085: int singleImplementationAccessFlags = singleImplementationClass
086: .getAccessFlags();
087: if ((singleImplementationAccessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) {
088: singleImplementationClass.accept(this );
089:
090: // See if the subinterface has a single implementation.
091: singleImplementationClass = singleImplementation(singleImplementationClass);
092: if (singleImplementationClass == null) {
093: return;
094: }
095:
096: singleImplementationAccessFlags = singleImplementationClass
097: .getAccessFlags();
098: }
099:
100: // The single implementation must contain all non-static methods of this
101: // interface, so invocations can easily be diverted.
102: for (int index = 0; index < programClass.u2methodsCount; index++) {
103: Method method = programClass.methods[index];
104: if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0
105: && singleImplementationClass.findMethod(method
106: .getName(programClass), method
107: .getDescriptor(programClass)) == null) {
108: return;
109: }
110: }
111:
112: // Doesn't the implementation have at least the same access as the
113: // interface?
114: if (AccessUtil.accessLevel(singleImplementationAccessFlags) < AccessUtil
115: .accessLevel(programClass.getAccessFlags())) {
116: // Are we allowed to fix the access?
117: if (allowAccessModification) {
118: // Fix the access.
119: ((ProgramClass) singleImplementationClass).u2accessFlags = AccessUtil
120: .replaceAccessFlags(
121: singleImplementationAccessFlags,
122: programClass.getAccessFlags());
123: } else {
124: // We can't give the implementation the access of the interface.
125: // Forget about inlining it after all.
126: return;
127: }
128: }
129:
130: if (DEBUG) {
131: System.out.println("Single implementation of ["
132: + programClass.getName() + "]: ["
133: + singleImplementationClass.getName() + "]");
134: }
135:
136: // Mark the interface and its single implementation.
137: markSingleImplementation(programClass,
138: singleImplementationClass);
139:
140: // Visit the interface, if required.
141: if (extraClassVisitor != null) {
142: singleImplementationClass.accept(extraClassVisitor);
143: }
144: }
145:
146: // Small utility methods.
147:
148: public static void markSingleImplementation(
149: VisitorAccepter visitorAccepter, Clazz singleImplementation) {
150: // The interface has a single implementation.
151: visitorAccepter.setVisitorInfo(singleImplementation);
152: }
153:
154: public static Clazz singleImplementation(
155: VisitorAccepter visitorAccepter) {
156: return visitorAccepter != null
157: && visitorAccepter.getVisitorInfo() instanceof Clazz ? (Clazz) visitorAccepter
158: .getVisitorInfo()
159: : null;
160: }
161: }
|