603 lines · 20.9 KB · Java
RawDownload
/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.jdi; import com.sun.jdi.*; import java.util.*;import java.util.ArrayList; public class ObjectReferenceImpl extends ValueImpl             implements ObjectReference, VMListener {     protected long ref;    private ReferenceType type = null;    private int gcDisableCount = 0;    boolean addedListener = false;     // This is cached only while the VM is suspended    protected static class Cache {        JDWP.ObjectReference.MonitorInfo monitorInfo = null;    }     private static final Cache noInitCache = new Cache();    private static final Cache markerCache = new Cache();    private Cache cache = noInitCache;     private void disableCache() {        synchronized (vm.state()) {            cache = null;        }    }     private void enableCache() {        synchronized (vm.state()) {            cache = markerCache;        }    }     // Override in subclasses    protected Cache newCache() {        return new Cache();    }     protected Cache getCache() {        synchronized (vm.state()) {            if (cache == noInitCache) {                if (vm.state().isSuspended()) {                    // Set cache now, otherwise newly created objects are                    // not cached until resuspend                    enableCache();                } else {                    disableCache();                }            }            if (cache == markerCache) {                cache = newCache();            }            return cache;        }    }     // Return the ClassTypeImpl upon which to invoke a method.    // By default it is our very own referenceType() but subclasses    // can override.    protected ClassTypeImpl invokableReferenceType(Method method) {        return (ClassTypeImpl)referenceType();    }     ObjectReferenceImpl(VirtualMachine aVm,long aRef) {        super(aVm);         ref = aRef;    }     protected String description() {        return "ObjectReference " + uniqueID();    }     /*     * VMListener implementation     */    public boolean vmSuspended(VMAction action) {        enableCache();        return true;    }     public boolean vmNotSuspended(VMAction action) {        // make sure that cache and listener management are synchronized        synchronized (vm.state()) {            if (cache != null && (vm.traceFlags & vm.TRACE_OBJREFS) != 0) {                vm.printTrace("Clearing temporary cache for " + description());            }            disableCache();            if (addedListener) {                /*                 * If a listener was added (i.e. this is not a                 * ObjectReference that adds a listener on startup),                 * remove it here.                 */                addedListener = false;                return false;  // false says remove            } else {                return true;            }        }    }     public boolean equals(Object obj) {        if ((obj != null) && (obj instanceof ObjectReferenceImpl)) {            ObjectReferenceImpl other = (ObjectReferenceImpl)obj;            return (ref() == other.ref()) &&                   super.equals(obj);        } else {            return false;        }    }     public int hashCode() {        return(int)ref();    }     public Type type() {        return referenceType();    }     public ReferenceType referenceType() {        if (type == null) {            try {                JDWP.ObjectReference.ReferenceType rtinfo =                    JDWP.ObjectReference.ReferenceType.process(vm, this);                type = vm.referenceType(rtinfo.typeID,                                        rtinfo.refTypeTag);            } catch (JDWPException exc) {                throw exc.toJDIException();            }        }        return type;    }     public Value getValue(Field sig) {        List<Field> list = new ArrayList<Field>(1);        list.add(sig);        Map map = getValues(list);        return(Value)map.get(sig);    }     public Map<Field,Value> getValues(List<? extends Field> theFields) {        validateMirrors(theFields);         List<Field> staticFields = new ArrayList<Field>(0);        int size = theFields.size();        List<Field> instanceFields = new ArrayList<Field>(size);         for (int i=0; i<size; i++) {            Field field = (Field)theFields.get(i);             // Make sure the field is valid            ((ReferenceTypeImpl)referenceType()).validateFieldAccess(field);             // FIX ME! We need to do some sanity checking            // here; make sure the field belongs to this            // object.            if (field.isStatic())                staticFields.add(field);            else {                instanceFields.add(field);            }        }         Map<Field, Value> map;        if (staticFields.size() > 0) {            map = referenceType().getValues(staticFields);        } else {            map = new HashMap<Field, Value>(size);        }         size = instanceFields.size();         JDWP.ObjectReference.GetValues.Field[] queryFields =                         new JDWP.ObjectReference.GetValues.Field[size];        for (int i=0; i<size; i++) {            FieldImpl field = (FieldImpl)instanceFields.get(i);/* thanks OTI */            queryFields[i] = new JDWP.ObjectReference.GetValues.Field(                                         field.ref());        }        ValueImpl[] values;        try {            values = JDWP.ObjectReference.GetValues.                                     process(vm, this, queryFields).values;        } catch (JDWPException exc) {            throw exc.toJDIException();        }         if (size != values.length) {            throw new InternalException(                         "Wrong number of values returned from target VM");        }        for (int i=0; i<size; i++) {            FieldImpl field = (FieldImpl)instanceFields.get(i);            map.put(field, values[i]);        }         return map;    }     public void setValue(Field field, Value value)                   throws InvalidTypeException, ClassNotLoadedException {         validateMirror(field);        validateMirrorOrNull(value);         // Make sure the field is valid        ((ReferenceTypeImpl)referenceType()).validateFieldSet(field);         if (field.isStatic()) {            ReferenceType type = referenceType();            if (type instanceof ClassType) {                ((ClassType)type).setValue(field, value);                return;            } else {                throw new IllegalArgumentException(                                    "Invalid type for static field set");            }        }         try {            JDWP.ObjectReference.SetValues.FieldValue[] fvals =                      new JDWP.ObjectReference.SetValues.FieldValue[1];            fvals[0] = new JDWP.ObjectReference.SetValues.FieldValue(                           ((FieldImpl)field).ref(),                           // Validate and convert if necessary                           ValueImpl.prepareForAssignment(value,                                                          (FieldImpl)field));            try {                JDWP.ObjectReference.SetValues.process(vm, this, fvals);            } catch (JDWPException exc) {                throw exc.toJDIException();            }        } catch (ClassNotLoadedException e) {            /*             * Since we got this exception,             * the field type must be a reference type. The value             * we're trying to set is null, but if the field's             * class has not yet been loaded through the enclosing             * class loader, then setting to null is essentially a             * no-op, and we should allow it without an exception.             */            if (value != null) {                throw e;            }        }    }     void validateMethodInvocation(Method method, int options)                                         throws InvalidTypeException,                                         InvocationException {         /*         * Method must be in this object's class, a superclass, or         * implemented interface         */        ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();        if (!declType.isAssignableFrom(this)) {            throw new IllegalArgumentException("Invalid method");        }         ClassTypeImpl clazz = invokableReferenceType(method);         /*         * Method must be a non-constructor         */        if (method.isConstructor()) {            throw new IllegalArgumentException("Cannot invoke constructor");        }         /*         * For nonvirtual invokes, method must have a body         */        if ((options & INVOKE_NONVIRTUAL) != 0) {            if (method.declaringType() instanceof InterfaceType) {                throw new IllegalArgumentException("Interface method");            } else if (method.isAbstract()) {                throw new IllegalArgumentException("Abstract method");            }        }         /*         * Get the class containing the method that will be invoked.         * This class is needed only for proper validation of the         * method argument types.         */        ClassTypeImpl invokedClass;        if ((options & INVOKE_NONVIRTUAL) != 0) {            // No overrides in non-virtual invokes            invokedClass = clazz;        } else {            /*             * For virtual invokes, find any override of the method.             * Since we are looking for a method with a real body, we             * don't need to bother with interfaces/abstract methods.             */            Method invoker = clazz.concreteMethodByName(method.name(),                                                        method.signature());            //  isAssignableFrom check above guarantees non-null            invokedClass = (ClassTypeImpl)invoker.declaringType();        }        /* The above code is left over from previous versions.         * We haven't had time to divine the intent.  jjh, 7/31/2003         */    }     PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,                                   final ClassTypeImpl refType,                                   final MethodImpl method,                                   final ValueImpl[] args,                                   final int options) {        CommandSender sender =            new CommandSender() {                public PacketStream send() {                    return JDWP.ObjectReference.InvokeMethod.enqueueCommand(                                          vm, ObjectReferenceImpl.this,                                          thread, refType,                                          method.ref(), args, options);                }        };         PacketStream stream;        if ((options & INVOKE_SINGLE_THREADED) != 0) {            stream = thread.sendResumingCommand(sender);        } else {            stream = vm.sendResumingCommand(sender);        }        return stream;    }     public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,                              List<? extends Value> origArguments, int options)                              throws InvalidTypeException,                                     IncompatibleThreadStateException,                                     InvocationException,                                     ClassNotLoadedException {        validateMirror(threadIntf);        validateMirror(methodIntf);        validateMirrorsOrNulls(origArguments);         MethodImpl method = (MethodImpl)methodIntf;        ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;         if (method.isStatic()) {            if (referenceType() instanceof ClassType) {                ClassType type = (ClassType)referenceType();                return type.invokeMethod(thread, method, origArguments, options);            } else {                throw new IllegalArgumentException("Invalid type for static method invocation");            }        }         validateMethodInvocation(method, options);         List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(                                                  origArguments);         ValueImpl[] args = arguments.toArray(new ValueImpl[0]);        JDWP.ObjectReference.InvokeMethod ret;        try {            PacketStream stream =                sendInvokeCommand(thread, invokableReferenceType(method),                                  method, args, options);            ret = JDWP.ObjectReference.InvokeMethod.waitForReply(vm, stream);        } catch (JDWPException exc) {            if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {                throw new IncompatibleThreadStateException();            } else {                throw exc.toJDIException();            }        }         /*         * There is an implict VM-wide suspend at the conclusion         * of a normal (non-single-threaded) method invoke         */        if ((options & INVOKE_SINGLE_THREADED) == 0) {            vm.notifySuspend();        }         if (ret.exception != null) {            throw new InvocationException(ret.exception);        } else {            return ret.returnValue;        }    }     /* leave synchronized to keep count accurate */    public synchronized void disableCollection() {        if (gcDisableCount == 0) {            try {                JDWP.ObjectReference.DisableCollection.process(vm, this);            } catch (JDWPException exc) {                throw exc.toJDIException();            }        }        gcDisableCount++;    }     /* leave synchronized to keep count accurate */    public synchronized void enableCollection() {        gcDisableCount--;         if (gcDisableCount == 0) {            try {                JDWP.ObjectReference.EnableCollection.process(vm, this);            } catch (JDWPException exc) {                // If already collected, no harm done, no exception                if (exc.errorCode() != JDWP.Error.INVALID_OBJECT) {                    throw exc.toJDIException();                }                return;            }        }    }     public boolean isCollected() {        try {            return JDWP.ObjectReference.IsCollected.process(vm, this).                                                              isCollected;        } catch (JDWPException exc) {            throw exc.toJDIException();        }    }     public long uniqueID() {        return ref();    }     JDWP.ObjectReference.MonitorInfo jdwpMonitorInfo()                             throws IncompatibleThreadStateException {        JDWP.ObjectReference.MonitorInfo info = null;        try {            Cache local;             // getCache() and addlistener() must be synchronized            // so that no events are lost.            synchronized (vm.state()) {                local = getCache();                 if (local != null) {                    info = local.monitorInfo;                     // Check if there will be something to cache                    // and there is not already a listener                    if (info == null && !vm.state().hasListener(this)) {                        /* For other, less numerous objects, this is done                         * in the constructor. Since there can be many                         * ObjectReferences, the VM listener is installed                         * and removed as needed.                         * Listener must be installed before process()                         */                        vm.state().addListener(this);                        addedListener = true;                    }                }            }            if (info == null) {                info = JDWP.ObjectReference.MonitorInfo.process(vm, this);                if (local != null) {                    local.monitorInfo = info;                    if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {                        vm.printTrace("ObjectReference " + uniqueID() +                                      " temporarily caching monitor info");                    }                }            }        } catch (JDWPException exc) {             if (exc.errorCode() == JDWP.Error.THREAD_NOT_SUSPENDED) {                 throw new IncompatibleThreadStateException();             } else {                 throw exc.toJDIException();

Lines 1–500 of 603.