1 /* 2 * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdi; 27 28 import com.sun.jdi.*; 29 30 import java.util.*; 31 import java.lang.ref.SoftReference; 32 33 public abstract class ReferenceTypeImpl extends TypeImpl 34 implements ReferenceType { 35 protected long ref; 36 private String signature = null; 37 private String genericSignature = null; 38 private boolean genericSignatureGotten = false; 39 private String baseSourceName = null; 40 private String baseSourceDir = null; 41 private String baseSourcePath = null; 42 protected int modifiers = -1; 43 private SoftReference<List<Field>> fieldsRef = null; 44 private SoftReference<List<Method>> methodsRef = null; 45 private SoftReference<SDE> sdeRef = null; 46 47 private boolean isClassLoaderCached = false; 48 private ClassLoaderReference classLoader = null; 49 private ClassObjectReference classObject = null; 50 51 private int status = 0; 52 private boolean isPrepared = false; 53 54 55 private boolean versionNumberGotten = false; 56 private int majorVersion; 57 private int minorVersion; 58 59 private boolean constantPoolInfoGotten = false; 60 private int constanPoolCount; 61 private byte[] constantPoolBytes; 62 private SoftReference<byte[]> constantPoolBytesRef = null; 63 64 /* to mark a SourceFile request that returned a genuine JDWP.Error.ABSENT_INFORMATION */ 65 private static final String ABSENT_BASE_SOURCE_NAME = "**ABSENT_BASE_SOURCE_NAME**"; 66 67 /* to mark when no info available */ 68 static final SDE NO_SDE_INFO_MARK = new SDE(); 69 70 // bits set when initialization was attempted (succeeded or failed) 71 private static final int INITIALIZED_OR_FAILED = 72 JDWP.ClassStatus.INITIALIZED | JDWP.ClassStatus.ERROR; 73 74 ReferenceTypeImpl(VirtualMachine aVm, long aRef)75 protected ReferenceTypeImpl(VirtualMachine aVm, long aRef) { 76 super(aVm); 77 ref = aRef; 78 genericSignatureGotten = false; 79 } 80 noticeRedefineClass()81 void noticeRedefineClass() { 82 //Invalidate information previously fetched and cached. 83 //These will be refreshed later on demand. 84 baseSourceName = null; 85 baseSourcePath = null; 86 modifiers = -1; 87 fieldsRef = null; 88 methodsRef = null; 89 sdeRef = null; 90 versionNumberGotten = false; 91 constantPoolInfoGotten = false; 92 } 93 getMethodMirror(long ref)94 Method getMethodMirror(long ref) { 95 if (ref == 0) { 96 // obsolete method 97 return new ObsoleteMethodImpl(vm, this); 98 } 99 // Fetch all methods for the class, check performance impact 100 // Needs no synchronization now, since methods() returns 101 // unmodifiable local data 102 Iterator<Method> it = methods().iterator(); 103 while (it.hasNext()) { 104 MethodImpl method = (MethodImpl)it.next(); 105 if (method.ref() == ref) { 106 return method; 107 } 108 } 109 throw new IllegalArgumentException("Invalid method id: " + ref); 110 } 111 getFieldMirror(long ref)112 Field getFieldMirror(long ref) { 113 // Fetch all fields for the class, check performance impact 114 // Needs no synchronization now, since fields() returns 115 // unmodifiable local data 116 Iterator<Field>it = fields().iterator(); 117 while (it.hasNext()) { 118 FieldImpl field = (FieldImpl)it.next(); 119 if (field.ref() == ref) { 120 return field; 121 } 122 } 123 throw new IllegalArgumentException("Invalid field id: " + ref); 124 } 125 equals(Object obj)126 public boolean equals(Object obj) { 127 if ((obj != null) && (obj instanceof ReferenceTypeImpl)) { 128 ReferenceTypeImpl other = (ReferenceTypeImpl)obj; 129 return (ref() == other.ref()) && 130 (vm.equals(other.virtualMachine())); 131 } else { 132 return false; 133 } 134 } 135 hashCode()136 public int hashCode() { 137 return(int)ref(); 138 } 139 compareTo(ReferenceType object)140 public int compareTo(ReferenceType object) { 141 /* 142 * Note that it is critical that compareTo() == 0 143 * implies that equals() == true. Otherwise, TreeSet 144 * will collapse classes. 145 * 146 * (Classes of the same name loaded by different class loaders 147 * or in different VMs must not return 0). 148 */ 149 ReferenceTypeImpl other = (ReferenceTypeImpl)object; 150 int comp = name().compareTo(other.name()); 151 if (comp == 0) { 152 long rf1 = ref(); 153 long rf2 = other.ref(); 154 // optimize for typical case: refs equal and VMs equal 155 if (rf1 == rf2) { 156 // sequenceNumbers are always positive 157 comp = vm.sequenceNumber - 158 ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber; 159 } else { 160 comp = (rf1 < rf2)? -1 : 1; 161 } 162 } 163 return comp; 164 } 165 signature()166 public String signature() { 167 if (signature == null) { 168 // Does not need synchronization, since worst-case 169 // static info is fetched twice 170 if (vm.canGet1_5LanguageFeatures()) { 171 /* 172 * we might as well get both the signature and the 173 * generic signature. 174 */ 175 genericSignature(); 176 } else { 177 try { 178 signature = JDWP.ReferenceType.Signature. 179 process(vm, this).signature; 180 } catch (JDWPException exc) { 181 throw exc.toJDIException(); 182 } 183 } 184 } 185 return signature; 186 } 187 genericSignature()188 public String genericSignature() { 189 // This gets both the signature and the generic signature 190 if (vm.canGet1_5LanguageFeatures() && !genericSignatureGotten) { 191 // Does not need synchronization, since worst-case 192 // static info is fetched twice 193 JDWP.ReferenceType.SignatureWithGeneric result; 194 try { 195 result = JDWP.ReferenceType.SignatureWithGeneric. 196 process(vm, this); 197 } catch (JDWPException exc) { 198 throw exc.toJDIException(); 199 } 200 signature = result.signature; 201 setGenericSignature(result.genericSignature); 202 } 203 return genericSignature; 204 } 205 classLoader()206 public ClassLoaderReference classLoader() { 207 if (!isClassLoaderCached) { 208 // Does not need synchronization, since worst-case 209 // static info is fetched twice 210 try { 211 classLoader = (ClassLoaderReference) 212 JDWP.ReferenceType.ClassLoader. 213 process(vm, this).classLoader; 214 isClassLoaderCached = true; 215 } catch (JDWPException exc) { 216 throw exc.toJDIException(); 217 } 218 } 219 return classLoader; 220 } 221 isPublic()222 public boolean isPublic() { 223 if (modifiers == -1) 224 getModifiers(); 225 226 return((modifiers & VMModifiers.PUBLIC) > 0); 227 } 228 isProtected()229 public boolean isProtected() { 230 if (modifiers == -1) 231 getModifiers(); 232 233 return((modifiers & VMModifiers.PROTECTED) > 0); 234 } 235 isPrivate()236 public boolean isPrivate() { 237 if (modifiers == -1) 238 getModifiers(); 239 240 return((modifiers & VMModifiers.PRIVATE) > 0); 241 } 242 isPackagePrivate()243 public boolean isPackagePrivate() { 244 return !isPublic() && !isPrivate() && !isProtected(); 245 } 246 isAbstract()247 public boolean isAbstract() { 248 if (modifiers == -1) 249 getModifiers(); 250 251 return((modifiers & VMModifiers.ABSTRACT) > 0); 252 } 253 isFinal()254 public boolean isFinal() { 255 if (modifiers == -1) 256 getModifiers(); 257 258 return((modifiers & VMModifiers.FINAL) > 0); 259 } 260 isStatic()261 public boolean isStatic() { 262 if (modifiers == -1) 263 getModifiers(); 264 265 return((modifiers & VMModifiers.STATIC) > 0); 266 } 267 isPrepared()268 public boolean isPrepared() { 269 // This ref type may have been prepared before we were getting 270 // events, so get it once. After that, 271 // this status flag is updated through the ClassPrepareEvent, 272 // there is no need for the expense of a JDWP query. 273 if (status == 0) { 274 updateStatus(); 275 } 276 return isPrepared; 277 } 278 isVerified()279 public boolean isVerified() { 280 // Once true, it never resets, so we don't need to update 281 if ((status & JDWP.ClassStatus.VERIFIED) == 0) { 282 updateStatus(); 283 } 284 return (status & JDWP.ClassStatus.VERIFIED) != 0; 285 } 286 isInitialized()287 public boolean isInitialized() { 288 // Once initialization succeeds or fails, it never resets, 289 // so we don't need to update 290 if ((status & INITIALIZED_OR_FAILED) == 0) { 291 updateStatus(); 292 } 293 return (status & JDWP.ClassStatus.INITIALIZED) != 0; 294 } 295 failedToInitialize()296 public boolean failedToInitialize() { 297 // Once initialization succeeds or fails, it never resets, 298 // so we don't need to update 299 if ((status & INITIALIZED_OR_FAILED) == 0) { 300 updateStatus(); 301 } 302 return (status & JDWP.ClassStatus.ERROR) != 0; 303 } 304 fields()305 public List<Field> fields() { 306 List<Field> fields = (fieldsRef == null) ? null : fieldsRef.get(); 307 if (fields == null) { 308 if (vm.canGet1_5LanguageFeatures()) { 309 JDWP.ReferenceType.FieldsWithGeneric.FieldInfo[] jdwpFields; 310 try { 311 jdwpFields = JDWP.ReferenceType.FieldsWithGeneric.process(vm, this).declared; 312 } catch (JDWPException exc) { 313 throw exc.toJDIException(); 314 } 315 fields = new ArrayList<Field>(jdwpFields.length); 316 for (int i=0; i<jdwpFields.length; i++) { 317 JDWP.ReferenceType.FieldsWithGeneric.FieldInfo fi 318 = jdwpFields[i]; 319 320 Field field = new FieldImpl(vm, this, fi.fieldID, 321 fi.name, fi.signature, 322 fi.genericSignature, 323 fi.modBits); 324 fields.add(field); 325 } 326 } else { 327 JDWP.ReferenceType.Fields.FieldInfo[] jdwpFields; 328 try { 329 jdwpFields = JDWP.ReferenceType.Fields. 330 process(vm, this).declared; 331 } catch (JDWPException exc) { 332 throw exc.toJDIException(); 333 } 334 fields = new ArrayList<Field>(jdwpFields.length); 335 for (int i=0; i<jdwpFields.length; i++) { 336 JDWP.ReferenceType.Fields.FieldInfo fi = jdwpFields[i]; 337 338 Field field = new FieldImpl(vm, this, fi.fieldID, 339 fi.name, fi.signature, 340 null, 341 fi.modBits); 342 fields.add(field); 343 } 344 } 345 346 fields = Collections.unmodifiableList(fields); 347 fieldsRef = new SoftReference<List<Field>>(fields); 348 } 349 return fields; 350 } 351 inheritedTypes()352 abstract List<? extends ReferenceType> inheritedTypes(); 353 addVisibleFields(List<Field> visibleList, Map<String, Field> visibleTable, List<String> ambiguousNames)354 void addVisibleFields(List<Field> visibleList, Map<String, Field> visibleTable, List<String> ambiguousNames) { 355 for (Field field : visibleFields()) { 356 String name = field.name(); 357 if (!ambiguousNames.contains(name)) { 358 Field duplicate = visibleTable.get(name); 359 if (duplicate == null) { 360 visibleList.add(field); 361 visibleTable.put(name, field); 362 } else if (!field.equals(duplicate)) { 363 ambiguousNames.add(name); 364 visibleTable.remove(name); 365 visibleList.remove(duplicate); 366 } else { 367 // identical field from two branches; do nothing 368 } 369 } 370 } 371 } 372 visibleFields()373 public List<Field> visibleFields() { 374 /* 375 * Maintain two different collections of visible fields. The 376 * list maintains a reasonable order for return. The 377 * hash map provides an efficient way to lookup visible fields 378 * by name, important for finding hidden or ambiguous fields. 379 */ 380 List<Field> visibleList = new ArrayList<Field>(); 381 Map<String, Field> visibleTable = new HashMap<String, Field>(); 382 383 /* Track fields removed from above collection due to ambiguity */ 384 List<String> ambiguousNames = new ArrayList<String>(); 385 386 /* Add inherited, visible fields */ 387 List<? extends ReferenceType> types = inheritedTypes(); 388 Iterator<? extends ReferenceType> iter = types.iterator(); 389 while (iter.hasNext()) { 390 /* 391 * TO DO: Be defensive and check for cyclic interface inheritance 392 */ 393 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); 394 type.addVisibleFields(visibleList, visibleTable, ambiguousNames); 395 } 396 397 /* 398 * Insert fields from this type, removing any inherited fields they 399 * hide. 400 */ 401 List<Field> retList = new ArrayList<Field>(fields()); 402 for (Field field : retList) { 403 Field hidden = visibleTable.get(field.name()); 404 if (hidden != null) { 405 visibleList.remove(hidden); 406 } 407 } 408 retList.addAll(visibleList); 409 return retList; 410 } 411 addAllFields(List<Field> fieldList, Set<ReferenceType> typeSet)412 void addAllFields(List<Field> fieldList, Set<ReferenceType> typeSet) { 413 /* Continue the recursion only if this type is new */ 414 if (!typeSet.contains(this)) { 415 typeSet.add((ReferenceType)this); 416 417 /* Add local fields */ 418 fieldList.addAll(fields()); 419 420 /* Add inherited fields */ 421 List<? extends ReferenceType> types = inheritedTypes(); 422 Iterator<? extends ReferenceType> iter = types.iterator(); 423 while (iter.hasNext()) { 424 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); 425 type.addAllFields(fieldList, typeSet); 426 } 427 } 428 } allFields()429 public List<Field> allFields() { 430 List<Field> fieldList = new ArrayList<Field>(); 431 Set<ReferenceType> typeSet = new HashSet<ReferenceType>(); 432 addAllFields(fieldList, typeSet); 433 return fieldList; 434 } 435 fieldByName(String fieldName)436 public Field fieldByName(String fieldName) { 437 List<Field> searchList = visibleFields(); 438 439 for (int i=0; i<searchList.size(); i++) { 440 Field f = searchList.get(i); 441 442 if (f.name().equals(fieldName)) { 443 return f; 444 } 445 } 446 //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name()); 447 return null; 448 } 449 methods()450 public List<Method> methods() { 451 List<Method> methods = (methodsRef == null) ? null : methodsRef.get(); 452 if (methods == null) { 453 if (!vm.canGet1_5LanguageFeatures()) { 454 methods = methods1_4(); 455 } else { 456 JDWP.ReferenceType.MethodsWithGeneric.MethodInfo[] declared; 457 try { 458 declared = JDWP.ReferenceType.MethodsWithGeneric. 459 process(vm, this).declared; 460 } catch (JDWPException exc) { 461 throw exc.toJDIException(); 462 } 463 methods = new ArrayList<Method>(declared.length); 464 for (int i=0; i<declared.length; i++) { 465 JDWP.ReferenceType.MethodsWithGeneric.MethodInfo 466 mi = declared[i]; 467 468 Method method = MethodImpl.createMethodImpl(vm, this, 469 mi.methodID, 470 mi.name, mi.signature, 471 mi.genericSignature, 472 mi.modBits); 473 methods.add(method); 474 } 475 } 476 methods = Collections.unmodifiableList(methods); 477 methodsRef = new SoftReference<List<Method>>(methods); 478 } 479 return methods; 480 } 481 methods1_4()482 private List<Method> methods1_4() { 483 List<Method> methods; 484 JDWP.ReferenceType.Methods.MethodInfo[] declared; 485 try { 486 declared = JDWP.ReferenceType.Methods. 487 process(vm, this).declared; 488 } catch (JDWPException exc) { 489 throw exc.toJDIException(); 490 } 491 methods = new ArrayList<Method>(declared.length); 492 for (int i=0; i<declared.length; i++) { 493 JDWP.ReferenceType.Methods.MethodInfo mi = declared[i]; 494 495 Method method = MethodImpl.createMethodImpl(vm, this, 496 mi.methodID, 497 mi.name, mi.signature, 498 null, 499 mi.modBits); 500 methods.add(method); 501 } 502 return methods; 503 } 504 505 /* 506 * Utility method used by subclasses to build lists of visible 507 * methods. 508 */ addToMethodMap(Map<String, Method> methodMap, List<Method> methodList)509 void addToMethodMap(Map<String, Method> methodMap, List<Method> methodList) { 510 for (Method method : methodList) 511 methodMap.put(method.name().concat(method.signature()), method); 512 } 513 addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces)514 abstract void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces); 515 visibleMethods()516 public List<Method> visibleMethods() { 517 /* 518 * Build a collection of all visible methods. The hash 519 * map allows us to do this efficiently by keying on the 520 * concatenation of name and signature. 521 */ 522 Map<String, Method> map = new HashMap<String, Method>(); 523 addVisibleMethods(map, new HashSet<InterfaceType>()); 524 525 /* 526 * ... but the hash map destroys order. Methods should be 527 * returned in a sensible order, as they are in allMethods(). 528 * So, start over with allMethods() and use the hash map 529 * to filter that ordered collection. 530 */ 531 List<Method> list = allMethods(); 532 list.retainAll(map.values()); 533 return list; 534 } 535 allMethods()536 abstract public List<Method> allMethods(); 537 methodsByName(String name)538 public List<Method> methodsByName(String name) { 539 List<Method> methods = visibleMethods(); 540 ArrayList<Method> retList = new ArrayList<Method>(methods.size()); 541 for (Method candidate : methods) { 542 if (candidate.name().equals(name)) { 543 retList.add(candidate); 544 } 545 } 546 retList.trimToSize(); 547 return retList; 548 } 549 methodsByName(String name, String signature)550 public List<Method> methodsByName(String name, String signature) { 551 List<Method> methods = visibleMethods(); 552 ArrayList<Method> retList = new ArrayList<Method>(methods.size()); 553 for (Method candidate : methods) { 554 if (candidate.name().equals(name) && 555 candidate.signature().equals(signature)) { 556 retList.add(candidate); 557 } 558 } 559 retList.trimToSize(); 560 return retList; 561 } 562 getInterfaces()563 List<InterfaceType> getInterfaces() { 564 InterfaceTypeImpl[] intfs; 565 try { 566 intfs = JDWP.ReferenceType.Interfaces. 567 process(vm, this).interfaces; 568 } catch (JDWPException exc) { 569 throw exc.toJDIException(); 570 } 571 return Arrays.asList((InterfaceType[])intfs); 572 } 573 nestedTypes()574 public List<ReferenceType> nestedTypes() { 575 List<ReferenceType> all = vm.allClasses(); 576 List<ReferenceType> nested = new ArrayList<ReferenceType>(); 577 String outername = name(); 578 int outerlen = outername.length(); 579 Iterator<ReferenceType> iter = all.iterator(); 580 while (iter.hasNext()) { 581 ReferenceType refType = iter.next(); 582 String name = refType.name(); 583 int len = name.length(); 584 /* The separator is historically '$' but could also be '#' */ 585 if ( len > outerlen && name.startsWith(outername) ) { 586 char c = name.charAt(outerlen); 587 if ( c =='$' || c== '#' ) { 588 nested.add(refType); 589 } 590 } 591 } 592 return nested; 593 } 594 getValue(Field sig)595 public Value getValue(Field sig) { 596 List<Field> list = new ArrayList<Field>(1); 597 list.add(sig); 598 Map<Field, Value> map = getValues(list); 599 return map.get(sig); 600 } 601 602 validateFieldAccess(Field field)603 void validateFieldAccess(Field field) { 604 /* 605 * Field must be in this object's class, a superclass, or 606 * implemented interface 607 */ 608 ReferenceTypeImpl declType = (ReferenceTypeImpl)field.declaringType(); 609 if (!declType.isAssignableFrom(this)) { 610 throw new IllegalArgumentException("Invalid field"); 611 } 612 } 613 validateFieldSet(Field field)614 void validateFieldSet(Field field) { 615 validateFieldAccess(field); 616 if (field.isFinal()) { 617 throw new IllegalArgumentException("Cannot set value of final field"); 618 } 619 } 620 621 /** 622 * Returns a map of field values 623 */ getValues(List<? extends Field> theFields)624 public Map<Field,Value> getValues(List<? extends Field> theFields) { 625 validateMirrors(theFields); 626 627 int size = theFields.size(); 628 JDWP.ReferenceType.GetValues.Field[] queryFields = 629 new JDWP.ReferenceType.GetValues.Field[size]; 630 631 for (int i=0; i<size; i++) { 632 FieldImpl field = (FieldImpl)theFields.get(i); 633 634 validateFieldAccess(field); 635 636 // Do more validation specific to ReferenceType field getting 637 if (!field.isStatic()) { 638 throw new IllegalArgumentException( 639 "Attempt to use non-static field with ReferenceType"); 640 } 641 queryFields[i] = new JDWP.ReferenceType.GetValues.Field( 642 field.ref()); 643 } 644 645 Map<Field, Value> map = new HashMap<Field, Value>(size); 646 647 ValueImpl[] values; 648 try { 649 values = JDWP.ReferenceType.GetValues. 650 process(vm, this, queryFields).values; 651 } catch (JDWPException exc) { 652 throw exc.toJDIException(); 653 } 654 655 if (size != values.length) { 656 throw new InternalException( 657 "Wrong number of values returned from target VM"); 658 } 659 for (int i=0; i<size; i++) { 660 FieldImpl field = (FieldImpl)theFields.get(i); 661 map.put(field, values[i]); 662 } 663 664 return map; 665 } 666 classObject()667 public ClassObjectReference classObject() { 668 if (classObject == null) { 669 // Are classObjects unique for an Object, or 670 // created each time? Is this spec'ed? 671 synchronized(this) { 672 if (classObject == null) { 673 try { 674 classObject = JDWP.ReferenceType.ClassObject. 675 process(vm, this).classObject; 676 } catch (JDWPException exc) { 677 throw exc.toJDIException(); 678 } 679 } 680 } 681 } 682 return classObject; 683 } 684 stratum(String stratumID)685 SDE.Stratum stratum(String stratumID) { 686 SDE sde = sourceDebugExtensionInfo(); 687 if (!sde.isValid()) { 688 sde = NO_SDE_INFO_MARK; 689 } 690 return sde.stratum(stratumID); 691 } 692 sourceName()693 public String sourceName() throws AbsentInformationException { 694 return sourceNames(vm.getDefaultStratum()).get(0); 695 } 696 sourceNames(String stratumID)697 public List<String> sourceNames(String stratumID) 698 throws AbsentInformationException { 699 SDE.Stratum stratum = stratum(stratumID); 700 if (stratum.isJava()) { 701 List<String> result = new ArrayList<String>(1); 702 result.add(baseSourceName()); 703 return result; 704 } 705 return stratum.sourceNames(this); 706 } 707 sourcePaths(String stratumID)708 public List<String> sourcePaths(String stratumID) 709 throws AbsentInformationException { 710 SDE.Stratum stratum = stratum(stratumID); 711 if (stratum.isJava()) { 712 List<String> result = new ArrayList<String>(1); 713 result.add(baseSourceDir() + baseSourceName()); 714 return result; 715 } 716 return stratum.sourcePaths(this); 717 } 718 baseSourceName()719 String baseSourceName() throws AbsentInformationException { 720 String bsn = baseSourceName; 721 if (bsn == null) { 722 // Does not need synchronization, since worst-case 723 // static info is fetched twice 724 try { 725 bsn = JDWP.ReferenceType.SourceFile. 726 process(vm, this).sourceFile; 727 } catch (JDWPException exc) { 728 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) { 729 bsn = ABSENT_BASE_SOURCE_NAME; 730 } else { 731 throw exc.toJDIException(); 732 } 733 } 734 baseSourceName = bsn; 735 } 736 if (bsn == ABSENT_BASE_SOURCE_NAME) { 737 throw new AbsentInformationException(); 738 } 739 return bsn; 740 } 741 baseSourcePath()742 String baseSourcePath() throws AbsentInformationException { 743 String bsp = baseSourcePath; 744 if (bsp == null) { 745 bsp = baseSourceDir() + baseSourceName(); 746 baseSourcePath = bsp; 747 } 748 return bsp; 749 } 750 baseSourceDir()751 String baseSourceDir() { 752 if (baseSourceDir == null) { 753 String typeName = name(); 754 StringBuffer sb = new StringBuffer(typeName.length() + 10); 755 int index = 0; 756 int nextIndex; 757 758 while ((nextIndex = typeName.indexOf('.', index)) > 0) { 759 sb.append(typeName.substring(index, nextIndex)); 760 sb.append(java.io.File.separatorChar); 761 index = nextIndex + 1; 762 } 763 baseSourceDir = sb.toString(); 764 } 765 return baseSourceDir; 766 } 767 sourceDebugExtension()768 public String sourceDebugExtension() 769 throws AbsentInformationException { 770 if (!vm.canGetSourceDebugExtension()) { 771 throw new UnsupportedOperationException(); 772 } 773 SDE sde = sourceDebugExtensionInfo(); 774 if (sde == NO_SDE_INFO_MARK) { 775 throw new AbsentInformationException(); 776 } 777 return sde.sourceDebugExtension; 778 } 779 sourceDebugExtensionInfo()780 private SDE sourceDebugExtensionInfo() { 781 if (!vm.canGetSourceDebugExtension()) { 782 return NO_SDE_INFO_MARK; 783 } 784 SDE sde = (sdeRef == null) ? null : sdeRef.get(); 785 if (sde == null) { 786 String extension = null; 787 try { 788 extension = JDWP.ReferenceType.SourceDebugExtension. 789 process(vm, this).extension; 790 } catch (JDWPException exc) { 791 if (exc.errorCode() != JDWP.Error.ABSENT_INFORMATION) { 792 sdeRef = new SoftReference<SDE>(NO_SDE_INFO_MARK); 793 throw exc.toJDIException(); 794 } 795 } 796 if (extension == null) { 797 sde = NO_SDE_INFO_MARK; 798 } else { 799 sde = new SDE(extension); 800 } 801 sdeRef = new SoftReference<SDE>(sde); 802 } 803 return sde; 804 } 805 availableStrata()806 public List<String> availableStrata() { 807 SDE sde = sourceDebugExtensionInfo(); 808 if (sde.isValid()) { 809 return sde.availableStrata(); 810 } else { 811 List<String> strata = new ArrayList<String>(); 812 strata.add(SDE.BASE_STRATUM_NAME); 813 return strata; 814 } 815 } 816 817 /** 818 * Always returns non-null stratumID 819 */ defaultStratum()820 public String defaultStratum() { 821 SDE sdei = sourceDebugExtensionInfo(); 822 if (sdei.isValid()) { 823 return sdei.defaultStratumId; 824 } else { 825 return SDE.BASE_STRATUM_NAME; 826 } 827 } 828 modifiers()829 public int modifiers() { 830 if (modifiers == -1) 831 getModifiers(); 832 833 return modifiers; 834 } 835 allLineLocations()836 public List<Location> allLineLocations() 837 throws AbsentInformationException { 838 return allLineLocations(vm.getDefaultStratum(), null); 839 } 840 allLineLocations(String stratumID, String sourceName)841 public List<Location> allLineLocations(String stratumID, String sourceName) 842 throws AbsentInformationException { 843 boolean someAbsent = false; // A method that should have info, didn't 844 SDE.Stratum stratum = stratum(stratumID); 845 List<Location> list = new ArrayList<Location>(); // location list 846 847 for (Iterator<Method> iter = methods().iterator(); iter.hasNext(); ) { 848 MethodImpl method = (MethodImpl)iter.next(); 849 try { 850 list.addAll( 851 method.allLineLocations(stratum, sourceName)); 852 } catch(AbsentInformationException exc) { 853 someAbsent = true; 854 } 855 } 856 857 // If we retrieved no line info, and at least one of the methods 858 // should have had some (as determined by an 859 // AbsentInformationException being thrown) then we rethrow 860 // the AbsentInformationException. 861 if (someAbsent && list.size() == 0) { 862 throw new AbsentInformationException(); 863 } 864 return list; 865 } 866 locationsOfLine(int lineNumber)867 public List<Location> locationsOfLine(int lineNumber) 868 throws AbsentInformationException { 869 return locationsOfLine(vm.getDefaultStratum(), 870 null, 871 lineNumber); 872 } 873 locationsOfLine(String stratumID, String sourceName, int lineNumber)874 public List<Location> locationsOfLine(String stratumID, 875 String sourceName, 876 int lineNumber) 877 throws AbsentInformationException { 878 // A method that should have info, didn't 879 boolean someAbsent = false; 880 // A method that should have info, did 881 boolean somePresent = false; 882 List<Method> methods = methods(); 883 SDE.Stratum stratum = stratum(stratumID); 884 885 List<Location> list = new ArrayList<Location>(); 886 887 Iterator<Method> iter = methods.iterator(); 888 while(iter.hasNext()) { 889 MethodImpl method = (MethodImpl)iter.next(); 890 // eliminate native and abstract to eliminate 891 // false positives 892 if (!method.isAbstract() && 893 !method.isNative()) { 894 try { 895 list.addAll( 896 method.locationsOfLine(stratum, 897 sourceName, 898 lineNumber)); 899 somePresent = true; 900 } catch(AbsentInformationException exc) { 901 someAbsent = true; 902 } 903 } 904 } 905 if (someAbsent && !somePresent) { 906 throw new AbsentInformationException(); 907 } 908 return list; 909 } 910 instances(long maxInstances)911 public List<ObjectReference> instances(long maxInstances) { 912 if (!vm.canGetInstanceInfo()) { 913 throw new UnsupportedOperationException( 914 "target does not support getting instances"); 915 } 916 917 if (maxInstances < 0) { 918 throw new IllegalArgumentException("maxInstances is less than zero: " 919 + maxInstances); 920 } 921 int intMax = (maxInstances > Integer.MAX_VALUE)? 922 Integer.MAX_VALUE: (int)maxInstances; 923 // JDWP can't currently handle more than this (in mustang) 924 925 try { 926 return Arrays.asList( 927 (ObjectReference[])JDWP.ReferenceType.Instances. 928 process(vm, this, intMax).instances); 929 } catch (JDWPException exc) { 930 throw exc.toJDIException(); 931 } 932 } 933 getClassFileVersion()934 private void getClassFileVersion() { 935 if (!vm.canGetClassFileVersion()) { 936 throw new UnsupportedOperationException(); 937 } 938 JDWP.ReferenceType.ClassFileVersion classFileVersion; 939 if (versionNumberGotten) { 940 return; 941 } else { 942 try { 943 classFileVersion = JDWP.ReferenceType.ClassFileVersion.process(vm, this); 944 } catch (JDWPException exc) { 945 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) { 946 majorVersion = 0; 947 minorVersion = 0; 948 versionNumberGotten = true; 949 return; 950 } else { 951 throw exc.toJDIException(); 952 } 953 } 954 majorVersion = classFileVersion.majorVersion; 955 minorVersion = classFileVersion.minorVersion; 956 versionNumberGotten = true; 957 } 958 } 959 majorVersion()960 public int majorVersion() { 961 try { 962 getClassFileVersion(); 963 } catch (RuntimeException exc) { 964 throw exc; 965 } 966 return majorVersion; 967 } 968 minorVersion()969 public int minorVersion() { 970 try { 971 getClassFileVersion(); 972 } catch (RuntimeException exc) { 973 throw exc; 974 } 975 return minorVersion; 976 } 977 getConstantPoolInfo()978 private byte[] getConstantPoolInfo() { 979 JDWP.ReferenceType.ConstantPool jdwpCPool; 980 if (!vm.canGetConstantPool()) { 981 throw new UnsupportedOperationException(); 982 } 983 if (constantPoolInfoGotten) { 984 if (constantPoolBytesRef == null) { 985 return null; 986 } 987 byte[] cpbytes = constantPoolBytesRef.get(); 988 if (cpbytes != null) { 989 return cpbytes; 990 } 991 } 992 993 try { 994 jdwpCPool = JDWP.ReferenceType.ConstantPool.process(vm, this); 995 } catch (JDWPException exc) { 996 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) { 997 constanPoolCount = 0; 998 constantPoolBytesRef = null; 999 constantPoolInfoGotten = true; 1000 return null; 1001 } else { 1002 throw exc.toJDIException(); 1003 } 1004 } 1005 byte[] cpbytes; 1006 constanPoolCount = jdwpCPool.count; 1007 cpbytes = jdwpCPool.bytes; 1008 constantPoolBytesRef = new SoftReference<byte[]>(cpbytes); 1009 constantPoolInfoGotten = true; 1010 return cpbytes; 1011 } 1012 constantPoolCount()1013 public int constantPoolCount() { 1014 try { 1015 getConstantPoolInfo(); 1016 } catch (RuntimeException exc) { 1017 throw exc; 1018 } 1019 return constanPoolCount; 1020 } 1021 constantPool()1022 public byte[] constantPool() { 1023 byte[] cpbytes; 1024 try { 1025 cpbytes = getConstantPoolInfo(); 1026 } catch (RuntimeException exc) { 1027 throw exc; 1028 } 1029 if (cpbytes != null) { 1030 /* 1031 * Arrays are always modifiable, so it is a little unsafe 1032 * to return the cached bytecodes directly; instead, we 1033 * make a clone at the cost of using more memory. 1034 */ 1035 return cpbytes.clone(); 1036 } else { 1037 return null; 1038 } 1039 } 1040 1041 // Does not need synchronization, since worst-case 1042 // static info is fetched twice getModifiers()1043 void getModifiers() { 1044 if (modifiers != -1) { 1045 return; 1046 } 1047 try { 1048 modifiers = JDWP.ReferenceType.Modifiers. 1049 process(vm, this).modBits; 1050 } catch (JDWPException exc) { 1051 throw exc.toJDIException(); 1052 } 1053 } 1054 decodeStatus(int status)1055 void decodeStatus(int status) { 1056 this.status = status; 1057 if ((status & JDWP.ClassStatus.PREPARED) != 0) { 1058 isPrepared = true; 1059 } 1060 } 1061 updateStatus()1062 void updateStatus() { 1063 try { 1064 decodeStatus(JDWP.ReferenceType.Status.process(vm, this).status); 1065 } catch (JDWPException exc) { 1066 throw exc.toJDIException(); 1067 } 1068 } 1069 markPrepared()1070 void markPrepared() { 1071 isPrepared = true; 1072 } 1073 ref()1074 long ref() { 1075 return ref; 1076 } 1077 indexOf(Method method)1078 int indexOf(Method method) { 1079 // Make sure they're all here - the obsolete method 1080 // won't be found and so will have index -1 1081 return methods().indexOf(method); 1082 } 1083 indexOf(Field field)1084 int indexOf(Field field) { 1085 // Make sure they're all here 1086 return fields().indexOf(field); 1087 } 1088 1089 /* 1090 * Return true if an instance of this type 1091 * can be assigned to a variable of the given type 1092 */ isAssignableTo(ReferenceType type)1093 abstract boolean isAssignableTo(ReferenceType type); 1094 isAssignableFrom(ReferenceType type)1095 boolean isAssignableFrom(ReferenceType type) { 1096 return ((ReferenceTypeImpl)type).isAssignableTo(this); 1097 } 1098 isAssignableFrom(ObjectReference object)1099 boolean isAssignableFrom(ObjectReference object) { 1100 return object == null || 1101 isAssignableFrom(object.referenceType()); 1102 } 1103 setStatus(int status)1104 void setStatus(int status) { 1105 decodeStatus(status); 1106 } 1107 setSignature(String signature)1108 void setSignature(String signature) { 1109 this.signature = signature; 1110 } 1111 setGenericSignature(String signature)1112 void setGenericSignature(String signature) { 1113 if (signature != null && signature.length() == 0) { 1114 this.genericSignature = null; 1115 } else{ 1116 this.genericSignature = signature; 1117 } 1118 this.genericSignatureGotten = true; 1119 } 1120 isPrimitiveArray(String signature)1121 private static boolean isPrimitiveArray(String signature) { 1122 int i = signature.lastIndexOf('['); 1123 /* 1124 * TO DO: Centralize JNI signature knowledge. 1125 * 1126 * Ref: 1127 * jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html 1128 */ 1129 boolean isPA; 1130 if (i < 0) { 1131 isPA = false; 1132 } else { 1133 char c = signature.charAt(i + 1); 1134 isPA = (c != 'L'); 1135 } 1136 return isPA; 1137 } 1138 findType(String signature)1139 Type findType(String signature) throws ClassNotLoadedException { 1140 Type type; 1141 if (signature.length() == 1) { 1142 /* OTI FIX: Must be a primitive type or the void type */ 1143 char sig = signature.charAt(0); 1144 if (sig == 'V') { 1145 type = vm.theVoidType(); 1146 } else { 1147 type = vm.primitiveTypeMirror((byte)sig); 1148 } 1149 } else { 1150 // Must be a reference type. 1151 ClassLoaderReferenceImpl loader = 1152 (ClassLoaderReferenceImpl)classLoader(); 1153 if ((loader == null) || 1154 (isPrimitiveArray(signature)) //Work around 4450091 1155 ) { 1156 // Caller wants type of boot class field 1157 type = vm.findBootType(signature); 1158 } else { 1159 // Caller wants type of non-boot class field 1160 type = loader.findType(signature); 1161 } 1162 } 1163 return type; 1164 } 1165 loaderString()1166 String loaderString() { 1167 if (classLoader() != null) { 1168 return "loaded by " + classLoader().toString(); 1169 } else { 1170 return "no class loader"; 1171 } 1172 } 1173 1174 } 1175