| /******************************************************************************* |
| * 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; |
| } |
| |
| } |