proguard.optimize.info.WrapperClassMarker Maven / Gradle / Ivy
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2018 GuardSquare NV
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.optimize.info;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.constant.*;
import proguard.classfile.instruction.*;
import proguard.classfile.util.*;
import proguard.classfile.visitor.*;
import proguard.evaluation.value.*;
import proguard.optimize.evaluation.StoringInvocationUnit;
/**
* This ClassVisitor marks all program classes that are a simple wrapper for a
* single non-null instance of another class.
*
* A wrapper class has
* - exactly one non-static field, which references an object,
* - exactly one initializer, with a single parameter that is never null,
* that initializes the field,
* - no subclasses.
*
* @see StoringInvocationUnit
* @author Eric Lafortune
*/
public class WrapperClassMarker
extends SimplifiedVisitor
implements ClassVisitor,
MemberVisitor,
AttributeVisitor
{
//*
private static final boolean DEBUG = false;
/*/
public static boolean DEBUG = System.getProperty("wcm") != null;
//*/
private final Constant[] INITIALIZER_CONSTANTS = new Constant[]
{
new FieldrefConstant(InstructionSequenceMatcher.A,
InstructionSequenceMatcher.B, null, null),
};
// Instruction pattern:
// this.x = arg0;
// super.;
// return;
private final Instruction[] INITIALIZER_INSTRUCTIONS = new Instruction[]
{
new VariableInstruction(InstructionConstants.OP_ALOAD_0, 0),
new VariableInstruction(InstructionConstants.OP_ALOAD_1, 1),
new ConstantInstruction(InstructionConstants.OP_PUTFIELD, 0),
new VariableInstruction(InstructionConstants.OP_ALOAD_0, 0),
new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, InstructionSequenceMatcher.X),
new SimpleInstruction(InstructionConstants.OP_RETURN),
};
private final InstructionSequenceMatcher INITIALIZER_MATCHER = new InstructionSequenceMatcher(INITIALIZER_CONSTANTS, INITIALIZER_INSTRUCTIONS);
// Fields acting as parameters and return values for the visitor methods.
private Clazz wrappedClass;
private int wrapCounter;
// Implementations for ClassVisitor.
public void visitProgramClass(ProgramClass programClass)
{
if (programClass.subClasses == null ||
programClass.subClasses.length == 0)
{
wrappedClass = null;
// Can we find one non-static field with a class type?
wrapCounter = 0;
programClass.fieldsAccept(this);
if (wrapCounter == 1)
{
// Can we find exactly one initializer that initializes this
// field?
wrapCounter = 0;
programClass.methodsAccept(this);
if (wrapCounter == 1)
{
setWrappedClass(programClass, wrappedClass);
}
}
}
}
// Implementations for MemberVisitor.
public void visitProgramField(ProgramClass programClass, ProgramField programField)
{
// Is the field non-static and of a class type?
if ((programField.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 &&
ClassUtil.isInternalClassType(programField.getDescriptor(programClass)) &&
!ClassUtil.isInternalArrayType(programField.getDescriptor(programClass)))
{
wrappedClass = programField.referencedClass;
if (wrappedClass != null)
{
wrapCounter++;
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
{
// Is the method an initializer?
if (ClassUtil.isInitializer(programMethod.getName(programClass)))
{
// Does it have exactly one parameter?
if (ClassUtil.internalMethodParameterCount(programMethod.getDescriptor(programClass)) == 1)
{
// Is the parameter a non-null reference?
Value value =
StoringInvocationUnit.getMethodParameterValue(programMethod, 1);
if (value != null &&
value.computationalType() == Value.TYPE_REFERENCE &&
value.referenceValue().isNotNull() == Value.ALWAYS)
{
// Does the method initialize the field?
programMethod.attributesAccept(programClass, this);
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
}
// Implementations for AttributeVisitor.
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
// Is the initializer initializing the field?
if (codeAttribute.u4codeLength == 10)
{
INITIALIZER_MATCHER.reset();
codeAttribute.instructionsAccept(clazz, method, INITIALIZER_MATCHER);
if (INITIALIZER_MATCHER.isMatching())
{
String initializerClassName = clazz.getName();
String fieldClassName = clazz.getClassName(INITIALIZER_MATCHER.matchedConstantIndex(InstructionSequenceMatcher.A));
if (fieldClassName.equals(initializerClassName))
{
wrapCounter++;
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
else
{
wrapCounter = Integer.MIN_VALUE;
}
}
// Small utility methods.
private static void setWrappedClass(Clazz clazz, Clazz wrappedClass)
{
if (DEBUG)
{
System.out.println("WrapperClassMarker: ["+clazz.getName()+"] wraps ["+wrappedClass.getName()+"]");
}
ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setWrappedClass(wrappedClass);
}
public static Clazz getWrappedClass(Clazz clazz)
{
return ClassOptimizationInfo.getClassOptimizationInfo(clazz).getWrappedClass();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy