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