1 | /******************************************************************************* |
2 | * Copyright (c) 2007, 2009 IBM Corporation and others. |
3 | * All rights reserved. This program and the accompanying materials |
4 | * are made available under the terms of the Eclipse Public License v1.0 |
5 | * which accompanies this distribution, and is available at |
6 | * http://www.eclipse.org/legal/epl-v10.html |
7 | * |
8 | * Contributors: |
9 | * IBM Corporation - initial API and implementation |
10 | *******************************************************************************/ |
11 | package org.eclipse.pde.api.tools.internal.builder; |
12 | |
13 | import java.util.ArrayList; |
14 | import java.util.HashMap; |
15 | import java.util.HashSet; |
16 | import java.util.Iterator; |
17 | import java.util.List; |
18 | import java.util.Set; |
19 | import java.util.SortedSet; |
20 | import java.util.Stack; |
21 | import java.util.TreeSet; |
22 | |
23 | import org.eclipse.core.runtime.CoreException; |
24 | import org.eclipse.core.runtime.IStatus; |
25 | import org.eclipse.core.runtime.Status; |
26 | import org.eclipse.osgi.util.NLS; |
27 | import org.eclipse.pde.api.tools.internal.model.AbstractApiTypeRoot; |
28 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
29 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; |
30 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
31 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiField; |
32 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember; |
33 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod; |
34 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; |
35 | import org.eclipse.pde.api.tools.internal.util.Signatures; |
36 | import org.eclipse.pde.api.tools.internal.util.Util; |
37 | import org.objectweb.asm.ClassAdapter; |
38 | import org.objectweb.asm.ClassReader; |
39 | import org.objectweb.asm.FieldVisitor; |
40 | import org.objectweb.asm.Label; |
41 | import org.objectweb.asm.MethodAdapter; |
42 | import org.objectweb.asm.MethodVisitor; |
43 | import org.objectweb.asm.Opcodes; |
44 | import org.objectweb.asm.Type; |
45 | import org.objectweb.asm.signature.SignatureReader; |
46 | import org.objectweb.asm.signature.SignatureVisitor; |
47 | import org.objectweb.asm.tree.ClassNode; |
48 | |
49 | /** |
50 | * Extracts references from a class file |
51 | * |
52 | * @since 1.0.0 |
53 | */ |
54 | public class ReferenceExtractor extends ClassAdapter { |
55 | |
56 | /** |
57 | * Constant used for controlling tracing in the visitor |
58 | */ |
59 | private static boolean DEBUG = Util.DEBUG; |
60 | |
61 | /** |
62 | * Method used for initializing tracing in the visitor |
63 | */ |
64 | public static void setDebug(boolean debugValue) { |
65 | DEBUG = debugValue || Util.DEBUG; |
66 | } |
67 | |
68 | /** |
69 | * A visitor for visiting java 5+ signatures |
70 | * TODO this visitor does not currently visit annotations |
71 | * |
72 | * ClassSignature = (visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* (visitSuperClass visitInterface* ) |
73 | * MethodSignature = (visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* (visitParameterType visitReturnType visitExceptionType* ) |
74 | * TypeSignature = visitBaseType | visitTypeVariable | visitArrayType | (visitClassType visitTypeArgument* (visitInnerClassType visitTypeArgument* )* visitEnd</tt> ) ) |
75 | */ |
76 | class ClassFileSignatureVisitor implements SignatureVisitor { |
77 | |
78 | protected int kind = -1; |
79 | protected int originalkind = -1; |
80 | protected int argumentcount = 0; |
81 | protected int type = 0; |
82 | protected String signature = null; |
83 | protected String name = null; |
84 | protected List references; |
85 | |
86 | public ClassFileSignatureVisitor() { |
87 | this.references = new ArrayList(); |
88 | } |
89 | |
90 | /** |
91 | * Resets the visitor to its initial state. |
92 | * This method should be called after processing is done with the visitor |
93 | */ |
94 | protected void reset() { |
95 | //do not reset argument count, as it is needed once the signature visitor is done |
96 | this.kind = -1; |
97 | this.originalkind = -1; |
98 | this.name = null; |
99 | this.signature = null; |
100 | this.type = 0; |
101 | this.references.clear(); |
102 | } |
103 | |
104 | /** |
105 | * Processes the type specified by the name for the current signature context. |
106 | * The kind flag is set to a parameterized type as subsequent calls to this method without visiting other nodes only occurs |
107 | * when we are processing parameterized types of generic declarations |
108 | * @param name the name of the type |
109 | */ |
110 | protected void processType(String name) { |
111 | Type type = ReferenceExtractor.this.resolveType(Type.getObjectType(name).getDescriptor()); |
112 | if(type != null) { |
113 | String tname = type.getClassName(); |
114 | if(tname.equals("E") || tname.equals("T")) { //$NON-NLS-1$//$NON-NLS-2$ |
115 | type = Type.getObjectType("java.lang.Object"); //$NON-NLS-1$ |
116 | tname = type.getClassName(); |
117 | } |
118 | if(ReferenceExtractor.this.consider(tname) && this.kind != -1) { |
119 | if(this.name != null && this.signature != null) { |
120 | this.references.add( |
121 | Reference.typeReference(ReferenceExtractor.this.getMember(), tname, this.signature, this.kind)); |
122 | } |
123 | } |
124 | } |
125 | this.kind = this.originalkind; |
126 | } |
127 | |
128 | /* (non-Javadoc) |
129 | * @see org.objectweb.asm.signature.SignatureVisitor#visitClassType(java.lang.String) |
130 | */ |
131 | public void visitClassType(String name) { |
132 | this.processType(name); |
133 | } |
134 | /* (non-Javadoc) |
135 | * @see org.objectweb.asm.signature.SignatureVisitor#visitFormalTypeParameter(java.lang.String) |
136 | */ |
137 | public void visitFormalTypeParameter(String name) { |
138 | if(this.type != TYPE) { |
139 | this.processType(name); |
140 | } |
141 | } |
142 | /* (non-Javadoc) |
143 | * @see org.objectweb.asm.signature.SignatureVisitor#visitTypeVariable(java.lang.String) |
144 | */ |
145 | public void visitTypeVariable(String name) { |
146 | } |
147 | /* (non-Javadoc) |
148 | * @see org.objectweb.asm.signature.SignatureVisitor#visitInnerClassType(java.lang.String) |
149 | */ |
150 | public void visitInnerClassType(String name) { |
151 | this.processType(name); |
152 | } |
153 | /* (non-Javadoc) |
154 | * @see org.objectweb.asm.signature.SignatureVisitor#visitParameterType() |
155 | */ |
156 | public SignatureVisitor visitParameterType() { |
157 | this.argumentcount++; |
158 | this.kind = IReference.REF_PARAMETER; |
159 | return this; |
160 | } |
161 | /* (non-Javadoc) |
162 | * @see org.objectweb.asm.signature.SignatureVisitor#visitInterface() |
163 | */ |
164 | public SignatureVisitor visitInterface() { |
165 | this.kind = IReference.REF_IMPLEMENTS; |
166 | return this; |
167 | } |
168 | /* (non-Javadoc) |
169 | * @see org.objectweb.asm.signature.SignatureVisitor#visitExceptionType() |
170 | */ |
171 | public SignatureVisitor visitExceptionType() { |
172 | this.kind = IReference.REF_THROWS; |
173 | return this; |
174 | } |
175 | /* (non-Javadoc) |
176 | * @see org.objectweb.asm.signature.SignatureVisitor#visitArrayType() |
177 | */ |
178 | public SignatureVisitor visitArrayType() { |
179 | return this; |
180 | } |
181 | /* (non-Javadoc) |
182 | * @see org.objectweb.asm.signature.SignatureVisitor#visitReturnType() |
183 | */ |
184 | public SignatureVisitor visitReturnType() { |
185 | this.kind = IReference.REF_RETURNTYPE; |
186 | return this; |
187 | } |
188 | /* (non-Javadoc) |
189 | * @see org.objectweb.asm.signature.SignatureVisitor#visitClassBound() |
190 | */ |
191 | public SignatureVisitor visitClassBound() { |
192 | this.kind = IReference.REF_PARAMETERIZED_TYPEDECL; |
193 | return this; |
194 | } |
195 | /* (non-Javadoc) |
196 | * @see org.objectweb.asm.signature.SignatureVisitor#visitInterfaceBound() |
197 | */ |
198 | public SignatureVisitor visitInterfaceBound() { |
199 | this.kind = IReference.REF_PARAMETERIZED_TYPEDECL; |
200 | return this; |
201 | } |
202 | /* (non-Javadoc) |
203 | * @see org.objectweb.asm.signature.SignatureVisitor#visitSuperclass() |
204 | */ |
205 | public SignatureVisitor visitSuperclass() { |
206 | this.kind = IReference.REF_EXTENDS; |
207 | return this; |
208 | } |
209 | /* (non-Javadoc) |
210 | * @see org.objectweb.asm.signature.SignatureVisitor#visitTypeArgument(char) |
211 | */ |
212 | public SignatureVisitor visitTypeArgument(char wildcard) { |
213 | return this; |
214 | } |
215 | /* (non-Javadoc) |
216 | * @see org.objectweb.asm.signature.SignatureVisitor#visitEnd() |
217 | */ |
218 | public void visitEnd() {} |
219 | |
220 | public void visitBaseType(char descriptor) { |
221 | switch(descriptor) { |
222 | case 'J' : |
223 | case 'D' : |
224 | argumentcount += 2; |
225 | break; |
226 | default : |
227 | this.argumentcount++; |
228 | } |
229 | } |
230 | public void visitTypeArgument() {} |
231 | } |
232 | |
233 | /** |
234 | * Visitor used to visit the methods of a type |
235 | * [ visitCode ( visitFrame | visit<i>X</i>Insn | visitLabel | visitTryCatchBlock | visitLocalVariable | visitLineNumber)* visitMaxs ] visitEnd |
236 | */ |
237 | class ClassFileMethodVisitor extends MethodAdapter { |
238 | int argumentcount = 0; |
239 | LinePositionTracker linePositionTracker; |
240 | /** |
241 | * Most recent string literal encountered. Used to infer Class.forName("...") references. |
242 | */ |
243 | String stringLiteral; |
244 | String methodName; |
245 | int lastLineNumber; |
246 | boolean implicitConstructor = false; |
247 | LocalLineNumberMarker localVariableMarker; |
248 | |
249 | HashMap labelsToLocalMarkers; |
250 | |
251 | /** |
252 | * Constructor |
253 | * @param mv |
254 | */ |
255 | public ClassFileMethodVisitor(MethodVisitor mv, String name, int argumentcount) { |
256 | super(mv); |
257 | this.argumentcount = argumentcount; |
258 | this.linePositionTracker = new LinePositionTracker(); |
259 | this.lastLineNumber = -1; |
260 | this.labelsToLocalMarkers = new HashMap(); |
261 | this.methodName = name; |
262 | } |
263 | /* (non-Javadoc) |
264 | * @see org.objectweb.asm.MethodAdapter#visitEnd() |
265 | */ |
266 | public void visitEnd() { |
267 | this.implicitConstructor = false; |
268 | this.argumentcount = 0; |
269 | ReferenceExtractor.this.exitMember(); |
270 | this.linePositionTracker.computeLineNumbers(); |
271 | this.labelsToLocalMarkers = null; |
272 | } |
273 | |
274 | /* (non-Javadoc) |
275 | * @see org.objectweb.asm.MethodAdapter#visitVarInsn(int, int) |
276 | */ |
277 | public void visitVarInsn(int opcode, int var) { |
278 | switch(opcode) { |
279 | case Opcodes.ASTORE : |
280 | if (this.lastLineNumber != -1) { |
281 | this.localVariableMarker = new LocalLineNumberMarker(this.lastLineNumber, var); |
282 | } |
283 | } |
284 | } |
285 | |
286 | /* (non-Javadoc) |
287 | * @see org.objectweb.asm.MethodAdapter#visitFieldInsn(int, java.lang.String, java.lang.String, java.lang.String) |
288 | */ |
289 | public void visitFieldInsn(int opcode, String owner, String name, String desc) { |
290 | int refType = -1; |
291 | switch (opcode) { |
292 | case Opcodes.PUTSTATIC: |
293 | refType = IReference.REF_PUTSTATIC; |
294 | break; |
295 | case Opcodes.PUTFIELD: |
296 | refType = IReference.REF_PUTFIELD; |
297 | break; |
298 | case Opcodes.GETSTATIC: |
299 | refType = IReference.REF_GETSTATIC; |
300 | break; |
301 | case Opcodes.GETFIELD: |
302 | refType = IReference.REF_GETFIELD; |
303 | break; |
304 | } |
305 | if (refType != -1) { |
306 | Reference reference = ReferenceExtractor.this.addFieldReference(Type.getObjectType(owner), name, refType); |
307 | if (reference != null) { |
308 | this.linePositionTracker.addLocation(reference); |
309 | } |
310 | } |
311 | } |
312 | |
313 | /* (non-Javadoc) |
314 | * @see org.objectweb.asm.MethodAdapter#visitTryCatchBlock(org.objectweb.asm.Label, org.objectweb.asm.Label, org.objectweb.asm.Label, java.lang.String) |
315 | */ |
316 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { |
317 | if(type != null) { |
318 | Type ctype = Type.getObjectType(type); |
319 | Reference reference = ReferenceExtractor.this.addTypeReference(ctype, IReference.REF_CATCHEXCEPTION); |
320 | if (reference != null) { |
321 | this.linePositionTracker.addCatchLabelInfos(reference, handler); |
322 | this.linePositionTracker.addLocation(reference); |
323 | } |
324 | } |
325 | } |
326 | |
327 | /* (non-Javadoc) |
328 | * @see org.objectweb.asm.MethodAdapter#visitLabel(Label) |
329 | */ |
330 | public void visitLabel(Label label) { |
331 | this.linePositionTracker.addLabel(label); |
332 | if (this.localVariableMarker != null) { |
333 | Object object = this.labelsToLocalMarkers.get(label); |
334 | if (object != null) { |
335 | // add in the list |
336 | if (object instanceof List) { |
337 | ((List) object).add(this.localVariableMarker); |
338 | } else { |
339 | List list = new ArrayList(); |
340 | list.add(object); |
341 | list.add(this.localVariableMarker); |
342 | this.labelsToLocalMarkers.put(label, list); |
343 | } |
344 | } else { |
345 | this.labelsToLocalMarkers.put(label, this.localVariableMarker); |
346 | } |
347 | this.localVariableMarker = null; |
348 | } |
349 | } |
350 | |
351 | /* (non-Javadoc) |
352 | * @see org.objectweb.asm.MethodAdapter#visitMethodInsn(int, java.lang.String, java.lang.String, java.lang.String) |
353 | */ |
354 | public void visitMethodInsn(int opcode, String owner, String name, String desc) { |
355 | Type declaringType = Type.getObjectType(owner); |
356 | int kind = -1; |
357 | switch(opcode){ |
358 | case Opcodes.INVOKESPECIAL: { |
359 | kind = ("<init>".equals(name) ? IReference.REF_CONSTRUCTORMETHOD : IReference.REF_SPECIALMETHOD); //$NON-NLS-1$ |
360 | if (kind == IReference.REF_CONSTRUCTORMETHOD) { |
361 | if(!implicitConstructor && this.methodName.equals("<init>") && !fSuperStack.isEmpty() && (fSuperStack.peek()).equals(declaringType.getClassName())) { //$NON-NLS-1$ |
362 | implicitConstructor = true; |
363 | kind = IReference.REF_SUPER_CONSTRUCTORMETHOD; |
364 | } |
365 | else { |
366 | Reference reference = ReferenceExtractor.this.addTypeReference(declaringType, IReference.REF_INSTANTIATE); |
367 | if (reference != null) { |
368 | this.linePositionTracker.addLocation(reference); |
369 | } |
370 | } |
371 | } |
372 | break; |
373 | } |
374 | case Opcodes.INVOKESTATIC: { |
375 | kind = IReference.REF_STATICMETHOD; |
376 | // check for reference to a class literal |
377 | if (name.equals("forName")) { //$NON-NLS-1$ |
378 | if (ReferenceExtractor.this.processName(owner).equals("java.lang.Class")) { //$NON-NLS-1$ |
379 | if (this.stringLiteral != null) { |
380 | Type classLiteral = Type.getObjectType(this.stringLiteral); |
381 | Reference reference = ReferenceExtractor.this.addTypeReference(classLiteral, IReference.REF_CONSTANTPOOL); |
382 | if (reference != null) { |
383 | this.linePositionTracker.addLocation(reference); |
384 | } |
385 | } |
386 | } |
387 | } |
388 | break; |
389 | } |
390 | case Opcodes.INVOKEVIRTUAL: { |
391 | kind = IReference.REF_VIRTUALMETHOD; |
392 | break; |
393 | } |
394 | case Opcodes.INVOKEINTERFACE: { |
395 | kind = IReference.REF_INTERFACEMETHOD; |
396 | break; |
397 | } |
398 | } |
399 | if(kind != -1) { |
400 | Reference reference = ReferenceExtractor.this.addMethodReference(declaringType, name, desc, kind); |
401 | if (reference != null) { |
402 | this.linePositionTracker.addLocation(reference); |
403 | } |
404 | } |
405 | this.stringLiteral = null; |
406 | } |
407 | |
408 | /* (non-Javadoc) |
409 | * @see org.objectweb.asm.MethodAdapter#visitMultiANewArrayInsn(java.lang.String, int) |
410 | */ |
411 | public void visitMultiANewArrayInsn(String desc, int dims) { |
412 | Type type = this.getTypeFromDescription(desc); |
413 | Reference reference = ReferenceExtractor.this.addTypeReference(type, IReference.REF_ARRAYALLOC); |
414 | if (reference != null) { |
415 | this.linePositionTracker.addLocation(reference); |
416 | } |
417 | } |
418 | |
419 | /* (non-Javadoc) |
420 | * @see org.objectweb.asm.MethodAdapter#visitLineNumber(int, org.objectweb.asm.Label) |
421 | */ |
422 | public void visitLineNumber(int line, Label start) { |
423 | this.lastLineNumber = line; |
424 | this.linePositionTracker.addLineInfo(line, start); |
425 | } |
426 | |
427 | public void visitCode() { |
428 | super.visitCode(); |
429 | } |
430 | |
431 | /* (non-Javadoc) |
432 | * @see java.lang.Object#toString() |
433 | */ |
434 | public String toString() { |
435 | StringBuffer buffer = new StringBuffer(); |
436 | buffer.append("Method visitor for: "); //$NON-NLS-1$ |
437 | buffer.append(methodName); |
438 | buffer.append("\nCurrent line number: "); //$NON-NLS-1$ |
439 | buffer.append(lastLineNumber); |
440 | return buffer.toString(); |
441 | } |
442 | |
443 | /** |
444 | * Creates a type from a type description. Works around bugs creating |
445 | * types from array type signatures in ASM. |
446 | * |
447 | * @param desc signature |
448 | * @return Type |
449 | */ |
450 | private Type getTypeFromDescription(String desc) { |
451 | String ldesc = desc; |
452 | while (ldesc.charAt(0) == '[') { |
453 | ldesc = ldesc.substring(1); |
454 | } |
455 | Type type = null; |
456 | if (ldesc.endsWith(";")) { //$NON-NLS-1$ |
457 | type = Type.getType(ldesc); |
458 | } else { |
459 | type = Type.getObjectType(ldesc); |
460 | } |
461 | return type; |
462 | } |
463 | /* (non-Javadoc) |
464 | * @see org.objectweb.asm.MethodAdapter#visitTypeInsn(int, java.lang.String) |
465 | */ |
466 | public void visitTypeInsn(int opcode, String desc) { |
467 | Type type = this.getTypeFromDescription(desc); |
468 | int kind = -1; |
469 | switch(opcode) { |
470 | case Opcodes.ANEWARRAY: { |
471 | kind = IReference.REF_ARRAYALLOC; |
472 | break; |
473 | } |
474 | case Opcodes.CHECKCAST: { |
475 | kind = IReference.REF_CHECKCAST; |
476 | break; |
477 | } |
478 | case Opcodes.INSTANCEOF: { |
479 | kind = IReference.REF_INSTANCEOF; |
480 | break; |
481 | } |
482 | case Opcodes.NEW: { |
483 | //we can omit the NEW case as it is caught by the constructor call |
484 | //handle it only for anonymous / local types |
485 | Reference ref = (Reference) fAnonymousTypes.get(processName(type.getInternalName())); |
486 | if(ref != null) { |
487 | this.linePositionTracker.addLocation(ref); |
488 | } |
489 | } |
490 | } |
491 | if(kind != -1) { |
492 | Reference reference = ReferenceExtractor.this.addTypeReference(type, kind); |
493 | if (reference != null) { |
494 | this.linePositionTracker.addLocation(reference); |
495 | } |
496 | } |
497 | } |
498 | |
499 | /* (non-Javadoc) |
500 | * @see org.objectweb.asm.MethodAdapter#visitLocalVariable(java.lang.String, java.lang.String, java.lang.String, org.objectweb.asm.Label, org.objectweb.asm.Label, int) |
501 | */ |
502 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { |
503 | if (desc.length() == 1) { |
504 | // base type |
505 | return; |
506 | } |
507 | if(index > this.argumentcount) { |
508 | Object object = this.labelsToLocalMarkers.get(start); |
509 | int lineNumber = -1; |
510 | if (object != null) { |
511 | if (object instanceof List) { |
512 | // list of potential localMarker |
513 | // iterate the list to find the one that matches the index |
514 | List markersList = (List) object; |
515 | LocalLineNumberMarker removeMarker = null; |
516 | loop: for(Iterator iterator = markersList.iterator(); iterator.hasNext(); ) { |
517 | LocalLineNumberMarker marker = (LocalLineNumberMarker) iterator.next(); |
518 | if (marker.varIndex == index) { |
519 | lineNumber = marker.lineNumber; |
520 | removeMarker = marker; |
521 | break loop; |
522 | } |
523 | } |
524 | if (removeMarker != null) { |
525 | markersList.remove(removeMarker); |
526 | if (markersList.isEmpty()) { |
527 | this.labelsToLocalMarkers.remove(start); |
528 | } |
529 | } |
530 | } else { |
531 | // single marker |
532 | LocalLineNumberMarker marker = (LocalLineNumberMarker) object; |
533 | if (marker.varIndex == index) { |
534 | lineNumber = marker.lineNumber; |
535 | this.labelsToLocalMarkers.remove(start); |
536 | } |
537 | } |
538 | } |
539 | if (lineNumber == -1) return; |
540 | if(signature != null) { |
541 | List references = ReferenceExtractor.this.processSignature(name, signature, IReference.REF_PARAMETERIZED_VARIABLE, METHOD); |
542 | for (Iterator iterator = references.iterator(); iterator.hasNext();) { |
543 | Reference reference = (Reference) iterator.next(); |
544 | reference.setLineNumber(lineNumber); |
545 | } |
546 | } else { |
547 | Type type = Type.getType(desc); |
548 | if(type.getSort() == Type.OBJECT) { |
549 | Reference reference = ReferenceExtractor.this.addTypeReference(type, IReference.REF_LOCALVARIABLEDECL); |
550 | if (reference != null) { |
551 | reference.setLineNumber(lineNumber); |
552 | } |
553 | } |
554 | } |
555 | } |
556 | } |
557 | |
558 | /* (non-Javadoc) |
559 | * @see org.objectweb.asm.MethodAdapter#visitLdcInsn(java.lang.Object) |
560 | */ |
561 | public void visitLdcInsn(Object cst) { |
562 | if(cst instanceof Type) { |
563 | Type type = (Type) cst; |
564 | Reference reference = ReferenceExtractor.this.addTypeReference(type, IReference.REF_CONSTANTPOOL); |
565 | if (reference != null) { |
566 | this.linePositionTracker.addLocation(reference); |
567 | } |
568 | } else if (cst instanceof String) { |
569 | String str = (String) cst; |
570 | this.stringLiteral = (Util.EMPTY_STRING.equals(str) ? null : str); |
571 | } |
572 | } |
573 | |
574 | |
575 | } |
576 | |
577 | static class LinePositionTracker { |
578 | List labelsAndLocations; |
579 | SortedSet lineInfos; |
580 | List catchLabelInfos; |
581 | HashMap lineMap; |
582 | |
583 | public LinePositionTracker() { |
584 | this.labelsAndLocations = new ArrayList(); |
585 | this.lineInfos = new TreeSet(); |
586 | this.catchLabelInfos = new ArrayList(); |
587 | this.lineMap = new HashMap(); |
588 | } |
589 | |
590 | void addLocation(Reference location) { |
591 | this.labelsAndLocations.add(location); |
592 | } |
593 | |
594 | void addLineInfo(int line, Label label) { |
595 | this.lineInfos.add(new LineInfo(line, label)); |
596 | this.lineMap.put(label, new Integer(line)); |
597 | } |
598 | |
599 | void addCatchLabelInfos(Reference location, Label label) { |
600 | this.catchLabelInfos.add(new LabelInfo(location, label)); |
601 | } |
602 | |
603 | void addLabel(Label label) { |
604 | this.labelsAndLocations.add(label); |
605 | } |
606 | |
607 | public void computeLineNumbers() { |
608 | |
609 | if (this.lineInfos.size() < 1 || this.labelsAndLocations.size() < 1) { |
610 | // nothing to do |
611 | return; |
612 | } |
613 | Iterator lineInfosIterator = this.lineInfos.iterator(); |
614 | LineInfo firstLineInfo = (LineInfo) lineInfosIterator.next(); |
615 | int currentLineNumber = firstLineInfo.line; |
616 | |
617 | List remainingCatchLabelInfos = new ArrayList(); |
618 | for (Iterator iterator = this.catchLabelInfos.iterator(); iterator.hasNext();) { |
619 | LabelInfo catchLabelInfo = (LabelInfo) iterator.next(); |
620 | Integer lineValue = (Integer) this.lineMap.get(catchLabelInfo.label); |
621 | if (lineValue != null) { |
622 | catchLabelInfo.location.setLineNumber(lineValue.intValue()); |
623 | } else { |
624 | remainingCatchLabelInfos.add(catchLabelInfo); |
625 | } |
626 | } |
627 | // Iterate over List of Labels and SourceLocations. |
628 | List computedEntries = new ArrayList(); |
629 | for (Iterator iterator = this.labelsAndLocations.iterator(); iterator.hasNext();) { |
630 | Object current = iterator.next(); |
631 | if (current instanceof Label) { |
632 | // label |
633 | Integer lineValue = (Integer) this.lineMap.get(current); |
634 | if (lineValue != null) { |
635 | computedEntries.add(new LineInfo(lineValue.intValue(), (Label) current)); |
636 | } else { |
637 | computedEntries.add(current); |
638 | } |
639 | } else { |
640 | // location |
641 | computedEntries.add(current); |
642 | } |
643 | } |
644 | List remaingEntriesTemp; |
645 | for (Iterator iterator = computedEntries.iterator(); iterator.hasNext();) { |
646 | Object current = iterator.next(); |
647 | if (current instanceof Label) { |
648 | // try to set the line number for remaining catch labels |
649 | if (remainingCatchLabelInfos != null) { |
650 | remaingEntriesTemp = new ArrayList(); |
651 | loop: for (Iterator catchLabelInfosIterator = remainingCatchLabelInfos.iterator(); catchLabelInfosIterator.hasNext();) { |
652 | LabelInfo catchLabelInfo = (LabelInfo) catchLabelInfosIterator.next(); |
653 | if (!current.equals(catchLabelInfo.label)) { |
654 | remaingEntriesTemp.add(catchLabelInfo); |
655 | continue loop; |
656 | } |
657 | catchLabelInfo.location.setLineNumber(currentLineNumber); |
658 | } |
659 | if (remaingEntriesTemp.size() == 0) { |
660 | remainingCatchLabelInfos = null; |
661 | } else { |
662 | remainingCatchLabelInfos = remaingEntriesTemp; |
663 | } |
664 | } |
665 | } else if (current instanceof Reference) { |
666 | Reference ref = (Reference) current; |
667 | if (ref.getLineNumber() == -1) { |
668 | ref.setLineNumber(currentLineNumber); |
669 | } else { |
670 | currentLineNumber = ref.getLineNumber(); |
671 | } |
672 | } else if (current instanceof LineInfo) { |
673 | LineInfo lineInfo = (LineInfo) current; |
674 | currentLineNumber = lineInfo.line; |
675 | } |
676 | } |
677 | } |
678 | } |
679 | |
680 | static class LabelInfo { |
681 | public Reference location; |
682 | public Label label; |
683 | |
684 | public LabelInfo(Reference location, Label label) { |
685 | this.location = location; |
686 | this.label = label; |
687 | } |
688 | |
689 | public String toString() { |
690 | StringBuffer buffer = new StringBuffer(); |
691 | buffer.append('(').append(this.label).append(',').append(this.location).append(')'); |
692 | return String.valueOf(buffer); |
693 | } |
694 | } |
695 | |
696 | static class LineInfo implements Comparable { |
697 | int line; |
698 | Label label; |
699 | |
700 | LineInfo(int line, Label label) { |
701 | this.line = line; |
702 | this.label = label; |
703 | } |
704 | |
705 | /* (non-Javadoc) |
706 | * @see java.lang.Comparable#compareTo(java.lang.Object) |
707 | */ |
708 | public int compareTo(Object o) { |
709 | return this.line - ((LineInfo) o).line; |
710 | } |
711 | public boolean equals(Object obj) { |
712 | if (obj instanceof LineInfo) { |
713 | LineInfo lineInfo2 = (LineInfo) obj; |
714 | return this.line == lineInfo2.line |
715 | && this.label.equals(lineInfo2.label); |
716 | } |
717 | return super.equals(obj); |
718 | } |
719 | public String toString() { |
720 | StringBuffer buffer = new StringBuffer(); |
721 | buffer.append('(').append(this.line).append(',').append(this.label).append(')'); |
722 | return String.valueOf(buffer); |
723 | } |
724 | } |
725 | |
726 | static class LocalLineNumberMarker { |
727 | int lineNumber; |
728 | int varIndex; |
729 | |
730 | public LocalLineNumberMarker(int line, int varIndex) { |
731 | this.lineNumber = line; |
732 | this.varIndex = varIndex; |
733 | } |
734 | public boolean equals(Object obj) { |
735 | if (obj instanceof LocalLineNumberMarker) { |
736 | LocalLineNumberMarker marker = (LocalLineNumberMarker) obj; |
737 | return this.lineNumber == marker.lineNumber && this.varIndex == marker.varIndex; |
738 | } |
739 | return false; |
740 | } |
741 | |
742 | public int hashCode() { |
743 | return this.varIndex; |
744 | } |
745 | } |
746 | |
747 | /** |
748 | * The list we collect references in. Entries in the list are |
749 | * of the type {@link org.eclipse.pde.api.tools.internal.provisional.builder.IReference} |
750 | */ |
751 | private Set collector = null; |
752 | |
753 | /** |
754 | * The full internal name of the class we are extracting references from |
755 | */ |
756 | private String classname = null; |
757 | |
758 | /** |
759 | * Current type being visited. |
760 | */ |
761 | private IApiType fType; |
762 | |
763 | /** |
764 | * Stack of members being visited. When a member is entered its |
765 | * element descriptor is pushed onto the stack. When a member |
766 | * is exited, the stack is popped. |
767 | */ |
768 | Stack fMemberStack = new Stack(); |
769 | |
770 | /** |
771 | * Stack of super types *names* (String) being visited. When a type is |
772 | * entered, its super type is pushed onto the stack. When a type |
773 | * is exited, the stack is popped. |
774 | */ |
775 | Stack fSuperStack = new Stack(); |
776 | |
777 | /** |
778 | * Mapping of anonymous type names to their reference |
779 | */ |
780 | HashMap fAnonymousTypes = new HashMap(); |
781 | |
782 | /** |
783 | * Whether to extract references to elements within the classfile |
784 | * being scanned. |
785 | */ |
786 | private boolean fIncludeLocalRefs = false; |
787 | |
788 | /** |
789 | * Bit mask of {@link ReferenceModifiers} to extract. |
790 | */ |
791 | private int fReferenceKinds = 0; |
792 | |
793 | /** |
794 | * Bit mask that determines if we need to visit members |
795 | */ |
796 | private static final int VISIT_MEMBERS_MASK = |
797 | IReference.MASK_REF_ALL ^ |
798 | (IReference.REF_EXTENDS | IReference.REF_IMPLEMENTS); |
799 | |
800 | /** |
801 | * If members should be visited for type visits |
802 | */ |
803 | private boolean fIsVisitMembers = false; |
804 | |
805 | /** |
806 | * Current field being visited, or <code>null</code> (when |
807 | * not within a field). |
808 | */ |
809 | private ClassFileSignatureVisitor signaturevisitor = new ClassFileSignatureVisitor(); |
810 | static int TYPE = 0, FIELD = 1, METHOD = 2; |
811 | |
812 | /** |
813 | * Constructor |
814 | * @param type the type to extract references from |
815 | * @param collector the listing of references to annotate from this pass |
816 | * @param referenceKinds kinds of references to extract as defined by {@link ReferenceModifiers} |
817 | */ |
818 | public ReferenceExtractor(IApiType type, Set collector, int referenceKinds) { |
819 | super(new ClassNode()); |
820 | fType = type; |
821 | this.collector = collector; |
822 | fReferenceKinds = referenceKinds; |
823 | fIsVisitMembers = (VISIT_MEMBERS_MASK & fReferenceKinds) > 0; |
824 | } |
825 | |
826 | /* (non-Javadoc) |
827 | * @see java.lang.Object#toString() |
828 | */ |
829 | public String toString() { |
830 | StringBuffer buffer = new StringBuffer(); |
831 | buffer.append("Reference extractor for: "); //$NON-NLS-1$ |
832 | buffer.append(fType.getName()); |
833 | buffer.append("\n"); //$NON-NLS-1$ |
834 | buffer.append("Reference kinds: "); //$NON-NLS-1$ |
835 | buffer.append(Reference.getReferenceText(fReferenceKinds)); |
836 | buffer.append("\n"); //$NON-NLS-1$ |
837 | buffer.append("Is visiting members: "); //$NON-NLS-1$ |
838 | buffer.append(fIsVisitMembers); |
839 | return buffer.toString(); |
840 | } |
841 | |
842 | /** |
843 | * Returns whether to consider a reference to the specified type. |
844 | * Configured by setting to include references within the same |
845 | * class file. |
846 | * |
847 | * @param owner |
848 | * @return true if considered, false otherwise |
849 | */ |
850 | protected boolean consider(String owner) { |
851 | if (this.fIncludeLocalRefs) { |
852 | return true; |
853 | } |
854 | return !(this.classname.equals(owner) || this.classname.startsWith(owner) || "<clinit>".equals(owner) || "this".equals(owner)); //$NON-NLS-1$ //$NON-NLS-2$ |
855 | } |
856 | |
857 | /** |
858 | * Returns whether the specified reference should be |
859 | * considered when extracting references. Configured by setting on whether |
860 | * to include references within the same class file. |
861 | * |
862 | * @param ref reference |
863 | * @return whether to include the reference |
864 | */ |
865 | protected boolean consider(Reference ref) { |
866 | if ((ref.getReferenceKind() & fReferenceKinds) == 0) { |
867 | return false; |
868 | } |
869 | if (this.fIncludeLocalRefs) { |
870 | return true; |
871 | } |
872 | // don't consider references to anonymous types or elements in them |
873 | String referencedTypeName = ref.getReferencedTypeName(); |
874 | if (ref.getReferenceKind() == IReference.REF_VIRTUALMETHOD || ref.getReferenceKind() == IReference.REF_OVERRIDE) { |
875 | return true; |
876 | } |
877 | if (referencedTypeName.startsWith(fType.getName())) { |
878 | // don't include references within this type or a member type |
879 | if (referencedTypeName.length() > fType.getName().length()) { |
880 | return referencedTypeName.charAt(fType.getName().length()) != '$'; |
881 | } |
882 | return false; |
883 | } |
884 | return true; |
885 | } |
886 | |
887 | /** |
888 | * Returns the full internal name (if available) from the given simple name. |
889 | * The returned name has been modified to be '.' separated |
890 | * @param name |
891 | * @return |
892 | */ |
893 | protected String processName(String name) { |
894 | String newname = name; |
895 | Type type = Type.getObjectType(name); |
896 | if(type != null && type.getSort() == Type.OBJECT) { |
897 | newname = type.getInternalName(); |
898 | } |
899 | return newname.replaceAll("/", "."); //$NON-NLS-1$ //$NON-NLS-2$ |
900 | } |
901 | |
902 | /** |
903 | * Adds a reference to the given type from the current member. Discards |
904 | * the reference if the type corresponds to the class file being scanned |
905 | * or if the type is a primitive type. |
906 | * |
907 | * @param type referenced type |
908 | * @param linenumber line number where referenced |
909 | * @param kind kind of reference |
910 | * @return reference added, or <code>null</code> if none |
911 | */ |
912 | protected Reference addTypeReference(Type type, int kind) { |
913 | Type rtype = this.resolveType(type.getDescriptor()); |
914 | if(rtype != null) { |
915 | return addReference( |
916 | Reference.typeReference(getMember(), rtype.getClassName(), kind)); |
917 | } |
918 | return null; |
919 | } |
920 | |
921 | /** |
922 | * Adds a reference to the given field from the current member. Discards |
923 | * the reference if the field is defined in the class file being scanned. |
924 | * |
925 | * @param declaringType type declaring the field being referenced |
926 | * @param name of the field being referenced |
927 | * @param linenumber line number where referenced |
928 | * @param kind kind of reference |
929 | * @return reference added, or <code>null</code> if none |
930 | */ |
931 | protected Reference addFieldReference(Type declaringType, String name, int kind) { |
932 | Type rtype = this.resolveType(declaringType.getDescriptor()); |
933 | if(rtype != null) { |
934 | return addReference( |
935 | Reference.fieldReference(getMember(), rtype.getClassName(), name, kind)); |
936 | } |
937 | return null; |
938 | } |
939 | |
940 | /** |
941 | * Adds a reference to the given method from the current member. Discards |
942 | * the reference if the method is defined in the class file being scanned. |
943 | * |
944 | * @param declaringType type declaring the method (but could be a virtual lookup) |
945 | * @param name of the method being referenced |
946 | * @param signature signature of the method |
947 | * @param linenumber line number where referenced |
948 | * @param kind kind of reference |
949 | * @return reference added, or <code>null</code> if none |
950 | */ |
951 | protected Reference addMethodReference(Type declaringType, String name, String signature, int kind) { |
952 | Type rtype = this.resolveType(declaringType.getDescriptor()); |
953 | if(rtype != null) { |
954 | return this.addReference( |
955 | Reference.methodReference(getMember(), rtype.getClassName(), name, signature, kind)); |
956 | } |
957 | return null; |
958 | } |
959 | |
960 | /** |
961 | * Adds a reference to the given target member from the given line number |
962 | * in the class file being scanned. If the target member is contained |
963 | * in the class file being scanned it is discarded based on the |
964 | * setting to include local references. |
965 | * |
966 | * @param target reference |
967 | * @param reference added, or <code>null</code> if none |
968 | */ |
969 | protected Reference addReference(Reference target) { |
970 | if(this.consider(target)) { |
971 | this.collector.add(target); |
972 | return target; |
973 | } |
974 | return null; |
975 | } |
976 | |
977 | /** |
978 | * Processes the member signature from the specified type with the given signature and kind. |
979 | * A member can be either a type, method, field or local variable |
980 | * |
981 | * @param name the name of the member to process |
982 | * @param signature the signature of the member to process |
983 | * @param kind the kind |
984 | * @param type the type of member wanting to use the visitor |
985 | * |
986 | * @return the collection of references created for this signature |
987 | */ |
988 | protected List processSignature(String name, String signature, int kind, int type) { |
989 | SignatureReader reader = new SignatureReader(signature); |
990 | this.signaturevisitor.kind = kind; |
991 | this.signaturevisitor.name = this.processName(name); |
992 | this.signaturevisitor.signature = signature; |
993 | this.signaturevisitor.originalkind = kind; |
994 | this.signaturevisitor.argumentcount = 0; |
995 | this.signaturevisitor.type = type; |
996 | if(kind == IReference.REF_PARAMETERIZED_TYPEDECL || kind == IReference.REF_PARAMETERIZED_METHODDECL) { |
997 | reader.accept(this.signaturevisitor); |
998 | } else { |
999 | reader.acceptType(this.signaturevisitor); |
1000 | } |
1001 | List result = new ArrayList(); |
1002 | result.addAll(this.signaturevisitor.references); |
1003 | this.collector.addAll(this.signaturevisitor.references); |
1004 | this.signaturevisitor.reset(); |
1005 | return result; |
1006 | } |
1007 | |
1008 | /** |
1009 | * Resolves the type from the string description. This method takes only type descriptions |
1010 | * as a parameter, all else will throw an exception from the ASM framework |
1011 | * If the description is an array, the underlying type of the array is returned. |
1012 | * @param desc |
1013 | * @return the {@link Type} of the description or <code>null</code> |
1014 | */ |
1015 | protected Type resolveType(String desc) { |
1016 | Type type = Type.getType(desc); |
1017 | if(type.getSort() == Type.OBJECT) { |
1018 | return type; |
1019 | } |
1020 | if(type.getSort() == Type.ARRAY) { |
1021 | type = type.getElementType(); |
1022 | if(type.getSort() == Type.OBJECT) { |
1023 | return type; |
1024 | } |
1025 | } |
1026 | return null; |
1027 | } |
1028 | |
1029 | /* (non-Javadoc) |
1030 | * @see org.objectweb.asm.ClassVisitor#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) |
1031 | */ |
1032 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
1033 | this.classname = this.processName(name); |
1034 | if(DEBUG) { |
1035 | System.out.println("Starting visit of type: ["+this.fType.getName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ |
1036 | } |
1037 | this.enterMember(this.fType); |
1038 | //if there is a signature we get more information from it, so we don't need to do both |
1039 | if(signature != null) { |
1040 | this.processSignature(name, signature, IReference.REF_PARAMETERIZED_TYPEDECL, TYPE); |
1041 | } |
1042 | else { |
1043 | if((access & Opcodes.ACC_INTERFACE) != 0) { |
1044 | //the type is an interface and we need to treat the interfaces set as extends, not implements |
1045 | Type supertype = null; |
1046 | for(int i = 0; i < interfaces.length; i++) { |
1047 | supertype = Type.getObjectType(interfaces[i]); |
1048 | this.addTypeReference(supertype, IReference.REF_EXTENDS); |
1049 | this.fSuperStack.add(supertype.getClassName()); |
1050 | } |
1051 | } |
1052 | else { |
1053 | Type supertype = null; |
1054 | if(superName != null) { |
1055 | supertype = Type.getObjectType(superName); |
1056 | this.addTypeReference(supertype, IReference.REF_EXTENDS); |
1057 | this.fSuperStack.add(supertype.getClassName()); |
1058 | } |
1059 | for(int i = 0; i < interfaces.length; i++) { |
1060 | supertype = Type.getObjectType(interfaces[i]); |
1061 | this.addTypeReference(supertype, IReference.REF_IMPLEMENTS); |
1062 | } |
1063 | } |
1064 | } |
1065 | } |
1066 | |
1067 | /* (non-Javadoc) |
1068 | * @see org.objectweb.asm.ClassVisitor#visitEnd() |
1069 | */ |
1070 | public void visitEnd() { |
1071 | this.exitMember(); |
1072 | if (!this.fSuperStack.isEmpty()) { |
1073 | String typeName = (String) this.fSuperStack.pop(); |
1074 | if(DEBUG) { |
1075 | System.out.println("ending visit of type: ["+typeName+"]"); //$NON-NLS-1$ //$NON-NLS-2$ |
1076 | } |
1077 | } |
1078 | } |
1079 | |
1080 | /* (non-Javadoc) |
1081 | * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object) |
1082 | */ |
1083 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
1084 | if (fIsVisitMembers) { |
1085 | IApiType owner = (IApiType) this.getMember(); |
1086 | IApiField field = owner.getField(name); |
1087 | if(field == null) { |
1088 | ApiPlugin.log( |
1089 | new Status( |
1090 | IStatus.WARNING, |
1091 | ApiPlugin.PLUGIN_ID, |
1092 | NLS.bind(BuilderMessages.ReferenceExtractor_failed_to_lookup_field, |
1093 | new String[] {name, Signatures.getQualifiedTypeSignature(owner)}) |
1094 | ) |
1095 | ); |
1096 | //if we can't find the method there is no point trying to process it |
1097 | return null; |
1098 | } |
1099 | this.enterMember(field); |
1100 | if((access & Opcodes.ACC_SYNTHETIC) == 0) { |
1101 | if(signature != null) { |
1102 | this.processSignature(name, signature, IReference.REF_PARAMETERIZED_FIELDDECL, FIELD); |
1103 | } else { |
1104 | this.addTypeReference(Type.getType(desc), IReference.REF_FIELDDECL); |
1105 | } |
1106 | } |
1107 | this.exitMember(); |
1108 | } |
1109 | return null; |
1110 | } |
1111 | |
1112 | /* (non-Javadoc) |
1113 | * @see org.objectweb.asm.ClassAdapter#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int) |
1114 | */ |
1115 | public void visitInnerClass(String name, String outerName, String innerName, int access) { |
1116 | try { |
1117 | String pname = processName(name); |
1118 | if(fType.getName().equals(pname) || !pname.startsWith(fType.getName())) { |
1119 | return; |
1120 | } |
1121 | IApiComponent comp = fType.getApiComponent(); |
1122 | if(comp == null) { |
1123 | return; |
1124 | } |
1125 | AbstractApiTypeRoot root = (AbstractApiTypeRoot) comp.findTypeRoot(pname); |
1126 | if(root != null) { |
1127 | IApiType type = root.getStructure(); |
1128 | if(type == null) { |
1129 | //do nothing for a bad classfile |
1130 | return; |
1131 | } |
1132 | Set refs = null; |
1133 | if(type.isAnonymous() || type.isLocal()) { |
1134 | //visit the class files for the dependent anonymous and local inner types |
1135 | refs = processInnerClass(type, IReference.REF_EXTENDS); |
1136 | if(refs.iterator().hasNext()) { |
1137 | fAnonymousTypes.put(pname, refs.iterator().next()); |
1138 | } |
1139 | } |
1140 | else { |
1141 | refs = processInnerClass(type, fReferenceKinds); |
1142 | } |
1143 | if(refs != null && !refs.isEmpty()) { |
1144 | this.collector.addAll(refs); |
1145 | } |
1146 | } |
1147 | } |
1148 | catch(CoreException ce) {} |
1149 | } |
1150 | |
1151 | /** |
1152 | * Processes the dependent inner class |
1153 | * @param type |
1154 | * @param refkinds |
1155 | * @return |
1156 | * @throws CoreException |
1157 | */ |
1158 | private Set processInnerClass(IApiType type, int refkinds) throws CoreException { |
1159 | HashSet refs = new HashSet(); |
1160 | ReferenceExtractor extractor = new ReferenceExtractor(type, refs, refkinds); |
1161 | ClassReader reader = new ClassReader(((AbstractApiTypeRoot)type.getTypeRoot()).getContents()); |
1162 | reader.accept(extractor, ClassReader.SKIP_FRAMES); |
1163 | return refs; |
1164 | } |
1165 | |
1166 | /* (non-Javadoc) |
1167 | * @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) |
1168 | */ |
1169 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
1170 | if (fIsVisitMembers) { |
1171 | IApiMember member = this.getMember(); |
1172 | IApiType owner = null; |
1173 | if (member instanceof IApiType) { |
1174 | owner = (IApiType) member; |
1175 | } else { |
1176 | try { |
1177 | owner = member.getEnclosingType(); |
1178 | } catch (CoreException e) { |
1179 | // should not happen for field or method |
1180 | ApiPlugin.log(e.getStatus()); |
1181 | } |
1182 | } |
1183 | IApiMethod method = owner.getMethod(name, desc); |
1184 | if(method == null) { |
1185 | ApiPlugin.log( |
1186 | new Status( |
1187 | IStatus.WARNING, |
1188 | ApiPlugin.PLUGIN_ID, |
1189 | NLS.bind(BuilderMessages.ReferenceExtractor_failed_to_lookup_method, |
1190 | new String[] {name, desc, Signatures.getQualifiedTypeSignature(owner)}) |
1191 | ) |
1192 | ); |
1193 | //if we can't find the method there is no point trying to process it |
1194 | return null; |
1195 | } |
1196 | this.enterMember(method); |
1197 | // record potential method override reference |
1198 | if ((access & (Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) > 0) { |
1199 | if (!this.fSuperStack.isEmpty()) { |
1200 | String superTypeName = (String) this.fSuperStack.peek(); |
1201 | addReference( |
1202 | Reference.methodReference(method, superTypeName, method.getName(), method.getSignature(), IReference.REF_OVERRIDE)); |
1203 | } |
1204 | } |
1205 | if((access & Opcodes.ACC_SYNTHETIC) == 0 && !"<clinit>".equals(name)) { //$NON-NLS-1$ |
1206 | int argumentcount = 0; |
1207 | if(signature != null) { |
1208 | this.processSignature(name, signature, IReference.REF_PARAMETERIZED_METHODDECL, METHOD); |
1209 | argumentcount = this.signaturevisitor.argumentcount; |
1210 | } |
1211 | else { |
1212 | Type[] arguments = Type.getArgumentTypes(desc); |
1213 | for(int i = 0; i < arguments.length; i++) { |
1214 | Type type = arguments[i]; |
1215 | this.addTypeReference(type, IReference.REF_PARAMETER); |
1216 | argumentcount += type.getSize(); |
1217 | } |
1218 | this.addTypeReference(Type.getReturnType(desc), IReference.REF_RETURNTYPE); |
1219 | if(exceptions != null) { |
1220 | for(int i = 0; i < exceptions.length; i++) { |
1221 | this.addTypeReference(Type.getObjectType(exceptions[i]), IReference.REF_THROWS); |
1222 | } |
1223 | } |
1224 | } |
1225 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); |
1226 | if(mv != null && ((access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0)) { |
1227 | return new ClassFileMethodVisitor(mv, name, argumentcount); |
1228 | } |
1229 | } |
1230 | } |
1231 | return null; |
1232 | } |
1233 | |
1234 | /** |
1235 | * Called when a member is entered. Pushes the member onto the member |
1236 | * stack. |
1237 | * |
1238 | * @param member current member |
1239 | */ |
1240 | protected void enterMember(IApiMember member) { |
1241 | this.fMemberStack.push(member); |
1242 | } |
1243 | |
1244 | /** |
1245 | * Called when a member is exited. Pops the top member off the stack. |
1246 | */ |
1247 | protected void exitMember() { |
1248 | this.fMemberStack.pop(); |
1249 | } |
1250 | |
1251 | /** |
1252 | * Returns the member currently being visited. |
1253 | * |
1254 | * @return current member |
1255 | */ |
1256 | protected IApiMember getMember() { |
1257 | return (IApiMember) this.fMemberStack.peek(); |
1258 | } |
1259 | } |