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