1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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 InvokeDynamicConstant constant pool entry with the
203     * given bootstrap method constant pool entry index, method name, and
204     * descriptor.
205     * @return the constant pool index of the InvokeDynamicConstant.
206     */
207    public int addInvokeDynamicConstant(int     bootstrapMethodIndex,
208                                        String  name,
209                                        String  descriptor,
210                                        Clazz[] referencedClasses)
211    {
212        return addInvokeDynamicConstant(bootstrapMethodIndex,
213                                        addNameAndTypeConstant(name, descriptor),
214                                        referencedClasses);
215    }
216
217
218    /**
219     * Finds or creates a InvokeDynamicConstant constant pool entry with the given
220     * class constant pool entry index and name and type constant pool entry
221     * index.
222     * @return the constant pool index of the InvokeDynamicConstant.
223     */
224    public int addInvokeDynamicConstant(int     bootstrapMethodIndex,
225                                        int     nameAndTypeIndex,
226                                        Clazz[] referencedClasses)
227    {
228        int        constantPoolCount = targetClass.u2constantPoolCount;
229        Constant[] constantPool      = targetClass.constantPool;
230
231        // Check if the entry already exists.
232        for (int index = 1; index < constantPoolCount; index++)
233        {
234            Constant constant = constantPool[index];
235
236            if (constant != null &&
237                constant.getTag() == ClassConstants.CONSTANT_InvokeDynamic)
238            {
239                InvokeDynamicConstant invokeDynamicConstant = (InvokeDynamicConstant)constant;
240                if (invokeDynamicConstant.u2bootstrapMethodAttributeIndex == bootstrapMethodIndex &&
241                    invokeDynamicConstant.u2nameAndTypeIndex     == nameAndTypeIndex)
242                {
243                    return index;
244                }
245            }
246        }
247
248        return addConstant(new InvokeDynamicConstant(bootstrapMethodIndex,
249                                                     nameAndTypeIndex,
250                                                     referencedClasses));
251    }
252
253
254    /**
255     * Finds or creates a MethodHandleConstant constant pool entry of the
256     * specified kind and with the given field ref, interface method ref,
257     * or method ref constant pool entry index.
258     * @return the constant pool index of the MethodHandleConstant.
259     */
260    public int addMethodHandleConstant(int referenceKind,
261                                       int referenceIndex)
262    {
263        int        constantPoolCount = targetClass.u2constantPoolCount;
264        Constant[] constantPool      = targetClass.constantPool;
265
266        // Check if the entry already exists.
267        for (int index = 1; index < constantPoolCount; index++)
268        {
269            Constant constant = constantPool[index];
270
271            if (constant != null &&
272                constant.getTag() == ClassConstants.CONSTANT_MethodHandle)
273            {
274                MethodHandleConstant methodHandleConstant = (MethodHandleConstant)constant;
275                if (methodHandleConstant.u1referenceKind  == referenceKind &&
276                    methodHandleConstant.u2referenceIndex == referenceIndex)
277                {
278                    return index;
279                }
280            }
281        }
282
283        return addConstant(new MethodHandleConstant(referenceKind,
284                                                    referenceIndex));
285    }
286
287
288    /**
289     * Finds or creates a FieldrefConstant constant pool entry for the given
290     * class and field.
291     * @return the constant pool index of the FieldrefConstant.
292     */
293    public int addFieldrefConstant(Clazz  referencedClass,
294                                   Member referencedMember)
295    {
296        return addFieldrefConstant(referencedClass.getName(),
297                                   referencedMember.getName(referencedClass),
298                                   referencedMember.getDescriptor(referencedClass),
299                                   referencedClass,
300                                   referencedMember);
301    }
302
303
304    /**
305     * Finds or creates a FieldrefConstant constant pool entry with the given
306     * class name, field name, and descriptor.
307     * @return the constant pool index of the FieldrefConstant.
308     */
309    public int addFieldrefConstant(String className,
310                                   String name,
311                                   String descriptor,
312                                   Clazz  referencedClass,
313                                   Member referencedMember)
314    {
315        return addFieldrefConstant(className,
316                                   addNameAndTypeConstant(name, descriptor),
317                                   referencedClass,
318                                   referencedMember);
319    }
320
321
322    /**
323     * Finds or creates a FieldrefConstant constant pool entry with the given
324     * class name, field name, and descriptor.
325     * @return the constant pool index of the FieldrefConstant.
326     */
327    public int addFieldrefConstant(String className,
328                                   int    nameAndTypeIndex,
329                                   Clazz  referencedClass,
330                                   Member referencedMember)
331    {
332        return addFieldrefConstant(addClassConstant(className, referencedClass),
333                                   nameAndTypeIndex,
334                                   referencedClass,
335                                   referencedMember);
336    }
337
338
339    /**
340     * Finds or creates a FieldrefConstant constant pool entry with the given
341     * class constant pool entry index, field name, and descriptor.
342     * @return the constant pool index of the FieldrefConstant.
343     */
344    public int addFieldrefConstant(int    classIndex,
345                                   String name,
346                                   String descriptor,
347                                   Clazz  referencedClass,
348                                   Member referencedMember)
349    {
350        return addFieldrefConstant(classIndex,
351                                   addNameAndTypeConstant(name, descriptor),
352                                   referencedClass,
353                                   referencedMember);
354    }
355
356
357    /**
358     * Finds or creates a FieldrefConstant constant pool entry with the given
359     * class constant pool entry index and name and type constant pool entry
360     * index.
361     * @return the constant pool index of the FieldrefConstant.
362     */
363    public int addFieldrefConstant(int    classIndex,
364                                   int    nameAndTypeIndex,
365                                   Clazz  referencedClass,
366                                   Member referencedMember)
367    {
368        int        constantPoolCount = targetClass.u2constantPoolCount;
369        Constant[] constantPool      = targetClass.constantPool;
370
371        // Check if the entry already exists.
372        for (int index = 1; index < constantPoolCount; index++)
373        {
374            Constant constant = constantPool[index];
375
376            if (constant != null &&
377                constant.getTag() == ClassConstants.CONSTANT_Fieldref)
378            {
379                FieldrefConstant fieldrefConstant = (FieldrefConstant)constant;
380                if (fieldrefConstant.u2classIndex         == classIndex &&
381                    fieldrefConstant.u2nameAndTypeIndex   == nameAndTypeIndex)
382                {
383                    return index;
384                }
385            }
386        }
387
388        return addConstant(new FieldrefConstant(classIndex,
389                                                nameAndTypeIndex,
390                                                referencedClass,
391                                                referencedMember));
392    }
393
394
395    /**
396     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
397     * given class name, method name, and descriptor.
398     * @return the constant pool index of the InterfaceMethodrefConstant.
399     */
400    public int addInterfaceMethodrefConstant(String className,
401                                             String name,
402                                             String descriptor,
403                                             Clazz  referencedClass,
404                                             Member referencedMember)
405    {
406        return addInterfaceMethodrefConstant(className,
407                                             addNameAndTypeConstant(name, descriptor),
408                                             referencedClass,
409                                             referencedMember);
410    }
411
412
413    /**
414     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
415     * given class name, method name, and descriptor.
416     * @return the constant pool index of the InterfaceMethodrefConstant.
417     */
418    public int addInterfaceMethodrefConstant(String className,
419                                             int    nameAndTypeIndex,
420                                             Clazz  referencedClass,
421                                             Member referencedMember)
422    {
423        return addInterfaceMethodrefConstant(addClassConstant(className, referencedClass),
424                                             nameAndTypeIndex,
425                                             referencedClass,
426                                             referencedMember);
427    }
428
429
430    /**
431     * Finds or creates a InterfaceMethodrefConstant constant pool entry for the
432     * given class and method.
433     * @return the constant pool index of the InterfaceMethodrefConstant.
434     */
435    public int addInterfaceMethodrefConstant(Clazz  referencedClass,
436                                             Member referencedMember)
437    {
438        return addInterfaceMethodrefConstant(referencedClass.getName(),
439                                             referencedMember.getName(referencedClass),
440                                             referencedMember.getDescriptor(referencedClass),
441                                             referencedClass,
442                                             referencedMember);
443    }
444
445
446    /**
447     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
448     * given class constant pool entry index, method name, and descriptor.
449     * @return the constant pool index of the InterfaceMethodrefConstant.
450     */
451    public int addInterfaceMethodrefConstant(int    classIndex,
452                                             String name,
453                                             String descriptor,
454                                             Clazz  referencedClass,
455                                             Member referencedMember)
456    {
457        return addInterfaceMethodrefConstant(classIndex,
458                                             addNameAndTypeConstant(name, descriptor),
459                                             referencedClass,
460                                             referencedMember);
461    }
462
463
464    /**
465     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
466     * given class constant pool entry index and name and type constant pool
467     * entry index.
468     * @return the constant pool index of the InterfaceMethodrefConstant.
469     */
470    public int addInterfaceMethodrefConstant(int    classIndex,
471                                             int    nameAndTypeIndex,
472                                             Clazz  referencedClass,
473                                             Member referencedMember)
474    {
475        int        constantPoolCount = targetClass.u2constantPoolCount;
476        Constant[] constantPool      = targetClass.constantPool;
477
478        // Check if the entry already exists.
479        for (int index = 1; index < constantPoolCount; index++)
480        {
481            Constant constant = constantPool[index];
482
483            if (constant != null &&
484                            constant.getTag() == ClassConstants.CONSTANT_InterfaceMethodref)
485            {
486                InterfaceMethodrefConstant methodrefConstant = (InterfaceMethodrefConstant)constant;
487                if (methodrefConstant.u2classIndex       == classIndex &&
488                    methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex)
489                {
490                    return index;
491                }
492            }
493        }
494
495        return addConstant(new InterfaceMethodrefConstant(classIndex,
496                                                          nameAndTypeIndex,
497                                                          referencedClass,
498                                                          referencedMember));
499    }
500
501
502    /**
503     * Finds or creates a MethodrefConstant constant pool entry for the given
504     * class and method.
505     * @return the constant pool index of the MethodrefConstant.
506     */
507    public int addMethodrefConstant(Clazz  referencedClass,
508                                    Member referencedMember)
509    {
510        return addMethodrefConstant(referencedClass.getName(),
511                                    referencedMember.getName(referencedClass),
512                                    referencedMember.getDescriptor(referencedClass),
513                                    referencedClass,
514                                    referencedMember);
515    }
516
517
518    /**
519     * Finds or creates a MethodrefConstant constant pool entry with the given
520     * class name, method name, and descriptor.
521     * @return the constant pool index of the MethodrefConstant.
522     */
523    public int addMethodrefConstant(String className,
524                                    String name,
525                                    String descriptor,
526                                    Clazz  referencedClass,
527                                    Member referencedMember)
528    {
529        return addMethodrefConstant(className,
530                                    addNameAndTypeConstant(name, descriptor),
531                                    referencedClass,
532                                    referencedMember);
533    }
534
535
536    /**
537     * Finds or creates a MethodrefConstant constant pool entry with the given
538     * class name, method name, and descriptor.
539     * @return the constant pool index of the MethodrefConstant.
540     */
541    public int addMethodrefConstant(String className,
542                                    int    nameAndTypeIndex,
543                                    Clazz  referencedClass,
544                                    Member referencedMember)
545    {
546        return addMethodrefConstant(addClassConstant(className, referencedClass),
547                                    nameAndTypeIndex,
548                                    referencedClass,
549                                    referencedMember);
550    }
551
552
553    /**
554     * Finds or creates a MethodrefConstant constant pool entry with the given
555     * class constant pool entry index, method name, and descriptor.
556     * @return the constant pool index of the MethodrefConstant.
557     */
558    public int addMethodrefConstant(int    classIndex,
559                                    String name,
560                                    String descriptor,
561                                    Clazz  referencedClass,
562                                    Member referencedMember)
563    {
564        return addMethodrefConstant(classIndex,
565                                    addNameAndTypeConstant(name, descriptor),
566                                    referencedClass,
567                                    referencedMember);
568    }
569
570
571    /**
572     * Finds or creates a MethodrefConstant constant pool entry with the given
573     * class constant pool entry index and name and type constant pool entry
574     * index.
575     * @return the constant pool index of the MethodrefConstant.
576     */
577    public int addMethodrefConstant(int    classIndex,
578                                    int    nameAndTypeIndex,
579                                    Clazz  referencedClass,
580                                    Member referencedMember)
581    {
582        int        constantPoolCount = targetClass.u2constantPoolCount;
583        Constant[] constantPool      = targetClass.constantPool;
584
585        // Check if the entry already exists.
586        for (int index = 1; index < constantPoolCount; index++)
587        {
588            Constant constant = constantPool[index];
589
590            if (constant != null &&
591                constant.getTag() == ClassConstants.CONSTANT_Methodref)
592            {
593                MethodrefConstant methodrefConstant = (MethodrefConstant)constant;
594                if (methodrefConstant.u2classIndex       == classIndex &&
595                    methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex)
596                {
597                    return index;
598                }
599            }
600        }
601
602        return addConstant(new MethodrefConstant(classIndex,
603                                                 nameAndTypeIndex,
604                                                 referencedClass,
605                                                 referencedMember));
606    }
607
608
609    /**
610     * Finds or creates a ClassConstant constant pool entry for the given class.
611     * @return the constant pool index of the ClassConstant.
612     */
613    public int addClassConstant(Clazz referencedClass)
614    {
615        return addClassConstant(referencedClass.getName(),
616                                referencedClass);
617    }
618
619
620    /**
621     * Finds or creates a ClassConstant constant pool entry with the given name.
622     * @return the constant pool index of the ClassConstant.
623     */
624    public int addClassConstant(String name,
625                                Clazz  referencedClass)
626    {
627        int        constantPoolCount = targetClass.u2constantPoolCount;
628        Constant[] constantPool      = targetClass.constantPool;
629
630        // Check if the entry already exists.
631        for (int index = 1; index < constantPoolCount; index++)
632        {
633            Constant constant = constantPool[index];
634
635            if (constant != null &&
636                constant.getTag() == ClassConstants.CONSTANT_Class)
637            {
638                ClassConstant classConstant = (ClassConstant)constant;
639                if (classConstant.getName(targetClass).equals(name))
640                {
641                    return index;
642                }
643            }
644        }
645
646        int nameIndex = addUtf8Constant(name);
647
648        return addConstant(new ClassConstant(nameIndex, referencedClass));
649    }
650
651
652    /**
653     * Finds or creates a MethodTypeConstant constant pool entry with the given
654     * type.
655     * @return the constant pool index of the MethodTypeConstant.
656     */
657    public int addMethodTypeConstant(String type)
658    {
659        int        constantPoolCount = targetClass.u2constantPoolCount;
660        Constant[] constantPool      = targetClass.constantPool;
661
662        // Check if the entry already exists.
663        for (int index = 1; index < constantPoolCount; index++)
664        {
665            Constant constant = constantPool[index];
666
667            if (constant != null &&
668                constant.getTag() == ClassConstants.CONSTANT_MethodType)
669            {
670                MethodTypeConstant methodTypeConstant = (MethodTypeConstant)constant;
671                if (methodTypeConstant.getType(targetClass).equals(type))
672                {
673                    return index;
674                }
675            }
676        }
677
678        return addConstant(new MethodTypeConstant(addUtf8Constant(type)));
679    }
680
681
682    /**
683     * Finds or creates a NameAndTypeConstant constant pool entry with the given
684     * name and type.
685     * @return the constant pool index of the NameAndTypeConstant.
686     */
687    public int addNameAndTypeConstant(String name,
688                                      String type)
689    {
690        int        constantPoolCount = targetClass.u2constantPoolCount;
691        Constant[] constantPool      = targetClass.constantPool;
692
693        // Check if the entry already exists.
694        for (int index = 1; index < constantPoolCount; index++)
695        {
696            Constant constant = constantPool[index];
697
698            if (constant != null &&
699                constant.getTag() == ClassConstants.CONSTANT_NameAndType)
700            {
701                NameAndTypeConstant nameAndTypeConstant = (NameAndTypeConstant)constant;
702                if (nameAndTypeConstant.getName(targetClass).equals(name) &&
703                    nameAndTypeConstant.getType(targetClass).equals(type))
704                {
705                    return index;
706                }
707            }
708        }
709
710        return addConstant(new NameAndTypeConstant(addUtf8Constant(name),
711                                                   addUtf8Constant(type)));
712    }
713
714
715    /**
716     * Finds or creates a Utf8Constant constant pool entry for the given string.
717     * @return the constant pool index of the Utf8Constant.
718     */
719    public int addUtf8Constant(String string)
720    {
721        int        constantPoolCount = targetClass.u2constantPoolCount;
722        Constant[] constantPool      = targetClass.constantPool;
723
724        // Check if the entry already exists.
725        for (int index = 1; index < constantPoolCount; index++)
726        {
727            Constant constant = constantPool[index];
728
729            if (constant != null &&
730                constant.getTag() == ClassConstants.CONSTANT_Utf8)
731            {
732                Utf8Constant utf8Constant = (Utf8Constant)constant;
733                if (utf8Constant.getString().equals(string))
734                {
735                    return index;
736                }
737            }
738        }
739
740        return addConstant(new Utf8Constant(string));
741    }
742
743
744    /**
745     * Adds a given constant pool entry to the end of the constant pool/
746     * @return the constant pool index for the added entry.
747     */
748    public int addConstant(Constant constant)
749    {
750        int        constantPoolCount = targetClass.u2constantPoolCount;
751        Constant[] constantPool      = targetClass.constantPool;
752
753        // Make sure there is enough space for another constant pool entry.
754        if (constantPool.length < constantPoolCount+2)
755        {
756            targetClass.constantPool = new Constant[constantPoolCount+2];
757            System.arraycopy(constantPool, 0,
758                             targetClass.constantPool, 0,
759                             constantPoolCount);
760            constantPool = targetClass.constantPool;
761        }
762
763        if (DEBUG)
764        {
765            System.out.println(targetClass.getName()+": adding ["+constant.getClass().getName()+"] at index "+targetClass.u2constantPoolCount);
766        }
767
768        // Create a new Utf8Constant for the given string.
769        constantPool[targetClass.u2constantPoolCount++] = constant;
770
771        // Long constants and double constants take up two entries in the
772        // constant pool.
773        int tag = constant.getTag();
774        if (tag == ClassConstants.CONSTANT_Long ||
775            tag == ClassConstants.CONSTANT_Double)
776        {
777            constantPool[targetClass.u2constantPoolCount++] = null;
778        }
779
780        return constantPoolCount;
781    }
782}
783