added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / classfmt / FieldInfo.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.classfmt;
12
13 import org.eclipse.jdt.core.Signature;
14 import org.eclipse.jdt.core.compiler.CharOperation;
15 import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
16 import org.eclipse.jdt.internal.compiler.env.IBinaryField;
17 import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
18 import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
19 import org.eclipse.jdt.internal.compiler.impl.CharConstant;
20 import org.eclipse.jdt.internal.compiler.impl.Constant;
21 import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
22 import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
23 import org.eclipse.jdt.internal.compiler.impl.IntConstant;
24 import org.eclipse.jdt.internal.compiler.impl.LongConstant;
25 import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
26 import org.eclipse.jdt.internal.compiler.impl.StringConstant;
27 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
28 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
29 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
30 import org.eclipse.jdt.internal.compiler.util.Util;
31
32 public class FieldInfo extends ClassFileStruct implements AttributeNamesConstants, IBinaryField, Comparable, TypeIds {
33         private int accessFlags;
34         private int attributeBytes;
35         private Constant constant;
36         private int[] constantPoolOffsets;
37         private char[] descriptor;
38         private char[] name;
39         private char[] signature;
40         private int signatureUtf8Offset;
41         private long tagBits;   
42         private Object wrappedConstantValue;
43 /**
44  * @param classFileBytes byte[]
45  * @param offsets int[]
46  * @param offset int
47  */
48 public FieldInfo (byte classFileBytes[], int offsets[], int offset) {
49         super(classFileBytes, offset);
50         constantPoolOffsets = offsets;
51         accessFlags = -1;
52         int attributesCount = u2At(6);
53         int readOffset = 8;
54         this.signatureUtf8Offset = -1;
55         for (int i = 0; i < attributesCount; i++) {
56                 // check the name of each attribute
57                 int utf8Offset = constantPoolOffsets[u2At(readOffset)] - structOffset;
58                 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
59                 if (attributeName.length > 0) {
60                         switch(attributeName[0]) {
61                                 case 'S' :
62                                         if (CharOperation.equals(AttributeNamesConstants.SignatureName, attributeName)) {
63                                                 this.signatureUtf8Offset = constantPoolOffsets[u2At(readOffset + 6)] - structOffset;
64                                         }
65                                         break;
66                                 case 'R' :
67                                         if (CharOperation.equals(attributeName, RuntimeVisibleAnnotationsName)) {
68                                                 decodeStandardAnnotations(readOffset);
69                                         }
70                         }
71                 }
72                 readOffset += (6 + u4At(readOffset + 2));
73         }
74         attributeBytes = readOffset;
75 }
76
77 public int compareTo(Object o) {
78         if (!(o instanceof FieldInfo)) {
79                 throw new ClassCastException();
80         }
81         return new String(this.getName()).compareTo(new String(((FieldInfo) o).getName()));
82 }
83 private int decodeAnnotation(int offset) {
84         int readOffset = offset;
85         int utf8Offset = this.constantPoolOffsets[u2At(offset)] - structOffset;
86         char[] typeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
87         typeName = Signature.toCharArray(typeName);
88         CharOperation.replace(typeName, '/', '.');
89         char[][] qualifiedTypeName = CharOperation.splitOn('.', typeName);
90         int numberOfPairs = u2At(offset + 2);
91         readOffset += 4;
92         if (qualifiedTypeName.length == 3) {
93                 char[] lastPart = qualifiedTypeName[2];
94                 if (lastPart[0] == 'D') {
95                         if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_DEPRECATED)) {
96                                 this.tagBits |= TagBits.AnnotationDeprecated;
97                                 return readOffset;              
98                         }
99                 }
100         }
101         for (int i = 0; i < numberOfPairs; i++) {
102                 readOffset += 2;
103                 readOffset = decodeElementValue(readOffset);
104         }
105         return readOffset;
106 }
107 private int decodeElementValue(int offset) {
108         int readOffset = offset;
109         int tag = u1At(readOffset);
110         readOffset++;
111         switch(tag) {
112                 case 'B' :
113                 case 'C' :
114                 case 'D' :
115                 case 'F' :
116                 case 'I' :
117                 case 'J' :
118                 case 'S' :
119                 case 'Z' :
120                 case 's' :
121                         readOffset += 2;
122                         break;
123                 case 'e' :
124                         readOffset += 4;
125                         break;
126                 case 'c' :
127                         readOffset += 2;
128                         break;
129                 case '@' :
130                         readOffset += decodeAnnotation(readOffset);
131                         break;
132                 case '[' :
133                         int numberOfValues = u2At(readOffset);
134                         readOffset += 2;
135                         for (int i = 0; i < numberOfValues; i++) {
136                                 readOffset = decodeElementValue(readOffset);
137                         }
138                         break;
139         }
140         return readOffset;
141 }
142 /**
143  * @param offset the offset is located at the beginning of the runtime visible 
144  * annotation attribute.
145  */
146 private void decodeStandardAnnotations(int offset) {
147         int numberOfAnnotations = u2At(offset + 6);
148         int readOffset = offset + 8;
149         for (int i = 0; i < numberOfAnnotations; i++) {
150                 readOffset = decodeAnnotation(readOffset);
151         }
152 }
153 /**
154  * Return the constant of the field.
155  * Return org.eclipse.jdt.internal.compiler.impl.Constant.NotAConstant if there is none.
156  * @return org.eclipse.jdt.internal.compiler.impl.Constant
157  */
158 public Constant getConstant() {
159         if (constant == null) {
160                 // read constant
161                 readConstantAttribute();
162         }
163         return constant;
164 }
165 public char[] getGenericSignature() {
166         if (this.signatureUtf8Offset != -1) {
167                 if (this.signature == null) {
168                         // decode the signature
169                         this.signature = utf8At(this.signatureUtf8Offset + 3, u2At(this.signatureUtf8Offset + 1));
170                 }
171                 return this.signature;
172         }
173         return null;
174 }
175 /**
176  * Answer an int whose bits are set according the access constants
177  * defined by the VM spec.
178  * Set the AccDeprecated and AccSynthetic bits if necessary
179  * @return int
180  */
181 public int getModifiers() {
182         if (this.accessFlags == -1) {
183                 // compute the accessflag. Don't forget the deprecated attribute
184                 this.accessFlags = u2At(0);
185                 readModifierRelatedAttributes();
186         }
187         return this.accessFlags;
188 }
189 /**
190  * Answer the name of the field.
191  * @return char[]
192  */
193 public char[] getName() {
194         if (name == null) {
195                 // read the name
196                 int utf8Offset = constantPoolOffsets[u2At(2)] - structOffset;
197                 name = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
198         }
199         return name;
200 }
201 public long getTagBits() {
202         return this.tagBits;
203 }
204 /**
205  * Answer the resolved name of the receiver's type in the
206  * class file format as specified in section 4.3.2 of the Java 2 VM spec.
207  *
208  * For example:
209  *   - java.lang.String is Ljava/lang/String;
210  *   - an int is I
211  *   - a 2 dimensional array of strings is [[Ljava/lang/String;
212  *   - an array of floats is [F
213  * @return char[]
214  */
215 public char[] getTypeName() {
216         if (descriptor == null) {
217                 // read the signature
218                 int utf8Offset = constantPoolOffsets[u2At(4)] - structOffset;
219                 descriptor = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
220         }
221         return descriptor;
222 }
223 /**
224  * Return a wrapper that contains the constant of the field.
225  * @return java.lang.Object
226  */
227 public Object getWrappedConstantValue() {
228
229         if (this.wrappedConstantValue == null) {
230                 if (hasConstant()) {
231                         Constant fieldConstant = getConstant();
232                         switch (fieldConstant.typeID()) {
233                                 case T_int :
234                                         this.wrappedConstantValue = new Integer(fieldConstant.intValue());
235                                         break;
236                                 case T_byte :
237                                         this.wrappedConstantValue = new Byte(fieldConstant.byteValue());
238                                         break;
239                                 case T_short :
240                                         this.wrappedConstantValue = new Short(fieldConstant.shortValue());
241                                         break;
242                                 case T_char :
243                                         this.wrappedConstantValue = new Character(fieldConstant.charValue());
244                                         break;
245                                 case T_float :
246                                         this.wrappedConstantValue = new Float(fieldConstant.floatValue());
247                                         break;
248                                 case T_double :
249                                         this.wrappedConstantValue = new Double(fieldConstant.doubleValue());
250                                         break;
251                                 case T_boolean :
252                                         this.wrappedConstantValue = Util.toBoolean(fieldConstant.booleanValue());
253                                         break;
254                                 case T_long :
255                                         this.wrappedConstantValue = new Long(fieldConstant.longValue());
256                                         break;
257                                 case T_JavaLangString :
258                                         this.wrappedConstantValue = fieldConstant.stringValue();
259                         }
260                 }
261         }
262         return this.wrappedConstantValue;
263 }
264 /**
265  * Return true if the field has a constant value attribute, false otherwise.
266  * @return boolean
267  */
268 public boolean hasConstant() {
269         return getConstant() != Constant.NotAConstant;
270 }
271 /**
272  * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
273  * will be therefore fully initialized and we can get rid of the bytes.
274  */
275 void initialize() {
276         getModifiers();
277         getName();
278         getConstant();
279         getTypeName();
280         getGenericSignature();
281         reset();
282 }
283 /**
284  * Return true if the field is a synthetic field, false otherwise.
285  * @return boolean
286  */
287 public boolean isSynthetic() {
288         return (getModifiers() & AccSynthetic) != 0;
289 }
290
291 private void readConstantAttribute() {
292         int attributesCount = u2At(6);
293         int readOffset = 8;
294         boolean isConstant = false;
295         for (int i = 0; i < attributesCount; i++) {
296                 int utf8Offset = constantPoolOffsets[u2At(readOffset)] - structOffset;
297                 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
298                 if (CharOperation
299                         .equals(attributeName, ConstantValueName)) {
300                         isConstant = true;
301                         // read the right constant
302                         int relativeOffset = constantPoolOffsets[u2At(readOffset + 6)] - structOffset;
303                         switch (u1At(relativeOffset)) {
304                                 case IntegerTag :
305                                         char[] sign = getTypeName();
306                                         if (sign.length == 1) {
307                                                 switch (sign[0]) {
308                                                         case 'Z' : // boolean constant
309                                                                 constant = new BooleanConstant(i4At(relativeOffset + 1) == 1);
310                                                                 break;
311                                                         case 'I' : // integer constant
312                                                                 constant = new IntConstant(i4At(relativeOffset + 1));
313                                                                 break;
314                                                         case 'C' : // char constant
315                                                                 constant = new CharConstant((char) i4At(relativeOffset + 1));
316                                                                 break;
317                                                         case 'B' : // byte constant
318                                                                 constant = new ByteConstant((byte) i4At(relativeOffset + 1));
319                                                                 break;
320                                                         case 'S' : // short constant
321                                                                 constant = new ShortConstant((short) i4At(relativeOffset + 1));
322                                                                 break;
323                                                         default:
324                                                                 constant = Constant.NotAConstant;                   
325                                                 }
326                                         } else {
327                                                 constant = Constant.NotAConstant;
328                                         }
329                                         break;
330                                 case FloatTag :
331                                         constant = new FloatConstant(floatAt(relativeOffset + 1));
332                                         break;
333                                 case DoubleTag :
334                                         constant = new DoubleConstant(doubleAt(relativeOffset + 1));
335                                         break;
336                                 case LongTag :
337                                         constant = new LongConstant(i8At(relativeOffset + 1));
338                                         break;
339                                 case StringTag :
340                                         utf8Offset = constantPoolOffsets[u2At(relativeOffset + 1)] - structOffset;
341                                         constant = 
342                                                 new StringConstant(
343                                                         String.valueOf(utf8At(utf8Offset + 3, u2At(utf8Offset + 1)))); 
344                                         break;
345                         }
346                 }
347                 readOffset += (6 + u4At(readOffset + 2));
348         }
349         if (!isConstant) {
350                 constant = Constant.NotAConstant;
351         }
352 }
353 private void readModifierRelatedAttributes() {
354         int attributesCount = u2At(6);
355         int readOffset = 8;
356         for (int i = 0; i < attributesCount; i++) {
357                 int utf8Offset = constantPoolOffsets[u2At(readOffset)] - structOffset;
358                 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
359                 // test added for obfuscated .class file. See 79772
360                 if (attributeName.length != 0) {
361                         switch(attributeName[0]) {
362                                 case 'D' :
363                                         if (CharOperation.equals(attributeName, DeprecatedName))
364                                                 this.accessFlags |= AccDeprecated;
365                                         break;
366                                 case 'S' :
367                                         if (CharOperation.equals(attributeName, SyntheticName))
368                                                 this.accessFlags |= AccSynthetic;
369                                         break;
370                         }
371                 }
372                 readOffset += (6 + u4At(readOffset + 2));
373         }
374 }
375 protected void reset() {
376         this.constantPoolOffsets = null;
377         super.reset();
378 }
379 /**
380  * Answer the size of the receiver in bytes.
381  * 
382  * @return int
383  */
384 public int sizeInBytes() {
385         return attributeBytes;
386 }
387 public void throwFormatException() throws ClassFormatException {
388         throw new ClassFormatException(ClassFormatException.ErrBadFieldInfo);
389 }
390 public String toString() {
391         StringBuffer buffer = new StringBuffer(this.getClass().getName());
392         int modifiers = getModifiers();
393         return buffer
394                 .append("{") //$NON-NLS-1$
395                 .append(
396                         ((modifiers & AccDeprecated) != 0 ? "deprecated " : "") //$NON-NLS-1$ //$NON-NLS-2$
397                                 + ((modifiers & 0x0001) == 1 ? "public " : "") //$NON-NLS-1$ //$NON-NLS-2$
398                                 + ((modifiers & 0x0002) == 0x0002 ? "private " : "") //$NON-NLS-1$ //$NON-NLS-2$
399                                 + ((modifiers & 0x0004) == 0x0004 ? "protected " : "") //$NON-NLS-1$ //$NON-NLS-2$
400                                 + ((modifiers & 0x0008) == 0x000008 ? "static " : "") //$NON-NLS-1$ //$NON-NLS-2$
401                                 + ((modifiers & 0x0010) == 0x0010 ? "final " : "") //$NON-NLS-1$ //$NON-NLS-2$
402                                 + ((modifiers & 0x0040) == 0x0040 ? "volatile " : "") //$NON-NLS-1$ //$NON-NLS-2$
403                                 + ((modifiers & 0x0080) == 0x0080 ? "transient " : "")) //$NON-NLS-1$ //$NON-NLS-2$
404                 .append(getTypeName())
405                 .append(" ") //$NON-NLS-1$
406                 .append(getName())
407                 .append(" ") //$NON-NLS-1$
408                 .append(getConstant())
409                 .append("}") //$NON-NLS-1$
410                 .toString(); 
411 }
412
413 }