blob: 090eaf3de644461fe2b05dea0e8a9dd629381a68 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdi.internal;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
import org.eclipse.jdi.internal.jdwp.JdwpID;
import org.eclipse.jdi.internal.jdwp.JdwpObjectID;
import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.InternalException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.Mirror;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.Type;
import com.sun.jdi.Value;
/**
* this class implements the corresponding interfaces declared by the JDI
* specification. See the com.sun.jdi package for more information.
*
*/
public class ArrayReferenceImpl extends ObjectReferenceImpl implements
ArrayReference {
/** JDWP Tag. */
public static final byte tag = JdwpID.ARRAY_TAG;
private int fLength = -1;
/**
* Creates new ArrayReferenceImpl.
* @param vmImpl the VM
* @param objectID the object ID
*/
public ArrayReferenceImpl(VirtualMachineImpl vmImpl, JdwpObjectID objectID) {
super("ArrayReference", vmImpl, objectID); //$NON-NLS-1$
}
/**
* @returns tag.
*/
@Override
public byte getTag() {
return tag;
}
/**
* Returns the {@link Value} from the given index
* @param index the index
* @return the {@link Value} at the given index
* @throws IndexOutOfBoundsException if the index is outside the bounds of the array
* @returns Returns an array component value.
*/
@Override
public Value getValue(int index) throws IndexOutOfBoundsException {
return getValues(index, 1).get(0);
}
/**
* @return all of the components in this array.
*/
@Override
public List<Value> getValues() {
return getValues(0, -1);
}
/**
* Gets all of the values starting at firstIndex and ending at firstIndex+length
*
* @param firstIndex the start
* @param length the number of values to return
* @return the list of {@link Value}s
* @throws IndexOutOfBoundsException if the index is outside the bounds of the array
* @returns Returns a range of array components.
*/
@Override
public List<Value> getValues(int firstIndex, int length)
throws IndexOutOfBoundsException {
int arrayLength = length();
if (firstIndex < 0 || firstIndex >= arrayLength) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Invalid_index_1);
}
if (length == -1) {
// length == -1 means all elements to the end.
length = arrayLength - firstIndex;
} else if (length < -1) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Invalid_number_of_value_to_get_from_array_1);
} else if (firstIndex + length > arrayLength) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Attempted_to_get_more_values_from_array_than_length_of_array_2);
}
// Note that this information should not be cached.
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
write(this, outData); // arrayObject
writeInt(firstIndex, "firstIndex", outData); //$NON-NLS-1$
writeInt(length, "length", outData); //$NON-NLS-1$
JdwpReplyPacket replyPacket = requestVM(
JdwpCommandPacket.AR_GET_VALUES, outBytes);
switch (replyPacket.errorCode()) {
case JdwpReplyPacket.INVALID_INDEX:
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Invalid_index_of_array_reference_given_1);
}
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
/*
* NOTE: The JDWP documentation is not clear on this: it turns out
* that the following is received from the VM: - type tag; - length
* of array; - values of elements.
*/
int type = readByte("type", JdwpID.tagMap(), replyData); //$NON-NLS-1$
int readLength = readInt("length", replyData); //$NON-NLS-1$
// See also ValueImpl.
switch (type) {
// Multidimensional array.
case ArrayReferenceImpl.tag:
// Object references.
case ClassLoaderReferenceImpl.tag:
case ClassObjectReferenceImpl.tag:
case StringReferenceImpl.tag:
case ObjectReferenceImpl.tag:
case ThreadGroupReferenceImpl.tag:
case ThreadReferenceImpl.tag:
return readObjectSequence(readLength, replyData);
// Primitive type.
case BooleanValueImpl.tag:
case ByteValueImpl.tag:
case CharValueImpl.tag:
case DoubleValueImpl.tag:
case FloatValueImpl.tag:
case IntegerValueImpl.tag:
case LongValueImpl.tag:
case ShortValueImpl.tag:
return readPrimitiveSequence(readLength, type, replyData);
case VoidValueImpl.tag:
case 0:
default:
throw new InternalException(
JDIMessages.ArrayReferenceImpl_Invalid_ArrayReference_Value_tag_encountered___2
+ type);
}
} catch (IOException e) {
defaultIOExceptionHandler(e);
return null;
} finally {
handledJdwpRequest();
}
}
/**
* Reads the given length of objects from the given stream
* @param length the number of objects to read
* @param in the stream to read from
* @return the {@link List} of {@link ValueImpl}s
* @throws IOException if the reading fails
* @returns Returns sequence of object reference values.
*/
private List<Value> readObjectSequence(int length, DataInputStream in)
throws IOException {
List<Value> elements = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
ValueImpl value = ObjectReferenceImpl
.readObjectRefWithTag(this, in);
elements.add(value);
}
return elements;
}
/**
* @param length
* the number of primitives to read
* @param type
* the type
* @param in
* the input stream
* @return Returns sequence of values of primitive type.
* @throws IOException
* if reading from the stream encounters a problem
*/
private List<Value> readPrimitiveSequence(int length, int type, DataInputStream in)
throws IOException {
List<Value> elements = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
ValueImpl value = ValueImpl.readWithoutTag(this, type, in);
elements.add(value);
}
return elements;
}
/**
* @return Returns the number of components in this array.
*/
@Override
public int length() {
if (fLength == -1) {
initJdwpRequest();
try {
JdwpReplyPacket replyPacket = requestVM(
JdwpCommandPacket.AR_LENGTH, this);
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
fLength = readInt("length", replyData); //$NON-NLS-1$
} catch (IOException e) {
defaultIOExceptionHandler(e);
return 0;
} finally {
handledJdwpRequest();
}
}
return fLength;
}
/**
* Replaces an array component with another value.
*
* @param index
* the index to set the value in
* @param value
* the new value
* @throws InvalidTypeException
* thrown if the types of the replacements do not match the
* underlying type of the {@link ArrayReference}
* @throws ClassNotLoadedException
* thrown if the class type for the {@link ArrayReference} is
* not loaded or has been GC'd
* @see #setValues(int, List, int, int)
*/
@Override
public void setValue(int index, Value value) throws InvalidTypeException,
ClassNotLoadedException {
ArrayList<Value> list = new ArrayList<>(1);
list.add(value);
setValues(index, list, 0, 1);
}
/**
* Replaces all array components with other values.
*
* @param values
* the new values to set in the array
* @throws InvalidTypeException
* thrown if the types of the replacements do not match the
* underlying type of the {@link ArrayReference}
* @throws ClassNotLoadedException
* thrown if the class type for the {@link ArrayReference} is
* not loaded or has been GC'd
* @see #setValues(int, List, int, int)
*/
@Override
public void setValues(List<? extends Value> values) throws InvalidTypeException,
ClassNotLoadedException {
setValues(0, values, 0, -1);
}
/**
* Replaces a range of array components with other values.
*
* @param index
* offset in this array to start replacing values at
* @param values
* replacement values
* @param srcIndex
* the first offset where values are copied from the given
* replacement values
* @param length
* the number of values to replace in this array
* @throws InvalidTypeException
* thrown if the types of the replacements do not match the
* underlying type of the {@link ArrayReference}
* @throws ClassNotLoadedException
* thrown if the class type for the {@link ArrayReference} is
* not loaded or has been GC'd
*/
@Override
public void setValues(int index, List<? extends Value> values, int srcIndex, int length)
throws InvalidTypeException, ClassNotLoadedException {
if (values == null || values.size() == 0) {
// trying to set nothing should do no work
return;
}
int valuesSize = values.size();
int arrayLength = length();
if (index < 0 || index >= arrayLength) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Invalid_index_1);
}
if (srcIndex < 0 || srcIndex >= valuesSize) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Invalid_srcIndex_2);
}
if (length < -1) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Invalid_number_of_value_to_set_in_array_3);
} else if (length == -1) {
// length == -1 indicates as much values as possible.
length = arrayLength - index;
int lengthTmp = valuesSize - srcIndex;
if (lengthTmp < length) {
length = lengthTmp;
}
} else if (index + length > arrayLength) {
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Attempted_to_set_more_values_in_array_than_length_of_array_3);
} else if (srcIndex + length > valuesSize) {
// Check if enough values are given.
throw new IndexOutOfBoundsException(
JDIMessages.ArrayReferenceImpl_Attempted_to_set_more_values_in_array_than_given_4);
}
// check and convert the values if needed.
List<Value> checkedValues = checkValues(
values.subList(srcIndex, srcIndex + length),
((ArrayTypeImpl) referenceType()).componentType());
// Note that this information should not be cached.
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
write(this, outData);
writeInt(index, "index", outData); //$NON-NLS-1$
writeInt(length, "length", outData); //$NON-NLS-1$
Iterator<Value> iterValues = checkedValues.iterator();
while (iterValues.hasNext()) {
ValueImpl value = (ValueImpl) iterValues.next();
if (value != null) {
value.write(this, outData);
} else {
ValueImpl.writeNull(this, outData);
}
}
JdwpReplyPacket replyPacket = requestVM(
JdwpCommandPacket.AR_SET_VALUES, outBytes);
switch (replyPacket.errorCode()) {
case JdwpReplyPacket.TYPE_MISMATCH:
throw new InvalidTypeException();
case JdwpReplyPacket.INVALID_CLASS:
throw new ClassNotLoadedException(type().name());
}
defaultReplyErrorHandler(replyPacket.errorCode());
} catch (IOException e) {
defaultIOExceptionHandler(e);
} finally {
handledJdwpRequest();
}
}
/**
* Check the type and the VM of the values. If the given type is a primitive
* type, the values may be converted to match this type.
*
* @param values
* the value(s) to check
* @param type
* the type to compare the values to
* @return the list of values converted to the given type
* @throws InvalidTypeException
* if the underlying type of an object in the list is not
* compatible
*
* @see ValueImpl#checkValue(Value, Type, VirtualMachineImpl)
*/
private List<Value> checkValues(List<? extends Value> values, Type type)
throws InvalidTypeException {
List<Value> checkedValues = new ArrayList<>(values.size());
Iterator<? extends Value> iterValues = values.iterator();
while (iterValues.hasNext()) {
checkedValues.add(ValueImpl.checkValue(iterValues.next(),
type, virtualMachineImpl()));
}
return checkedValues;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdi.internal.ObjectReferenceImpl#toString()
*/
@Override
public String toString() {
try {
StringBuffer buf = new StringBuffer(type().name());
// Insert length of string between (last) square braces.
buf.insert(buf.length() - 1, length());
// Append space and idString.
buf.append(' ');
buf.append(idString());
return buf.toString();
} catch (ObjectCollectedException e) {
return JDIMessages.ArrayReferenceImpl__Garbage_Collected__ArrayReference_5
+ "[" + length() + "] " + idString(); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
return fDescription;
}
}
/**
* Reads JDWP representation and returns new instance.
*
* @param target
* the target {@link Mirror} object
* @param in
* the input stream to read from
* @return Reads JDWP representation and returns new instance.
* @throws IOException
* if there is a problem reading from the stream
*/
public static ArrayReferenceImpl read(MirrorImpl target, DataInputStream in)
throws IOException {
VirtualMachineImpl vmImpl = target.virtualMachineImpl();
JdwpObjectID ID = new JdwpObjectID(vmImpl);
ID.read(in);
if (target.fVerboseWriter != null) {
target.fVerboseWriter.println("arrayReference", ID.value()); //$NON-NLS-1$
}
if (ID.isNull()) {
return null;
}
ArrayReferenceImpl mirror = new ArrayReferenceImpl(vmImpl, ID);
return mirror;
}
}