1 /*
2  * Copyright (c) 2002, 2012, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 package sun.jvm.hotspot.jdi;
26 
27 import java.io.*;
28 
29 import com.sun.jdi.*;
30 
31 import sun.jvm.hotspot.memory.SystemDictionary;
32 import sun.jvm.hotspot.oops.Instance;
33 import sun.jvm.hotspot.oops.InstanceKlass;
34 import sun.jvm.hotspot.oops.ArrayKlass;
35 import sun.jvm.hotspot.oops.JVMDIClassStatus;
36 import sun.jvm.hotspot.oops.Klass;
37 import sun.jvm.hotspot.oops.ObjArray;
38 import sun.jvm.hotspot.oops.Oop;
39 import sun.jvm.hotspot.oops.Symbol;
40 import sun.jvm.hotspot.oops.DefaultHeapVisitor;
41 import sun.jvm.hotspot.utilities.Assert;
42 
43 import java.util.*;
44 import java.lang.ref.SoftReference;
45 
46 public abstract class ReferenceTypeImpl extends TypeImpl
47 implements ReferenceType {
48     protected Klass       saKlass;          // This can be an InstanceKlass or an ArrayKlass
49     protected Symbol      typeNameSymbol;   // This is used in vm.classesByName to speedup search
50     private int           modifiers = -1;
51     private String        signature = null;
52     private SoftReference sdeRef = null;
53     private SoftReference fieldsCache;
54     private SoftReference allFieldsCache;
55     private SoftReference methodsCache;
56     private SoftReference allMethodsCache;
57     private SoftReference nestedTypesCache;
58     private SoftReference methodInvokesCache;
59 
60     /* to mark when no info available */
61     static final SDE NO_SDE_INFO_MARK = new SDE();
62 
ReferenceTypeImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Klass klass)63     protected ReferenceTypeImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Klass klass) {
64         super(aVm);
65         saKlass = klass;
66         typeNameSymbol = saKlass.getName();
67         if (Assert.ASSERTS_ENABLED) {
68             Assert.that(typeNameSymbol != null, "null type name for a Klass");
69         }
70     }
71 
typeNameAsSymbol()72     Symbol typeNameAsSymbol() {
73         return typeNameSymbol;
74     }
75 
getMethodMirror(sun.jvm.hotspot.oops.Method ref)76     Method getMethodMirror(sun.jvm.hotspot.oops.Method ref) {
77         // SA creates new Method objects when they are referenced which means
78         // that the incoming object might not be the same object as on our
79         // even though it is the same method. So do an address compare by
80         // calling equals rather than just reference compare.
81         Iterator it = methods().iterator();
82         while (it.hasNext()) {
83             MethodImpl method = (MethodImpl)it.next();
84             if (ref.equals(method.ref())) {
85                 return method;
86             }
87         }
88         if (ref.getMethodHolder().equals(SystemDictionary.getMethodHandleKlass())) {
89           // invoke methods are generated as needed, so make mirrors as needed
90           List mis = null;
91           if (methodInvokesCache == null) {
92             mis = new ArrayList();
93             methodInvokesCache = new SoftReference(mis);
94           } else {
95             mis = (List)methodInvokesCache.get();
96           }
97           it = mis.iterator();
98           while (it.hasNext()) {
99             MethodImpl method = (MethodImpl)it.next();
100             if (ref.equals(method.ref())) {
101               return method;
102             }
103           }
104 
105           MethodImpl method = MethodImpl.createMethodImpl(vm, this, ref);
106           mis.add(method);
107           return method;
108         }
109         throw new IllegalArgumentException("Invalid method id: " + ref);
110     }
111 
equals(Object obj)112     public boolean equals(Object obj) {
113         if ((obj != null) && (obj instanceof ReferenceTypeImpl)) {
114             ReferenceTypeImpl other = (ReferenceTypeImpl)obj;
115             return (ref().equals(other.ref())) &&
116                 (vm.equals(other.virtualMachine()));
117         } else {
118             return false;
119         }
120     }
121 
hashCode()122     public int hashCode() {
123         return saKlass.hashCode();
124     }
125 
compareTo(ReferenceType refType)126     public int compareTo(ReferenceType refType) {
127         /*
128          * Note that it is critical that compareTo() == 0
129          * implies that equals() == true. Otherwise, TreeSet
130          * will collapse classes.
131          *
132          * (Classes of the same name loaded by different class loaders
133          * or in different VMs must not return 0).
134          */
135         ReferenceTypeImpl other = (ReferenceTypeImpl)refType;
136         int comp = name().compareTo(other.name());
137         if (comp == 0) {
138             Klass rf1 = ref();
139             Klass rf2 = other.ref();
140             // optimize for typical case: refs equal and VMs equal
141             if (rf1.equals(rf2)) {
142                 // sequenceNumbers are always positive
143                 comp = vm.sequenceNumber -
144                  ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber;
145             } else {
146                 comp = rf1.getAddress().minus(rf2.getAddress()) < 0? -1 : 1;
147             }
148         }
149         return comp;
150     }
151 
152     public String signature() {
153         if (signature == null) {
154             signature = saKlass.signature();
155         }
156         return signature;
157     }
158 
159     // refer to JvmtiEnv::GetClassSignature.
160     // null is returned for array klasses.
161     public String genericSignature() {
162         if (saKlass instanceof ArrayKlass) {
163             return null;
164         } else {
165             Symbol genSig = ((InstanceKlass)saKlass).getGenericSignature();
166             return (genSig != null)? genSig.asString() : null;
167         }
168     }
169 
170     public ClassLoaderReference classLoader() {
171       Instance xx = (Instance)(((InstanceKlass)saKlass).getClassLoader());
172       return (ClassLoaderReferenceImpl)vm.classLoaderMirror(xx);
173     }
174 
175     public boolean isPublic() {
176         return((modifiers() & VMModifiers.PUBLIC) != 0);
177     }
178 
179     public boolean isProtected() {
180         return((modifiers() & VMModifiers.PROTECTED) != 0);
181     }
182 
183     public boolean isPrivate() {
184         return((modifiers() & VMModifiers.PRIVATE) != 0);
185     }
186 
187     public boolean isPackagePrivate() {
188         return !isPublic() && !isPrivate() && !isProtected();
189     }
190 
191     public boolean isAbstract() {
192         return((modifiers() & VMModifiers.ABSTRACT) != 0);
193     }
194 
195     public boolean isFinal() {
196         return((modifiers() & VMModifiers.FINAL) != 0);
197     }
198 
199     public boolean isStatic() {
200         return((modifiers() & VMModifiers.STATIC) != 0);
201     }
202 
203     public boolean isPrepared() {
204         return (saKlass.getClassStatus() & JVMDIClassStatus.PREPARED) != 0;
205     }
206 
207     final void checkPrepared() throws ClassNotPreparedException {
208         if (! isPrepared()) {
209             throw new ClassNotPreparedException();
210         }
211     }
212 
213     public boolean isVerified() {
214         return (saKlass.getClassStatus() & JVMDIClassStatus.VERIFIED) != 0;
215     }
216 
217     public boolean isInitialized() {
218         return (saKlass.getClassStatus() & JVMDIClassStatus.INITIALIZED) != 0;
219     }
220 
221     public boolean failedToInitialize() {
222         return (saKlass.getClassStatus() & JVMDIClassStatus.ERROR) != 0;
223     }
224 
225     private boolean isThrowableBacktraceField(sun.jvm.hotspot.oops.Field fld) {
226         // refer to JvmtiEnv::GetClassFields in jvmtiEnv.cpp.
227         // We want to filter out java.lang.Throwable.backtrace (see 4446677).
228         // It contains some Method*s that aren't quite real Objects.
229         if (fld.getFieldHolder().getName().equals(vm.javaLangThrowable()) &&
230             fld.getID().getName().equals("backtrace")) {
231             return true;
232         } else {
233             return false;
234         }
235     }
236 
237     public final List fields() throws ClassNotPreparedException {
238         List fields = (fieldsCache != null)? (List) fieldsCache.get() : null;
239         if (fields == null) {
240             checkPrepared();
241             if (saKlass instanceof ArrayKlass) {
242                 fields = new ArrayList(0);
243             } else {
244                 // Get a list of the sa Field types
245                 List saFields = ((InstanceKlass)saKlass).getImmediateFields();
246 
247                 // Create a list of our Field types
248                 int len = saFields.size();
249                 fields = new ArrayList(len);
250                 for (int ii = 0; ii < len; ii++) {
251                     sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii);
252                     if (! isThrowableBacktraceField(curField)) {
253                         fields.add(new FieldImpl(vm, this, curField));
254                     }
255                 }
256             }
257             fields = Collections.unmodifiableList(fields);
258             fieldsCache = new SoftReference(fields);
259         }
260         return fields;
261     }
262 
263     public final List allFields() throws ClassNotPreparedException {
264         List allFields = (allFieldsCache != null)? (List) allFieldsCache.get() : null;
265         if (allFields == null) {
266             checkPrepared();
267             if (saKlass instanceof ArrayKlass) {
268                 // is 'length' a field of array klasses? To maintain
269                 // consistency with JVMDI-JDI we return 0 size.
270                 allFields = new ArrayList(0);
271             } else {
272                 List saFields;
273 
274                 // Get a list of the sa Field types
275                 saFields = ((InstanceKlass)saKlass).getAllFields();
276 
277                 // Create a list of our Field types
278                 int len = saFields.size();
279                 allFields = new ArrayList(len);
280                 for (int ii = 0; ii < len; ii++) {
281                     sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii);
282                     if (! isThrowableBacktraceField(curField)) {
283                         allFields.add(new FieldImpl(vm, vm.referenceType(curField.getFieldHolder()), curField));
284                     }
285                 }
286             }
287             allFields = Collections.unmodifiableList(allFields);
288             allFieldsCache = new SoftReference(allFields);
289         }
290         return allFields;
291     }
292 
293     abstract List inheritedTypes();
294 
295     void addVisibleFields(List visibleList, Map visibleTable, List ambiguousNames) {
296         List list = visibleFields();
297         Iterator iter = list.iterator();
298         while (iter.hasNext()) {
299             Field field = (Field)iter.next();
300             String name = field.name();
301             if (!ambiguousNames.contains(name)) {
302                 Field duplicate = (Field)visibleTable.get(name);
303                 if (duplicate == null) {
304                     visibleList.add(field);
305                     visibleTable.put(name, field);
306                 } else if (!field.equals(duplicate)) {
307                     ambiguousNames.add(name);
308                     visibleTable.remove(name);
309                     visibleList.remove(duplicate);
310                 } else {
311                     // identical field from two branches; do nothing
312                 }
313             }
314         }
315     }
316 
317     public final List visibleFields() throws ClassNotPreparedException {
318         checkPrepared();
319         /*
320          * Maintain two different collections of visible fields. The
321          * list maintains a reasonable order for return. The
322          * hash map provides an efficient way to lookup visible fields
323          * by name, important for finding hidden or ambiguous fields.
324          */
325         List visibleList = new ArrayList();
326         Map  visibleTable = new HashMap();
327 
328         /* Track fields removed from above collection due to ambiguity */
329         List ambiguousNames = new ArrayList();
330 
331         /* Add inherited, visible fields */
332         List types = inheritedTypes();
333         Iterator iter = types.iterator();
334         while (iter.hasNext()) {
335             /*
336              * TO DO: Be defensive and check for cyclic interface inheritance
337              */
338             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
339             type.addVisibleFields(visibleList, visibleTable, ambiguousNames);
340         }
341 
342         /*
343          * Insert fields from this type, removing any inherited fields they
344          * hide.
345          */
346         List retList = new ArrayList(fields());
347         iter = retList.iterator();
348         while (iter.hasNext()) {
349             Field field = (Field)iter.next();
350             Field hidden = (Field)visibleTable.get(field.name());
351             if (hidden != null) {
352                 visibleList.remove(hidden);
353             }
354         }
355         retList.addAll(visibleList);
356         return retList;
357     }
358 
359    public final Field fieldByName(String fieldName) throws ClassNotPreparedException {
360         java.util.List searchList;
361         Field f;
362 
363         // visibleFields calls checkPrepared
364         searchList = visibleFields();
365 
366         for (int i=0; i<searchList.size(); i++) {
367             f = (Field)searchList.get(i);
368 
369             if (f.name().equals(fieldName)) {
370                 return f;
371             }
372         }
373         //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name());
374         return null;
375     }
376 
377     public final List methods() throws ClassNotPreparedException {
378         List methods = (methodsCache != null)? (List) methodsCache.get() : null;
379         if (methods == null) {
380             checkPrepared();
381             if (saKlass instanceof ArrayKlass) {
382                 methods = new ArrayList(0);
383             } else {
384                 List saMethods;
385                 // Get a list of the SA Method types
386                 saMethods = ((InstanceKlass)saKlass).getImmediateMethods();
387 
388                 // Create a list of our MethodImpl types
389                 int len = saMethods.size();
390                 methods = new ArrayList(len);
391                 for (int ii = 0; ii < len; ii++) {
392                     methods.add(MethodImpl.createMethodImpl(vm, this, (sun.jvm.hotspot.oops.Method)saMethods.get(ii)));
393                 }
394             }
395             methods = Collections.unmodifiableList(methods);
396             methodsCache = new SoftReference(methods);
397         }
398         return methods;
399     }
400 
401     abstract List getAllMethods();
402     public final List allMethods() throws ClassNotPreparedException {
403         List allMethods = (allMethodsCache != null)? (List) allMethodsCache.get() : null;
404         if (allMethods == null) {
405             checkPrepared();
406             allMethods = Collections.unmodifiableList(getAllMethods());
407             allMethodsCache = new SoftReference(allMethods);
408         }
409         return allMethods;
410     }
411 
412     /*
413      * Utility method used by subclasses to build lists of visible
414      * methods.
415      */
416     void addToMethodMap(Map methodMap, List methodList) {
417         Iterator iter = methodList.iterator();
418         while (iter.hasNext()) {
419             Method method = (Method)iter.next();
420             methodMap.put(method.name().concat(method.signature()), method);
421         }
422     }
423 
424     abstract void addVisibleMethods(Map methodMap);
425     public final List visibleMethods() throws ClassNotPreparedException {
426         checkPrepared();
427         /*
428          * Build a collection of all visible methods. The hash
429          * map allows us to do this efficiently by keying on the
430          * concatenation of name and signature.
431          */
432         //System.out.println("jj: RTI: Calling addVisibleMethods for:" + this);
433         Map map = new HashMap();
434         addVisibleMethods(map);
435 
436         /*
437          * ... but the hash map destroys order. Methods should be
438          * returned in a sensible order, as they are in allMethods().
439          * So, start over with allMethods() and use the hash map
440          * to filter that ordered collection.
441          */
442         //System.out.println("jj: RTI: Calling allMethods for:" + this);
443 
444         List list = new ArrayList(allMethods());
445         //System.out.println("jj: allMethods = " + jjstr(list));
446         //System.out.println("jj: map = " + map.toString());
447         //System.out.println("jj: map = " + jjstr(map.values()));
448         list.retainAll(map.values());
449         //System.out.println("jj: map = " + jjstr(list));
450         //System.exit(0);
451         return list;
452     }
453 
454     static Object prev;
455 
456     static public String jjstr(Collection cc) {
457         StringBuffer buf = new StringBuffer();
458         buf.append("[");
459         Iterator i = cc.iterator();
460         boolean hasNext = i.hasNext();
461         while (hasNext) {
462             Object o = i.next();
463             if (prev == null) {
464                 prev = o;
465             } else {
466                 System.out.println("prev == curr?" + prev.equals(o));
467                 System.out.println("prev == curr?" + (prev == o));
468             }
469             buf.append( o + "@" + o.hashCode());
470             //buf.append( ((Object)o).toString());
471             hasNext = i.hasNext();
472             if (hasNext)
473                 buf.append(", ");
474         }
475 
476         buf.append("]");
477         return buf.toString();
478     }
479 
480     public final List methodsByName(String name) throws ClassNotPreparedException {
481         // visibleMethods calls checkPrepared
482         List methods = visibleMethods();
483         ArrayList retList = new ArrayList(methods.size());
484         Iterator iter = methods.iterator();
485         while (iter.hasNext()) {
486             Method candidate = (Method)iter.next();
487             if (candidate.name().equals(name)) {
488                 retList.add(candidate);
489             }
490         }
491         retList.trimToSize();
492         return retList;
493     }
494 
495     public final List methodsByName(String name, String signature) throws ClassNotPreparedException {
496         // visibleMethods calls checkPrepared
497         List methods = visibleMethods();
498         ArrayList retList = new ArrayList(methods.size());
499         Iterator iter = methods.iterator();
500         while (iter.hasNext()) {
501             Method candidate = (Method)iter.next();
502             if (candidate.name().equals(name) &&
503                 candidate.signature().equals(signature)) {
504                 retList.add(candidate);
505             }
506         }
507         retList.trimToSize();
508         return retList;
509     }
510 
511 
512     List getInterfaces() {
513         List myInterfaces;
514         if (saKlass instanceof ArrayKlass) {
515             // Actually, JLS says arrays implement Cloneable and Serializable
516             // But, JVMDI-JDI just returns 0 interfaces for arrays. We follow
517             // the same for consistency.
518             myInterfaces = new ArrayList(0);
519         } else {
520             // Get a list of the sa InstanceKlass types
521             List saInterfaces = ((InstanceKlass)saKlass).getDirectImplementedInterfaces();
522 
523             // Create a list of our InterfaceTypes
524             int len = saInterfaces.size();
525             myInterfaces = new ArrayList(len);
526             for (int ii = 0; ii < len; ii++) {
527                 myInterfaces.add(new InterfaceTypeImpl(vm, (InstanceKlass)saInterfaces.get(ii)));
528             }
529         }
530         return myInterfaces;
531     }
532 
533     public final List nestedTypes() {
534         List nestedTypes = (nestedTypesCache != null)? (List) nestedTypesCache.get() : null;
535         if (nestedTypes == null) {
536             if (saKlass instanceof ArrayKlass) {
537                 nestedTypes = new ArrayList(0);
538             } else {
539                 ClassLoaderReference cl = classLoader();
540                 List classes = null;
541                 if (cl != null) {
542                    classes = cl.visibleClasses();
543                 } else {
544                    classes = vm.bootstrapClasses();
545                 }
546                 nestedTypes = new ArrayList();
547                 Iterator iter = classes.iterator();
548                 while (iter.hasNext()) {
549                     ReferenceTypeImpl refType = (ReferenceTypeImpl)iter.next();
550                     Symbol candidateName = refType.ref().getName();
551                     if (((InstanceKlass)saKlass).isInnerOrLocalClassName(candidateName)) {
552                         nestedTypes.add(refType);
553                     }
554                 }
555             }
556             nestedTypes = Collections.unmodifiableList(nestedTypes);
557             nestedTypesCache = new SoftReference(nestedTypes);
558         }
559         return nestedTypes;
560     }
561 
562     public Value getValue(Field sig) {
563         List list = new ArrayList(1);
564         list.add(sig);
565         Map map = getValues(list);
566         return(Value)map.get(sig);
567     }
568 
569     /**
570      * Returns a map of field values
571      */
572     public Map getValues(List theFields) {
573         //validateMirrors();
574         int size = theFields.size();
575         Map map = new HashMap(size);
576         for (int ii=0; ii<size; ii++) {
577             FieldImpl fieldImpl = (FieldImpl)theFields.get(ii);
578 
579             validateFieldAccess(fieldImpl);
580             // Do more validation specific to ReferenceType field getting
581             if (!fieldImpl.isStatic()) {
582                 throw new IllegalArgumentException(
583                      "Attempt to use non-static field with ReferenceType: " +
584                      fieldImpl.name());
585             }
586             map.put(fieldImpl, fieldImpl.getValue());
587         }
588         return map;
589     }
590 
591     void validateFieldAccess(Field field) {
592        /*
593         * Field must be in this object's class, a superclass, or
594         * implemented interface
595         */
596         ReferenceTypeImpl declType = (ReferenceTypeImpl)field.declaringType();
597         if (!declType.isAssignableFrom(this)) {
598             throw new IllegalArgumentException("Invalid field");
599         }
600     }
601 
602     public ClassObjectReference classObject() {
603         return vm.classObjectMirror(ref().getJavaMirror());
604     }
605 
606     SDE.Stratum stratum(String stratumID) {
607         SDE sde = sourceDebugExtensionInfo();
608         if (!sde.isValid()) {
609             sde = NO_SDE_INFO_MARK;
610         }
611         return sde.stratum(stratumID);
612     }
613 
614     public String sourceName() throws AbsentInformationException {
615         return (String)(sourceNames(vm.getDefaultStratum()).get(0));
616     }
617 
618     public List sourceNames(String stratumID)
619                                 throws AbsentInformationException {
620         SDE.Stratum stratum = stratum(stratumID);
621         if (stratum.isJava()) {
622             List result = new ArrayList(1);
623             result.add(baseSourceName());
624             return result;
625         }
626         return stratum.sourceNames(this);
627     }
628 
629     public List sourcePaths(String stratumID)
630                                 throws AbsentInformationException {
631         SDE.Stratum stratum = stratum(stratumID);
632         if (stratum.isJava()) {
633             List result = new ArrayList(1);
634             result.add(baseSourceDir() + baseSourceName());
635             return result;
636         }
637         return stratum.sourcePaths(this);
638     }
639 
640     String baseSourceName() throws AbsentInformationException {
641       if (saKlass instanceof ArrayKlass) {
642             throw new AbsentInformationException();
643       }
644       Symbol sym = ((InstanceKlass)saKlass).getSourceFileName();
645       if (sym != null) {
646           return sym.asString();
647       } else {
648           throw new AbsentInformationException();
649       }
650     }
651 
652     String baseSourcePath() throws AbsentInformationException {
653         return baseSourceDir() + baseSourceName();
654     }
655 
656     String baseSourceDir() {
657         String typeName = name();
658         StringBuffer sb = new StringBuffer(typeName.length() + 10);
659         int index = 0;
660         int nextIndex;
661 
662         while ((nextIndex = typeName.indexOf('.', index)) > 0) {
663             sb.append(typeName.substring(index, nextIndex));
664             sb.append(java.io.File.separatorChar);
665             index = nextIndex + 1;
666         }
667         return sb.toString();
668     }
669 
670     public String sourceDebugExtension()
671                            throws AbsentInformationException {
672         if (!vm.canGetSourceDebugExtension()) {
673             throw new UnsupportedOperationException();
674         }
675         SDE sde = sourceDebugExtensionInfo();
676         if (sde == NO_SDE_INFO_MARK) {
677             throw new AbsentInformationException();
678         }
679         return sde.sourceDebugExtension;
680     }
681 
682     private SDE sourceDebugExtensionInfo() {
683         if (!vm.canGetSourceDebugExtension()) {
684             return NO_SDE_INFO_MARK;
685         }
686         SDE sde = null;
687         sde = (sdeRef == null) ?  null : (SDE)sdeRef.get();
688         if (sde == null) {
689            String extension = null;
690            if (saKlass instanceof InstanceKlass) {
691               extension = ((InstanceKlass)saKlass).getSourceDebugExtension();
692            }
693            if (extension == null) {
694               sde = NO_SDE_INFO_MARK;
695            } else {
696               sde = new SDE(extension);
697            }
698            sdeRef = new SoftReference(sde);
699         }
700         return sde;
701     }
702 
703     public List availableStrata() {
704         SDE sde = sourceDebugExtensionInfo();
705         if (sde.isValid()) {
706             return sde.availableStrata();
707         } else {
708             List strata = new ArrayList();
709             strata.add(SDE.BASE_STRATUM_NAME);
710             return strata;
711         }
712     }
713 
714     /**
715      * Always returns non-null stratumID
716      */
717     public String defaultStratum() {
718         SDE sdei = sourceDebugExtensionInfo();
719         if (sdei.isValid()) {
720             return sdei.defaultStratumId;
721         } else {
722             return SDE.BASE_STRATUM_NAME;
723         }
724     }
725 
726     public final int modifiers() {
727         if (modifiers == -1) {
728             modifiers = getModifiers();
729         }
730         return modifiers;
731     }
732 
733     // new method since 1.6.
734     // Real body will be supplied later.
735     public List instances(long maxInstances) {
736         if (!vm.canGetInstanceInfo()) {
737             throw new UnsupportedOperationException(
738                       "target does not support getting instances");
739         }
740 
741         if (maxInstances < 0) {
742             throw new IllegalArgumentException("maxInstances is less than zero: "
743                                               + maxInstances);
744         }
745 
746         final List objects = new ArrayList(0);
747         if (isAbstract() || (this instanceof InterfaceType)) {
748             return objects;
749         }
750 
751         final Klass givenKls = this.ref();
752         final long max = maxInstances;
753         vm.saObjectHeap().iterate(new DefaultHeapVisitor() {
754                 private long instCount=0;
755                 public boolean doObj(Oop oop) {
756                     if (givenKls.equals(oop.getKlass())) {
757                         objects.add(vm.objectMirror(oop));
758                                                 instCount++;
759                     }
760                     if (max > 0 && instCount >= max) {
761                         return true;
762                                         }
763                                         return false;
764                 }
765             });
766         return objects;
767     }
768 
769     int getModifiers() {
770         return (int) saKlass.getClassModifiers();
771     }
772 
773     public List allLineLocations()
774                             throws AbsentInformationException {
775         return allLineLocations(vm.getDefaultStratum(), null);
776     }
777 
778     public List allLineLocations(String stratumID, String sourceName)
779                             throws AbsentInformationException {
780         checkPrepared();
781         boolean someAbsent = false; // A method that should have info, didn't
782         SDE.Stratum stratum = stratum(stratumID);
783         List list = new ArrayList();  // location list
784 
785         for (Iterator iter = methods().iterator(); iter.hasNext(); ) {
786             MethodImpl method = (MethodImpl)iter.next();
787             try {
788                 list.addAll(
789                    method.allLineLocations(stratum.id(), sourceName));
790             } catch(AbsentInformationException exc) {
791                 someAbsent = true;
792             }
793         }
794 
795         // If we retrieved no line info, and at least one of the methods
796         // should have had some (as determined by an
797         // AbsentInformationException being thrown) then we rethrow
798         // the AbsentInformationException.
799         if (someAbsent && list.size() == 0) {
800             throw new AbsentInformationException();
801         }
802         return list;
803     }
804 
805     public List locationsOfLine(int lineNumber)
806                            throws AbsentInformationException {
807         return locationsOfLine(vm.getDefaultStratum(),
808                                null,
809                                lineNumber);
810     }
811 
812     public List locationsOfLine(String stratumID,
813                                 String sourceName,
814                                 int lineNumber)
815                            throws AbsentInformationException {
816         checkPrepared();
817         // A method that should have info, didn't
818         boolean someAbsent = false;
819         // A method that should have info, did
820         boolean somePresent = false;
821         List methods = methods();
822         SDE.Stratum stratum = stratum(stratumID);
823 
824         List list = new ArrayList();
825 
826         Iterator iter = methods.iterator();
827         while(iter.hasNext()) {
828             MethodImpl method = (MethodImpl)iter.next();
829             // eliminate native and abstract to eliminate
830             // false positives
831             if (!method.isAbstract() &&
832                 !method.isNative()) {
833                 try {
834                     list.addAll(
835                        method.locationsOfLine(stratum.id(),
836                                               sourceName,
837                                               lineNumber));
838                     somePresent = true;
839                 } catch(AbsentInformationException exc) {
840                     someAbsent = true;
841                 }
842             }
843         }
844         if (someAbsent && !somePresent) {
845             throw new AbsentInformationException();
846         }
847         return list;
848     }
849 
850     Klass ref() {
851         return saKlass;
852     }
853 
854 
855     /*
856      * Return true if an instance of this type
857      * can be assigned to a variable of the given type
858      */
859     abstract boolean isAssignableTo(ReferenceType type);
860 
861     boolean isAssignableFrom(ReferenceType type) {
862         return ((ReferenceTypeImpl)type).isAssignableTo(this);
863     }
864 
865     boolean isAssignableFrom(ObjectReference object) {
866         return object == null ||
867             isAssignableFrom(object.referenceType());
868     }
869 
870     int indexOf(Method method) {
871         // Make sure they're all here - the obsolete method
872         // won't be found and so will have index -1
873         return methods().indexOf(method);
874     }
875 
876     int indexOf(Field field) {
877         // Make sure they're all here
878         return fields().indexOf(field);
879     }
880 
881     private static boolean isPrimitiveArray(String signature) {
882         int i = signature.lastIndexOf('[');
883         /*
884          * TO DO: Centralize JNI signature knowledge.
885          *
886          * Ref:
887          *  jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html
888          */
889         boolean isPA;
890         if (i < 0) {
891             isPA = false;
892         } else {
893             char c = signature.charAt(i + 1);
894             isPA = (c != 'L');
895         }
896         return isPA;
897     }
898 
899     Type findType(String signature) throws ClassNotLoadedException {
900         Type type;
901         if (signature.length() == 1) {
902             /* OTI FIX: Must be a primitive type or the void type */
903             char sig = signature.charAt(0);
904             if (sig == 'V') {
905                 type = vm.theVoidType();
906             } else {
907                 type = vm.primitiveTypeMirror(sig);
908             }
909         } else {
910             // Must be a reference type.
911             ClassLoaderReferenceImpl loader =
912                        (ClassLoaderReferenceImpl)classLoader();
913             if ((loader == null) ||
914                 (isPrimitiveArray(signature)) //Work around 4450091
915                 ) {
916                 // Caller wants type of boot class field
917                 type = vm.findBootType(signature);
918             } else {
919                 // Caller wants type of non-boot class field
920                 type = loader.findType(signature);
921             }
922         }
923         return type;
924     }
925 
926     String loaderString() {
927         if (classLoader() != null) {
928             return "loaded by " + classLoader().toString();
929         } else {
930             return "loaded by bootstrap loader";
931         }
932     }
933 
934     long uniqueID() {
935         return vm.getAddressValue(ref().getJavaMirror());
936     }
937 
938     // new method since 1.6
939     public int majorVersion() {
940         if (!vm.canGetClassFileVersion()) {
941             throw new UnsupportedOperationException("Cannot get class file version");
942         }
943         return (int)((InstanceKlass)saKlass).majorVersion();
944     }
945 
946     // new method since 1.6
947     public int minorVersion() {
948         if (!vm.canGetClassFileVersion()) {
949             throw new UnsupportedOperationException("Cannot get class file version");
950         }
951         return (int)((InstanceKlass)saKlass).minorVersion();
952     }
953 
954     // new method since 1.6
955     public int constantPoolCount() {
956         if (!vm.canGetConstantPool()) {
957             throw new UnsupportedOperationException("Cannot get constant pool");
958         }
959         if (saKlass instanceof ArrayKlass) {
960             return 0;
961         } else {
962             return (int)((InstanceKlass)saKlass).getConstants().getLength();
963         }
964     }
965 
966     // new method since 1.6
967     public byte[] constantPool() {
968         if (!vm.canGetConstantPool()) {
969             throw new UnsupportedOperationException("Cannot get constant pool");
970         }
971         if (this instanceof ArrayType || this instanceof PrimitiveType) {
972                         byte bytes[] = new byte[0];
973             return bytes;
974         } else {
975             ByteArrayOutputStream bs = new ByteArrayOutputStream();
976             try {
977                 ((InstanceKlass)saKlass).getConstants().writeBytes(bs);
978             } catch (IOException ex) {
979                                 ex.printStackTrace();
980                                 byte bytes[] = new byte[0];
981                                 return bytes;
982             }
983             return bs.toByteArray();
984         }
985     }
986 }
987