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.constant.*;
25
26/**
27 * This class can add constant pool entries to a given class.
28 *
29 * @author Eric Lafortune
30 */
31public class ConstantPoolEditor
32{
33    private static final boolean DEBUG = false;
34
35    private ProgramClass targetClass;
36
37
38    /**
39     * Creates a new ConstantPoolEditor that will edit constants in the given
40     * target class.
41     */
42    public ConstantPoolEditor(ProgramClass targetClass)
43    {
44        this.targetClass = targetClass;
45    }
46
47
48    /**
49     * Finds or creates a IntegerConstant constant pool entry with the given
50     * value.
51     * @return the constant pool index of the Utf8Constant.
52     */
53    public int addIntegerConstant(int value)
54    {
55        int        constantPoolCount = targetClass.u2constantPoolCount;
56        Constant[] constantPool      = targetClass.constantPool;
57
58        // Check if the entry already exists.
59        for (int index = 1; index < constantPoolCount; index++)
60        {
61            Constant constant = constantPool[index];
62
63            if (constant != null &&
64                constant.getTag() == ClassConstants.CONSTANT_Integer)
65            {
66                IntegerConstant integerConstant = (IntegerConstant)constant;
67                if (integerConstant.getValue() == value)
68                {
69                    return index;
70                }
71            }
72        }
73
74        return addConstant(new IntegerConstant(value));
75    }
76
77
78    /**
79     * Finds or creates a LongConstant constant pool entry with the given value.
80     * @return the constant pool index of the LongConstant.
81     */
82    public int addLongConstant(long value)
83    {
84        int        constantPoolCount = targetClass.u2constantPoolCount;
85        Constant[] constantPool      = targetClass.constantPool;
86
87        // Check if the entry already exists.
88        for (int index = 1; index < constantPoolCount; index++)
89        {
90            Constant constant = constantPool[index];
91
92            if (constant != null &&
93                constant.getTag() == ClassConstants.CONSTANT_Long)
94            {
95                LongConstant longConstant = (LongConstant)constant;
96                if (longConstant.getValue() == value)
97                {
98                    return index;
99                }
100            }
101        }
102
103        return addConstant(new LongConstant(value));
104    }
105
106
107    /**
108     * Finds or creates a FloatConstant constant pool entry with the given
109     * value.
110     * @return the constant pool index of the FloatConstant.
111     */
112    public int addFloatConstant(float value)
113    {
114        int        constantPoolCount = targetClass.u2constantPoolCount;
115        Constant[] constantPool      = targetClass.constantPool;
116
117        // Check if the entry already exists.
118        for (int index = 1; index < constantPoolCount; index++)
119        {
120            Constant constant = constantPool[index];
121
122            if (constant != null &&
123                constant.getTag() == ClassConstants.CONSTANT_Float)
124            {
125                FloatConstant floatConstant = (FloatConstant)constant;
126                if (floatConstant.getValue() == value)
127                {
128                    return index;
129                }
130            }
131        }
132
133        return addConstant(new FloatConstant(value));
134    }
135
136
137    /**
138     * Finds or creates a DoubleConstant constant pool entry with the given
139     * value.
140     * @return the constant pool index of the DoubleConstant.
141     */
142    public int addDoubleConstant(double value)
143    {
144        int        constantPoolCount = targetClass.u2constantPoolCount;
145        Constant[] constantPool      = targetClass.constantPool;
146
147        // Check if the entry already exists.
148        for (int index = 1; index < constantPoolCount; index++)
149        {
150            Constant constant = constantPool[index];
151
152            if (constant != null &&
153                constant.getTag() == ClassConstants.CONSTANT_Double)
154            {
155                DoubleConstant doubleConstant = (DoubleConstant)constant;
156                if (doubleConstant.getValue() == value)
157                {
158                    return index;
159                }
160            }
161        }
162
163        return addConstant(new DoubleConstant(value));
164    }
165
166
167    /**
168     * Finds or creates a StringConstant constant pool entry with the given
169     * value.
170     * @return the constant pool index of the StringConstant.
171     */
172    public int addStringConstant(String string,
173                                 Clazz  referencedClass,
174                                 Member referencedMember)
175    {
176        int        constantPoolCount = targetClass.u2constantPoolCount;
177        Constant[] constantPool      = targetClass.constantPool;
178
179        // Check if the entry already exists.
180        for (int index = 1; index < constantPoolCount; index++)
181        {
182            Constant constant = constantPool[index];
183
184            if (constant != null &&
185                constant.getTag() == ClassConstants.CONSTANT_String)
186            {
187                StringConstant stringConstant = (StringConstant)constant;
188                if (stringConstant.getString(targetClass).equals(string))
189                {
190                    return index;
191                }
192            }
193        }
194
195        return addConstant(new StringConstant(addUtf8Constant(string),
196                                              referencedClass,
197                                              referencedMember));
198    }
199
200
201    /**
202     * Finds or creates a FieldrefConstant constant pool entry for the given
203     * class and field.
204     * @return the constant pool index of the FieldrefConstant.
205     */
206    public int addFieldrefConstant(Clazz  referencedClass,
207                                   Member referencedMember)
208    {
209        return addFieldrefConstant(referencedClass.getName(),
210                                   referencedMember.getName(referencedClass),
211                                   referencedMember.getDescriptor(referencedClass),
212                                   referencedClass,
213                                   referencedMember);
214    }
215
216
217    /**
218     * Finds or creates a FieldrefConstant constant pool entry with the given
219     * class name, field name, and descriptor.
220     * @return the constant pool index of the FieldrefConstant.
221     */
222    public int addFieldrefConstant(String className,
223                                   String name,
224                                   String descriptor,
225                                   Clazz  referencedClass,
226                                   Member referencedMember)
227    {
228        return addFieldrefConstant(className,
229                                   addNameAndTypeConstant(name, descriptor),
230                                   referencedClass,
231                                   referencedMember);
232    }
233
234
235    /**
236     * Finds or creates a FieldrefConstant constant pool entry with the given
237     * class name, field name, and descriptor.
238     * @return the constant pool index of the FieldrefConstant.
239     */
240    public int addFieldrefConstant(String className,
241                                   int    nameAndTypeIndex,
242                                   Clazz  referencedClass,
243                                   Member referencedMember)
244    {
245        return addFieldrefConstant(addClassConstant(className, referencedClass),
246                                   nameAndTypeIndex,
247                                   referencedClass,
248                                   referencedMember);
249    }
250
251
252    /**
253     * Finds or creates a FieldrefConstant constant pool entry with the given
254     * class constant pool entry index, field name, and descriptor.
255     * @return the constant pool index of the FieldrefConstant.
256     */
257    public int addFieldrefConstant(int    classIndex,
258                                   String name,
259                                   String descriptor,
260                                   Clazz  referencedClass,
261                                   Member referencedMember)
262    {
263        return addFieldrefConstant(classIndex,
264                                   addNameAndTypeConstant(name, descriptor),
265                                   referencedClass,
266                                   referencedMember);
267    }
268
269
270    /**
271     * Finds or creates a FieldrefConstant constant pool entry with the given
272     * class constant pool entry index and name and type constant pool entry
273     * index.
274     * @return the constant pool index of the FieldrefConstant.
275     */
276    public int addFieldrefConstant(int    classIndex,
277                                   int    nameAndTypeIndex,
278                                   Clazz  referencedClass,
279                                   Member referencedMember)
280    {
281        int        constantPoolCount = targetClass.u2constantPoolCount;
282        Constant[] constantPool      = targetClass.constantPool;
283
284        // Check if the entry already exists.
285        for (int index = 1; index < constantPoolCount; index++)
286        {
287            Constant constant = constantPool[index];
288
289            if (constant != null &&
290                constant.getTag() == ClassConstants.CONSTANT_Fieldref)
291            {
292                FieldrefConstant fieldrefConstant = (FieldrefConstant)constant;
293                if (fieldrefConstant.u2classIndex         == classIndex &&
294                    fieldrefConstant.u2nameAndTypeIndex   == nameAndTypeIndex)
295                {
296                    return index;
297                }
298            }
299        }
300
301        return addConstant(new FieldrefConstant(classIndex,
302                                                nameAndTypeIndex,
303                                                referencedClass,
304                                                referencedMember));
305    }
306
307
308    /**
309     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
310     * given class name, method name, and descriptor.
311     * @return the constant pool index of the InterfaceMethodrefConstant.
312     */
313    public int addInterfaceMethodrefConstant(String className,
314                                             String name,
315                                             String descriptor,
316                                             Clazz  referencedClass,
317                                             Member referencedMember)
318    {
319        return addInterfaceMethodrefConstant(className,
320                                             addNameAndTypeConstant(name, descriptor),
321                                             referencedClass,
322                                             referencedMember);
323    }
324
325
326    /**
327     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
328     * given class name, method name, and descriptor.
329     * @return the constant pool index of the InterfaceMethodrefConstant.
330     */
331    public int addInterfaceMethodrefConstant(String className,
332                                             int    nameAndTypeIndex,
333                                             Clazz  referencedClass,
334                                             Member referencedMember)
335    {
336        return addInterfaceMethodrefConstant(addClassConstant(className, referencedClass),
337                                             nameAndTypeIndex,
338                                             referencedClass,
339                                             referencedMember);
340    }
341
342
343    /**
344     * Finds or creates a InterfaceMethodrefConstant constant pool entry for the
345     * given class and method.
346     * @return the constant pool index of the InterfaceMethodrefConstant.
347     */
348    public int addInterfaceMethodrefConstant(Clazz  referencedClass,
349                                             Member referencedMember)
350    {
351        return addInterfaceMethodrefConstant(referencedClass.getName(),
352                                             referencedMember.getName(referencedClass),
353                                             referencedMember.getDescriptor(referencedClass),
354                                             referencedClass,
355                                             referencedMember);
356    }
357
358
359    /**
360     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
361     * given class constant pool entry index, method name, and descriptor.
362     * @return the constant pool index of the InterfaceMethodrefConstant.
363     */
364    public int addInterfaceMethodrefConstant(int    classIndex,
365                                             String name,
366                                             String descriptor,
367                                             Clazz  referencedClass,
368                                             Member referencedMember)
369    {
370        return addInterfaceMethodrefConstant(classIndex,
371                                             addNameAndTypeConstant(name, descriptor),
372                                             referencedClass,
373                                             referencedMember);
374    }
375
376
377    /**
378     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
379     * given class constant pool entry index and name and type constant pool
380     * entry index.
381     * @return the constant pool index of the InterfaceMethodrefConstant.
382     */
383    public int addInterfaceMethodrefConstant(int    classIndex,
384                                             int    nameAndTypeIndex,
385                                             Clazz  referencedClass,
386                                             Member referencedMember)
387    {
388        int        constantPoolCount = targetClass.u2constantPoolCount;
389        Constant[] constantPool      = targetClass.constantPool;
390
391        // Check if the entry already exists.
392        for (int index = 1; index < constantPoolCount; index++)
393        {
394            Constant constant = constantPool[index];
395
396            if (constant != null &&
397                            constant.getTag() == ClassConstants.CONSTANT_InterfaceMethodref)
398            {
399                InterfaceMethodrefConstant methodrefConstant = (InterfaceMethodrefConstant)constant;
400                if (methodrefConstant.u2classIndex       == classIndex &&
401                    methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex)
402                {
403                    return index;
404                }
405            }
406        }
407
408        return addConstant(new InterfaceMethodrefConstant(classIndex,
409                                                          nameAndTypeIndex,
410                                                          referencedClass,
411                                                          referencedMember));
412    }
413
414
415    /**
416     * Finds or creates a MethodrefConstant constant pool entry for the given
417     * class and method.
418     * @return the constant pool index of the MethodrefConstant.
419     */
420    public int addMethodrefConstant(Clazz  referencedClass,
421                                    Member referencedMember)
422    {
423        return addMethodrefConstant(referencedClass.getName(),
424                                    referencedMember.getName(referencedClass),
425                                    referencedMember.getDescriptor(referencedClass),
426                                    referencedClass,
427                                    referencedMember);
428    }
429
430
431    /**
432     * Finds or creates a MethodrefConstant constant pool entry with the given
433     * class name, method name, and descriptor.
434     * @return the constant pool index of the MethodrefConstant.
435     */
436    public int addMethodrefConstant(String className,
437                                    String name,
438                                    String descriptor,
439                                    Clazz  referencedClass,
440                                    Member referencedMember)
441    {
442        return addMethodrefConstant(className,
443                                    addNameAndTypeConstant(name, descriptor),
444                                    referencedClass,
445                                    referencedMember);
446    }
447
448
449    /**
450     * Finds or creates a MethodrefConstant constant pool entry with the given
451     * class name, method name, and descriptor.
452     * @return the constant pool index of the MethodrefConstant.
453     */
454    public int addMethodrefConstant(String className,
455                                    int    nameAndTypeIndex,
456                                    Clazz  referencedClass,
457                                    Member referencedMember)
458    {
459        return addMethodrefConstant(addClassConstant(className, referencedClass),
460                                    nameAndTypeIndex,
461                                    referencedClass,
462                                    referencedMember);
463    }
464
465
466    /**
467     * Finds or creates a MethodrefConstant constant pool entry with the given
468     * class constant pool entry index, method name, and descriptor.
469     * @return the constant pool index of the MethodrefConstant.
470     */
471    public int addMethodrefConstant(int    classIndex,
472                                    String name,
473                                    String descriptor,
474                                    Clazz  referencedClass,
475                                    Member referencedMember)
476    {
477        return addMethodrefConstant(classIndex,
478                                    addNameAndTypeConstant(name, descriptor),
479                                    referencedClass,
480                                    referencedMember);
481    }
482
483
484    /**
485     * Finds or creates a MethodrefConstant constant pool entry with the given
486     * class constant pool entry index and name and type constant pool entry
487     * index.
488     * @return the constant pool index of the MethodrefConstant.
489     */
490    public int addMethodrefConstant(int    classIndex,
491                                    int    nameAndTypeIndex,
492                                    Clazz  referencedClass,
493                                    Member referencedMember)
494    {
495        int        constantPoolCount = targetClass.u2constantPoolCount;
496        Constant[] constantPool      = targetClass.constantPool;
497
498        // Check if the entry already exists.
499        for (int index = 1; index < constantPoolCount; index++)
500        {
501            Constant constant = constantPool[index];
502
503            if (constant != null &&
504                constant.getTag() == ClassConstants.CONSTANT_Methodref)
505            {
506                MethodrefConstant methodrefConstant = (MethodrefConstant)constant;
507                if (methodrefConstant.u2classIndex       == classIndex &&
508                    methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex)
509                {
510                    return index;
511                }
512            }
513        }
514
515        return addConstant(new MethodrefConstant(classIndex,
516                                                 nameAndTypeIndex,
517                                                 referencedClass,
518                                                 referencedMember));
519    }
520
521
522    /**
523     * Finds or creates a ClassConstant constant pool entry for the given class.
524     * @return the constant pool index of the ClassConstant.
525     */
526    public int addClassConstant(Clazz referencedClass)
527    {
528        return addClassConstant(referencedClass.getName(),
529                                referencedClass);
530    }
531
532
533    /**
534     * Finds or creates a ClassConstant constant pool entry with the given name.
535     * @return the constant pool index of the ClassConstant.
536     */
537    public int addClassConstant(String name,
538                                Clazz  referencedClass)
539    {
540        int        constantPoolCount = targetClass.u2constantPoolCount;
541        Constant[] constantPool      = targetClass.constantPool;
542
543        // Check if the entry already exists.
544        for (int index = 1; index < constantPoolCount; index++)
545        {
546            Constant constant = constantPool[index];
547
548            if (constant != null &&
549                constant.getTag() == ClassConstants.CONSTANT_Class)
550            {
551                ClassConstant classConstant = (ClassConstant)constant;
552                if (classConstant.getName(targetClass).equals(name))
553                {
554                    return index;
555                }
556            }
557        }
558
559        int nameIndex = addUtf8Constant(name);
560
561        return addConstant(new ClassConstant(nameIndex, referencedClass));
562    }
563
564
565    /**
566     * Finds or creates a NameAndTypeConstant constant pool entry with the given
567     * name and type.
568     * @return the constant pool index of the NameAndTypeConstant.
569     */
570    public int addNameAndTypeConstant(String name,
571                                      String type)
572    {
573        int        constantPoolCount = targetClass.u2constantPoolCount;
574        Constant[] constantPool      = targetClass.constantPool;
575
576        // Check if the entry already exists.
577        for (int index = 1; index < constantPoolCount; index++)
578        {
579            Constant constant = constantPool[index];
580
581            if (constant != null &&
582                constant.getTag() == ClassConstants.CONSTANT_NameAndType)
583            {
584                NameAndTypeConstant nameAndTypeConstant = (NameAndTypeConstant)constant;
585                if (nameAndTypeConstant.getName(targetClass).equals(name) &&
586                    nameAndTypeConstant.getType(targetClass).equals(type))
587                {
588                    return index;
589                }
590            }
591        }
592
593        return addConstant(new NameAndTypeConstant(addUtf8Constant(name),
594                                                   addUtf8Constant(type)));
595    }
596
597
598    /**
599     * Finds or creates a Utf8Constant constant pool entry for the given string.
600     * @return the constant pool index of the Utf8Constant.
601     */
602    public int addUtf8Constant(String string)
603    {
604        int        constantPoolCount = targetClass.u2constantPoolCount;
605        Constant[] constantPool      = targetClass.constantPool;
606
607        // Check if the entry already exists.
608        for (int index = 1; index < constantPoolCount; index++)
609        {
610            Constant constant = constantPool[index];
611
612            if (constant != null &&
613                constant.getTag() == ClassConstants.CONSTANT_Utf8)
614            {
615                Utf8Constant utf8Constant = (Utf8Constant)constant;
616                if (utf8Constant.getString().equals(string))
617                {
618                    return index;
619                }
620            }
621        }
622
623        return addConstant(new Utf8Constant(string));
624    }
625
626
627    /**
628     * Adds a given constant pool entry to the end of the constant pool/
629     * @return the constant pool index for the added entry.
630     */
631    public int addConstant(Constant constant)
632    {
633        int        constantPoolCount = targetClass.u2constantPoolCount;
634        Constant[] constantPool      = targetClass.constantPool;
635
636        // Make sure there is enough space for another constant pool entry.
637        if (constantPool.length < constantPoolCount+2)
638        {
639            targetClass.constantPool = new Constant[constantPoolCount+2];
640            System.arraycopy(constantPool, 0,
641                             targetClass.constantPool, 0,
642                             constantPoolCount);
643            constantPool = targetClass.constantPool;
644        }
645
646        if (DEBUG)
647        {
648            System.out.println(targetClass.getName()+": adding ["+constant.getClass().getName()+"] at index "+targetClass.u2constantPoolCount);
649        }
650
651        // Create a new Utf8Constant for the given string.
652        constantPool[targetClass.u2constantPoolCount++] = constant;
653
654        // Long constants and double constants take up two entries in the
655        // constant pool.
656        int tag = constant.getTag();
657        if (tag == ClassConstants.CONSTANT_Long ||
658            tag == ClassConstants.CONSTANT_Double)
659        {
660            constantPool[targetClass.u2constantPoolCount++] = null;
661        }
662
663        return constantPoolCount;
664    }
665}
666