blob: 5f939bb2e02c31b9f47d0418c0ecfd52311b0182 [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
*
* 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.classfile.editor;
import proguard.classfile.*;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.util.SimplifiedVisitor;
import proguard.classfile.visitor.MemberVisitor;
/**
* This ConstantVisitor adds all class members that it visits to the given
* target class.
*
* @author Eric Lafortune
*/
public class MemberAdder
extends SimplifiedVisitor
implements MemberVisitor
{
//*
private static final boolean DEBUG = false;
/*/
private static boolean DEBUG = true;
//*/
private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0];
private final ProgramClass targetClass;
// private final boolean addFields;
private final ConstantAdder constantAdder;
private final ClassEditor classEditor;
private final ConstantPoolEditor constantPoolEditor;
/**
* Creates a new MemberAdder that will copy methods into the given target
* class.
* @param targetClass the class to which all visited class members will be
* added.
*/
// * @param addFields specifies whether fields should be added, or fused
// * with the present fields.
public MemberAdder(ProgramClass targetClass)//),
// boolean addFields)
{
this.targetClass = targetClass;
// this.addFields = addFields;
constantAdder = new ConstantAdder(targetClass);
classEditor = new ClassEditor(targetClass);
constantPoolEditor = new ConstantPoolEditor(targetClass);
}
// Implementations for MemberVisitor.
public void visitProgramField(ProgramClass programClass, ProgramField programField)
{
String name = programField.getName(programClass);
String descriptor = programField.getDescriptor(programClass);
int accessFlags = programField.getAccessFlags();
// Does the target class already have such a field?
ProgramField targetField = (ProgramField)targetClass.findField(name, descriptor);
if (targetField != null)
{
// Is the field private or static?
int targetAccessFlags = targetField.getAccessFlags();
if ((targetAccessFlags &
(ClassConstants.INTERNAL_ACC_PRIVATE |
ClassConstants.INTERNAL_ACC_STATIC)) != 0)
{
if (DEBUG)
{
System.out.println("MemberAdder: renaming field ["+targetClass+"."+targetField.getName(targetClass)+" "+targetField.getDescriptor(targetClass)+"]");
}
// Rename the private or static field.
targetField.u2nameIndex =
constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, targetClass.getName()));
}
// else
// {
// // Keep the non-private and non-static field, but update its
// // contents, in order to keep any references to it valid.
// if (DEBUG)
// {
// System.out.println("MemberAdder: updating field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]");
// }
//
// // Combine the access flags.
// targetField.u2accessFlags = accessFlags | targetAccessFlags;
//
// // Add and replace any attributes.
// programField.attributesAccept(programClass,
// new AttributeAdder(targetClass,
// targetField,
// true));
//
// // Don't add a new field.
// return;
// }
}
if (DEBUG)
{
System.out.println("MemberAdder: copying field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]");
}
// Create a copy of the field.
ProgramField newProgramField =
new ProgramField(accessFlags,
constantAdder.addConstant(programClass, programField.u2nameIndex),
constantAdder.addConstant(programClass, programField.u2descriptorIndex),
0,
programField.u2attributesCount > 0 ?
new Attribute[programField.u2attributesCount] :
EMPTY_ATTRIBUTES,
programField.referencedClass);
// Link to its visitor info.
newProgramField.setVisitorInfo(programField);
// Copy its attributes.
programField.attributesAccept(programClass,
new AttributeAdder(targetClass,
newProgramField,
false));
// Add the completed field.
classEditor.addField(newProgramField);
}
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
{
String name = programMethod.getName(programClass);
String descriptor = programMethod.getDescriptor(programClass);
int accessFlags = programMethod.getAccessFlags();
// Does the target class already have such a method?
ProgramMethod targetMethod = (ProgramMethod)targetClass.findMethod(name, descriptor);
if (targetMethod != null)
{
// is this source method abstract?
if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
{
// Keep the target method.
if (DEBUG)
{
System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]");
}
// Don't add a new method.
return;
}
// Is the target method abstract?
int targetAccessFlags = targetMethod.getAccessFlags();
if ((targetAccessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
{
// Keep the abstract method, but update its contents, in order
// to keep any references to it valid.
if (DEBUG)
{
System.out.println("MemberAdder: updating method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]");
}
// Replace the access flags.
targetMethod.u2accessFlags =
accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL;
// Add and replace the attributes.
programMethod.attributesAccept(programClass,
new AttributeAdder(targetClass,
targetMethod,
true));
// Don't add a new method.
return;
}
if (DEBUG)
{
System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]");
}
// Rename the private (non-abstract) or static method.
targetMethod.u2nameIndex =
constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, descriptor));
}
if (DEBUG)
{
System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]");
}
// Create a copy of the method.
ProgramMethod newProgramMethod =
new ProgramMethod(accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL,
constantAdder.addConstant(programClass, programMethod.u2nameIndex),
constantAdder.addConstant(programClass, programMethod.u2descriptorIndex),
0,
programMethod.u2attributesCount > 0 ?
new Attribute[programMethod.u2attributesCount] :
EMPTY_ATTRIBUTES,
programMethod.referencedClasses != null ?
(Clazz[])programMethod.referencedClasses.clone() :
null);
// Link to its visitor info.
newProgramMethod.setVisitorInfo(programMethod);
// Copy its attributes.
programMethod.attributesAccept(programClass,
new AttributeAdder(targetClass,
newProgramMethod,
false));
// Add the completed method.
classEditor.addMethod(newProgramMethod);
}
// Small utility methods.
/**
* Returns a unique class member name, based on the given name and descriptor.
*/
private String newUniqueMemberName(String name, String descriptor)
{
return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
ClassConstants.INTERNAL_METHOD_NAME_INIT :
name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
}
}