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.attribute.Attribute; 25import proguard.classfile.util.SimplifiedVisitor; 26import proguard.classfile.visitor.MemberVisitor; 27 28/** 29 * This ConstantVisitor adds all class members that it visits to the given 30 * target class. 31 * 32 * @author Eric Lafortune 33 */ 34public class MemberAdder 35extends SimplifiedVisitor 36implements MemberVisitor 37{ 38 //* 39 private static final boolean DEBUG = false; 40 /*/ 41 private static boolean DEBUG = true; 42 //*/ 43 44 45 private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; 46 47 48 private final ProgramClass targetClass; 49// private final boolean addFields; 50 51 private final ConstantAdder constantAdder; 52 private final ClassEditor classEditor; 53 private final ConstantPoolEditor constantPoolEditor; 54 55 56 /** 57 * Creates a new MemberAdder that will copy methods into the given target 58 * class. 59 * @param targetClass the class to which all visited class members will be 60 * added. 61 */ 62// * @param addFields specifies whether fields should be added, or fused 63// * with the present fields. 64 public MemberAdder(ProgramClass targetClass)//), 65// boolean addFields) 66 { 67 this.targetClass = targetClass; 68// this.addFields = addFields; 69 70 constantAdder = new ConstantAdder(targetClass); 71 classEditor = new ClassEditor(targetClass); 72 constantPoolEditor = new ConstantPoolEditor(targetClass); 73 } 74 75 76 // Implementations for MemberVisitor. 77 78 public void visitProgramField(ProgramClass programClass, ProgramField programField) 79 { 80 String name = programField.getName(programClass); 81 String descriptor = programField.getDescriptor(programClass); 82 int accessFlags = programField.getAccessFlags(); 83 84 // Does the target class already have such a field? 85 ProgramField targetField = (ProgramField)targetClass.findField(name, descriptor); 86 if (targetField != null) 87 { 88 // Is the field private or static? 89 int targetAccessFlags = targetField.getAccessFlags(); 90 if ((targetAccessFlags & 91 (ClassConstants.INTERNAL_ACC_PRIVATE | 92 ClassConstants.INTERNAL_ACC_STATIC)) != 0) 93 { 94 if (DEBUG) 95 { 96 System.out.println("MemberAdder: renaming field ["+targetClass+"."+targetField.getName(targetClass)+" "+targetField.getDescriptor(targetClass)+"]"); 97 } 98 99 // Rename the private or static field. 100 targetField.u2nameIndex = 101 constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, targetClass.getName())); 102 } 103// else 104// { 105// // Keep the non-private and non-static field, but update its 106// // contents, in order to keep any references to it valid. 107// if (DEBUG) 108// { 109// System.out.println("MemberAdder: updating field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 110// } 111// 112// // Combine the access flags. 113// targetField.u2accessFlags = accessFlags | targetAccessFlags; 114// 115// // Add and replace any attributes. 116// programField.attributesAccept(programClass, 117// new AttributeAdder(targetClass, 118// targetField, 119// true)); 120// 121// // Don't add a new field. 122// return; 123// } 124 } 125 126 if (DEBUG) 127 { 128 System.out.println("MemberAdder: copying field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 129 } 130 131 // Create a copy of the field. 132 ProgramField newProgramField = 133 new ProgramField(accessFlags, 134 constantAdder.addConstant(programClass, programField.u2nameIndex), 135 constantAdder.addConstant(programClass, programField.u2descriptorIndex), 136 0, 137 programField.u2attributesCount > 0 ? 138 new Attribute[programField.u2attributesCount] : 139 EMPTY_ATTRIBUTES, 140 programField.referencedClass); 141 142 // Link to its visitor info. 143 newProgramField.setVisitorInfo(programField); 144 145 // Copy its attributes. 146 programField.attributesAccept(programClass, 147 new AttributeAdder(targetClass, 148 newProgramField, 149 false)); 150 151 // Add the completed field. 152 classEditor.addField(newProgramField); 153 } 154 155 156 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 157 { 158 String name = programMethod.getName(programClass); 159 String descriptor = programMethod.getDescriptor(programClass); 160 int accessFlags = programMethod.getAccessFlags(); 161 162 // Does the target class already have such a method? 163 ProgramMethod targetMethod = (ProgramMethod)targetClass.findMethod(name, descriptor); 164 if (targetMethod != null) 165 { 166 // is this source method abstract? 167 if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) 168 { 169 // Keep the target method. 170 if (DEBUG) 171 { 172 System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 173 } 174 175 // Don't add a new method. 176 return; 177 } 178 179 // Is the target method abstract? 180 int targetAccessFlags = targetMethod.getAccessFlags(); 181 if ((targetAccessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) 182 { 183 // Keep the abstract method, but update its contents, in order 184 // to keep any references to it valid. 185 if (DEBUG) 186 { 187 System.out.println("MemberAdder: updating method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 188 } 189 190 // Replace the access flags. 191 targetMethod.u2accessFlags = 192 accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL; 193 194 // Add and replace the attributes. 195 programMethod.attributesAccept(programClass, 196 new AttributeAdder(targetClass, 197 targetMethod, 198 true)); 199 200 // Don't add a new method. 201 return; 202 } 203 204 if (DEBUG) 205 { 206 System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); 207 } 208 209 // Rename the private (non-abstract) or static method. 210 targetMethod.u2nameIndex = 211 constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, descriptor)); 212 } 213 214 if (DEBUG) 215 { 216 System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 217 } 218 219 // Create a copy of the method. 220 ProgramMethod newProgramMethod = 221 new ProgramMethod(accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL, 222 constantAdder.addConstant(programClass, programMethod.u2nameIndex), 223 constantAdder.addConstant(programClass, programMethod.u2descriptorIndex), 224 0, 225 programMethod.u2attributesCount > 0 ? 226 new Attribute[programMethod.u2attributesCount] : 227 EMPTY_ATTRIBUTES, 228 programMethod.referencedClasses != null ? 229 (Clazz[])programMethod.referencedClasses.clone() : 230 null); 231 232 // Link to its visitor info. 233 newProgramMethod.setVisitorInfo(programMethod); 234 235 // Copy its attributes. 236 programMethod.attributesAccept(programClass, 237 new AttributeAdder(targetClass, 238 newProgramMethod, 239 false)); 240 241 // Add the completed method. 242 classEditor.addMethod(newProgramMethod); 243 } 244 245 246 // Small utility methods. 247 248 /** 249 * Returns a unique class member name, based on the given name and descriptor. 250 */ 251 private String newUniqueMemberName(String name, String descriptor) 252 { 253 return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 254 ClassConstants.INTERNAL_METHOD_NAME_INIT : 255 name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); 256 } 257} 258