001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdi.internal;
011:
012: import java.io.ByteArrayOutputStream;
013: import java.io.DataInputStream;
014: import java.io.DataOutputStream;
015: import java.io.IOException;
016: import java.util.ArrayList;
017: import java.util.HashMap;
018: import java.util.Iterator;
019: import java.util.List;
020: import java.util.Map;
021:
022: import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
023: import org.eclipse.jdi.internal.jdwp.JdwpID;
024: import org.eclipse.jdi.internal.jdwp.JdwpObjectID;
025: import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
026:
027: import com.sun.jdi.ArrayType;
028: import com.sun.jdi.ClassNotLoadedException;
029: import com.sun.jdi.Field;
030: import com.sun.jdi.IncompatibleThreadStateException;
031: import com.sun.jdi.InternalException;
032: import com.sun.jdi.InvalidTypeException;
033: import com.sun.jdi.InvocationException;
034: import com.sun.jdi.Method;
035: import com.sun.jdi.ObjectCollectedException;
036: import com.sun.jdi.ObjectReference;
037: import com.sun.jdi.ReferenceType;
038: import com.sun.jdi.ThreadReference;
039: import com.sun.jdi.Type;
040: import com.sun.jdi.VMDisconnectedException;
041: import com.sun.jdi.Value;
042:
043: /**
044: * this class implements the corresponding interfaces
045: * declared by the JDI specification. See the com.sun.jdi package
046: * for more information.
047: *
048: */
049: public class ObjectReferenceImpl extends ValueImpl implements
050: ObjectReference {
051: /** JDWP Tag. */
052: public static final byte tag = JdwpID.OBJECT_TAG;
053:
054: /** ObjectID of object that corresponds to this reference. */
055: private JdwpObjectID fObjectID;
056: /**
057: * Cached reference type. This value is safe for caching because
058: * the type of an object never changes.
059: */
060: private ReferenceType fReferenceType;
061:
062: /**
063: * Creates new ObjectReferenceImpl.
064: */
065: public ObjectReferenceImpl(VirtualMachineImpl vmImpl,
066: JdwpObjectID objectID) {
067: this ("ObjectReference", vmImpl, objectID); //$NON-NLS-1$
068: }
069:
070: /**
071: * Creates new ObjectReferenceImpl.
072: */
073: public ObjectReferenceImpl(String description,
074: VirtualMachineImpl vmImpl, JdwpObjectID objectID) {
075: super (description, vmImpl);
076: fObjectID = objectID;
077: }
078:
079: /**
080: * @returns tag.
081: */
082: public byte getTag() {
083: return tag;
084: }
085:
086: /**
087: * @return Returns Jdwp Object ID.
088: */
089: public JdwpObjectID getObjectID() {
090: return fObjectID;
091: }
092:
093: /**
094: * Prevents garbage collection for this object.
095: */
096: public void disableCollection() {
097: initJdwpRequest();
098: try {
099: JdwpReplyPacket replyPacket = requestVM(
100: JdwpCommandPacket.OR_DISABLE_COLLECTION, this );
101: defaultReplyErrorHandler(replyPacket.errorCode());
102: } finally {
103: handledJdwpRequest();
104: }
105: }
106:
107: /**
108: * Permits garbage collection for this object.
109: */
110: public void enableCollection() {
111: initJdwpRequest();
112: try {
113: JdwpReplyPacket replyPacket = requestVM(
114: JdwpCommandPacket.OR_ENABLE_COLLECTION, this );
115: defaultReplyErrorHandler(replyPacket.errorCode());
116: } finally {
117: handledJdwpRequest();
118: }
119: }
120:
121: /**
122: * Inner class used to return monitor info.
123: */
124: private class MonitorInfo {
125: ThreadReferenceImpl owner;
126: int entryCount;
127: ArrayList waiters;
128: }
129:
130: /**
131: * @return Returns monitor info.
132: */
133: private MonitorInfo monitorInfo()
134: throws IncompatibleThreadStateException {
135: if (!virtualMachine().canGetMonitorInfo()) {
136: throw new UnsupportedOperationException();
137: }
138: // Note that this information should not be cached.
139: initJdwpRequest();
140: try {
141: JdwpReplyPacket replyPacket = requestVM(
142: JdwpCommandPacket.OR_MONITOR_INFO, this );
143: switch (replyPacket.errorCode()) {
144: case JdwpReplyPacket.INVALID_THREAD:
145: throw new IncompatibleThreadStateException();
146: case JdwpReplyPacket.THREAD_NOT_SUSPENDED:
147: throw new IncompatibleThreadStateException();
148: }
149:
150: defaultReplyErrorHandler(replyPacket.errorCode());
151:
152: DataInputStream replyData = replyPacket.dataInStream();
153: MonitorInfo result = new MonitorInfo();
154: result.owner = ThreadReferenceImpl.read(this , replyData);
155: result.entryCount = readInt("entry count", replyData); //$NON-NLS-1$
156: int nrOfWaiters = readInt("nr of waiters", replyData); //$NON-NLS-1$
157: result.waiters = new ArrayList(nrOfWaiters);
158: for (int i = 0; i < nrOfWaiters; i++)
159: result.waiters.add(ThreadReferenceImpl.read(this ,
160: replyData));
161: return result;
162: } catch (IOException e) {
163: defaultIOExceptionHandler(e);
164: return null;
165: } finally {
166: handledJdwpRequest();
167: }
168: }
169:
170: /**
171: * @return Returns an ThreadReference for the thread, if any, which currently owns this object's monitor.
172: */
173: public ThreadReference owningThread()
174: throws IncompatibleThreadStateException {
175: return monitorInfo().owner;
176: }
177:
178: /**
179: * @return Returns the number times this object's monitor has been entered by the current owning thread.
180: */
181: public int entryCount() throws IncompatibleThreadStateException {
182: return monitorInfo().entryCount;
183: }
184:
185: /**
186: * @return Returns a List containing a ThreadReference for each thread currently waiting for this object's monitor.
187: */
188: public List waitingThreads()
189: throws IncompatibleThreadStateException {
190: return monitorInfo().waiters;
191: }
192:
193: /**
194: * @return Returns the value of a given instance or static field in this object.
195: */
196: public Value getValue(Field field) {
197: ArrayList list = new ArrayList(1);
198: list.add(field);
199: return (ValueImpl) getValues(list).get(field);
200: }
201:
202: /**
203: * @return Returns objects that directly reference this object.
204: * Only objects that are reachable for the purposes of garbage collection are returned.
205: * Note that an object can also be referenced in other ways, such as from a local variable in a stack frame, or from a JNI global reference. Such non-object referrers are not returned by this method.
206: *
207: * @since 3.3
208: */
209: public List referringObjects(long maxReferrers)
210: throws UnsupportedOperationException,
211: IllegalArgumentException {
212: try {
213: int max = (int) maxReferrers;
214: if (maxReferrers >= Integer.MAX_VALUE) {
215: max = Integer.MAX_VALUE;
216: }
217: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
218: DataOutputStream outData = new DataOutputStream(outBytes);
219: this .getObjectID().write(outData);
220: writeInt(max, "max referrers", outData); //$NON-NLS-1$
221:
222: JdwpReplyPacket replyPacket = requestVM(
223: JdwpCommandPacket.OR_REFERRING_OBJECTS, outBytes);
224: switch (replyPacket.errorCode()) {
225: case JdwpReplyPacket.NOT_IMPLEMENTED:
226: throw new UnsupportedOperationException(
227: JDIMessages.ReferenceTypeImpl_27);
228: case JdwpReplyPacket.ILLEGAL_ARGUMENT:
229: throw new IllegalArgumentException(
230: JDIMessages.ReferenceTypeImpl_26);
231: case JdwpReplyPacket.INVALID_OBJECT:
232: throw new ObjectCollectedException(
233: JDIMessages.ObjectReferenceImpl_object_not_known);
234: case JdwpReplyPacket.VM_DEAD:
235: throw new VMDisconnectedException(JDIMessages.vm_dead);
236: }
237: defaultReplyErrorHandler(replyPacket.errorCode());
238:
239: DataInputStream replyData = replyPacket.dataInStream();
240: int elements = readInt("elements", replyData); //$NON-NLS-1$
241: if (max > 0 && elements > max) {
242: elements = max;
243: }
244: ArrayList list = new ArrayList();
245: for (int i = 0; i < elements; i++) {
246: list.add(ValueImpl.readWithTag(this , replyData));
247: }
248: return list;
249: } catch (IOException e) {
250: defaultIOExceptionHandler(e);
251: return null;
252: } finally {
253: handledJdwpRequest();
254: }
255: }
256:
257: /**
258: * @return Returns the value of multiple instance and/or static fields in this object.
259: */
260: public Map getValues(List allFields) {
261: // if the field list is empty, nothing to do.
262: if (allFields.isEmpty()) {
263: return new HashMap();
264: }
265: // Note that this information should not be cached.
266: initJdwpRequest();
267: try {
268: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
269: DataOutputStream outData = new DataOutputStream(outBytes);
270:
271: /*
272: * Distinguish static fields from non-static fields:
273: * For static fields ReferenceTypeImpl.getValues() must be used.
274: */
275: List staticFields = new ArrayList();
276: List nonStaticFields = new ArrayList();
277:
278: // Separate static and non-static fields.
279: int allFieldsSize = allFields.size();
280: for (int i = 0; i < allFieldsSize; i++) {
281: FieldImpl field = (FieldImpl) allFields.get(i);
282: checkVM(field);
283: if (field.isStatic())
284: staticFields.add(field);
285: else
286: nonStaticFields.add(field);
287: }
288:
289: // First get values for the static fields.
290: Map resultMap;
291: if (staticFields.isEmpty()) {
292: resultMap = new HashMap();
293: } else {
294: resultMap = referenceType().getValues(staticFields);
295: }
296:
297: // if no non-static fields are requested, return directly the result.
298: if (nonStaticFields.isEmpty()) {
299: return resultMap;
300: }
301: // Then get the values for the non-static fields.
302: int nonStaticFieldsSize = nonStaticFields.size();
303: write(this , outData);
304: writeInt(nonStaticFieldsSize, "size", outData); //$NON-NLS-1$
305: for (int i = 0; i < nonStaticFieldsSize; i++) {
306: FieldImpl field = (FieldImpl) nonStaticFields.get(i);
307: field.write(this , outData);
308: }
309:
310: JdwpReplyPacket replyPacket = requestVM(
311: JdwpCommandPacket.OR_GET_VALUES, outBytes);
312: defaultReplyErrorHandler(replyPacket.errorCode());
313:
314: DataInputStream replyData = replyPacket.dataInStream();
315: int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
316: if (nrOfElements != nonStaticFieldsSize)
317: throw new InternalError(
318: JDIMessages.ObjectReferenceImpl_Retrieved_a_different_number_of_values_from_the_VM_than_requested_1);
319:
320: for (int i = 0; i < nrOfElements; i++) {
321: resultMap.put(nonStaticFields.get(i), ValueImpl
322: .readWithTag(this , replyData));
323: }
324: return resultMap;
325: } catch (IOException e) {
326: defaultIOExceptionHandler(e);
327: return null;
328: } finally {
329: handledJdwpRequest();
330: }
331: }
332:
333: /**
334: * @return Returns the hash code value.
335: */
336: public int hashCode() {
337: return fObjectID.hashCode();
338: }
339:
340: /**
341: * @return Returns true if two mirrors refer to the same entity in the target VM.
342: * @see java.lang.Object#equals(Object)
343: */
344: public boolean equals(Object object) {
345:
346: return object != null
347: && object.getClass().equals(this .getClass())
348: && fObjectID
349: .equals(((ObjectReferenceImpl) object).fObjectID)
350: && virtualMachine().equals(
351: ((MirrorImpl) object).virtualMachine());
352: }
353:
354: /**
355: * @return Returns Jdwp version of given options.
356: */
357: private int optionsToJdwpOptions(int options) {
358: int jdwpOptions = 0;
359: if ((options & INVOKE_SINGLE_THREADED) != 0) {
360: jdwpOptions |= MethodImpl.INVOKE_SINGLE_THREADED_JDWP;
361: }
362: if ((options & INVOKE_NONVIRTUAL) != 0) {
363: jdwpOptions |= MethodImpl.INVOKE_NONVIRTUAL_JDWP;
364: }
365: return jdwpOptions;
366: }
367:
368: /**
369: * Invokes the specified static Method in the target VM.
370: * @return Returns a Value mirror of the invoked method's return value.
371: */
372: public Value invokeMethod(ThreadReference thread, Method method,
373: List arguments, int options) throws InvalidTypeException,
374: ClassNotLoadedException, IncompatibleThreadStateException,
375: InvocationException {
376: checkVM(thread);
377: checkVM(method);
378: ThreadReferenceImpl threadImpl = (ThreadReferenceImpl) thread;
379: MethodImpl methodImpl = (MethodImpl) method;
380:
381: // Perform some checks for IllegalArgumentException.
382: if (!isAValidMethod(method))
383: throw new IllegalArgumentException(
384: JDIMessages.ObjectReferenceImpl_Class_does_not_contain_given_method_2);
385: if (method.argumentTypeNames().size() != arguments.size())
386: throw new IllegalArgumentException(
387: JDIMessages.ObjectReferenceImpl_Number_of_arguments_doesn__t_match_3);
388: if (method.isConstructor() || method.isStaticInitializer())
389: throw new IllegalArgumentException(
390: JDIMessages.ObjectReferenceImpl_Method_is_constructor_or_intitializer_4);
391: if ((options & INVOKE_NONVIRTUAL) != 0 && method.isAbstract())
392: throw new IllegalArgumentException(
393: JDIMessages.ObjectReferenceImpl_Method_is_abstract_and_can_therefore_not_be_invoked_nonvirtual_5);
394:
395: // check the type and the vm of the argument, convert the value if needed.
396: List checkedArguments = ValueImpl.checkValues(arguments, method
397: .argumentTypes(), virtualMachineImpl());
398:
399: initJdwpRequest();
400: try {
401: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
402: DataOutputStream outData = new DataOutputStream(outBytes);
403: write(this , outData);
404: threadImpl.write(this , outData);
405: ((ReferenceTypeImpl) referenceType()).write(this , outData);
406: methodImpl.write(this , outData);
407:
408: writeInt(checkedArguments.size(), "size", outData); //$NON-NLS-1$
409: Iterator iter = checkedArguments.iterator();
410: while (iter.hasNext()) {
411: ValueImpl elt = (ValueImpl) iter.next();
412: if (elt != null) {
413: elt.writeWithTag(this , outData);
414: } else {
415: ValueImpl.writeNullWithTag(this , outData);
416: }
417: }
418:
419: writeInt(optionsToJdwpOptions(options),
420: "options", MethodImpl.getInvokeOptions(), outData); //$NON-NLS-1$
421:
422: JdwpReplyPacket replyPacket = requestVM(
423: JdwpCommandPacket.OR_INVOKE_METHOD, outBytes);
424: switch (replyPacket.errorCode()) {
425: case JdwpReplyPacket.TYPE_MISMATCH:
426: throw new InvalidTypeException();
427: case JdwpReplyPacket.INVALID_CLASS:
428: throw new ClassNotLoadedException(
429: JDIMessages.ObjectReferenceImpl_One_of_the_arguments_of_ObjectReference_invokeMethod___6);
430: case JdwpReplyPacket.INVALID_THREAD:
431: throw new IncompatibleThreadStateException();
432: case JdwpReplyPacket.THREAD_NOT_SUSPENDED:
433: throw new IncompatibleThreadStateException();
434: case JdwpReplyPacket.INVALID_TYPESTATE:
435: throw new IncompatibleThreadStateException();
436: }
437: defaultReplyErrorHandler(replyPacket.errorCode());
438: DataInputStream replyData = replyPacket.dataInStream();
439: ValueImpl value = ValueImpl.readWithTag(this , replyData);
440: ObjectReferenceImpl exception = ObjectReferenceImpl
441: .readObjectRefWithTag(this , replyData);
442: if (exception != null)
443: throw new InvocationException(exception);
444: return value;
445: } catch (IOException e) {
446: defaultIOExceptionHandler(e);
447: return null;
448: } finally {
449: handledJdwpRequest();
450: }
451: }
452:
453: private boolean isAValidMethod(Method method) {
454: ReferenceType refType = referenceType();
455: if (refType instanceof ArrayType) {
456: // if the object is an array, check if the method is declared in java.lang.Object
457: return "java.lang.Object".equals(method.declaringType().name()); //$NON-NLS-1$
458: }
459: return refType.allMethods().contains(method);
460: }
461:
462: /**
463: * @return Returns if this object has been garbage collected in the target VM.
464: */
465: public boolean isCollected() {
466: // Note that this information should not be cached.
467: initJdwpRequest();
468: try {
469: JdwpReplyPacket replyPacket = requestVM(
470: JdwpCommandPacket.OR_IS_COLLECTED, this );
471: switch (replyPacket.errorCode()) {
472: case JdwpReplyPacket.INVALID_OBJECT:
473: return true;
474: case JdwpReplyPacket.NOT_IMPLEMENTED:
475: // Workaround for problem in J2ME WTK (wireless toolkit)
476: // @see Bug 12966
477: try {
478: referenceType();
479: } catch (ObjectCollectedException e) {
480: return true;
481: }
482: return false;
483: default:
484: defaultReplyErrorHandler(replyPacket.errorCode());
485: break;
486: }
487: DataInputStream replyData = replyPacket.dataInStream();
488: boolean result = readBoolean("is collected", replyData); //$NON-NLS-1$
489: return result;
490: } catch (IOException e) {
491: defaultIOExceptionHandler(e);
492: return false;
493: } finally {
494: handledJdwpRequest();
495: }
496: }
497:
498: /**
499: * @return Returns the ReferenceType that mirrors the type of this object.
500: */
501: public ReferenceType referenceType() {
502: if (fReferenceType != null) {
503: return fReferenceType;
504: }
505: initJdwpRequest();
506: try {
507: JdwpReplyPacket replyPacket = requestVM(
508: JdwpCommandPacket.OR_REFERENCE_TYPE, this );
509: defaultReplyErrorHandler(replyPacket.errorCode());
510: DataInputStream replyData = replyPacket.dataInStream();
511: fReferenceType = ReferenceTypeImpl.readWithTypeTag(this ,
512: replyData);
513: return fReferenceType;
514: } catch (IOException e) {
515: defaultIOExceptionHandler(e);
516: return null;
517: } finally {
518: handledJdwpRequest();
519: }
520: }
521:
522: /**
523: * @return Returns the Type that mirrors the type of this object.
524: */
525: public Type type() {
526: return referenceType();
527: }
528:
529: /**
530: * Sets the value of a given instance or static field in this object.
531: */
532: public void setValue(Field field, Value value)
533: throws InvalidTypeException, ClassNotLoadedException {
534: // Note that this information should not be cached.
535: initJdwpRequest();
536: try {
537: ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
538: DataOutputStream outData = new DataOutputStream(outBytes);
539: write(this , outData);
540: writeInt(1, "size", outData); // We only set one field //$NON-NLS-1$
541: checkVM(field);
542: ((FieldImpl) field).write(this , outData);
543:
544: // check the type and the vm of the value. Convert the value if needed
545: ValueImpl checkedValue = ValueImpl.checkValue(value, field
546: .type(), virtualMachineImpl());
547:
548: if (checkedValue != null) {
549: checkedValue.write(this , outData);
550: } else {
551: ValueImpl.writeNull(this , outData);
552: }
553:
554: JdwpReplyPacket replyPacket = requestVM(
555: JdwpCommandPacket.OR_SET_VALUES, outBytes);
556: switch (replyPacket.errorCode()) {
557: case JdwpReplyPacket.TYPE_MISMATCH:
558: throw new InvalidTypeException();
559: case JdwpReplyPacket.INVALID_CLASS:
560: throw new ClassNotLoadedException(referenceType()
561: .name());
562: }
563: defaultReplyErrorHandler(replyPacket.errorCode());
564: } catch (IOException e) {
565: defaultIOExceptionHandler(e);
566: } finally {
567: handledJdwpRequest();
568: }
569: }
570:
571: /**
572: * @return Returns a unique identifier for this ObjectReference.
573: */
574: public long uniqueID() {
575: return fObjectID.value();
576: }
577:
578: /**
579: * @return Returns string with value of ID.
580: */
581: public String idString() {
582: return "(id=" + fObjectID + ")"; //$NON-NLS-1$ //$NON-NLS-2$
583: }
584:
585: /**
586: * @return Returns description of Mirror object.
587: */
588: public String toString() {
589: try {
590: return type().toString() + " " + idString(); //$NON-NLS-1$
591: } catch (ObjectCollectedException e) {
592: return JDIMessages.ObjectReferenceImpl__Garbage_Collected__ObjectReference__8
593: + idString();
594: } catch (Exception e) {
595: return fDescription;
596: }
597: }
598:
599: /**
600: * @return Reads JDWP representation and returns new instance.
601: */
602: public static ObjectReferenceImpl readObjectRefWithoutTag(
603: MirrorImpl target, DataInputStream in) throws IOException {
604: VirtualMachineImpl vmImpl = target.virtualMachineImpl();
605: JdwpObjectID ID = new JdwpObjectID(vmImpl);
606: ID.read(in);
607: if (target.fVerboseWriter != null)
608: target.fVerboseWriter
609: .println("objectReference", ID.value()); //$NON-NLS-1$
610:
611: if (ID.isNull())
612: return null;
613:
614: ObjectReferenceImpl mirror = new ObjectReferenceImpl(vmImpl, ID);
615: return mirror;
616: }
617:
618: /**
619: * @return Reads JDWP representation and returns new instance.
620: */
621: public static ObjectReferenceImpl readObjectRefWithTag(
622: MirrorImpl target, DataInputStream in) throws IOException {
623: byte objectTag = target.readByte(
624: "object tag", JdwpID.tagMap(), in); //$NON-NLS-1$
625: switch (objectTag) {
626: case 0:
627: return null;
628: case ObjectReferenceImpl.tag:
629: return ObjectReferenceImpl.readObjectRefWithoutTag(target,
630: in);
631: case ArrayReferenceImpl.tag:
632: return ArrayReferenceImpl.read(target, in);
633: case ClassLoaderReferenceImpl.tag:
634: return ClassLoaderReferenceImpl.read(target, in);
635: case ClassObjectReferenceImpl.tag:
636: return ClassObjectReferenceImpl.read(target, in);
637: case StringReferenceImpl.tag:
638: return StringReferenceImpl.read(target, in);
639: case ThreadGroupReferenceImpl.tag:
640: return ThreadGroupReferenceImpl.read(target, in);
641: case ThreadReferenceImpl.tag:
642: return ThreadReferenceImpl.read(target, in);
643: }
644: throw new InternalException(
645: JDIMessages.ObjectReferenceImpl_Invalid_ObjectID_tag_encountered___9
646: + objectTag);
647: }
648:
649: /**
650: * Writes JDWP representation without tag.
651: */
652: public void write(MirrorImpl target, DataOutputStream out)
653: throws IOException {
654: fObjectID.write(out);
655: if (target.fVerboseWriter != null)
656: target.fVerboseWriter.println(
657: "objectReference", fObjectID.value()); //$NON-NLS-1$
658: }
659: }
|