• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1998, 2015, 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.util.ArrayList;
32 
33 public class ObjectReferenceImpl extends ValueImpl
34              implements ObjectReference, VMListener {
35 
36     protected long ref;
37     private ReferenceType type = null;
38     private int gcDisableCount = 0;
39     boolean addedListener = false;
40 
41     // This is cached only while the VM is suspended
42     protected static class Cache {
43         JDWP.ObjectReference.MonitorInfo monitorInfo = null;
44     }
45 
46     private static final Cache noInitCache = new Cache();
47     private static final Cache markerCache = new Cache();
48     private Cache cache = noInitCache;
49 
disableCache()50     private void disableCache() {
51         synchronized (vm.state()) {
52             cache = null;
53         }
54     }
55 
enableCache()56     private void enableCache() {
57         synchronized (vm.state()) {
58             cache = markerCache;
59         }
60     }
61 
62     // Override in subclasses
newCache()63     protected Cache newCache() {
64         return new Cache();
65     }
66 
getCache()67     protected Cache getCache() {
68         synchronized (vm.state()) {
69             if (cache == noInitCache) {
70                 if (vm.state().isSuspended()) {
71                     // Set cache now, otherwise newly created objects are
72                     // not cached until resuspend
73                     enableCache();
74                 } else {
75                     disableCache();
76                 }
77             }
78             if (cache == markerCache) {
79                 cache = newCache();
80             }
81             return cache;
82         }
83     }
84 
85     // Return the ClassTypeImpl upon which to invoke a method.
86     // By default it is our very own referenceType() but subclasses
87     // can override.
invokableReferenceType(Method method)88     protected ClassTypeImpl invokableReferenceType(Method method) {
89         return (ClassTypeImpl)referenceType();
90     }
91 
ObjectReferenceImpl(VirtualMachine aVm,long aRef)92     ObjectReferenceImpl(VirtualMachine aVm,long aRef) {
93         super(aVm);
94 
95         ref = aRef;
96     }
97 
description()98     protected String description() {
99         return "ObjectReference " + uniqueID();
100     }
101 
102     /*
103      * VMListener implementation
104      */
vmSuspended(VMAction action)105     public boolean vmSuspended(VMAction action) {
106         enableCache();
107         return true;
108     }
109 
vmNotSuspended(VMAction action)110     public boolean vmNotSuspended(VMAction action) {
111         // make sure that cache and listener management are synchronized
112         synchronized (vm.state()) {
113             if (cache != null && (vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {
114                 vm.printTrace("Clearing temporary cache for " + description());
115             }
116             disableCache();
117             if (addedListener) {
118                 /*
119                  * If a listener was added (i.e. this is not a
120                  * ObjectReference that adds a listener on startup),
121                  * remove it here.
122                  */
123                 addedListener = false;
124                 return false;  // false says remove
125             } else {
126                 return true;
127             }
128         }
129     }
130 
equals(Object obj)131     public boolean equals(Object obj) {
132         if ((obj != null) && (obj instanceof ObjectReferenceImpl)) {
133             ObjectReferenceImpl other = (ObjectReferenceImpl)obj;
134             return (ref() == other.ref()) &&
135                    super.equals(obj);
136         } else {
137             return false;
138         }
139     }
140 
hashCode()141     public int hashCode() {
142         return(int)ref();
143     }
144 
type()145     public Type type() {
146         return referenceType();
147     }
148 
referenceType()149     public ReferenceType referenceType() {
150         if (type == null) {
151             try {
152                 JDWP.ObjectReference.ReferenceType rtinfo =
153                     JDWP.ObjectReference.ReferenceType.process(vm, this);
154                 type = vm.referenceType(rtinfo.typeID,
155                                         rtinfo.refTypeTag);
156             } catch (JDWPException exc) {
157                 throw exc.toJDIException();
158             }
159         }
160         return type;
161     }
162 
getValue(Field sig)163     public Value getValue(Field sig) {
164         List<Field> list = new ArrayList<Field>(1);
165         list.add(sig);
166         Map<Field, Value> map = getValues(list);
167         return map.get(sig);
168     }
169 
getValues(List<? extends Field> theFields)170     public Map<Field,Value> getValues(List<? extends Field> theFields) {
171         validateMirrors(theFields);
172 
173         List<Field> staticFields = new ArrayList<Field>(0);
174         int size = theFields.size();
175         List<Field> instanceFields = new ArrayList<Field>(size);
176 
177         for (int i=0; i<size; i++) {
178             Field field = (Field)theFields.get(i);
179 
180             // Make sure the field is valid
181             ((ReferenceTypeImpl)referenceType()).validateFieldAccess(field);
182 
183             // FIX ME! We need to do some sanity checking
184             // here; make sure the field belongs to this
185             // object.
186             if (field.isStatic())
187                 staticFields.add(field);
188             else {
189                 instanceFields.add(field);
190             }
191         }
192 
193         Map<Field, Value> map;
194         if (staticFields.size() > 0) {
195             map = referenceType().getValues(staticFields);
196         } else {
197             map = new HashMap<Field, Value>(size);
198         }
199 
200         size = instanceFields.size();
201 
202         JDWP.ObjectReference.GetValues.Field[] queryFields =
203                          new JDWP.ObjectReference.GetValues.Field[size];
204         for (int i=0; i<size; i++) {
205             FieldImpl field = (FieldImpl)instanceFields.get(i);/* thanks OTI */
206             queryFields[i] = new JDWP.ObjectReference.GetValues.Field(
207                                          field.ref());
208         }
209         ValueImpl[] values;
210         try {
211             values = JDWP.ObjectReference.GetValues.
212                                      process(vm, this, queryFields).values;
213         } catch (JDWPException exc) {
214             throw exc.toJDIException();
215         }
216 
217         if (size != values.length) {
218             throw new InternalException(
219                          "Wrong number of values returned from target VM");
220         }
221         for (int i=0; i<size; i++) {
222             FieldImpl field = (FieldImpl)instanceFields.get(i);
223             map.put(field, values[i]);
224         }
225 
226         return map;
227     }
228 
setValue(Field field, Value value)229     public void setValue(Field field, Value value)
230                    throws InvalidTypeException, ClassNotLoadedException {
231 
232         validateMirror(field);
233         validateMirrorOrNull(value);
234 
235         // Make sure the field is valid
236         ((ReferenceTypeImpl)referenceType()).validateFieldSet(field);
237 
238         if (field.isStatic()) {
239             ReferenceType type = referenceType();
240             if (type instanceof ClassType) {
241                 ((ClassType)type).setValue(field, value);
242                 return;
243             } else {
244                 throw new IllegalArgumentException(
245                                     "Invalid type for static field set");
246             }
247         }
248 
249         try {
250             JDWP.ObjectReference.SetValues.FieldValue[] fvals =
251                       new JDWP.ObjectReference.SetValues.FieldValue[1];
252             fvals[0] = new JDWP.ObjectReference.SetValues.FieldValue(
253                            ((FieldImpl)field).ref(),
254                            // Validate and convert if necessary
255                            ValueImpl.prepareForAssignment(value,
256                                                           (FieldImpl)field));
257             try {
258                 JDWP.ObjectReference.SetValues.process(vm, this, fvals);
259             } catch (JDWPException exc) {
260                 throw exc.toJDIException();
261             }
262         } catch (ClassNotLoadedException e) {
263             /*
264              * Since we got this exception,
265              * the field type must be a reference type. The value
266              * we're trying to set is null, but if the field's
267              * class has not yet been loaded through the enclosing
268              * class loader, then setting to null is essentially a
269              * no-op, and we should allow it without an exception.
270              */
271             if (value != null) {
272                 throw e;
273             }
274         }
275     }
276 
validateMethodInvocation(Method method, int options)277     void validateMethodInvocation(Method method, int options)
278                                          throws InvalidTypeException,
279                                          InvocationException {
280         /*
281          * Method must be in this object's class, a superclass, or
282          * implemented interface
283          */
284         ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
285         if (!declType.isAssignableFrom(this)) {
286             throw new IllegalArgumentException("Invalid method");
287         }
288 
289         if (declType instanceof ClassTypeImpl) {
290             validateClassMethodInvocation(method, options);
291         } else if (declType instanceof InterfaceTypeImpl) {
292             validateIfaceMethodInvocation(method, options);
293         } else {
294             throw new InvalidTypeException();
295         }
296     }
297 
validateClassMethodInvocation(Method method, int options)298     void validateClassMethodInvocation(Method method, int options)
299                                          throws InvalidTypeException,
300                                          InvocationException {
301 
302         ClassTypeImpl clazz = invokableReferenceType(method);
303 
304         /*
305          * Method must be a non-constructor
306          */
307         if (method.isConstructor()) {
308             throw new IllegalArgumentException("Cannot invoke constructor");
309         }
310 
311         /*
312          * For nonvirtual invokes, method must have a body
313          */
314         if (isNonVirtual(options)) {
315             if (method.isAbstract()) {
316                 throw new IllegalArgumentException("Abstract method");
317             }
318         }
319 
320         /*
321          * Get the class containing the method that will be invoked.
322          * This class is needed only for proper validation of the
323          * method argument types.
324          */
325         ClassTypeImpl invokedClass;
326         if (isNonVirtual(options)) {
327             // No overrides in non-virtual invokes
328             invokedClass = clazz;
329         } else {
330             /*
331              * For virtual invokes, find any override of the method.
332              * Since we are looking for a method with a real body, we
333              * don't need to bother with interfaces/abstract methods.
334              */
335             Method invoker = clazz.concreteMethodByName(method.name(),
336                                                         method.signature());
337             //  invoker is supposed to be non-null under normal circumstances
338             invokedClass = (ClassTypeImpl)invoker.declaringType();
339         }
340         /* The above code is left over from previous versions.
341          * We haven't had time to divine the intent.  jjh, 7/31/2003
342          */
343     }
344 
validateIfaceMethodInvocation(Method method, int options)345     void validateIfaceMethodInvocation(Method method, int options)
346                                          throws InvalidTypeException,
347                                          InvocationException {
348         /*
349          * Only default methods allowed for nonvirtual invokes
350          */
351         if (isNonVirtual(options) && !method.isDefault()) {
352             throw new IllegalArgumentException("Not a default method");
353         }
354     }
355 
sendInvokeCommand(final ThreadReferenceImpl thread, final ClassTypeImpl refType, final MethodImpl method, final ValueImpl[] args, final int options)356     PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
357                                    final ClassTypeImpl refType,
358                                    final MethodImpl method,
359                                    final ValueImpl[] args,
360                                    final int options) {
361         CommandSender sender =
362             new CommandSender() {
363                 public PacketStream send() {
364                     return JDWP.ObjectReference.InvokeMethod.enqueueCommand(
365                                           vm, ObjectReferenceImpl.this,
366                                           thread, refType,
367                                           method.ref(), args, options);
368                 }
369         };
370 
371         PacketStream stream;
372         if ((options & INVOKE_SINGLE_THREADED) != 0) {
373             stream = thread.sendResumingCommand(sender);
374         } else {
375             stream = vm.sendResumingCommand(sender);
376         }
377         return stream;
378     }
379 
invokeMethod(ThreadReference threadIntf, Method methodIntf, List<? extends Value> origArguments, int options)380     public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
381                               List<? extends Value> origArguments, int options)
382                               throws InvalidTypeException,
383                                      IncompatibleThreadStateException,
384                                      InvocationException,
385                                      ClassNotLoadedException {
386         validateMirror(threadIntf);
387         validateMirror(methodIntf);
388         validateMirrorsOrNulls(origArguments);
389 
390         MethodImpl method = (MethodImpl)methodIntf;
391         ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
392 
393         if (method.isStatic()) {
394             if (referenceType() instanceof InterfaceType) {
395                 InterfaceType type = (InterfaceType)referenceType();
396                 return type.invokeMethod(thread, method, origArguments, options);
397             } else if (referenceType() instanceof ClassType) {
398                 ClassType type = (ClassType)referenceType();
399                 return type.invokeMethod(thread, method, origArguments, options);
400             } else {
401                 throw new IllegalArgumentException("Invalid type for static method invocation");
402             }
403         }
404 
405         validateMethodInvocation(method, options);
406 
407         List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(
408                                                   origArguments);
409 
410         ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
411         JDWP.ObjectReference.InvokeMethod ret;
412         try {
413             PacketStream stream =
414                 sendInvokeCommand(thread, invokableReferenceType(method),
415                                   method, args, options);
416             ret = JDWP.ObjectReference.InvokeMethod.waitForReply(vm, stream);
417         } catch (JDWPException exc) {
418             if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
419                 throw new IncompatibleThreadStateException();
420             } else {
421                 throw exc.toJDIException();
422             }
423         }
424 
425         /*
426          * There is an implict VM-wide suspend at the conclusion
427          * of a normal (non-single-threaded) method invoke
428          */
429         if ((options & INVOKE_SINGLE_THREADED) == 0) {
430             vm.notifySuspend();
431         }
432 
433         if (ret.exception != null) {
434             throw new InvocationException(ret.exception);
435         } else {
436             return ret.returnValue;
437         }
438     }
439 
440     /* leave synchronized to keep count accurate */
disableCollection()441     public synchronized void disableCollection() {
442         if (gcDisableCount == 0) {
443             try {
444                 JDWP.ObjectReference.DisableCollection.process(vm, this);
445             } catch (JDWPException exc) {
446                 throw exc.toJDIException();
447             }
448         }
449         gcDisableCount++;
450     }
451 
452     /* leave synchronized to keep count accurate */
enableCollection()453     public synchronized void enableCollection() {
454         gcDisableCount--;
455 
456         if (gcDisableCount == 0) {
457             try {
458                 JDWP.ObjectReference.EnableCollection.process(vm, this);
459             } catch (JDWPException exc) {
460                 // If already collected, no harm done, no exception
461                 if (exc.errorCode() != JDWP.Error.INVALID_OBJECT) {
462                     throw exc.toJDIException();
463                 }
464                 return;
465             }
466         }
467     }
468 
isCollected()469     public boolean isCollected() {
470         try {
471             return JDWP.ObjectReference.IsCollected.process(vm, this).
472                                                               isCollected;
473         } catch (JDWPException exc) {
474             throw exc.toJDIException();
475         }
476     }
477 
uniqueID()478     public long uniqueID() {
479         return ref();
480     }
481 
jdwpMonitorInfo()482     JDWP.ObjectReference.MonitorInfo jdwpMonitorInfo()
483                              throws IncompatibleThreadStateException {
484         JDWP.ObjectReference.MonitorInfo info = null;
485         try {
486             Cache local;
487 
488             // getCache() and addlistener() must be synchronized
489             // so that no events are lost.
490             synchronized (vm.state()) {
491                 local = getCache();
492 
493                 if (local != null) {
494                     info = local.monitorInfo;
495 
496                     // Check if there will be something to cache
497                     // and there is not already a listener
498                     if (info == null && !vm.state().hasListener(this)) {
499                         /* For other, less numerous objects, this is done
500                          * in the constructor. Since there can be many
501                          * ObjectReferences, the VM listener is installed
502                          * and removed as needed.
503                          * Listener must be installed before process()
504                          */
505                         vm.state().addListener(this);
506                         addedListener = true;
507                     }
508                 }
509             }
510             if (info == null) {
511                 info = JDWP.ObjectReference.MonitorInfo.process(vm, this);
512                 if (local != null) {
513                     local.monitorInfo = info;
514                     if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {
515                         vm.printTrace("ObjectReference " + uniqueID() +
516                                       " temporarily caching monitor info");
517                     }
518                 }
519             }
520         } catch (JDWPException exc) {
521              if (exc.errorCode() == JDWP.Error.THREAD_NOT_SUSPENDED) {
522                  throw new IncompatibleThreadStateException();
523              } else {
524                  throw exc.toJDIException();
525              }
526          }
527         return info;
528     }
529 
waitingThreads()530     public List<ThreadReference> waitingThreads() throws IncompatibleThreadStateException {
531         return Arrays.asList((ThreadReference[])jdwpMonitorInfo().waiters);
532     }
533 
owningThread()534     public ThreadReference owningThread() throws IncompatibleThreadStateException {
535         return jdwpMonitorInfo().owner;
536     }
537 
entryCount()538     public int entryCount() throws IncompatibleThreadStateException {
539         return jdwpMonitorInfo().entryCount;
540     }
541 
542 
referringObjects(long maxReferrers)543     public List<ObjectReference> referringObjects(long maxReferrers) {
544         if (!vm.canGetInstanceInfo()) {
545             throw new UnsupportedOperationException(
546                 "target does not support getting referring objects");
547         }
548 
549         if (maxReferrers < 0) {
550             throw new IllegalArgumentException("maxReferrers is less than zero: "
551                                               + maxReferrers);
552         }
553 
554         int intMax = (maxReferrers > Integer.MAX_VALUE)?
555             Integer.MAX_VALUE: (int)maxReferrers;
556         // JDWP can't currently handle more than this (in mustang)
557 
558         try {
559             return Arrays.asList((ObjectReference[])JDWP.ObjectReference.ReferringObjects.
560                                 process(vm, this, intMax).referringObjects);
561         } catch (JDWPException exc) {
562             throw exc.toJDIException();
563         }
564     }
565 
ref()566     long ref() {
567         return ref;
568     }
569 
isClassObject()570     boolean isClassObject() {
571         /*
572          * Don't need to worry about subclasses since java.lang.Class is final.
573          */
574         return referenceType().name().equals("java.lang.Class");
575     }
576 
prepareForAssignmentTo(ValueContainer destination)577     ValueImpl prepareForAssignmentTo(ValueContainer destination)
578                                  throws InvalidTypeException,
579                                         ClassNotLoadedException {
580 
581         validateAssignment(destination);
582         return this;            // conversion never necessary
583     }
584 
validateAssignment(ValueContainer destination)585     void validateAssignment(ValueContainer destination)
586                             throws InvalidTypeException, ClassNotLoadedException {
587 
588         /*
589          * Do these simpler checks before attempting a query of the destination's
590          * type which might cause a confusing ClassNotLoadedException if
591          * the destination is primitive or an array.
592          */
593         /*
594          * TO DO: Centralize JNI signature knowledge
595          */
596         if (destination.signature().length() == 1) {
597             throw new InvalidTypeException("Can't assign object value to primitive");
598         }
599         if ((destination.signature().charAt(0) == '[') &&
600             (type().signature().charAt(0) != '[')) {
601             throw new InvalidTypeException("Can't assign non-array value to an array");
602         }
603         if ("void".equals(destination.typeName())) {
604             throw new InvalidTypeException("Can't assign object value to a void");
605         }
606 
607         // Validate assignment
608         ReferenceType destType = (ReferenceTypeImpl)destination.type();
609         ReferenceTypeImpl myType = (ReferenceTypeImpl)referenceType();
610         if (!myType.isAssignableTo(destType)) {
611             JNITypeParser parser = new JNITypeParser(destType.signature());
612             String destTypeName = parser.typeName();
613             throw new InvalidTypeException("Can't assign " +
614                                            type().name() +
615                                            " to " + destTypeName);
616         }
617     }
618 
619 
toString()620     public String toString() {
621         return "instance of " + referenceType().name() + "(id=" + uniqueID() + ")";
622     }
623 
typeValueKey()624     byte typeValueKey() {
625         return JDWP.Tag.OBJECT;
626     }
627 
isNonVirtual(int options)628     private static boolean isNonVirtual(int options) {
629         return (options & INVOKE_NONVIRTUAL) != 0;
630     }
631 }
632