KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > evaluation > value > ReferenceValue


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 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  */

21 package proguard.evaluation.value;
22
23 import proguard.classfile.*;
24 import proguard.classfile.visitor.ClassCollector;
25 import proguard.classfile.util.ClassUtil;
26
27 import java.util.*;
28
29 /**
30  * This class represents a partially evaluated reference value. It has a type
31  * and a flag that indicates whether the value could be <code>null</code>. If
32  * the type is <code>null</code>, the value is <code>null</code>.
33  *
34  * @author Eric Lafortune
35  */

36 public class ReferenceValue extends Category1Value
37 {
38     private static final boolean DEBUG = false;
39
40
41     private String JavaDoc type;
42     private Clazz referencedClass;
43     private boolean mayBeNull;
44
45
46     /**
47      * Creates a new ReferenceValue.
48      */

49     public ReferenceValue(String JavaDoc type,
50                           Clazz referencedClass,
51                           boolean mayBeNull)
52     {
53         this.type = type;
54         this.referencedClass = referencedClass;
55         this.mayBeNull = mayBeNull;
56     }
57
58
59     /**
60      * Returns the type.
61      */

62     public String JavaDoc getType()
63     {
64         return type;
65     }
66
67
68     /**
69      * Returns the class that is referenced by the type.
70      */

71     public Clazz getReferencedClass()
72     {
73         return referencedClass;
74     }
75
76
77     // Basic unary methods.
78

79     /**
80      * Returns whether the type is <code>null</code>.
81      */

82     public int isNull()
83     {
84         return type == null ? ALWAYS :
85                mayBeNull ? MAYBE :
86                               NEVER;
87     }
88
89
90     /**
91      * Returns whether the type is an instance of the given type.
92      */

93     public int instanceOf(String JavaDoc otherType, Clazz otherReferencedClass)
94     {
95         String JavaDoc thisType = this.type;
96
97         // If this type is null, it is never an instance of any class.
98
if (thisType == null)
99         {
100             return NEVER;
101         }
102
103         // Start taking into account the type dimensions.
104
int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
105         int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
106         int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);
107
108         // Strip any common array prefixes.
109
thisType = thisType.substring(commonDimensionCount);
110         otherType = otherType.substring(commonDimensionCount);
111
112         // If either stripped type is a primitive type, we can tell right away.
113
if (commonDimensionCount > 0 &&
114             (ClassUtil.isInternalPrimitiveType(thisType.charAt(0)) ||
115              ClassUtil.isInternalPrimitiveType(otherType.charAt(0))))
116         {
117             return !thisType.equals(otherType) ? NEVER :
118                    mayBeNull ? MAYBE :
119                                                  ALWAYS;
120         }
121
122         // Strip the class type prefix and suffix of this type, if any.
123
if (thisDimensionCount == commonDimensionCount)
124         {
125             thisType = ClassUtil.internalClassNameFromClassType(thisType);
126         }
127
128         // Strip the class type prefix and suffix of the other type, if any.
129
if (otherDimensionCount == commonDimensionCount)
130         {
131             otherType = ClassUtil.internalClassNameFromClassType(otherType);
132         }
133
134         // If this type is an array type, and the other type is not
135
// java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
136
// this type can never be an instance.
137
if (thisDimensionCount > otherDimensionCount &&
138             !ClassUtil.isInternalArrayInterfaceName(otherType))
139         {
140             return NEVER;
141         }
142
143         // If the other type is an array type, and this type is not
144
// java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
145
// this type can never be an instance.
146
if (thisDimensionCount < otherDimensionCount &&
147             !ClassUtil.isInternalArrayInterfaceName(thisType))
148         {
149             return NEVER;
150         }
151
152         // If this type may be null, it might not be an instance of any class.
153
if (mayBeNull)
154         {
155             return MAYBE;
156         }
157
158         // If this type is equal to the other type, or if the other type is
159
// java.lang.Object, this type is always an instance.
160
if (thisType.equals(otherType) ||
161             ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(otherType))
162         {
163             return ALWAYS;
164         }
165
166         // If this type is an array type, it's ok.
167
if (thisDimensionCount > otherDimensionCount)
168         {
169             return ALWAYS;
170         }
171
172         // If the other type is an array type, it might be ok.
173
if (thisDimensionCount < otherDimensionCount)
174         {
175             return MAYBE;
176         }
177
178         // If the value extends the type, we're sure.
179
return referencedClass != null &&
180                otherReferencedClass != null &&
181                referencedClass.extendsOrImplements(otherReferencedClass) ?
182                    ALWAYS :
183                    MAYBE;
184     }
185
186
187     /**
188      * Returns the length of the array, assuming this type is an array.
189      */

190     public IntegerValue arrayLength(ValueFactory valueFactory)
191     {
192         return valueFactory.createIntegerValue();
193     }
194
195
196     /**
197      * Returns the value of the array at the given index, assuming this type
198      * is an array.
199      */

200     public Value arrayLoad(IntegerValue integerValue, ValueFactory valueFactory)
201     {
202         return
203             type == null ? ValueFactory.REFERENCE_VALUE_NULL :
204             !ClassUtil.isInternalArrayType(type) ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL :
205                                                    valueFactory.createValue(type.substring(1),
206                                                                             referencedClass,
207                                                                             true);
208     }
209
210
211     // Basic binary methods.
212

213     /**
214      * Returns the generalization of this ReferenceValue and the given other
215      * ReferenceValue.
216      */

217     public ReferenceValue generalize(ReferenceValue other)
218     {
219         String JavaDoc thisType = this.type;
220         String JavaDoc otherType = other.type;
221
222         // If both types are nul, the generalization is null too.
223
if (thisType == null && otherType == null)
224         {
225             return ValueFactory.REFERENCE_VALUE_NULL;
226         }
227
228         // If this type is null, the generalization is the other type, maybe null.
229
if (thisType == null)
230         {
231             return other.generalizeMayBeNull(true);
232         }
233
234         // If the other type is null, the generalization is this type, maybe null.
235
if (otherType == null)
236         {
237             return this.generalizeMayBeNull(true);
238         }
239
240         boolean mayBeNull = this.mayBeNull || other.mayBeNull;
241
242         // If the two types are equal, the generalization remains the same, maybe null.
243
if (thisType.equals(otherType))
244         {
245             return this.generalizeMayBeNull(mayBeNull);
246         }
247
248         // Start taking into account the type dimensions.
249
int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
250         int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
251         int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);
252
253         if (thisDimensionCount == otherDimensionCount)
254         {
255             // See if we can take into account the referenced classes.
256
Clazz thisReferencedClass = this.referencedClass;
257             Clazz otherReferencedClass = other.referencedClass;
258
259             if (thisReferencedClass != null &&
260                 otherReferencedClass != null)
261             {
262                 if (thisReferencedClass.extendsOrImplements(otherReferencedClass))
263                 {
264                     return other.generalizeMayBeNull(mayBeNull);
265                 }
266
267                 if (otherReferencedClass.extendsOrImplements(thisReferencedClass))
268                 {
269                     return this.generalizeMayBeNull(mayBeNull);
270                 }
271
272                 // Collect the super classes and interfaces of this class.
273
Set thisSuperClasses = new HashSet();
274                 thisReferencedClass.hierarchyAccept(false, true, true, false,
275                                                     new ClassCollector(thisSuperClasses));
276
277                 // Collect the superclasses and interfaces of this class.
278
Set otherSuperClasses = new HashSet();
279                 otherReferencedClass.hierarchyAccept(false, true, true, false,
280                                                      new ClassCollector(otherSuperClasses));
281
282                 if (DEBUG)
283                 {
284                     System.out.println("ReferenceValue.generalize this ["+thisReferencedClass.getName()+"] with other ["+otherReferencedClass.getName()+"]");
285                     System.out.println(" This super classes: "+thisSuperClasses);
286                     System.out.println(" Other super classes: "+otherSuperClasses);
287                 }
288
289                 // Find the common superclasses.
290
thisSuperClasses.retainAll(otherSuperClasses);
291
292                 if (DEBUG)
293                 {
294                     System.out.println(" Common super classes: "+thisSuperClasses);
295                 }
296
297                 // Find a class that is a subclass of all common superclasses,
298
// or that at least has the maximum number of common superclasses.
299
Clazz commonClazz = null;
300
301                 int maximumSuperClassCount = -1;
302
303                 // Go over all common superclasses to find it. In case of
304
// multiple subclasses, keep the lowest one alphabetically,
305
// in order to ensure that the choice is deterministic.
306
Iterator commonSuperClasses = thisSuperClasses.iterator();
307                 while (commonSuperClasses.hasNext())
308                 {
309                     Clazz commonSuperClass = (Clazz)commonSuperClasses.next();
310
311                     int superClassCount = superClassCount(commonSuperClass, thisSuperClasses);
312                     if (maximumSuperClassCount < superClassCount ||
313                         (maximumSuperClassCount == superClassCount &&
314                          commonClazz.getName().compareTo(commonSuperClass.getName()) > 0))
315                     {
316                         commonClazz = commonSuperClass;
317                         maximumSuperClassCount = superClassCount;
318                     }
319                 }
320
321                 if (commonClazz == null)
322                 {
323                     throw new IllegalArgumentException JavaDoc("Can't find common super class of ["+thisType+"] and ["+otherType+"]");
324                 }
325
326                 if (DEBUG)
327                 {
328                     System.out.println(" Best common class: ["+commonClazz.getName()+"]");
329                 }
330
331                 // TODO: Handle more difficult cases, with multiple global subclasses.
332

333                 return new ReferenceValue(commonDimensionCount == 0 ?
334                                               commonClazz.getName() :
335                                               ClassUtil.internalArrayTypeFromClassName(commonClazz.getName(),
336                                                                                        commonDimensionCount),
337                                           commonClazz,
338                                           mayBeNull);
339             }
340         }
341         else if (thisDimensionCount > otherDimensionCount)
342         {
343             // See if the other type is an interface type of arrays.
344
if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(otherType)))
345             {
346                 return other.generalizeMayBeNull(mayBeNull);
347             }
348         }
349         else if (thisDimensionCount < otherDimensionCount)
350         {
351             // See if this type is an interface type of arrays.
352
if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(thisType)))
353             {
354                 return this.generalizeMayBeNull(mayBeNull);
355             }
356         }
357
358         // Reduce the common dimension count if either type is an array of
359
// primitives type of this dimension.
360
if (commonDimensionCount > 0 &&
361             (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) ||
362              ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount)))
363         {
364             commonDimensionCount--;
365         }
366
367         // Fall back on a basic Object or array of Objects type.
368
return commonDimensionCount == 0 ?
369             mayBeNull ?
370                 ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL :
371                 ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL :
372             new ReferenceValue(ClassUtil.internalArrayTypeFromClassName(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT,
373                                                                         commonDimensionCount),
374                                null,
375                                mayBeNull);
376     }
377
378
379     /**
380      * Returns if the number of superclasses of the given class in the given
381      * set of classes.
382      */

383     private int superClassCount(Clazz subClass, Set classes)
384     {
385         int count = 0;
386
387         Iterator iterator = classes.iterator();
388
389         while (iterator.hasNext())
390         {
391             Clazz clazz = (Clazz)iterator.next();
392             if (subClass.extendsOrImplements(clazz))
393             {
394                 count++;
395             }
396         }
397
398         //System.out.println("ReferenceValue.superClassCount: ["+subClass.getName()+"]: "+count);
399

400         return count;
401     }
402
403
404     /**
405      * Returns whether this ReferenceValue is equal to the given other
406      * ReferenceValue.
407      * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
408      */

409     public int equal(ReferenceValue other)
410     {
411         return this.type == null && other.type == null ? ALWAYS : MAYBE;
412     }
413
414
415     // Derived unary methods.
416

417     /**
418      * Returns whether this ReferenceValue is not <code>null</code>.
419      * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
420      */

421     public final int isNotNull()
422     {
423         return -isNull();
424     }
425
426
427     /**
428      * Returns the generalization of this ReferenceValue and the given other
429      * ReferenceValue.
430      */

431     private ReferenceValue generalizeMayBeNull(boolean mayBeNull)
432     {
433         return this.mayBeNull || !mayBeNull ?
434             this :
435             new ReferenceValue(this.type, this.referencedClass, true);
436     }
437
438
439     // Derived binary methods.
440

441     /**
442      * Returns whether this ReferenceValue and the given ReferenceValue are different.
443      * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
444      */

445     public final int notEqual(ReferenceValue other)
446     {
447         return -equal(other);
448     }
449
450
451     // Implementations for Value.
452

453     public final ReferenceValue referenceValue()
454     {
455         return this;
456     }
457
458     public final Value generalize(Value other)
459     {
460         return this.generalize(other.referenceValue());
461     }
462
463     public boolean isSpecific()
464     {
465         return type == null;
466     }
467
468     public final int computationalType()
469     {
470         return TYPE_REFERENCE;
471     }
472
473     public final String JavaDoc internalType()
474     {
475         return
476             type == null ? ClassConstants.INTERNAL_TYPE_JAVA_LANG_OBJECT :
477             ClassUtil.isInternalArrayType(type) ? type :
478                                                   ClassConstants.INTERNAL_TYPE_CLASS_START +
479                                                   type +
480                                                   ClassConstants.INTERNAL_TYPE_CLASS_END;
481     }
482
483
484     // Implementations for Object.
485

486     public boolean equals(Object JavaDoc object)
487     {
488         if (object == null ||
489             this.getClass() != object.getClass())
490         {
491             return false;
492         }
493
494         ReferenceValue other = (ReferenceValue)object;
495         return this.type == null ? other.type == null :
496                                    (this.mayBeNull == other.mayBeNull &&
497                                     this.type.equals(other.type));
498     }
499
500
501     public int hashCode()
502     {
503         return this.getClass().hashCode() ^
504                (type == null ? 0 : type.hashCode() ^ (mayBeNull ? 0 : 1));
505     }
506
507
508     public String JavaDoc toString()
509     {
510         return "a:" + (type == null ?
511             "null" :
512             type + (referencedClass == null ? "?" : "") + (mayBeNull ? "" : "!"));
513     }
514 }
515
Popular Tags