1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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 MemberVisitor copies all class members that it visits to the given 30 * target class. Their visitor info is set to the class members from which they 31 * were copied. 32 * 33 * @author Eric Lafortune 34 */ 35public class MemberAdder 36extends SimplifiedVisitor 37implements MemberVisitor 38{ 39 //* 40 private static final boolean DEBUG = false; 41 /*/ 42 private static boolean DEBUG = true; 43 //*/ 44 45 46 private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; 47 48 49 private final ProgramClass targetClass; 50// private final boolean addFields; 51 private final MemberVisitor extraMemberVisitor; 52 53 private final ConstantAdder constantAdder; 54 private final ClassEditor classEditor; 55 private final ConstantPoolEditor constantPoolEditor; 56 57 58 /** 59 * Creates a new MemberAdder that will copy methods into the given target 60 * class. 61 * @param targetClass the class to which all visited class members will be 62 * added. 63 */ 64 public MemberAdder(ProgramClass targetClass) 65 { 66 this(targetClass, null); 67 } 68 69 70 /** 71 * Creates a new MemberAdder that will copy methods into the given target 72 * class. 73 * @param targetClass the class to which all visited class members 74 * will be added. 75 * @param extraMemberVisitor an optional member visitor that visits each 76 * new member right after it has been added. This 77 * allows changing the visitor info, for instance. 78 */ 79// * @param addFields specifies whether fields should be added, or fused 80// * with the present fields. 81 public MemberAdder(ProgramClass targetClass, 82// boolean addFields, 83 MemberVisitor extraMemberVisitor) 84 { 85 this.targetClass = targetClass; 86// this.addFields = addFields; 87 this.extraMemberVisitor = extraMemberVisitor; 88 89 constantAdder = new ConstantAdder(targetClass); 90 classEditor = new ClassEditor(targetClass); 91 constantPoolEditor = new ConstantPoolEditor(targetClass); 92 } 93 94 95 // Implementations for MemberVisitor. 96 97 public void visitProgramField(ProgramClass programClass, ProgramField programField) 98 { 99 //String name = programField.getName(programClass); 100 //String descriptor = programField.getDescriptor(programClass); 101 int accessFlags = programField.getAccessFlags(); 102 103 // TODO: Handle field with the same name and descriptor in the target class. 104 // We currently avoid this case, since renaming the identical field 105 // still causes confused field references. 106 //// Does the target class already have such a field? 107 //ProgramField targetField = (ProgramField)targetClass.findField(name, descriptor); 108 //if (targetField != null) 109 //{ 110 // // Is the field private or static? 111 // int targetAccessFlags = targetField.getAccessFlags(); 112 // if ((targetAccessFlags & 113 // (ClassConstants.ACC_PRIVATE | 114 // ClassConstants.ACC_STATIC)) != 0) 115 // { 116 // if (DEBUG) 117 // { 118 // System.out.println("MemberAdder: renaming field ["+targetClass+"."+targetField.getName(targetClass)+" "+targetField.getDescriptor(targetClass)+"]"); 119 // } 120 // 121 // // Rename the private or static field. 122 // targetField.u2nameIndex = 123 // constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, targetClass.getName())); 124 // } 125 // else 126 // { 127 // // Keep the non-private and non-static field, but update its 128 // // contents, in order to keep any references to it valid. 129 // if (DEBUG) 130 // { 131 // System.out.println("MemberAdder: updating field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 132 // } 133 // 134 // // Combine the access flags. 135 // targetField.u2accessFlags = accessFlags | targetAccessFlags; 136 // 137 // // Add and replace any attributes. 138 // programField.attributesAccept(programClass, 139 // new AttributeAdder(targetClass, 140 // targetField, 141 // true)); 142 // 143 // // Don't add a new field. 144 // return; 145 // } 146 //} 147 148 if (DEBUG) 149 { 150 System.out.println("MemberAdder: copying field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 151 } 152 153 // Create a copy of the field. 154 ProgramField newProgramField = 155 new ProgramField(accessFlags, 156 constantAdder.addConstant(programClass, programField.u2nameIndex), 157 constantAdder.addConstant(programClass, programField.u2descriptorIndex), 158 0, 159 programField.u2attributesCount > 0 ? 160 new Attribute[programField.u2attributesCount] : 161 EMPTY_ATTRIBUTES, 162 programField.referencedClass); 163 164 // Link to its visitor info. 165 newProgramField.setVisitorInfo(programField); 166 167 // Copy its attributes. 168 programField.attributesAccept(programClass, 169 new AttributeAdder(targetClass, 170 newProgramField, 171 false)); 172 173 // Add the completed field. 174 classEditor.addField(newProgramField); 175 176 // Visit the newly added field, if necessary. 177 if (extraMemberVisitor != null) 178 { 179 extraMemberVisitor.visitProgramField(targetClass, newProgramField); 180 } 181 } 182 183 184 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 185 { 186 String name = programMethod.getName(programClass); 187 String descriptor = programMethod.getDescriptor(programClass); 188 int accessFlags = programMethod.getAccessFlags(); 189 190 // Does the target class already have such a method? 191 ProgramMethod targetMethod = (ProgramMethod)targetClass.findMethod(name, descriptor); 192 if (targetMethod != null) 193 { 194 // is this source method abstract? 195 if ((accessFlags & ClassConstants.ACC_ABSTRACT) != 0) 196 { 197 // Keep the target method. 198 if (DEBUG) 199 { 200 System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 201 } 202 203 // Don't add a new method. 204 return; 205 } 206 207 // Is the target method abstract? 208 int targetAccessFlags = targetMethod.getAccessFlags(); 209 if ((targetAccessFlags & ClassConstants.ACC_ABSTRACT) != 0) 210 { 211 // Keep the abstract method, but update its contents, in order 212 // to keep any references to it valid. 213 if (DEBUG) 214 { 215 System.out.println("MemberAdder: updating method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 216 } 217 218 // Replace the access flags. 219 targetMethod.u2accessFlags = 220 accessFlags & ~ClassConstants.ACC_FINAL; 221 222 // Add and replace the attributes. 223 programMethod.attributesAccept(programClass, 224 new AttributeAdder(targetClass, 225 targetMethod, 226 true)); 227 228 // Don't add a new method. 229 return; 230 } 231 232 if (DEBUG) 233 { 234 System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); 235 } 236 237 // TODO: Handle non-abstract method with the same name and descriptor in the target class. 238 // We currently avoid this case, since renaming the identical method 239 // still causes confused method references. 240 //// Rename the private (non-abstract) or static method. 241 //targetMethod.u2nameIndex = 242 // constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, descriptor)); 243 } 244 245 if (DEBUG) 246 { 247 System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); 248 } 249 250 // Create a copy of the method. 251 ProgramMethod newProgramMethod = 252 new ProgramMethod(accessFlags & ~ClassConstants.ACC_FINAL, 253 constantAdder.addConstant(programClass, programMethod.u2nameIndex), 254 constantAdder.addConstant(programClass, programMethod.u2descriptorIndex), 255 0, 256 programMethod.u2attributesCount > 0 ? 257 new Attribute[programMethod.u2attributesCount] : 258 EMPTY_ATTRIBUTES, 259 programMethod.referencedClasses != null ? 260 (Clazz[])programMethod.referencedClasses.clone() : 261 null); 262 263 // Link to its visitor info. 264 newProgramMethod.setVisitorInfo(programMethod); 265 266 // Copy its attributes. 267 programMethod.attributesAccept(programClass, 268 new AttributeAdder(targetClass, 269 newProgramMethod, 270 false)); 271 272 // Add the completed method. 273 classEditor.addMethod(newProgramMethod); 274 275 // Visit the newly added method, if necessary. 276 if (extraMemberVisitor != null) 277 { 278 extraMemberVisitor.visitProgramMethod(targetClass, newProgramMethod); 279 } 280 } 281 282 283 // Small utility methods. 284 285 /** 286 * Returns a unique class member name, based on the given name and descriptor. 287 */ 288 private String newUniqueMemberName(String name, String descriptor) 289 { 290 return name.equals(ClassConstants.METHOD_NAME_INIT) ? 291 ClassConstants.METHOD_NAME_INIT : 292 name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); 293 } 294} 295