added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / classfmt / MethodInfo.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.IBinaryMethod;
17 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
18 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
19
20 public class MethodInfo extends ClassFileStruct implements IBinaryMethod, AttributeNamesConstants, Comparable {
21         static private final char[][] noException = CharOperation.NO_CHAR_CHAR;
22         private int accessFlags;
23         private int attributeBytes;
24         private int[] constantPoolOffsets;
25         private char[] descriptor;
26         private char[][] exceptionNames;
27         private char[] name;
28         private char[] signature;
29         private int signatureUtf8Offset;
30         private long tagBits;   
31         
32 /**
33  * @param classFileBytes byte[]
34  * @param offsets int[]
35  * @param offset int
36  */
37 public MethodInfo (byte classFileBytes[], int offsets[], int offset) {
38         super(classFileBytes, offset);
39         constantPoolOffsets = offsets;
40         accessFlags = -1;
41         int attributesCount = u2At(6);
42         int readOffset = 8;
43         this.signatureUtf8Offset = -1;
44         for (int i = 0; i < attributesCount; i++) {
45                 // check the name of each attribute
46                 int utf8Offset = constantPoolOffsets[u2At(readOffset)] - structOffset;
47                 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
48                 if (attributeName.length > 0) {
49                         switch(attributeName[0]) {
50                                 case 'S' :
51                                         if (CharOperation.equals(AttributeNamesConstants.SignatureName, attributeName)) {
52                                                 this.signatureUtf8Offset = constantPoolOffsets[u2At(readOffset + 6)] - structOffset;
53                                         }
54                                         break;
55                                 case 'R' :
56                                         if (CharOperation.equals(attributeName, RuntimeVisibleAnnotationsName)) {
57                                                 decodeStandardAnnotations(readOffset);
58                                         }
59                         }
60                 }
61                 readOffset += (6 + u4At(readOffset + 2));
62         }
63         attributeBytes = readOffset;
64 }
65 public int compareTo(Object o) {
66         if (!(o instanceof MethodInfo)) {
67                 throw new ClassCastException();
68         }
69
70         MethodInfo otherMethod = (MethodInfo) o;
71         int result = new String(this.getSelector()).compareTo(new String(otherMethod.getSelector()));
72         if (result != 0) return result;
73         return new String(this.getMethodDescriptor()).compareTo(new String(otherMethod.getMethodDescriptor()));
74 }
75 private int decodeAnnotation(int offset) {
76         int readOffset = offset;
77         int utf8Offset = this.constantPoolOffsets[u2At(offset)] - structOffset;
78         char[] typeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
79         typeName = Signature.toCharArray(typeName);
80         CharOperation.replace(typeName, '/', '.');
81         char[][] qualifiedTypeName = CharOperation.splitOn('.', typeName);
82         int numberOfPairs = u2At(offset + 2);
83         readOffset += 4;
84         if (qualifiedTypeName.length == 3) {
85                 char[] lastPart = qualifiedTypeName[2];
86                 if (lastPart[0] == 'D') {
87                         if (CharOperation.equals(qualifiedTypeName, TypeConstants.JAVA_LANG_DEPRECATED)) {
88                                 this.tagBits |= TagBits.AnnotationDeprecated;
89                                 return readOffset;              
90                         }
91                 }
92         }
93         for (int i = 0; i < numberOfPairs; i++) {
94                 readOffset += 2;
95                 readOffset = decodeElementValue(readOffset);
96         }
97         return readOffset;
98 }
99 private int decodeElementValue(int offset) {
100         int readOffset = offset;
101         int tag = u1At(readOffset);
102         readOffset++;
103         switch(tag) {
104                 case 'B' :
105                 case 'C' :
106                 case 'D' :
107                 case 'F' :
108                 case 'I' :
109                 case 'J' :
110                 case 'S' :
111                 case 'Z' :
112                 case 's' :
113                         readOffset += 2;
114                         break;
115                 case 'e' :
116                         readOffset += 4;
117                         break;
118                 case 'c' :
119                         readOffset += 2;
120                         break;
121                 case '@' :
122                         readOffset += decodeAnnotation(readOffset);
123                         break;
124                 case '[' :
125                         int numberOfValues = u2At(readOffset);
126                         readOffset += 2;
127                         for (int i = 0; i < numberOfValues; i++) {
128                                 readOffset = decodeElementValue(readOffset);
129                         }
130                         break;
131         }
132         return readOffset;
133 }
134 /**
135  * @param offset the offset is located at the beginning of the runtime visible 
136  * annotation attribute.
137  */
138 private void decodeStandardAnnotations(int offset) {
139         int numberOfAnnotations = u2At(offset + 6);
140         int readOffset = offset + 8;
141         for (int i = 0; i < numberOfAnnotations; i++) {
142                 readOffset = decodeAnnotation(readOffset);
143         }
144 }
145 /**
146  * @see org.eclipse.jdt.internal.compiler.env.IGenericMethod#getArgumentNames()
147  */
148 public char[][] getArgumentNames() {
149         return null;
150 }
151 /**
152  * Answer the resolved names of the exception types in the
153  * class file format as specified in section 4.2 of the Java 2 VM spec
154  * or null if the array is empty.
155  *
156  * For example, java.lang.String is java/lang/String.
157  * @return char[][]
158  */
159 public char[][] getExceptionTypeNames() {
160         if (exceptionNames == null) {
161                 readExceptionAttributes();
162         }
163         return exceptionNames;
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 the receiver's method descriptor which describes the parameter &
177  * return types as specified in section 4.3.3 of the Java 2 VM spec.
178  *
179  * For example:
180  *   - int foo(String) is (Ljava/lang/String;)I
181  *   - void foo(Object[]) is (I)[Ljava/lang/Object;
182  * @return char[]
183  */
184 public char[] getMethodDescriptor() {
185         if (descriptor == null) {
186                 // read the name
187                 int utf8Offset = constantPoolOffsets[u2At(4)] - structOffset;
188                 descriptor = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
189         }
190         return descriptor;
191 }
192 /**
193  * Answer an int whose bits are set according the access constants
194  * defined by the VM spec.
195  * Set the AccDeprecated and AccSynthetic bits if necessary
196  * @return int
197  */
198 public int getModifiers() {
199         if (this.accessFlags == -1) {
200                 // compute the accessflag. Don't forget the deprecated attribute
201                 this.accessFlags = u2At(0);
202                 readModifierRelatedAttributes();
203         }
204         return this.accessFlags;
205 }
206 /**
207  * Answer the name of the method.
208  *
209  * For a constructor, answer <init> & <clinit> for a clinit method.
210  * @return char[]
211  */
212 public char[] getSelector() {
213         if (name == null) {
214                 // read the name
215                 int utf8Offset = constantPoolOffsets[u2At(2)] - structOffset;
216                 name = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
217         }
218         return name;
219 }
220 public long getTagBits() {
221         return this.tagBits;
222 }
223 /**
224  * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
225  * will be therefore fully initialized and we can get rid of the bytes.
226  */
227 void initialize() {
228         getModifiers();
229         getSelector();
230         getMethodDescriptor();
231         getExceptionTypeNames();
232         getGenericSignature();
233         reset();
234 }
235 /**
236  * Answer true if the method is a class initializer, false otherwise.
237  * @return boolean
238  */
239 public boolean isClinit() {
240         char[] selector = getSelector();
241         return selector[0] == '<' && selector.length == 8; // Can only match <clinit>
242 }
243 /**
244  * Answer true if the method is a constructor, false otherwise.
245  * @return boolean
246  */
247 public boolean isConstructor() {
248         char[] selector = getSelector();
249         return selector[0] == '<' && selector.length == 6; // Can only match <init>
250 }
251 /**
252  * Return true if the field is a synthetic method, false otherwise.
253  * @return boolean
254  */
255 public boolean isSynthetic() {
256         return (getModifiers() & AccSynthetic) != 0;
257 }
258 private void readExceptionAttributes() {
259         int attributesCount = u2At(6);
260         int readOffset = 8;
261         for (int i = 0; i < attributesCount; i++) {
262                 int utf8Offset = constantPoolOffsets[u2At(readOffset)] - structOffset;
263                 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
264                 if (CharOperation.equals(attributeName, ExceptionsName)) {
265                         // read the number of exception entries
266                         int entriesNumber = u2At(readOffset + 6);
267                         // place the readOffset at the beginning of the exceptions table
268                         readOffset += 8;
269                         if (entriesNumber == 0) {
270                                 exceptionNames = noException;
271                         } else {
272                                 exceptionNames = new char[entriesNumber][];
273                                 for (int j = 0; j < entriesNumber; j++) {
274                                         utf8Offset = 
275                                                 constantPoolOffsets[u2At(
276                                                         constantPoolOffsets[u2At(readOffset)] - structOffset + 1)]
277                                                         - structOffset; 
278                                         exceptionNames[j] = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
279                                         readOffset += 2;
280                                 }
281                         }
282                 } else {
283                         readOffset += (6 + u4At(readOffset + 2));
284                 }
285         }
286         if (exceptionNames == null) {
287                 exceptionNames = noException;
288         }
289 }
290 private void readModifierRelatedAttributes() {
291         int attributesCount = u2At(6);
292         int readOffset = 8;
293         for (int i = 0; i < attributesCount; i++) {
294                 int utf8Offset = constantPoolOffsets[u2At(readOffset)] - structOffset;
295                 char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
296                 // test added for obfuscated .class file. See 79772
297                 if (attributeName.length != 0) {
298                         switch(attributeName[0]) {
299                                 case 'D' :
300                                         if (CharOperation.equals(attributeName, DeprecatedName))
301                                                 this.accessFlags |= AccDeprecated;
302                                         break;
303                                 case 'S' :
304                                         if (CharOperation.equals(attributeName, SyntheticName))
305                                                 this.accessFlags |= AccSynthetic;
306                                         break;
307                                 case 'A' :
308                                         if (CharOperation.equals(attributeName, AnnotationDefaultName))
309                                                 this.accessFlags |= AccAnnotationDefault;
310                                         break;
311                         }
312                 }
313                 readOffset += (6 + u4At(readOffset + 2));
314         }
315 }
316 protected void reset() {
317         this.constantPoolOffsets = null;
318         super.reset();
319 }
320 /**
321  * Answer the size of the receiver in bytes.
322  * 
323  * @return int
324  */
325 public int sizeInBytes() {
326         return attributeBytes;
327 }
328 public String toString() {
329         int modifiers = getModifiers();
330         char[] desc = getGenericSignature();
331         if (desc == null)
332                 desc = getMethodDescriptor();
333         StringBuffer buffer = new StringBuffer(this.getClass().getName());
334         return buffer
335                 .append("{") //$NON-NLS-1$
336                 .append(
337                         ((modifiers & AccDeprecated) != 0 ? "deprecated " : "") //$NON-NLS-1$ //$NON-NLS-2$
338                                 + ((modifiers & 0x0001) == 1 ? "public " : "") //$NON-NLS-1$ //$NON-NLS-2$
339                                 + ((modifiers & 0x0002) == 0x0002 ? "private " : "") //$NON-NLS-1$ //$NON-NLS-2$
340                                 + ((modifiers & 0x0004) == 0x0004 ? "protected " : "") //$NON-NLS-1$ //$NON-NLS-2$
341                                 + ((modifiers & 0x0008) == 0x000008 ? "static " : "") //$NON-NLS-1$ //$NON-NLS-2$
342                                 + ((modifiers & 0x0010) == 0x0010 ? "final " : "") //$NON-NLS-1$ //$NON-NLS-2$
343                                 + ((modifiers & 0x0040) == 0x0040 ? "volatile " : "") //$NON-NLS-1$ //$NON-NLS-2$
344                                 + ((modifiers & 0x0080) == 0x0080 ? "varargs " : "")) //$NON-NLS-1$ //$NON-NLS-2$
345                 .append(getSelector())
346                 .append(desc)
347                 .append("}") //$NON-NLS-1$
348                 .toString(); 
349 }
350 }