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 }