blob: 4af0e8e8a45939900bf4fc15747a7f2cb504dcca [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.tools.jdi;
27
28import com.sun.jdi.*;
29
30import java.util.*;
31import java.lang.ref.SoftReference;
32
33public abstract class ReferenceTypeImpl extends TypeImpl
34implements 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 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 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 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 List<Field> list = visibleFields();
356 Iterator iter = list.iterator();
357 while (iter.hasNext()) {
358 Field field = (Field)iter.next();
359 String name = field.name();
360 if (!ambiguousNames.contains(name)) {
361 Field duplicate = (Field)visibleTable.get(name);
362 if (duplicate == null) {
363 visibleList.add(field);
364 visibleTable.put(name, field);
365 } else if (!field.equals(duplicate)) {
366 ambiguousNames.add(name);
367 visibleTable.remove(name);
368 visibleList.remove(duplicate);
369 } else {
370 // identical field from two branches; do nothing
371 }
372 }
373 }
374 }
375
376 public List<Field> visibleFields() {
377 /*
378 * Maintain two different collections of visible fields. The
379 * list maintains a reasonable order for return. The
380 * hash map provides an efficient way to lookup visible fields
381 * by name, important for finding hidden or ambiguous fields.
382 */
383 List<Field> visibleList = new ArrayList<Field>();
384 Map<String, Field> visibleTable = new HashMap<String, Field>();
385
386 /* Track fields removed from above collection due to ambiguity */
387 List<String> ambiguousNames = new ArrayList<String>();
388
389 /* Add inherited, visible fields */
390 List<? extends ReferenceType> types = inheritedTypes();
391 Iterator iter = types.iterator();
392 while (iter.hasNext()) {
393 /*
394 * TO DO: Be defensive and check for cyclic interface inheritance
395 */
396 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
397 type.addVisibleFields(visibleList, visibleTable, ambiguousNames);
398 }
399
400 /*
401 * Insert fields from this type, removing any inherited fields they
402 * hide.
403 */
404 List<Field> retList = new ArrayList<Field>(fields());
405 iter = retList.iterator();
406 while (iter.hasNext()) {
407 Field field = (Field)iter.next();
408 Field hidden = (Field)visibleTable.get(field.name());
409 if (hidden != null) {
410 visibleList.remove(hidden);
411 }
412 }
413 retList.addAll(visibleList);
414 return retList;
415 }
416
417 void addAllFields(List<Field> fieldList, Set<ReferenceType> typeSet) {
418 /* Continue the recursion only if this type is new */
419 if (!typeSet.contains(this)) {
420 typeSet.add((ReferenceType)this);
421
422 /* Add local fields */
423 fieldList.addAll(fields());
424
425 /* Add inherited fields */
426 List<? extends ReferenceType> types = inheritedTypes();
427 Iterator iter = types.iterator();
428 while (iter.hasNext()) {
429 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
430 type.addAllFields(fieldList, typeSet);
431 }
432 }
433 }
434 public List<Field> allFields() {
435 List<Field> fieldList = new ArrayList<Field>();
436 Set<ReferenceType> typeSet = new HashSet<ReferenceType>();
437 addAllFields(fieldList, typeSet);
438 return fieldList;
439 }
440
441 public Field fieldByName(String fieldName) {
442 java.util.List searchList;
443 Field f;
444
445 searchList = visibleFields();
446
447 for (int i=0; i<searchList.size(); i++) {
448 f = (Field)searchList.get(i);
449
450 if (f.name().equals(fieldName)) {
451 return f;
452 }
453 }
454 //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name());
455 return null;
456 }
457
458 public List<Method> methods() {
459 List<Method> methods = (methodsRef == null) ? null : methodsRef.get();
460 if (methods == null) {
461 if (!vm.canGet1_5LanguageFeatures()) {
462 methods = methods1_4();
463 } else {
464 JDWP.ReferenceType.MethodsWithGeneric.MethodInfo[] declared;
465 try {
466 declared = JDWP.ReferenceType.MethodsWithGeneric.
467 process(vm, this).declared;
468 } catch (JDWPException exc) {
469 throw exc.toJDIException();
470 }
471 methods = new ArrayList<Method>(declared.length);
472 for (int i=0; i<declared.length; i++) {
473 JDWP.ReferenceType.MethodsWithGeneric.MethodInfo
474 mi = declared[i];
475
476 Method method = MethodImpl.createMethodImpl(vm, this,
477 mi.methodID,
478 mi.name, mi.signature,
479 mi.genericSignature,
480 mi.modBits);
481 methods.add(method);
482 }
483 }
484 methods = Collections.unmodifiableList(methods);
485 methodsRef = new SoftReference<List<Method>>(methods);
486 }
487 return methods;
488 }
489
490 private List<Method> methods1_4() {
491 List<Method> methods;
492 JDWP.ReferenceType.Methods.MethodInfo[] declared;
493 try {
494 declared = JDWP.ReferenceType.Methods.
495 process(vm, this).declared;
496 } catch (JDWPException exc) {
497 throw exc.toJDIException();
498 }
499 methods = new ArrayList<Method>(declared.length);
500 for (int i=0; i<declared.length; i++) {
501 JDWP.ReferenceType.Methods.MethodInfo mi = declared[i];
502
503 Method method = MethodImpl.createMethodImpl(vm, this,
504 mi.methodID,
505 mi.name, mi.signature,
506 null,
507 mi.modBits);
508 methods.add(method);
509 }
510 return methods;
511 }
512
513 /*
514 * Utility method used by subclasses to build lists of visible
515 * methods.
516 */
517 void addToMethodMap(Map<String, Method> methodMap, List<Method> methodList) {
518 Iterator iter = methodList.iterator();
519 while (iter.hasNext()) {
520 Method method = (Method)iter.next();
521 methodMap.put(method.name().concat(method.signature()), method);
522 }
523 }
524
525 abstract void addVisibleMethods(Map<String, Method> methodMap);
526
527 public List<Method> visibleMethods() {
528 /*
529 * Build a collection of all visible methods. The hash
530 * map allows us to do this efficiently by keying on the
531 * concatenation of name and signature.
532 */
533 Map<String, Method> map = new HashMap<String, Method>();
534 addVisibleMethods(map);
535
536 /*
537 * ... but the hash map destroys order. Methods should be
538 * returned in a sensible order, as they are in allMethods().
539 * So, start over with allMethods() and use the hash map
540 * to filter that ordered collection.
541 */
542 List<Method> list = allMethods();
543 list.retainAll(map.values());
544 return list;
545 }
546
547 abstract public List<Method> allMethods();
548
549 public List<Method> methodsByName(String name) {
550 List<Method> methods = visibleMethods();
551 ArrayList<Method> retList = new ArrayList<Method>(methods.size());
552 Iterator iter = methods.iterator();
553 while (iter.hasNext()) {
554 Method candidate = (Method)iter.next();
555 if (candidate.name().equals(name)) {
556 retList.add(candidate);
557 }
558 }
559 retList.trimToSize();
560 return retList;
561 }
562
563 public List<Method> methodsByName(String name, String signature) {
564 List<Method> methods = visibleMethods();
565 ArrayList<Method> retList = new ArrayList<Method>(methods.size());
566 Iterator iter = methods.iterator();
567 while (iter.hasNext()) {
568 Method candidate = (Method)iter.next();
569 if (candidate.name().equals(name) &&
570 candidate.signature().equals(signature)) {
571 retList.add(candidate);
572 }
573 }
574 retList.trimToSize();
575 return retList;
576 }
577
578 List<InterfaceType> getInterfaces() {
579 InterfaceTypeImpl[] intfs;
580 try {
581 intfs = JDWP.ReferenceType.Interfaces.
582 process(vm, this).interfaces;
583 } catch (JDWPException exc) {
584 throw exc.toJDIException();
585 }
586 return Arrays.asList((InterfaceType[])intfs);
587 }
588
589 public List<ReferenceType> nestedTypes() {
590 List all = vm.allClasses();
591 List<ReferenceType> nested = new ArrayList<ReferenceType>();
592 String outername = name();
593 int outerlen = outername.length();
594 Iterator iter = all.iterator();
595 while (iter.hasNext()) {
596 ReferenceType refType = (ReferenceType)iter.next();
597 String name = refType.name();
598 int len = name.length();
599 /* The separator is historically '$' but could also be '#' */
600 if ( len > outerlen && name.startsWith(outername) ) {
601 char c = name.charAt(outerlen);
602 if ( c =='$' || c== '#' ) {
603 nested.add(refType);
604 }
605 }
606 }
607 return nested;
608 }
609
610 public Value getValue(Field sig) {
611 List<Field> list = new ArrayList<Field>(1);
612 list.add(sig);
613 Map map = getValues(list);
614 return(Value)map.get(sig);
615 }
616
617
618 void validateFieldAccess(Field field) {
619 /*
620 * Field must be in this object's class, a superclass, or
621 * implemented interface
622 */
623 ReferenceTypeImpl declType = (ReferenceTypeImpl)field.declaringType();
624 if (!declType.isAssignableFrom(this)) {
625 throw new IllegalArgumentException("Invalid field");
626 }
627 }
628
629 void validateFieldSet(Field field) {
630 validateFieldAccess(field);
631 if (field.isFinal()) {
632 throw new IllegalArgumentException("Cannot set value of final field");
633 }
634 }
635
636 /**
637 * Returns a map of field values
638 */
639 public Map<Field,Value> getValues(List<? extends Field> theFields) {
640 validateMirrors(theFields);
641
642 int size = theFields.size();
643 JDWP.ReferenceType.GetValues.Field[] queryFields =
644 new JDWP.ReferenceType.GetValues.Field[size];
645
646 for (int i=0; i<size; i++) {
647 FieldImpl field = (FieldImpl)theFields.get(i);
648
649 validateFieldAccess(field);
650
651 // Do more validation specific to ReferenceType field getting
652 if (!field.isStatic()) {
653 throw new IllegalArgumentException(
654 "Attempt to use non-static field with ReferenceType");
655 }
656 queryFields[i] = new JDWP.ReferenceType.GetValues.Field(
657 field.ref());
658 }
659
660 Map<Field, Value> map = new HashMap<Field, Value>(size);
661
662 ValueImpl[] values;
663 try {
664 values = JDWP.ReferenceType.GetValues.
665 process(vm, this, queryFields).values;
666 } catch (JDWPException exc) {
667 throw exc.toJDIException();
668 }
669
670 if (size != values.length) {
671 throw new InternalException(
672 "Wrong number of values returned from target VM");
673 }
674 for (int i=0; i<size; i++) {
675 FieldImpl field = (FieldImpl)theFields.get(i);
676 map.put(field, values[i]);
677 }
678
679 return map;
680 }
681
682 public ClassObjectReference classObject() {
683 if (classObject == null) {
684 // Are classObjects unique for an Object, or
685 // created each time? Is this spec'ed?
686 synchronized(this) {
687 if (classObject == null) {
688 try {
689 classObject = JDWP.ReferenceType.ClassObject.
690 process(vm, this).classObject;
691 } catch (JDWPException exc) {
692 throw exc.toJDIException();
693 }
694 }
695 }
696 }
697 return classObject;
698 }
699
700 SDE.Stratum stratum(String stratumID) {
701 SDE sde = sourceDebugExtensionInfo();
702 if (!sde.isValid()) {
703 sde = NO_SDE_INFO_MARK;
704 }
705 return sde.stratum(stratumID);
706 }
707
708 public String sourceName() throws AbsentInformationException {
709 return (String)(sourceNames(vm.getDefaultStratum()).get(0));
710 }
711
712 public List<String> sourceNames(String stratumID)
713 throws AbsentInformationException {
714 SDE.Stratum stratum = stratum(stratumID);
715 if (stratum.isJava()) {
716 List<String> result = new ArrayList<String>(1);
717 result.add(baseSourceName());
718 return result;
719 }
720 return stratum.sourceNames(this);
721 }
722
723 public List<String> sourcePaths(String stratumID)
724 throws AbsentInformationException {
725 SDE.Stratum stratum = stratum(stratumID);
726 if (stratum.isJava()) {
727 List<String> result = new ArrayList<String>(1);
728 result.add(baseSourceDir() + baseSourceName());
729 return result;
730 }
731 return stratum.sourcePaths(this);
732 }
733
734 String baseSourceName() throws AbsentInformationException {
735 String bsn = baseSourceName;
736 if (bsn == null) {
737 // Does not need synchronization, since worst-case
738 // static info is fetched twice
739 try {
740 bsn = JDWP.ReferenceType.SourceFile.
741 process(vm, this).sourceFile;
742 } catch (JDWPException exc) {
743 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
744 bsn = ABSENT_BASE_SOURCE_NAME;
745 } else {
746 throw exc.toJDIException();
747 }
748 }
749 baseSourceName = bsn;
750 }
751 if (bsn == ABSENT_BASE_SOURCE_NAME) {
752 throw new AbsentInformationException();
753 }
754 return bsn;
755 }
756
757 String baseSourcePath() throws AbsentInformationException {
758 String bsp = baseSourcePath;
759 if (bsp == null) {
760 bsp = baseSourceDir() + baseSourceName();
761 baseSourcePath = bsp;
762 }
763 return bsp;
764 }
765
766 String baseSourceDir() {
767 if (baseSourceDir == null) {
768 String typeName = name();
769 StringBuffer sb = new StringBuffer(typeName.length() + 10);
770 int index = 0;
771 int nextIndex;
772
773 while ((nextIndex = typeName.indexOf('.', index)) > 0) {
774 sb.append(typeName.substring(index, nextIndex));
775 sb.append(java.io.File.separatorChar);
776 index = nextIndex + 1;
777 }
778 baseSourceDir = sb.toString();
779 }
780 return baseSourceDir;
781 }
782
783 public String sourceDebugExtension()
784 throws AbsentInformationException {
785 if (!vm.canGetSourceDebugExtension()) {
786 throw new UnsupportedOperationException();
787 }
788 SDE sde = sourceDebugExtensionInfo();
789 if (sde == NO_SDE_INFO_MARK) {
790 throw new AbsentInformationException();
791 }
792 return sde.sourceDebugExtension;
793 }
794
795 private SDE sourceDebugExtensionInfo() {
796 if (!vm.canGetSourceDebugExtension()) {
797 return NO_SDE_INFO_MARK;
798 }
799 SDE sde = (sdeRef == null) ? null : (SDE)sdeRef.get();
800 if (sde == null) {
801 String extension = null;
802 try {
803 extension = JDWP.ReferenceType.SourceDebugExtension.
804 process(vm, this).extension;
805 } catch (JDWPException exc) {
806 if (exc.errorCode() != JDWP.Error.ABSENT_INFORMATION) {
807 sdeRef = new SoftReference<SDE>(NO_SDE_INFO_MARK);
808 throw exc.toJDIException();
809 }
810 }
811 if (extension == null) {
812 sde = NO_SDE_INFO_MARK;
813 } else {
814 sde = new SDE(extension);
815 }
816 sdeRef = new SoftReference<SDE>(sde);
817 }
818 return sde;
819 }
820
821 public List<String> availableStrata() {
822 SDE sde = sourceDebugExtensionInfo();
823 if (sde.isValid()) {
824 return sde.availableStrata();
825 } else {
826 List<String> strata = new ArrayList<String>();
827 strata.add(SDE.BASE_STRATUM_NAME);
828 return strata;
829 }
830 }
831
832 /**
833 * Always returns non-null stratumID
834 */
835 public String defaultStratum() {
836 SDE sdei = sourceDebugExtensionInfo();
837 if (sdei.isValid()) {
838 return sdei.defaultStratumId;
839 } else {
840 return SDE.BASE_STRATUM_NAME;
841 }
842 }
843
844 public int modifiers() {
845 if (modifiers == -1)
846 getModifiers();
847
848 return modifiers;
849 }
850
851 public List<Location> allLineLocations()
852 throws AbsentInformationException {
853 return allLineLocations(vm.getDefaultStratum(), null);
854 }
855
856 public List<Location> allLineLocations(String stratumID, String sourceName)
857 throws AbsentInformationException {
858 boolean someAbsent = false; // A method that should have info, didn't
859 SDE.Stratum stratum = stratum(stratumID);
860 List<Location> list = new ArrayList<Location>(); // location list
861
862 for (Iterator iter = methods().iterator(); iter.hasNext(); ) {
863 MethodImpl method = (MethodImpl)iter.next();
864 try {
865 list.addAll(
866 method.allLineLocations(stratum, sourceName));
867 } catch(AbsentInformationException exc) {
868 someAbsent = true;
869 }
870 }
871
872 // If we retrieved no line info, and at least one of the methods
873 // should have had some (as determined by an
874 // AbsentInformationException being thrown) then we rethrow
875 // the AbsentInformationException.
876 if (someAbsent && list.size() == 0) {
877 throw new AbsentInformationException();
878 }
879 return list;
880 }
881
882 public List<Location> locationsOfLine(int lineNumber)
883 throws AbsentInformationException {
884 return locationsOfLine(vm.getDefaultStratum(),
885 null,
886 lineNumber);
887 }
888
889 public List<Location> locationsOfLine(String stratumID,
890 String sourceName,
891 int lineNumber)
892 throws AbsentInformationException {
893 // A method that should have info, didn't
894 boolean someAbsent = false;
895 // A method that should have info, did
896 boolean somePresent = false;
897 List<Method> methods = methods();
898 SDE.Stratum stratum = stratum(stratumID);
899
900 List<Location> list = new ArrayList<Location>();
901
902 Iterator iter = methods.iterator();
903 while(iter.hasNext()) {
904 MethodImpl method = (MethodImpl)iter.next();
905 // eliminate native and abstract to eliminate
906 // false positives
907 if (!method.isAbstract() &&
908 !method.isNative()) {
909 try {
910 list.addAll(
911 method.locationsOfLine(stratum,
912 sourceName,
913 lineNumber));
914 somePresent = true;
915 } catch(AbsentInformationException exc) {
916 someAbsent = true;
917 }
918 }
919 }
920 if (someAbsent && !somePresent) {
921 throw new AbsentInformationException();
922 }
923 return list;
924 }
925
926 public List<ObjectReference> instances(long maxInstances) {
927 if (!vm.canGetInstanceInfo()) {
928 throw new UnsupportedOperationException(
929 "target does not support getting instances");
930 }
931
932 if (maxInstances < 0) {
933 throw new IllegalArgumentException("maxInstances is less than zero: "
934 + maxInstances);
935 }
936 int intMax = (maxInstances > Integer.MAX_VALUE)?
937 Integer.MAX_VALUE: (int)maxInstances;
938 // JDWP can't currently handle more than this (in mustang)
939
940 try {
941 return Arrays.asList(
942 (ObjectReference[])JDWP.ReferenceType.Instances.
943 process(vm, this, intMax).instances);
944 } catch (JDWPException exc) {
945 throw exc.toJDIException();
946 }
947 }
948
949 private void getClassFileVersion() {
950 if (!vm.canGetClassFileVersion()) {
951 throw new UnsupportedOperationException();
952 }
953 JDWP.ReferenceType.ClassFileVersion classFileVersion;
954 if (versionNumberGotten) {
955 return;
956 } else {
957 try {
958 classFileVersion = JDWP.ReferenceType.ClassFileVersion.process(vm, this);
959 } catch (JDWPException exc) {
960 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
961 majorVersion = 0;
962 minorVersion = 0;
963 versionNumberGotten = true;
964 return;
965 } else {
966 throw exc.toJDIException();
967 }
968 }
969 majorVersion = classFileVersion.majorVersion;
970 minorVersion = classFileVersion.minorVersion;
971 versionNumberGotten = true;
972 }
973 }
974
975 public int majorVersion() {
976 try {
977 getClassFileVersion();
978 } catch (RuntimeException exc) {
979 throw exc;
980 }
981 return majorVersion;
982 }
983
984 public int minorVersion() {
985 try {
986 getClassFileVersion();
987 } catch (RuntimeException exc) {
988 throw exc;
989 }
990 return minorVersion;
991 }
992
993 private void getConstantPoolInfo() {
994 JDWP.ReferenceType.ConstantPool jdwpCPool;
995 if (!vm.canGetConstantPool()) {
996 throw new UnsupportedOperationException();
997 }
998 if (constantPoolInfoGotten) {
999 return;
1000 } else {
1001 try {
1002 jdwpCPool = JDWP.ReferenceType.ConstantPool.process(vm, this);
1003 } catch (JDWPException exc) {
1004 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
1005 constanPoolCount = 0;
1006 constantPoolBytesRef = null;
1007 constantPoolInfoGotten = true;
1008 return;
1009 } else {
1010 throw exc.toJDIException();
1011 }
1012 }
1013 byte[] cpbytes;
1014 constanPoolCount = jdwpCPool.count;
1015 cpbytes = jdwpCPool.bytes;
1016 constantPoolBytesRef = new SoftReference<byte[]>(cpbytes);
1017 constantPoolInfoGotten = true;
1018 }
1019 }
1020
1021 public int constantPoolCount() {
1022 try {
1023 getConstantPoolInfo();
1024 } catch (RuntimeException exc) {
1025 throw exc;
1026 }
1027 return constanPoolCount;
1028 }
1029
1030 public byte[] constantPool() {
1031 try {
1032 getConstantPoolInfo();
1033 } catch (RuntimeException exc) {
1034 throw exc;
1035 }
1036 if (constantPoolBytesRef != null) {
1037 byte[] cpbytes = (byte[])constantPoolBytesRef.get();
1038 /*
1039 * Arrays are always modifiable, so it is a little unsafe
1040 * to return the cached bytecodes directly; instead, we
1041 * make a clone at the cost of using more memory.
1042 */
1043 return (byte[])cpbytes.clone();
1044 } else {
1045 return null;
1046 }
1047 }
1048
1049 // Does not need synchronization, since worst-case
1050 // static info is fetched twice
1051 void getModifiers() {
1052 if (modifiers != -1) {
1053 return;
1054 }
1055 try {
1056 modifiers = JDWP.ReferenceType.Modifiers.
1057 process(vm, this).modBits;
1058 } catch (JDWPException exc) {
1059 throw exc.toJDIException();
1060 }
1061 }
1062
1063 void decodeStatus(int status) {
1064 this.status = status;
1065 if ((status & JDWP.ClassStatus.PREPARED) != 0) {
1066 isPrepared = true;
1067 }
1068 }
1069
1070 void updateStatus() {
1071 try {
1072 decodeStatus(JDWP.ReferenceType.Status.process(vm, this).status);
1073 } catch (JDWPException exc) {
1074 throw exc.toJDIException();
1075 }
1076 }
1077
1078 void markPrepared() {
1079 isPrepared = true;
1080 }
1081
1082 long ref() {
1083 return ref;
1084 }
1085
1086 int indexOf(Method method) {
1087 // Make sure they're all here - the obsolete method
1088 // won't be found and so will have index -1
1089 return methods().indexOf(method);
1090 }
1091
1092 int indexOf(Field field) {
1093 // Make sure they're all here
1094 return fields().indexOf(field);
1095 }
1096
1097 /*
1098 * Return true if an instance of this type
1099 * can be assigned to a variable of the given type
1100 */
1101 abstract boolean isAssignableTo(ReferenceType type);
1102
1103 boolean isAssignableFrom(ReferenceType type) {
1104 return ((ReferenceTypeImpl)type).isAssignableTo(this);
1105 }
1106
1107 boolean isAssignableFrom(ObjectReference object) {
1108 return object == null ||
1109 isAssignableFrom(object.referenceType());
1110 }
1111
1112 void setStatus(int status) {
1113 decodeStatus(status);
1114 }
1115
1116 void setSignature(String signature) {
1117 this.signature = signature;
1118 }
1119
1120 void setGenericSignature(String signature) {
1121 if (signature != null && signature.length() == 0) {
1122 this.genericSignature = null;
1123 } else{
1124 this.genericSignature = signature;
1125 }
1126 this.genericSignatureGotten = true;
1127 }
1128
1129 private static boolean isPrimitiveArray(String signature) {
1130 int i = signature.lastIndexOf('[');
1131 /*
1132 * TO DO: Centralize JNI signature knowledge.
1133 *
1134 * Ref:
1135 * jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html
1136 */
1137 boolean isPA;
1138 if (i < 0) {
1139 isPA = false;
1140 } else {
1141 char c = signature.charAt(i + 1);
1142 isPA = (c != 'L');
1143 }
1144 return isPA;
1145 }
1146
1147 Type findType(String signature) throws ClassNotLoadedException {
1148 Type type;
1149 if (signature.length() == 1) {
1150 /* OTI FIX: Must be a primitive type or the void type */
1151 char sig = signature.charAt(0);
1152 if (sig == 'V') {
1153 type = vm.theVoidType();
1154 } else {
1155 type = vm.primitiveTypeMirror((byte)sig);
1156 }
1157 } else {
1158 // Must be a reference type.
1159 ClassLoaderReferenceImpl loader =
1160 (ClassLoaderReferenceImpl)classLoader();
1161 if ((loader == null) ||
1162 (isPrimitiveArray(signature)) //Work around 4450091
1163 ) {
1164 // Caller wants type of boot class field
1165 type = vm.findBootType(signature);
1166 } else {
1167 // Caller wants type of non-boot class field
1168 type = loader.findType(signature);
1169 }
1170 }
1171 return type;
1172 }
1173
1174 String loaderString() {
1175 if (classLoader() != null) {
1176 return "loaded by " + classLoader().toString();
1177 } else {
1178 return "no class loader";
1179 }
1180 }
1181
1182}