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;
22
23import proguard.classfile.attribute.Attribute;
24import proguard.classfile.attribute.visitor.AttributeVisitor;
25import proguard.classfile.constant.*;
26import proguard.classfile.constant.visitor.ConstantVisitor;
27import proguard.classfile.util.ClassSubHierarchyInitializer;
28import proguard.classfile.visitor.*;
29
30/**
31 * This Clazz is a complete representation of the data in a Java class.
32 *
33 * @author Eric Lafortune
34 */
35public class ProgramClass implements Clazz
36{
37    public int             u4magic;
38    public int             u4version;
39    public int             u2constantPoolCount;
40    public Constant[]      constantPool;
41    public int             u2accessFlags;
42    public int             u2thisClass;
43    public int             u2superClass;
44    public int             u2interfacesCount;
45    public int[]           u2interfaces;
46    public int             u2fieldsCount;
47    public ProgramField[]  fields;
48    public int             u2methodsCount;
49    public ProgramMethod[] methods;
50    public int             u2attributesCount;
51    public Attribute[]     attributes;
52
53    /**
54     * An extra field pointing to the subclasses of this class.
55     * This field is filled out by the {@link ClassSubHierarchyInitializer}.
56     */
57    public Clazz[] subClasses;
58
59    /**
60     * An extra field in which visitors can store information.
61     */
62    public Object visitorInfo;
63
64
65    /**
66     * Creates an uninitialized ProgramClass.
67     */
68    public ProgramClass() {}
69
70
71    /**
72     * Returns the Constant at the given index in the constant pool.
73     */
74    public Constant getConstant(int constantIndex)
75    {
76        return constantPool[constantIndex];
77    }
78
79
80    // Implementations for Clazz.
81
82    public int getAccessFlags()
83    {
84        return u2accessFlags;
85    }
86
87    public String getName()
88    {
89        return getClassName(u2thisClass);
90    }
91
92    public String getSuperName()
93    {
94        return u2superClass == 0 ? null : getClassName(u2superClass);
95    }
96
97    public int getInterfaceCount()
98    {
99        return u2interfacesCount;
100    }
101
102    public String getInterfaceName(int index)
103    {
104        return getClassName(u2interfaces[index]);
105    }
106
107    public int getTag(int constantIndex)
108    {
109        return constantPool[constantIndex].getTag();
110    }
111
112    public String getString(int constantIndex)
113    {
114        try
115        {
116            return ((Utf8Constant)constantPool[constantIndex]).getString();
117        }
118        catch (ClassCastException ex)
119        {
120            throw ((IllegalStateException)new IllegalStateException("Expected Utf8Constant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
121        }
122    }
123
124    public String getStringString(int constantIndex)
125    {
126        try
127        {
128            return ((StringConstant)constantPool[constantIndex]).getString(this);
129        }
130        catch (ClassCastException ex)
131        {
132            throw ((IllegalStateException)new IllegalStateException("Expected StringConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
133        }
134    }
135
136    public String getClassName(int constantIndex)
137    {
138        try
139        {
140            return ((ClassConstant)constantPool[constantIndex]).getName(this);
141        }
142        catch (ClassCastException ex)
143        {
144            throw ((IllegalStateException)new IllegalStateException("Expected ClassConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
145        }
146    }
147
148    public String getName(int constantIndex)
149    {
150        try
151        {
152            return ((NameAndTypeConstant)constantPool[constantIndex]).getName(this);
153        }
154        catch (ClassCastException ex)
155        {
156            throw ((IllegalStateException)new IllegalStateException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
157        }
158    }
159
160    public String getType(int constantIndex)
161    {
162        try
163        {
164            return ((NameAndTypeConstant)constantPool[constantIndex]).getType(this);
165        }
166        catch (ClassCastException ex)
167        {
168            throw ((IllegalStateException)new IllegalStateException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
169        }
170    }
171
172
173    public String getRefName(int constantIndex)
174    {
175        try
176        {
177            return ((RefConstant)constantPool[constantIndex]).getName(this);
178        }
179        catch (ClassCastException ex)
180        {
181            throw ((IllegalStateException)new IllegalStateException("Expected RefConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
182        }
183    }
184
185    public String getRefType(int constantIndex)
186    {
187        try
188        {
189            return ((RefConstant)constantPool[constantIndex]).getType(this);
190        }
191        catch (ClassCastException ex)
192        {
193            throw ((IllegalStateException)new IllegalStateException("Expected RefConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
194        }
195    }
196
197
198    public void addSubClass(Clazz clazz)
199    {
200        if (subClasses == null)
201        {
202            subClasses = new Clazz[1];
203        }
204        else
205        {
206            // Copy the old elements into new larger array.
207            Clazz[] temp = new Clazz[subClasses.length+1];
208            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
209            subClasses = temp;
210        }
211
212        subClasses[subClasses.length-1] = clazz;
213    }
214
215
216    public Clazz getSuperClass()
217    {
218        return u2superClass != 0 ?
219            ((ClassConstant)constantPool[u2superClass]).referencedClass :
220            null;
221    }
222
223
224    public Clazz getInterface(int index)
225    {
226        return ((ClassConstant)constantPool[u2interfaces[index]]).referencedClass;
227    }
228
229
230    public boolean extends_(Clazz clazz)
231    {
232        if (this.equals(clazz))
233        {
234            return true;
235        }
236
237        Clazz superClass = getSuperClass();
238        return superClass != null &&
239               superClass.extends_(clazz);
240    }
241
242
243    public boolean extends_(String className)
244    {
245        if (getName().equals(className))
246        {
247            return true;
248        }
249
250        Clazz superClass = getSuperClass();
251        return superClass != null &&
252               superClass.extends_(className);
253    }
254
255
256    public boolean extendsOrImplements(Clazz clazz)
257    {
258        if (this.equals(clazz))
259        {
260            return true;
261        }
262
263        Clazz superClass = getSuperClass();
264        if (superClass != null &&
265            superClass.extendsOrImplements(clazz))
266        {
267            return true;
268        }
269
270        for (int index = 0; index < u2interfacesCount; index++)
271        {
272            Clazz interfaceClass = getInterface(index);
273            if (interfaceClass != null &&
274                interfaceClass.extendsOrImplements(clazz))
275            {
276                return true;
277            }
278        }
279
280        return false;
281    }
282
283
284    public boolean extendsOrImplements(String className)
285    {
286        if (getName().equals(className))
287        {
288            return true;
289        }
290
291        Clazz superClass = getSuperClass();
292        if (superClass != null &&
293            superClass.extendsOrImplements(className))
294        {
295            return true;
296        }
297
298        for (int index = 0; index < u2interfacesCount; index++)
299        {
300            Clazz interfaceClass = getInterface(index);
301            if (interfaceClass != null &&
302                interfaceClass.extendsOrImplements(className))
303            {
304                return true;
305            }
306        }
307
308        return false;
309    }
310
311
312    public Field findField(String name, String descriptor)
313    {
314        for (int index = 0; index < u2fieldsCount; index++)
315        {
316            Field field = fields[index];
317            if ((name       == null || field.getName(this).equals(name)) &&
318                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
319            {
320                return field;
321            }
322        }
323
324        return null;
325    }
326
327
328    public Method findMethod(String name, String descriptor)
329    {
330        for (int index = 0; index < u2methodsCount; index++)
331        {
332            Method method = methods[index];
333            if ((name       == null || method.getName(this).equals(name)) &&
334                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
335            {
336                return method;
337            }
338        }
339
340        return null;
341    }
342
343
344    public void accept(ClassVisitor classVisitor)
345    {
346        classVisitor.visitProgramClass(this);
347    }
348
349
350    public void hierarchyAccept(boolean      visitThisClass,
351                                boolean      visitSuperClass,
352                                boolean      visitInterfaces,
353                                boolean      visitSubclasses,
354                                ClassVisitor classVisitor)
355    {
356        // First visit the current classfile.
357        if (visitThisClass)
358        {
359            accept(classVisitor);
360        }
361
362        // Then visit its superclass, recursively.
363        if (visitSuperClass)
364        {
365            Clazz superClass = getSuperClass();
366            if (superClass != null)
367            {
368                superClass.hierarchyAccept(true,
369                                           true,
370                                           visitInterfaces,
371                                           false,
372                                           classVisitor);
373            }
374        }
375
376        // Then visit its interfaces, recursively.
377        if (visitInterfaces)
378        {
379            // Visit the interfaces of the superclasses, if we haven't done so yet.
380            if (!visitSuperClass)
381            {
382                Clazz superClass = getSuperClass();
383                if (superClass != null)
384                {
385                    superClass.hierarchyAccept(false,
386                                               false,
387                                               true,
388                                               false,
389                                               classVisitor);
390                }
391            }
392
393            // Visit the interfaces.
394            for (int index = 0; index < u2interfacesCount; index++)
395            {
396                Clazz interfaceClass = getInterface(index);
397                if (interfaceClass != null)
398                {
399                    interfaceClass.hierarchyAccept(true,
400                                                   false,
401                                                   true,
402                                                   false,
403                                                   classVisitor);
404                }
405            }
406        }
407
408        // Then visit its subclasses, recursively.
409        if (visitSubclasses)
410        {
411            if (subClasses != null)
412            {
413                for (int index = 0; index < subClasses.length; index++)
414                {
415                    Clazz subClass = subClasses[index];
416                    subClass.hierarchyAccept(true,
417                                             false,
418                                             false,
419                                             true,
420                                             classVisitor);
421                }
422            }
423        }
424    }
425
426
427    public void subclassesAccept(ClassVisitor classVisitor)
428    {
429        if (subClasses != null)
430        {
431            for (int index = 0; index < subClasses.length; index++)
432            {
433                subClasses[index].accept(classVisitor);
434            }
435        }
436    }
437
438
439    public void constantPoolEntriesAccept(ConstantVisitor constantVisitor)
440    {
441        for (int index = 1; index < u2constantPoolCount; index++)
442        {
443            if (constantPool[index] != null)
444            {
445                constantPool[index].accept(this, constantVisitor);
446            }
447        }
448    }
449
450
451    public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor)
452    {
453        constantPool[index].accept(this, constantVisitor);
454    }
455
456
457    public void thisClassConstantAccept(ConstantVisitor constantVisitor)
458    {
459        constantPool[u2thisClass].accept(this, constantVisitor);
460    }
461
462
463    public void superClassConstantAccept(ConstantVisitor constantVisitor)
464    {
465        if (u2superClass != 0)
466        {
467            constantPool[u2superClass].accept(this, constantVisitor);
468        }
469    }
470
471
472    public void interfaceConstantsAccept(ConstantVisitor constantVisitor)
473    {
474        for (int index = 0; index < u2interfacesCount; index++)
475        {
476            constantPool[u2interfaces[index]].accept(this, constantVisitor);
477        }
478    }
479
480
481    public void fieldsAccept(MemberVisitor memberVisitor)
482    {
483        for (int index = 0; index < u2fieldsCount; index++)
484        {
485            fields[index].accept(this, memberVisitor);
486        }
487    }
488
489
490    public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor)
491    {
492        Field field = findField(name, descriptor);
493        if (field != null)
494        {
495            field.accept(this, memberVisitor);
496        }
497    }
498
499
500    public void methodsAccept(MemberVisitor memberVisitor)
501    {
502        for (int index = 0; index < u2methodsCount; index++)
503        {
504            methods[index].accept(this, memberVisitor);
505        }
506    }
507
508
509    public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor)
510    {
511        Method method = findMethod(name, descriptor);
512        if (method != null)
513        {
514            method.accept(this, memberVisitor);
515        }
516    }
517
518
519    public boolean mayHaveImplementations(Method method)
520    {
521        return
522            (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 &&
523            (method == null ||
524             ((method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
525                                          ClassConstants.INTERNAL_ACC_STATIC  |
526                                          ClassConstants.INTERNAL_ACC_FINAL)) == 0 &&
527              !method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)));
528    }
529
530
531    public void attributesAccept(AttributeVisitor attributeVisitor)
532    {
533        for (int index = 0; index < u2attributesCount; index++)
534        {
535            attributes[index].accept(this, attributeVisitor);
536        }
537    }
538
539
540    public void attributeAccept(String name, AttributeVisitor attributeVisitor)
541    {
542        for (int index = 0; index < u2attributesCount; index++)
543        {
544            Attribute attribute = attributes[index];
545            if (attribute.getAttributeName(this).equals(name))
546            {
547                attribute.accept(this, attributeVisitor);
548            }
549        }
550    }
551
552
553    // Implementations for VisitorAccepter.
554
555    public Object getVisitorInfo()
556    {
557        return visitorInfo;
558    }
559
560    public void setVisitorInfo(Object visitorInfo)
561    {
562        this.visitorInfo = visitorInfo;
563    }
564
565
566    // Implementations for Object.
567
568    public String toString()
569    {
570        return "ProgramClass("+getName()+")";
571    }
572}
573