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 
ReferenceTypeImpl(VirtualMachine aVm, long aRef)75     protected ReferenceTypeImpl(VirtualMachine aVm, long aRef) {
76         super(aVm);
77         ref = aRef;
78         genericSignatureGotten = false;
79     }
80 
noticeRedefineClass()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 
getMethodMirror(long ref)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 
getFieldMirror(long ref)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 
equals(Object obj)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 
hashCode()136     public int hashCode() {
137         return(int)ref();
138     }
139 
compareTo(ReferenceType object)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 
signature()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 
genericSignature()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 
classLoader()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 
isPublic()222     public boolean isPublic() {
223         if (modifiers == -1)
224             getModifiers();
225 
226         return((modifiers & VMModifiers.PUBLIC) > 0);
227     }
228 
isProtected()229     public boolean isProtected() {
230         if (modifiers == -1)
231             getModifiers();
232 
233         return((modifiers & VMModifiers.PROTECTED) > 0);
234     }
235 
isPrivate()236     public boolean isPrivate() {
237         if (modifiers == -1)
238             getModifiers();
239 
240         return((modifiers & VMModifiers.PRIVATE) > 0);
241     }
242 
isPackagePrivate()243     public boolean isPackagePrivate() {
244         return !isPublic() && !isPrivate() && !isProtected();
245     }
246 
isAbstract()247     public boolean isAbstract() {
248         if (modifiers == -1)
249             getModifiers();
250 
251         return((modifiers & VMModifiers.ABSTRACT) > 0);
252     }
253 
isFinal()254     public boolean isFinal() {
255         if (modifiers == -1)
256             getModifiers();
257 
258         return((modifiers & VMModifiers.FINAL) > 0);
259     }
260 
isStatic()261     public boolean isStatic() {
262         if (modifiers == -1)
263             getModifiers();
264 
265         return((modifiers & VMModifiers.STATIC) > 0);
266     }
267 
isPrepared()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 
isVerified()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 
isInitialized()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 
failedToInitialize()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 
fields()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 
inheritedTypes()352     abstract List<? extends ReferenceType> inheritedTypes();
353 
addVisibleFields(List<Field> visibleList, Map<String, Field> visibleTable, List<String> ambiguousNames)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 
visibleFields()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 
addAllFields(List<Field> fieldList, Set<ReferenceType> typeSet)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     }
allFields()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 
fieldByName(String fieldName)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 
methods()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 
methods1_4()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      */
addToMethodMap(Map<String, Method> methodMap, List<Method> methodList)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 
addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces)514     abstract void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces);
515 
visibleMethods()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, new HashSet<InterfaceType>());
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 
allMethods()536     abstract public List<Method> allMethods();
537 
methodsByName(String name)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 
methodsByName(String name, String signature)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 
getInterfaces()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 
nestedTypes()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 
getValue(Field sig)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 
validateFieldAccess(Field field)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 
validateFieldSet(Field field)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      */
getValues(List<? extends Field> theFields)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 
classObject()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 
stratum(String stratumID)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 
sourceName()693     public String sourceName() throws AbsentInformationException {
694         return sourceNames(vm.getDefaultStratum()).get(0);
695     }
696 
sourceNames(String stratumID)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 
sourcePaths(String stratumID)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 
baseSourceName()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 
baseSourcePath()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 
baseSourceDir()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 
sourceDebugExtension()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 
sourceDebugExtensionInfo()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 
availableStrata()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      */
defaultStratum()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 
modifiers()829     public int modifiers() {
830         if (modifiers == -1)
831             getModifiers();
832 
833         return modifiers;
834     }
835 
allLineLocations()836     public List<Location> allLineLocations()
837                             throws AbsentInformationException {
838         return allLineLocations(vm.getDefaultStratum(), null);
839     }
840 
allLineLocations(String stratumID, String sourceName)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 
locationsOfLine(int lineNumber)867     public List<Location> locationsOfLine(int lineNumber)
868                            throws AbsentInformationException {
869         return locationsOfLine(vm.getDefaultStratum(),
870                                null,
871                                lineNumber);
872     }
873 
locationsOfLine(String stratumID, String sourceName, int lineNumber)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 
instances(long maxInstances)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 
getClassFileVersion()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 
majorVersion()960     public int majorVersion() {
961         try {
962             getClassFileVersion();
963         } catch (RuntimeException exc) {
964             throw exc;
965         }
966         return majorVersion;
967     }
968 
minorVersion()969     public int minorVersion() {
970         try {
971             getClassFileVersion();
972         } catch (RuntimeException exc) {
973             throw exc;
974         }
975         return minorVersion;
976     }
977 
getConstantPoolInfo()978     private byte[] getConstantPoolInfo() {
979         JDWP.ReferenceType.ConstantPool jdwpCPool;
980         if (!vm.canGetConstantPool()) {
981             throw new UnsupportedOperationException();
982         }
983         if (constantPoolInfoGotten) {
984             if (constantPoolBytesRef == null) {
985                 return null;
986             }
987             byte[] cpbytes = constantPoolBytesRef.get();
988             if (cpbytes != null) {
989                 return cpbytes;
990             }
991         }
992 
993         try {
994             jdwpCPool = JDWP.ReferenceType.ConstantPool.process(vm, this);
995         } catch (JDWPException exc) {
996             if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
997                 constanPoolCount = 0;
998                 constantPoolBytesRef = null;
999                 constantPoolInfoGotten = true;
1000                 return null;
1001             } else {
1002                 throw exc.toJDIException();
1003             }
1004         }
1005         byte[] cpbytes;
1006         constanPoolCount = jdwpCPool.count;
1007         cpbytes = jdwpCPool.bytes;
1008         constantPoolBytesRef = new SoftReference<byte[]>(cpbytes);
1009         constantPoolInfoGotten = true;
1010         return cpbytes;
1011     }
1012 
constantPoolCount()1013     public int constantPoolCount() {
1014         try {
1015             getConstantPoolInfo();
1016         } catch (RuntimeException exc) {
1017             throw exc;
1018         }
1019         return constanPoolCount;
1020     }
1021 
constantPool()1022     public byte[] constantPool() {
1023         byte[] cpbytes;
1024         try {
1025             cpbytes = getConstantPoolInfo();
1026         } catch (RuntimeException exc) {
1027             throw exc;
1028         }
1029         if (cpbytes != null) {
1030             /*
1031              * Arrays are always modifiable, so it is a little unsafe
1032              * to return the cached bytecodes directly; instead, we
1033              * make a clone at the cost of using more memory.
1034              */
1035             return cpbytes.clone();
1036         } else {
1037             return null;
1038         }
1039     }
1040 
1041     // Does not need synchronization, since worst-case
1042     // static info is fetched twice
getModifiers()1043     void getModifiers() {
1044         if (modifiers != -1) {
1045             return;
1046         }
1047         try {
1048             modifiers = JDWP.ReferenceType.Modifiers.
1049                                   process(vm, this).modBits;
1050         } catch (JDWPException exc) {
1051             throw exc.toJDIException();
1052         }
1053     }
1054 
decodeStatus(int status)1055     void decodeStatus(int status) {
1056         this.status = status;
1057         if ((status & JDWP.ClassStatus.PREPARED) != 0) {
1058             isPrepared = true;
1059         }
1060     }
1061 
updateStatus()1062     void updateStatus() {
1063         try {
1064             decodeStatus(JDWP.ReferenceType.Status.process(vm, this).status);
1065         } catch (JDWPException exc) {
1066             throw exc.toJDIException();
1067         }
1068     }
1069 
markPrepared()1070     void markPrepared() {
1071         isPrepared = true;
1072     }
1073 
ref()1074     long ref() {
1075         return ref;
1076     }
1077 
indexOf(Method method)1078     int indexOf(Method method) {
1079         // Make sure they're all here - the obsolete method
1080         // won't be found and so will have index -1
1081         return methods().indexOf(method);
1082     }
1083 
indexOf(Field field)1084     int indexOf(Field field) {
1085         // Make sure they're all here
1086         return fields().indexOf(field);
1087     }
1088 
1089     /*
1090      * Return true if an instance of this type
1091      * can be assigned to a variable of the given type
1092      */
isAssignableTo(ReferenceType type)1093     abstract boolean isAssignableTo(ReferenceType type);
1094 
isAssignableFrom(ReferenceType type)1095     boolean isAssignableFrom(ReferenceType type) {
1096         return ((ReferenceTypeImpl)type).isAssignableTo(this);
1097     }
1098 
isAssignableFrom(ObjectReference object)1099     boolean isAssignableFrom(ObjectReference object) {
1100         return object == null ||
1101                isAssignableFrom(object.referenceType());
1102     }
1103 
setStatus(int status)1104     void setStatus(int status) {
1105         decodeStatus(status);
1106     }
1107 
setSignature(String signature)1108     void setSignature(String signature) {
1109         this.signature = signature;
1110     }
1111 
setGenericSignature(String signature)1112     void setGenericSignature(String signature) {
1113         if (signature != null && signature.length() == 0) {
1114             this.genericSignature = null;
1115         } else{
1116             this.genericSignature = signature;
1117         }
1118         this.genericSignatureGotten = true;
1119     }
1120 
isPrimitiveArray(String signature)1121     private static boolean isPrimitiveArray(String signature) {
1122         int i = signature.lastIndexOf('[');
1123         /*
1124          * TO DO: Centralize JNI signature knowledge.
1125          *
1126          * Ref:
1127          *  jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html
1128          */
1129         boolean isPA;
1130         if (i < 0) {
1131             isPA = false;
1132         } else {
1133             char c = signature.charAt(i + 1);
1134             isPA = (c != 'L');
1135         }
1136         return isPA;
1137     }
1138 
findType(String signature)1139     Type findType(String signature) throws ClassNotLoadedException {
1140         Type type;
1141         if (signature.length() == 1) {
1142             /* OTI FIX: Must be a primitive type or the void type */
1143             char sig = signature.charAt(0);
1144             if (sig == 'V') {
1145                 type = vm.theVoidType();
1146             } else {
1147                 type = vm.primitiveTypeMirror((byte)sig);
1148             }
1149         } else {
1150             // Must be a reference type.
1151             ClassLoaderReferenceImpl loader =
1152                        (ClassLoaderReferenceImpl)classLoader();
1153             if ((loader == null) ||
1154                 (isPrimitiveArray(signature)) //Work around 4450091
1155                 ) {
1156                 // Caller wants type of boot class field
1157                 type = vm.findBootType(signature);
1158             } else {
1159                 // Caller wants type of non-boot class field
1160                 type = loader.findType(signature);
1161             }
1162         }
1163         return type;
1164     }
1165 
loaderString()1166     String loaderString() {
1167         if (classLoader() != null) {
1168             return "loaded by " + classLoader().toString();
1169         } else {
1170             return "no class loader";
1171         }
1172     }
1173 
1174 }
1175