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