/*
* Copyright 2010 Sun Microsystems, Inc. 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.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package org.visage.jdi;
import org.visage.jdi.event.VisageEventQueue;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.Value;
import com.sun.jdi.ShortValue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author sundar
*/
public class VisageObjectReference extends VisageValue implements ObjectReference {
public VisageObjectReference(VisageVirtualMachine visagevm, ObjectReference underlying) {
super(visagevm, underlying);
}
public List<ObjectReference> referringObjects(long count) {
return VisageWrapper.wrapObjectReferences(virtualMachine(), underlying().referringObjects(count));
}
public void disableCollection() {
underlying().disableCollection();
}
public void enableCollection() {
underlying().enableCollection();
}
public int entryCount() throws IncompatibleThreadStateException {
return underlying().entryCount();
}
public int getFlagWord(Field field) {
VisageReferenceType clazz = (VisageReferenceType)referenceType();
// could this be a java field inherited by an visage class??
if (!clazz.isVisageType()) {
return 0;
}
Field jdiField = VisageWrapper.unwrap(field);
String jdiFieldName = jdiField.name();
String vflgFieldName = "VFLG" + jdiFieldName;
Field vflgField = clazz.fieldByName(vflgFieldName);
if (vflgField == null) {
// not all fields have a VFLG, eg, a private field that isn't accessed
return 0;
}
Value vflgValue = underlying().getValue(VisageWrapper.unwrap(vflgField));
return((ShortValue)vflgValue).value();
}
private boolean areFlagBitsSet(Field field, int mask) {
return (getFlagWord(field) & mask) == mask;
}
/**
* JDI addition: Determines if a field of this object can be modified. For example,
* a field declared with a bind cannot be modified.
*
* @return <code>true</code> if the specified field is read only; false otherwise.
*/
public boolean isReadOnly(Field field) {
return areFlagBitsSet(field, virtualMachine().VisageReadOnlyFlagMask());
}
/**
* JDI addition: Determines if the value of a field of this object is valid. A value
* is invalid if a new value has been specified for the field, but not yet
* stored into the field, for example, because the field is lazily bound.
*
* @return <code>true</code> if the value of the specified field is invalid; false otherwise.
*/
public boolean isInvalid(Field field) {
return areFlagBitsSet(field, virtualMachine().VisageInvalidFlagMask());
}
/**
* JDI addition: Determines if a field was declared with a bind clause.
*
* @return <code>true</code> if the specified field was declared with a bind clause; false otherwise.
*/
public boolean isBound(Field field) {
return areFlagBitsSet(field, virtualMachine().VisageBoundFlagMask());
}
/**
* JDI extension: This will call the get function for the field if one exists via invokeMethod.
* The call to invokeMethod is preceded by a call to {@link VisageEventQueue#setEventControl(boolean)} passing true
* and is followed by a call to {@link VisageEventQueue#setEventControl(boolean)} passing false.
*
* If an invokeMethod Exception occurs, it is saved and can be accessed by calling
* {@link VisageVirtualMachine#lastFieldAccessException()}. In this case,
* the default value for the type of the field is returned for a PrimitiveType,
* while null is returned for a non PrimitiveType.
*/
public Value getValue(Field field) {
virtualMachine().setLastFieldAccessException(null);
Field jdiField = VisageWrapper.unwrap(field);
VisageReferenceType wrappedClass = (VisageReferenceType)referenceType();
if (!wrappedClass.isVisageType()) {
return VisageWrapper.wrap(virtualMachine(), underlying().getValue(jdiField));
}
//get$xxxx methods exist for fields except private fields which have no binders
ReferenceType unwrappedClass = VisageWrapper.unwrap(referenceType());
List<Method> mth = unwrappedClass.methodsByName("get" + jdiField.name());
if (mth.size() == 0) {
return VisageWrapper.wrap(virtualMachine(), underlying().getValue(jdiField));
}
Exception theExc = null;
VisageEventQueue eq = virtualMachine().eventQueue();
try {
eq.setEventControl(true);
return invokeMethod(virtualMachine().uiThread(), mth.get(0), new ArrayList<Value>(0), ObjectReference.INVOKE_SINGLE_THREADED);
} catch(InvalidTypeException ee) {
theExc = ee;
} catch(ClassNotLoadedException ee) {
theExc = ee;
} catch(IncompatibleThreadStateException ee) {
theExc = ee;
} catch(InvocationException ee) {
theExc = ee;
} finally {
eq.setEventControl(false);
}
// We don't have to catch IllegalArgumentException. It is an unchecked exception for invokeMethod
// and for getValue
virtualMachine().setLastFieldAccessException(theExc);
try {
return virtualMachine().defaultValue(field.type());
} catch(ClassNotLoadedException ee) {
// The type has to be a ReferenceType for which we return null;
return null;
}
}
/**
* JDI extension: This will call the get function for a field if one exists via invokeMethod.
* The call to invokeMethod is preceded by a call to {@link VisageEventQueue#setEventControl(boolean)} passing true
* and is followed by a call to {@link VisageEventQueue#setEventControl(boolean)} passing false.
*
* If an invokeMethod Exception occurs, it is saved and can be accessed by calling
* {@link VisageVirtualMachine#lastFieldAccessException()}. In this case,
* the default value for the type of the field is returned for a PrimitiveType,
* while null is returned for a non PrimitiveType.
*/
public Map<Field, Value> getValues(List<? extends Field> wrappedFields) {
virtualMachine().setLastFieldAccessException(null);
// We will find fields which have no getters, and call the underlying
// getValues to get values for all of them in one fell swoop.
Map<Field, Field> unwrappedToWrappedMap = new HashMap<Field, Field>();
List<Field> noGetterUnwrappedFields = new ArrayList<Field>(); // fields that don't have getters
// But first, for fields that do have getters, call invokeMethod
// or we will call VisageGetValue for each, depending on doInvokes
Map<Field, Value> result = new HashMap<Field, Value>();
VisageReferenceType wrappedClass = (VisageReferenceType)referenceType();
ReferenceType unwrappedClass = VisageWrapper.unwrap(wrappedClass);
// Create the above Maps and lists
for (Field wrappedField : wrappedFields) {
Field unwrapped = VisageWrapper.unwrap(wrappedField);
if (wrappedClass.isVisageType()) {
List<Method> mth = unwrappedClass.methodsByName("get" + unwrapped.name());
if (mth.size() == 0) {
// No getter
unwrappedToWrappedMap.put(unwrapped, wrappedField);
noGetterUnwrappedFields.add(unwrapped);
} else {
// Field has a getter
result.put(wrappedField, getValue(wrappedField));
}
} else {
// Java type
unwrappedToWrappedMap.put(unwrapped, wrappedField);
noGetterUnwrappedFields.add(unwrapped);
}
}
// Get values for all the noGetter fields. Note that this gets them in a single JDWP trip
Map<Field, Value> unwrappedFieldValues = underlying().getValues(noGetterUnwrappedFields);
// for each input Field, create a result map entry with that field as the
// key, and the value returned by getValues, or null if the field is invalid.
// Make a pass over the unwrapped no getter fields and for each, put its
// wrapped version, and wrapped value into the result Map.
for (Map.Entry<Field, Field> unwrappedEntry: unwrappedToWrappedMap.entrySet()) {
Field wrappedField = unwrappedEntry.getValue();
Value resultValue = VisageWrapper.wrap(virtualMachine(),
unwrappedFieldValues.get(unwrappedEntry.getKey()));
result.put(wrappedField, resultValue);
}
return result;
}
public VisageValue invokeMethod(ThreadReference thread, Method method, List<? extends Value> values, int options)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException {
Value value =
underlying().invokeMethod(
VisageWrapper.unwrap(thread), VisageWrapper.unwrap(method),
VisageWrapper.unwrapValues(values), options);
return VisageWrapper.wrap(virtualMachine(), value);
}
public boolean isCollected() {
return underlying().isCollected();
}
public VisageThreadReference owningThread() throws IncompatibleThreadStateException {
return VisageWrapper.wrap(virtualMachine(), underlying().owningThread());
}
public VisageReferenceType referenceType() {
return VisageWrapper.wrap(virtualMachine(), underlying().referenceType());
}
/**
* JDI extension: This will call the set function if one exists via invokeMethod.
* The call to invokeMethod is preceded by a call to {@link VisageEventQueue#setEventControl(boolean)} passing true
* and is followed by a call to {@link VisageEventQueue#setEventControl(boolean)} passing false.
*
* If an invokeMethod Exception occurs, it is saved and can be accessed by calling
* {@link VisageVirtualMachine#lastFieldAccessException()}.
*/
public void setValue(Field field, Value value) throws
InvalidTypeException, ClassNotLoadedException {
virtualMachine().setLastFieldAccessException(null);
Field jdiField = VisageWrapper.unwrap(field);
Value jdiValue = VisageWrapper.unwrap(value);
VisageReferenceType clazz = (VisageReferenceType)referenceType();
if (!clazz.isVisageType()) {
underlying().setValue(jdiField, jdiValue);
return;
}
if (isReadOnly(field)) {
throw new IllegalArgumentException("Error: Cannot set value of a read-only field: " + field);
}
if (isBound(field)) {
throw new IllegalArgumentException("Error: Cannot set value of a bound field: " + field);
}
//get$xxxx methods exist for fields except private fields which have no binders
List<Method> mth = clazz.methodsByName("set" + jdiField.name());
if (mth.size() == 0) {
// there is no setter
underlying().setValue(jdiField, jdiValue);
return;
}
// there is a setter
ArrayList<Value> args = new ArrayList<Value>(1);
args.add(jdiValue);
Exception theExc = null;
VisageEventQueue eq = virtualMachine().eventQueue();
try {
eq.setEventControl(true);
invokeMethod(virtualMachine().uiThread(), mth.get(0), args, ObjectReference.INVOKE_SINGLE_THREADED);
} catch(InvalidTypeException ee) {
theExc = ee;
} catch(ClassNotLoadedException ee) {
theExc = ee;
} catch(IncompatibleThreadStateException ee) {
theExc = ee;
} catch(InvocationException ee) {
theExc = ee;
} finally {
eq.setEventControl(false);
}
// We don't have to catch IllegalArgumentException. It is an unchecked exception for invokeMethod
// and for getValue
virtualMachine().setLastFieldAccessException(theExc);
}
public long uniqueID() {
return underlying().uniqueID();
}
public List<ThreadReference> waitingThreads() throws IncompatibleThreadStateException {
return VisageWrapper.wrapThreads(virtualMachine(), underlying().waitingThreads());
}
@Override
protected ObjectReference underlying() {
return (ObjectReference) super.underlying();
}
}