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;
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 getRefClassName(int constantIndex)
174    {
175        try
176        {
177            return ((RefConstant)constantPool[constantIndex]).getClassName(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 getRefName(int constantIndex)
186    {
187        try
188        {
189            return ((RefConstant)constantPool[constantIndex]).getName(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    public String getRefType(int constantIndex)
198    {
199        try
200        {
201            return ((RefConstant)constantPool[constantIndex]).getType(this);
202        }
203        catch (ClassCastException ex)
204        {
205            throw ((IllegalStateException)new IllegalStateException("Expected RefConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex));
206        }
207    }
208
209
210    public void addSubClass(Clazz clazz)
211    {
212        if (subClasses == null)
213        {
214            subClasses = new Clazz[1];
215        }
216        else
217        {
218            // Copy the old elements into new larger array.
219            Clazz[] temp = new Clazz[subClasses.length+1];
220            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
221            subClasses = temp;
222        }
223
224        subClasses[subClasses.length-1] = clazz;
225    }
226
227
228    public Clazz getSuperClass()
229    {
230        return u2superClass != 0 ?
231            ((ClassConstant)constantPool[u2superClass]).referencedClass :
232            null;
233    }
234
235
236    public Clazz getInterface(int index)
237    {
238        return ((ClassConstant)constantPool[u2interfaces[index]]).referencedClass;
239    }
240
241
242    public boolean extends_(Clazz clazz)
243    {
244        if (this.equals(clazz))
245        {
246            return true;
247        }
248
249        Clazz superClass = getSuperClass();
250        return superClass != null &&
251               superClass.extends_(clazz);
252    }
253
254
255    public boolean extends_(String className)
256    {
257        if (getName().equals(className))
258        {
259            return true;
260        }
261
262        Clazz superClass = getSuperClass();
263        return superClass != null &&
264               superClass.extends_(className);
265    }
266
267
268    public boolean extendsOrImplements(Clazz clazz)
269    {
270        if (this.equals(clazz))
271        {
272            return true;
273        }
274
275        Clazz superClass = getSuperClass();
276        if (superClass != null &&
277            superClass.extendsOrImplements(clazz))
278        {
279            return true;
280        }
281
282        for (int index = 0; index < u2interfacesCount; index++)
283        {
284            Clazz interfaceClass = getInterface(index);
285            if (interfaceClass != null &&
286                interfaceClass.extendsOrImplements(clazz))
287            {
288                return true;
289            }
290        }
291
292        return false;
293    }
294
295
296    public boolean extendsOrImplements(String className)
297    {
298        if (getName().equals(className))
299        {
300            return true;
301        }
302
303        Clazz superClass = getSuperClass();
304        if (superClass != null &&
305            superClass.extendsOrImplements(className))
306        {
307            return true;
308        }
309
310        for (int index = 0; index < u2interfacesCount; index++)
311        {
312            Clazz interfaceClass = getInterface(index);
313            if (interfaceClass != null &&
314                interfaceClass.extendsOrImplements(className))
315            {
316                return true;
317            }
318        }
319
320        return false;
321    }
322
323
324    public Field findField(String name, String descriptor)
325    {
326        for (int index = 0; index < u2fieldsCount; index++)
327        {
328            Field field = fields[index];
329            if ((name       == null || field.getName(this).equals(name)) &&
330                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
331            {
332                return field;
333            }
334        }
335
336        return null;
337    }
338
339
340    public Method findMethod(String name, String descriptor)
341    {
342        for (int index = 0; index < u2methodsCount; index++)
343        {
344            Method method = methods[index];
345            if ((name       == null || method.getName(this).equals(name)) &&
346                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
347            {
348                return method;
349            }
350        }
351
352        return null;
353    }
354
355
356    public void accept(ClassVisitor classVisitor)
357    {
358        classVisitor.visitProgramClass(this);
359    }
360
361
362    public void hierarchyAccept(boolean      visitThisClass,
363                                boolean      visitSuperClass,
364                                boolean      visitInterfaces,
365                                boolean      visitSubclasses,
366                                ClassVisitor classVisitor)
367    {
368        // First visit the current classfile.
369        if (visitThisClass)
370        {
371            accept(classVisitor);
372        }
373
374        // Then visit its superclass, recursively.
375        if (visitSuperClass)
376        {
377            Clazz superClass = getSuperClass();
378            if (superClass != null)
379            {
380                superClass.hierarchyAccept(true,
381                                           true,
382                                           visitInterfaces,
383                                           false,
384                                           classVisitor);
385            }
386        }
387
388        // Then visit its interfaces, recursively.
389        if (visitInterfaces)
390        {
391            // Visit the interfaces of the superclasses, if we haven't done so yet.
392            if (!visitSuperClass)
393            {
394                Clazz superClass = getSuperClass();
395                if (superClass != null)
396                {
397                    superClass.hierarchyAccept(false,
398                                               false,
399                                               true,
400                                               false,
401                                               classVisitor);
402                }
403            }
404
405            // Visit the interfaces.
406            for (int index = 0; index < u2interfacesCount; index++)
407            {
408                Clazz interfaceClass = getInterface(index);
409                if (interfaceClass != null)
410                {
411                    interfaceClass.hierarchyAccept(true,
412                                                   false,
413                                                   true,
414                                                   false,
415                                                   classVisitor);
416                }
417            }
418        }
419
420        // Then visit its subclasses, recursively.
421        if (visitSubclasses)
422        {
423            if (subClasses != null)
424            {
425                for (int index = 0; index < subClasses.length; index++)
426                {
427                    Clazz subClass = subClasses[index];
428                    subClass.hierarchyAccept(true,
429                                             false,
430                                             false,
431                                             true,
432                                             classVisitor);
433                }
434            }
435        }
436    }
437
438
439    public void subclassesAccept(ClassVisitor classVisitor)
440    {
441        if (subClasses != null)
442        {
443            for (int index = 0; index < subClasses.length; index++)
444            {
445                subClasses[index].accept(classVisitor);
446            }
447        }
448    }
449
450
451    public void constantPoolEntriesAccept(ConstantVisitor constantVisitor)
452    {
453        for (int index = 1; index < u2constantPoolCount; index++)
454        {
455            if (constantPool[index] != null)
456            {
457                constantPool[index].accept(this, constantVisitor);
458            }
459        }
460    }
461
462
463    public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor)
464    {
465        constantPool[index].accept(this, constantVisitor);
466    }
467
468
469    public void thisClassConstantAccept(ConstantVisitor constantVisitor)
470    {
471        constantPool[u2thisClass].accept(this, constantVisitor);
472    }
473
474
475    public void superClassConstantAccept(ConstantVisitor constantVisitor)
476    {
477        if (u2superClass != 0)
478        {
479            constantPool[u2superClass].accept(this, constantVisitor);
480        }
481    }
482
483
484    public void interfaceConstantsAccept(ConstantVisitor constantVisitor)
485    {
486        for (int index = 0; index < u2interfacesCount; index++)
487        {
488            constantPool[u2interfaces[index]].accept(this, constantVisitor);
489        }
490    }
491
492
493    public void fieldsAccept(MemberVisitor memberVisitor)
494    {
495        for (int index = 0; index < u2fieldsCount; index++)
496        {
497            fields[index].accept(this, memberVisitor);
498        }
499    }
500
501
502    public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor)
503    {
504        Field field = findField(name, descriptor);
505        if (field != null)
506        {
507            field.accept(this, memberVisitor);
508        }
509    }
510
511
512    public void methodsAccept(MemberVisitor memberVisitor)
513    {
514        for (int index = 0; index < u2methodsCount; index++)
515        {
516            methods[index].accept(this, memberVisitor);
517        }
518    }
519
520
521    public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor)
522    {
523        Method method = findMethod(name, descriptor);
524        if (method != null)
525        {
526            method.accept(this, memberVisitor);
527        }
528    }
529
530
531    public boolean mayHaveImplementations(Method method)
532    {
533        return
534            (u2accessFlags & ClassConstants.ACC_FINAL) == 0 &&
535            (method == null ||
536             ((method.getAccessFlags() & (ClassConstants.ACC_PRIVATE |
537                                          ClassConstants.ACC_STATIC  |
538                                          ClassConstants.ACC_FINAL)) == 0 &&
539              !method.getName(this).equals(ClassConstants.METHOD_NAME_INIT)));
540    }
541
542
543    public void attributesAccept(AttributeVisitor attributeVisitor)
544    {
545        for (int index = 0; index < u2attributesCount; index++)
546        {
547            attributes[index].accept(this, attributeVisitor);
548        }
549    }
550
551
552    public void attributeAccept(String name, AttributeVisitor attributeVisitor)
553    {
554        for (int index = 0; index < u2attributesCount; index++)
555        {
556            Attribute attribute = attributes[index];
557            if (attribute.getAttributeName(this).equals(name))
558            {
559                attribute.accept(this, attributeVisitor);
560            }
561        }
562    }
563
564
565    // Implementations for VisitorAccepter.
566
567    public Object getVisitorInfo()
568    {
569        return visitorInfo;
570    }
571
572    public void setVisitorInfo(Object visitorInfo)
573    {
574        this.visitorInfo = visitorInfo;
575    }
576
577
578    // Implementations for Object.
579
580    public String toString()
581    {
582        return "ProgramClass("+getName()+")";
583    }
584}
585