| 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 | } |