Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java
38920 views
/*1* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.tools.jdi;2627import com.sun.jdi.*;2829import java.util.*;30import java.util.ArrayList;3132public class ObjectReferenceImpl extends ValueImpl33implements ObjectReference, VMListener {3435protected long ref;36private ReferenceType type = null;37private int gcDisableCount = 0;38boolean addedListener = false;3940// This is cached only while the VM is suspended41protected static class Cache {42JDWP.ObjectReference.MonitorInfo monitorInfo = null;43}4445private static final Cache noInitCache = new Cache();46private static final Cache markerCache = new Cache();47private Cache cache = noInitCache;4849private void disableCache() {50synchronized (vm.state()) {51cache = null;52}53}5455private void enableCache() {56synchronized (vm.state()) {57cache = markerCache;58}59}6061// Override in subclasses62protected Cache newCache() {63return new Cache();64}6566protected Cache getCache() {67synchronized (vm.state()) {68if (cache == noInitCache) {69if (vm.state().isSuspended()) {70// Set cache now, otherwise newly created objects are71// not cached until resuspend72enableCache();73} else {74disableCache();75}76}77if (cache == markerCache) {78cache = newCache();79}80return cache;81}82}8384// Return the ClassTypeImpl upon which to invoke a method.85// By default it is our very own referenceType() but subclasses86// can override.87protected ClassTypeImpl invokableReferenceType(Method method) {88return (ClassTypeImpl)referenceType();89}9091ObjectReferenceImpl(VirtualMachine aVm,long aRef) {92super(aVm);9394ref = aRef;95}9697protected String description() {98return "ObjectReference " + uniqueID();99}100101/*102* VMListener implementation103*/104public boolean vmSuspended(VMAction action) {105enableCache();106return true;107}108109public boolean vmNotSuspended(VMAction action) {110// make sure that cache and listener management are synchronized111synchronized (vm.state()) {112if (cache != null && (vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {113vm.printTrace("Clearing temporary cache for " + description());114}115disableCache();116if (addedListener) {117/*118* If a listener was added (i.e. this is not a119* ObjectReference that adds a listener on startup),120* remove it here.121*/122addedListener = false;123return false; // false says remove124} else {125return true;126}127}128}129130public boolean equals(Object obj) {131if ((obj != null) && (obj instanceof ObjectReferenceImpl)) {132ObjectReferenceImpl other = (ObjectReferenceImpl)obj;133return (ref() == other.ref()) &&134super.equals(obj);135} else {136return false;137}138}139140public int hashCode() {141return(int)ref();142}143144public Type type() {145return referenceType();146}147148public ReferenceType referenceType() {149if (type == null) {150try {151JDWP.ObjectReference.ReferenceType rtinfo =152JDWP.ObjectReference.ReferenceType.process(vm, this);153type = vm.referenceType(rtinfo.typeID,154rtinfo.refTypeTag);155} catch (JDWPException exc) {156throw exc.toJDIException();157}158}159return type;160}161162public Value getValue(Field sig) {163List<Field> list = new ArrayList<Field>(1);164list.add(sig);165Map<Field, Value> map = getValues(list);166return map.get(sig);167}168169public Map<Field,Value> getValues(List<? extends Field> theFields) {170validateMirrors(theFields);171172List<Field> staticFields = new ArrayList<Field>(0);173int size = theFields.size();174List<Field> instanceFields = new ArrayList<Field>(size);175176for (int i=0; i<size; i++) {177Field field = (Field)theFields.get(i);178179// Make sure the field is valid180((ReferenceTypeImpl)referenceType()).validateFieldAccess(field);181182// FIX ME! We need to do some sanity checking183// here; make sure the field belongs to this184// object.185if (field.isStatic())186staticFields.add(field);187else {188instanceFields.add(field);189}190}191192Map<Field, Value> map;193if (staticFields.size() > 0) {194map = referenceType().getValues(staticFields);195} else {196map = new HashMap<Field, Value>(size);197}198199size = instanceFields.size();200201JDWP.ObjectReference.GetValues.Field[] queryFields =202new JDWP.ObjectReference.GetValues.Field[size];203for (int i=0; i<size; i++) {204FieldImpl field = (FieldImpl)instanceFields.get(i);/* thanks OTI */205queryFields[i] = new JDWP.ObjectReference.GetValues.Field(206field.ref());207}208ValueImpl[] values;209try {210values = JDWP.ObjectReference.GetValues.211process(vm, this, queryFields).values;212} catch (JDWPException exc) {213throw exc.toJDIException();214}215216if (size != values.length) {217throw new InternalException(218"Wrong number of values returned from target VM");219}220for (int i=0; i<size; i++) {221FieldImpl field = (FieldImpl)instanceFields.get(i);222map.put(field, values[i]);223}224225return map;226}227228public void setValue(Field field, Value value)229throws InvalidTypeException, ClassNotLoadedException {230231validateMirror(field);232validateMirrorOrNull(value);233234// Make sure the field is valid235((ReferenceTypeImpl)referenceType()).validateFieldSet(field);236237if (field.isStatic()) {238ReferenceType type = referenceType();239if (type instanceof ClassType) {240((ClassType)type).setValue(field, value);241return;242} else {243throw new IllegalArgumentException(244"Invalid type for static field set");245}246}247248try {249JDWP.ObjectReference.SetValues.FieldValue[] fvals =250new JDWP.ObjectReference.SetValues.FieldValue[1];251fvals[0] = new JDWP.ObjectReference.SetValues.FieldValue(252((FieldImpl)field).ref(),253// Validate and convert if necessary254ValueImpl.prepareForAssignment(value,255(FieldImpl)field));256try {257JDWP.ObjectReference.SetValues.process(vm, this, fvals);258} catch (JDWPException exc) {259throw exc.toJDIException();260}261} catch (ClassNotLoadedException e) {262/*263* Since we got this exception,264* the field type must be a reference type. The value265* we're trying to set is null, but if the field's266* class has not yet been loaded through the enclosing267* class loader, then setting to null is essentially a268* no-op, and we should allow it without an exception.269*/270if (value != null) {271throw e;272}273}274}275276void validateMethodInvocation(Method method, int options)277throws InvalidTypeException,278InvocationException {279/*280* Method must be in this object's class, a superclass, or281* implemented interface282*/283ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();284if (!declType.isAssignableFrom(this)) {285throw new IllegalArgumentException("Invalid method");286}287288if (declType instanceof ClassTypeImpl) {289validateClassMethodInvocation(method, options);290} else if (declType instanceof InterfaceTypeImpl) {291validateIfaceMethodInvocation(method, options);292} else {293throw new InvalidTypeException();294}295}296297void validateClassMethodInvocation(Method method, int options)298throws InvalidTypeException,299InvocationException {300301ClassTypeImpl clazz = invokableReferenceType(method);302303/*304* Method must be a non-constructor305*/306if (method.isConstructor()) {307throw new IllegalArgumentException("Cannot invoke constructor");308}309310/*311* For nonvirtual invokes, method must have a body312*/313if (isNonVirtual(options)) {314if (method.isAbstract()) {315throw new IllegalArgumentException("Abstract method");316}317}318319/*320* Get the class containing the method that will be invoked.321* This class is needed only for proper validation of the322* method argument types.323*/324ClassTypeImpl invokedClass;325if (isNonVirtual(options)) {326// No overrides in non-virtual invokes327invokedClass = clazz;328} else {329/*330* For virtual invokes, find any override of the method.331* Since we are looking for a method with a real body, we332* don't need to bother with interfaces/abstract methods.333*/334Method invoker = clazz.concreteMethodByName(method.name(),335method.signature());336// invoker is supposed to be non-null under normal circumstances337invokedClass = (ClassTypeImpl)invoker.declaringType();338}339/* The above code is left over from previous versions.340* We haven't had time to divine the intent. jjh, 7/31/2003341*/342}343344void validateIfaceMethodInvocation(Method method, int options)345throws InvalidTypeException,346InvocationException {347/*348* Only default methods allowed for nonvirtual invokes349*/350if (isNonVirtual(options) && !method.isDefault()) {351throw new IllegalArgumentException("Not a default method");352}353}354355PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,356final ClassTypeImpl refType,357final MethodImpl method,358final ValueImpl[] args,359final int options) {360CommandSender sender =361new CommandSender() {362public PacketStream send() {363return JDWP.ObjectReference.InvokeMethod.enqueueCommand(364vm, ObjectReferenceImpl.this,365thread, refType,366method.ref(), args, options);367}368};369370PacketStream stream;371if ((options & INVOKE_SINGLE_THREADED) != 0) {372stream = thread.sendResumingCommand(sender);373} else {374stream = vm.sendResumingCommand(sender);375}376return stream;377}378379public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,380List<? extends Value> origArguments, int options)381throws InvalidTypeException,382IncompatibleThreadStateException,383InvocationException,384ClassNotLoadedException {385validateMirror(threadIntf);386validateMirror(methodIntf);387validateMirrorsOrNulls(origArguments);388389MethodImpl method = (MethodImpl)methodIntf;390ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;391392if (method.isStatic()) {393if (referenceType() instanceof InterfaceType) {394InterfaceType type = (InterfaceType)referenceType();395return type.invokeMethod(thread, method, origArguments, options);396} else if (referenceType() instanceof ClassType) {397ClassType type = (ClassType)referenceType();398return type.invokeMethod(thread, method, origArguments, options);399} else {400throw new IllegalArgumentException("Invalid type for static method invocation");401}402}403404validateMethodInvocation(method, options);405406List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(407origArguments);408409ValueImpl[] args = arguments.toArray(new ValueImpl[0]);410JDWP.ObjectReference.InvokeMethod ret;411try {412PacketStream stream =413sendInvokeCommand(thread, invokableReferenceType(method),414method, args, options);415ret = JDWP.ObjectReference.InvokeMethod.waitForReply(vm, stream);416} catch (JDWPException exc) {417if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {418throw new IncompatibleThreadStateException();419} else {420throw exc.toJDIException();421}422}423424/*425* There is an implict VM-wide suspend at the conclusion426* of a normal (non-single-threaded) method invoke427*/428if ((options & INVOKE_SINGLE_THREADED) == 0) {429vm.notifySuspend();430}431432if (ret.exception != null) {433throw new InvocationException(ret.exception);434} else {435return ret.returnValue;436}437}438439/* leave synchronized to keep count accurate */440public synchronized void disableCollection() {441if (gcDisableCount == 0) {442try {443JDWP.ObjectReference.DisableCollection.process(vm, this);444} catch (JDWPException exc) {445throw exc.toJDIException();446}447}448gcDisableCount++;449}450451/* leave synchronized to keep count accurate */452public synchronized void enableCollection() {453gcDisableCount--;454455if (gcDisableCount == 0) {456try {457JDWP.ObjectReference.EnableCollection.process(vm, this);458} catch (JDWPException exc) {459// If already collected, no harm done, no exception460if (exc.errorCode() != JDWP.Error.INVALID_OBJECT) {461throw exc.toJDIException();462}463return;464}465}466}467468public boolean isCollected() {469try {470return JDWP.ObjectReference.IsCollected.process(vm, this).471isCollected;472} catch (JDWPException exc) {473throw exc.toJDIException();474}475}476477public long uniqueID() {478return ref();479}480481JDWP.ObjectReference.MonitorInfo jdwpMonitorInfo()482throws IncompatibleThreadStateException {483JDWP.ObjectReference.MonitorInfo info = null;484try {485Cache local;486487// getCache() and addlistener() must be synchronized488// so that no events are lost.489synchronized (vm.state()) {490local = getCache();491492if (local != null) {493info = local.monitorInfo;494495// Check if there will be something to cache496// and there is not already a listener497if (info == null && !vm.state().hasListener(this)) {498/* For other, less numerous objects, this is done499* in the constructor. Since there can be many500* ObjectReferences, the VM listener is installed501* and removed as needed.502* Listener must be installed before process()503*/504vm.state().addListener(this);505addedListener = true;506}507}508}509if (info == null) {510info = JDWP.ObjectReference.MonitorInfo.process(vm, this);511if (local != null) {512local.monitorInfo = info;513if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {514vm.printTrace("ObjectReference " + uniqueID() +515" temporarily caching monitor info");516}517}518}519} catch (JDWPException exc) {520if (exc.errorCode() == JDWP.Error.THREAD_NOT_SUSPENDED) {521throw new IncompatibleThreadStateException();522} else {523throw exc.toJDIException();524}525}526return info;527}528529public List<ThreadReference> waitingThreads() throws IncompatibleThreadStateException {530return Arrays.asList((ThreadReference[])jdwpMonitorInfo().waiters);531}532533public ThreadReference owningThread() throws IncompatibleThreadStateException {534return jdwpMonitorInfo().owner;535}536537public int entryCount() throws IncompatibleThreadStateException {538return jdwpMonitorInfo().entryCount;539}540541542public List<ObjectReference> referringObjects(long maxReferrers) {543if (!vm.canGetInstanceInfo()) {544throw new UnsupportedOperationException(545"target does not support getting referring objects");546}547548if (maxReferrers < 0) {549throw new IllegalArgumentException("maxReferrers is less than zero: "550+ maxReferrers);551}552553int intMax = (maxReferrers > Integer.MAX_VALUE)?554Integer.MAX_VALUE: (int)maxReferrers;555// JDWP can't currently handle more than this (in mustang)556557try {558return Arrays.asList((ObjectReference[])JDWP.ObjectReference.ReferringObjects.559process(vm, this, intMax).referringObjects);560} catch (JDWPException exc) {561throw exc.toJDIException();562}563}564565long ref() {566return ref;567}568569boolean isClassObject() {570/*571* Don't need to worry about subclasses since java.lang.Class is final.572*/573return referenceType().name().equals("java.lang.Class");574}575576ValueImpl prepareForAssignmentTo(ValueContainer destination)577throws InvalidTypeException,578ClassNotLoadedException {579580validateAssignment(destination);581return this; // conversion never necessary582}583584void validateAssignment(ValueContainer destination)585throws InvalidTypeException, ClassNotLoadedException {586587/*588* Do these simpler checks before attempting a query of the destination's589* type which might cause a confusing ClassNotLoadedException if590* the destination is primitive or an array.591*/592/*593* TO DO: Centralize JNI signature knowledge594*/595if (destination.signature().length() == 1) {596throw new InvalidTypeException("Can't assign object value to primitive");597}598if ((destination.signature().charAt(0) == '[') &&599(type().signature().charAt(0) != '[')) {600throw new InvalidTypeException("Can't assign non-array value to an array");601}602if ("void".equals(destination.typeName())) {603throw new InvalidTypeException("Can't assign object value to a void");604}605606// Validate assignment607ReferenceType destType = (ReferenceTypeImpl)destination.type();608ReferenceTypeImpl myType = (ReferenceTypeImpl)referenceType();609if (!myType.isAssignableTo(destType)) {610JNITypeParser parser = new JNITypeParser(destType.signature());611String destTypeName = parser.typeName();612throw new InvalidTypeException("Can't assign " +613type().name() +614" to " + destTypeName);615}616}617618619public String toString() {620return "instance of " + referenceType().name() + "(id=" + uniqueID() + ")";621}622623byte typeValueKey() {624return JDWP.Tag.OBJECT;625}626627private static boolean isNonVirtual(int options) {628return (options & INVOKE_NONVIRTUAL) != 0;629}630}631632633