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