blob: e3cc96927ac3b6820800ac4267a550e67df48671 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.core.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.core.IJavaVariable;
import com.ibm.icu.text.MessageFormat;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassObjectReference;
import com.sun.jdi.Field;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.PrimitiveValue;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StringReference;
import com.sun.jdi.Type;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.Value;
import com.sun.jdi.VoidValue;
/**
* Represents the value of a java variable
*
* @see IJavaValue
*/
public class JDIValue extends JDIDebugElement implements IJavaValue {
private Value fValue;
private List<IJavaVariable> fVariables;
/**
* A flag indicating if this value is still allocated (valid)
*/
private boolean fAllocated = true;
/**
* When created for a logical structure we hold onto the original
* non-logical value for purposes of equality. This way a logical
* structure's children remain more stable in the variables view.
*
* This is <code>null</code> when not created for a logical structure.
*/
protected IJavaValue fLogicalParent;
/**
* Constructor
*
* @param target
* debug target that this value belongs to
* @param value
* the underlying value this value represents
*/
public JDIValue(JDIDebugTarget target, Value value) {
super(target);
fValue = value;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jdt.internal.debug.core.model.JDIDebugElement#getAdapter(
* java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> adapter) {
if (adapter == IJavaValue.class) {
return (T) this;
}
return super.getAdapter(adapter);
}
/**
* Creates the appropriate kind of value - i.e. a primitive value, object,
* class object, array, <code>null</code> or void.
*/
public static JDIValue createValue(JDIDebugTarget target, Value value) {
if (value == null) {
return new JDINullValue(target);
}
if (value instanceof ArrayReference) {
return new JDIArrayValue(target, (ArrayReference) value);
}
if (value instanceof ClassObjectReference) {
return new JDIClassObjectValue(target, (ClassObjectReference) value);
}
if (value instanceof ObjectReference) {
return new JDIObjectValue(target, (ObjectReference) value);
}
if (value instanceof PrimitiveValue) {
return new JDIPrimitiveValue(target, value);
}
if (value instanceof VoidValue) {
return new JDIVoidValue(target);
}
return new JDIValue(target, value);
}
/**
* @see IValue#getValueString()
*/
@Override
public String getValueString() throws DebugException {
if (fValue == null) {
return JDIDebugModelMessages.JDIValue_null_4;
}
if (fValue instanceof StringReference) {
try {
return ((StringReference) fValue).value();
} catch (ObjectCollectedException e) {
return JDIDebugModelMessages.JDIValue_deallocated;
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_value,
new Object[] { e.toString() }), e);
// execution will not reach this line, as
// #targetRequestFailed will thrown an exception
return null;
}
}
if (fValue instanceof ObjectReference) {
StringBuilder name = new StringBuilder();
if (fValue instanceof ClassObjectReference) {
name.append('(');
name.append(((ClassObjectReference) fValue).reflectedType());
name.append(')');
}
long id = 0;
try {
id = ((ObjectReference) fValue).uniqueID();
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_unique_id,
new Object[] { e.toString() }), e);
// execution will not reach this line, as
// #targetRequestFailed will thrown an exception
return null;
}
name.append(" "); //$NON-NLS-1$
name.append(MessageFormat.format(
JDIDebugModelMessages.JDIValue_id_8,
new Object[] { String.valueOf(id) }));
return name.toString();
}
// see bug 43285
return String.valueOf(fValue);
}
/**
* @see IValue#getReferenceTypeName()
*/
@Override
public String getReferenceTypeName() throws DebugException {
try {
if (fValue == null) {
return JDIDebugModelMessages.JDIValue_null_4;
}
return getUnderlyingType().name();
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_reference_type_name,
new Object[] { e.toString() }), e);
// execution will not reach this line, as
// #targetRequestFailed will thrown an exception
return null;
}
}
/**
* @see Object#hashCode()
*/
@Override
public int hashCode() {
if (fValue == null) {
return getClass().hashCode();
}
return fValue.hashCode();
}
/**
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof JDIValue) {
Value other = ((JDIValue) o).getUnderlyingValue();
if (fValue == null) {
return false;
}
if (other == null) {
return false;
}
return fValue.equals(other);
}
return false;
}
/**
* @see IValue#getVariables()
*/
@Override
public IVariable[] getVariables() throws DebugException {
List<IJavaVariable> list = getVariablesList();
return list.toArray(new IVariable[list.size()]);
}
/**
* Returns a list of variables that are children of this value. The result
* is cached.
*
* @return list of variable children
* @throws DebugException
*/
protected synchronized List<IJavaVariable> getVariablesList() throws DebugException {
if (fVariables != null) {
return fVariables;
} else if (fValue instanceof ObjectReference) {
ObjectReference object = (ObjectReference) fValue;
fVariables = new ArrayList<>();
if (isArray()) {
try {
int length = getArrayLength();
for (int i = 0; i < length; i++) {
fVariables.add(new JDIArrayEntryVariable(
getJavaDebugTarget(), getArrayReference(), i,
fLogicalParent));
}
} catch (DebugException e) {
if (e.getCause() instanceof ObjectCollectedException) {
return Collections.EMPTY_LIST;
}
throw e;
}
} else {
List<Field> fields = null;
try {
ReferenceType refType = object.referenceType();
fields = refType.allFields();
} catch (ObjectCollectedException e) {
return Collections.EMPTY_LIST;
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_fields,
new Object[] { e.toString() }), e);
// execution will not reach this line, as
// #targetRequestFailed will thrown an exception
return null;
}
Iterator<Field> list = fields.iterator();
while (list.hasNext()) {
Field field = list.next();
fVariables.add(new JDIFieldVariable(
(JDIDebugTarget) getDebugTarget(), field, object,
fLogicalParent));
}
Collections.sort(fVariables, new Comparator<IJavaVariable>() {
@Override
public int compare(IJavaVariable a, IJavaVariable b) {
return sortChildren(a, b);
}
});
}
return fVariables;
} else {
return Collections.EMPTY_LIST;
}
}
/**
* Group statics and instance variables, sort alphabetically within each
* group.
*/
protected int sortChildren(Object a, Object b) {
IJavaVariable v1 = (IJavaVariable) a;
IJavaVariable v2 = (IJavaVariable) b;
try {
boolean v1isStatic = v1.isStatic();
boolean v2isStatic = v2.isStatic();
if (v1isStatic && !v2isStatic) {
return -1;
}
if (!v1isStatic && v2isStatic) {
return 1;
}
return v1.getName().compareToIgnoreCase(v2.getName());
} catch (DebugException de) {
logError(de);
return -1;
}
}
/**
* Returns whether this value is an array
*/
protected boolean isArray() {
return fValue instanceof ArrayReference;
}
/**
* Returns this value as an array reference, or <code>null</code>
*/
protected ArrayReference getArrayReference() {
if (isArray()) {
return (ArrayReference) fValue;
}
return null;
}
/**
* @see IValue#isAllocated()
*/
@Override
public boolean isAllocated() throws DebugException {
if (fAllocated) {
if (fValue instanceof ObjectReference) {
try {
fAllocated = !((ObjectReference) fValue).isCollected();
} catch (VMDisconnectedException e) {
// if the VM disconnects, this value is not allocated
fAllocated = false;
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_is_collected,
new Object[] { e.toString() }), e);
// execution will fall through, as
// #targetRequestFailed will thrown an exception
}
} else {
JDIDebugTarget dt = (JDIDebugTarget) getDebugTarget();
fAllocated = dt.isAvailable();
}
}
return fAllocated;
}
/**
* @see IJavaValue#getSignature()
*/
@Override
public String getSignature() throws DebugException {
try {
if (fValue != null) {
return fValue.type().signature();
}
return null;
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_type_signature,
new Object[] { e.toString() }), e);
// execution will not reach this line, as
// #targetRequestFailed will thrown an exception
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.debug.core.IJavaValue#getGenericSignature()
*/
@Override
public String getGenericSignature() throws DebugException {
try {
if (fValue != null) {
Type type = fValue.type();
if (type instanceof ReferenceType) {
return ((ReferenceType) type).genericSignature();
}
return null;
}
return null;
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_type_signature,
new Object[] { e.toString() }), e);
// execution will not reach this line, as
// #targetRequestFailed will thrown an exception
return null;
}
}
/**
* @see IJavaValue#getArrayLength()
*/
public int getArrayLength() throws DebugException {
if (isArray()) {
try {
return getArrayReference().length();
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_length_of_array,
new Object[] { e.toString() }), e);
}
}
return -1;
}
/**
* Returns this value's underlying JDI value
*/
protected Value getUnderlyingValue() {
return fValue;
}
/**
* @see IJavaValue#getJavaType()
*/
@Override
public IJavaType getJavaType() throws DebugException {
return JDIType.createType((JDIDebugTarget) getDebugTarget(),
getUnderlyingType());
}
/**
* Returns this value's underlying type.
*
* @return type
* @exception DebugException
* if this method fails. Reasons include:
* <ul>
* <li>Failure communicating with the VM. The
* DebugException's status code contains the underlying
* exception responsible for the failure.</li>
*/
protected Type getUnderlyingType() throws DebugException {
try {
return getUnderlyingValue().type();
} catch (RuntimeException e) {
targetRequestFailed(MessageFormat.format(
JDIDebugModelMessages.JDIValue_exception_retrieving_type,
new Object[] { e.toString() }), e);
// execution will not fall through to here,
// as #requestFailed will throw an exception
return null;
}
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getUnderlyingValue().toString();
}
/**
* @see IValue#hasVariables()
*/
@Override
public boolean hasVariables() throws DebugException {
return getVariablesList().size() > 0;
}
/**
* Sets the value that is the original non-logical value that this child
* value was computed for.
*
* @param logicalParent
* parent value
*/
public void setLogicalParent(IJavaValue logicalParent) {
fLogicalParent = logicalParent;
}
/**
* Returns the value that is the original non-logical value that this child
* value was computed for or <code>null</code> if none
*
* @param logicalParent
* parent value or <code>null</code>
*/
public IJavaValue getLogicalParent() {
return fLogicalParent;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.debug.core.IJavaValue#isNull()
*/
@Override
public boolean isNull() {
return false;
}
}