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