/* * 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 com.sun.jdi.BooleanValue; import com.sun.jdi.ByteValue; import com.sun.jdi.CharValue; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.DoubleValue; import com.sun.jdi.Field; import com.sun.jdi.FloatValue; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.IntegerValue; import com.sun.jdi.InvalidTypeException; import com.sun.jdi.InvocationException; import com.sun.jdi.LongValue; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; import com.sun.jdi.ShortValue; import com.sun.jdi.StringReference; import com.sun.jdi.Value; import com.sun.jdi.ClassType; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * Represents an Visage sequence (instanceof org.visage.runtime.sequence.Sequence). * For now, this class provides an ArrayReference-like interface for Sequences - in the future * we'll add more operations such as sequence insert/delete/slice etc. * * @author sundar */ public class VisageSequenceReference extends VisageObjectReference { // keep this in sync. with org.visage.runtime.TypeInfo.Types enum. /** * The possible types of a sequence element */ public enum Types { INT, FLOAT, OBJECT, DOUBLE, BOOLEAN, LONG, SHORT, BYTE, CHAR, OTHER } // element type of this sequence private Types elementType; public VisageSequenceReference(VisageVirtualMachine visagevm, ObjectReference underlying) { super(visagevm, underlying); } /** * Returns the element type of this sequence. * * @return the type of this sequences's elements */ public Types getElementType() { if (elementType == null) { Method getElementTypeMethod = virtualMachine().visageSequenceType().getElementTypeMethod(); Exception theExc = null; try { Value typeInfo = invokeMethod(virtualMachine().uiThread(), getElementTypeMethod, Collections.EMPTY_LIST, ClassType.INVOKE_SINGLE_THREADED); elementType = typesFromTypeInfo((ObjectReference)typeInfo); } catch(InvalidTypeException ee) { theExc = ee; } catch(ClassNotLoadedException ee) { theExc = ee; } catch(IncompatibleThreadStateException ee) { theExc = ee; } catch(InvocationException ee) { theExc = ee; } if (theExc != null) { virtualMachine().setLastFieldAccessException(theExc); } } return elementType; } /** * Returns the number of elements in this sequence * * @return the integer count of elements in this sequence. */ public int size() { Method sizeMethod = virtualMachine().visageSequenceType().sizeMethod(); Exception theExc = null; try { Value value = invokeMethod(virtualMachine().uiThread(), sizeMethod, Collections.EMPTY_LIST, ClassType.INVOKE_SINGLE_THREADED); return ((IntegerValue)value).intValue(); } catch(InvalidTypeException ee) { theExc = ee; } catch(ClassNotLoadedException ee) { theExc = ee; } catch(IncompatibleThreadStateException ee) { theExc = ee; } catch(InvocationException ee) { theExc = ee; } virtualMachine().setLastFieldAccessException(theExc); return 0; } /** * Returns the number of elements in this sequence. * (This is a synonym for size().) * * @return the integer count of elements in this sequence. */ public int length() { return size(); } /** * Returns the value of a sequence element. * * @param index the index of the element to retrieve * @return the {@link Value} at the given index, or the default value for * the sequence's element type * if <CODE><I>index</I></CODE> is outside the range of this sequence, * that is, if either of the following are true: * <PRE> * <I>index</I> < 0 * <I>index</I> >= {@link #length() length()} </PRE> */ public Value getValue(int index) { Types type = getElementType(); switch (type) { case INT: return getValueAsInt(index); case FLOAT: return getValueAsFloat(index); case OBJECT: return getValueAsObject(index); case DOUBLE: return getValueAsDouble(index); case BOOLEAN: return getValueAsBoolean(index); case LONG: return getValueAsLong(index); case SHORT: return getValueAsShort(index); case BYTE: return getValueAsByte(index); case CHAR: return getValueAsChar(index); case OTHER: return getValueAsObject(index); default: throw new IllegalArgumentException("Invalid sequence element type"); } } /** * Returns a range of sequence elements. * * @param index the index of the first element to retrieve * @param length the number of elements to retrieve, or -1 to * retrieve all elements to the end of this sequence. * @return a list of {@link Value} objects, one for each requested * sequence element ordered by array index. When there are * no elements in the specified range (e.g. * <CODE><I>length</I></CODE> is zero) an empty list is returned * Returns the default value for the sequence's element type for indicies in the * specified range that are outside the range of the sequence. */ public List<Value> getValues(int index, int length) { List<Value> values = new ArrayList<Value>(length); for (int i = 0; i < length; i++) { values.add(getValue(index + i)); } return values; } /** * Replace a sequence element with another value. * * Object values must be assignment compatible with the element type. * (This implies that the component type must be loaded through the * declaring class's class loader). Primitive values must be * assignment compatible with the component type. * * @param value the new value * @param index the index of the component to set. If this is beyond the * end of the sequence, the new value is appended to the sequence. * * @throws InvalidTypeException if the type of <CODE><I>value</I></CODE> * is not compatible with the declared type of sequence elements. * @throws ClassNotLoadedException if the sequence element type * has not yet been loaded through the appropriate class loader. * @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link com.sun.jdi.VirtualMachine#canBeModified()}. * @return a new sequence with the specified element replaced/added. */ public VisageSequenceReference setValue(int index, Value value) { Types type = getElementType(); switch (type) { case INT: return setIntValue(index, (IntegerValue)value); case FLOAT: return setFloatValue(index, (FloatValue)value); case OBJECT: return setObjectValue(index, (ObjectReference)value); case DOUBLE: return setDoubleValue(index, (DoubleValue)value); case BOOLEAN: return setBooleanValue(index, (BooleanValue)value); case LONG: return setLongValue(index, (LongValue)value); case SHORT: return setShortValue(index, (ShortValue)value); case BYTE: return setByteValue(index, (ByteValue)value); case CHAR: return setCharValue(index, (CharValue)value); case OTHER: return setObjectValue(index, (ObjectReference)value); default: throw new IllegalArgumentException("Invalid sequence element type"); } } /** * Return a sequence which is a copy of this sequence with the first {@link #length()} elements * replaced by the elements in <CODE><I>values</I></CODE> * * @throws InvalidTypeException if the type of an element of <CODE><I>values</I></CODE> * is not compatible with the declared type of sequence elements. * * @return a copy of this sequence with the first {@link #length()} elements replaced by the * elements of <CODE><I>values</I></CODE> */ public VisageSequenceReference setValues(List<? extends Value> values) { final int len = length(); VisageSequenceReference result = null; Iterator<? extends Value> valuesItr = values.iterator(); for (int i = 0; i < len; i++) { if (! valuesItr.hasNext()) { break; } result = setValue(i, valuesItr.next()); } return result; } // Internals only below this point private BooleanValue getValueAsBoolean(int index) { Method getAsBooleanMethod = virtualMachine().visageSequenceType().getAsBooleanMethod(); return (BooleanValue) getElement(getAsBooleanMethod, index); } private CharValue getValueAsChar(int index) { Method getAsCharMethod = virtualMachine().visageSequenceType().getAsCharMethod(); return (CharValue) getElement(getAsCharMethod, index); } private ByteValue getValueAsByte(int index) { Method getAsByteMethod = virtualMachine().visageSequenceType().getAsByteMethod(); return (ByteValue) getElement(getAsByteMethod, index); } private ShortValue getValueAsShort(int index) { Method getAsShortMethod = virtualMachine().visageSequenceType().getAsShortMethod(); return (ShortValue) getElement(getAsShortMethod, index); } private IntegerValue getValueAsInt(int index) { Method getAsIntMethod = virtualMachine().visageSequenceType().getAsIntMethod(); return (IntegerValue) getElement(getAsIntMethod, index); } private LongValue getValueAsLong(int index) { Method getAsLongMethod = virtualMachine().visageSequenceType().getAsLongMethod(); return (LongValue) getElement(getAsLongMethod, index); } private FloatValue getValueAsFloat(int index) { Method getAsFloatMethod = virtualMachine().visageSequenceType().getAsFloatMethod(); return (FloatValue) getElement(getAsFloatMethod, index); } private DoubleValue getValueAsDouble(int index) { Method getAsDoubleMethod = virtualMachine().visageSequenceType().getAsDoubleMethod(); return (DoubleValue) getElement(getAsDoubleMethod, index); } private ObjectReference getValueAsObject(int index) { Method getMethod = virtualMachine().visageSequenceType().getMethod(); return (ObjectReference) getElement(getMethod, index); } private VisageSequenceReference setIntValue(int index, IntegerValue value) { Method setIntElementMethod = virtualMachine().visageSequencesType().setIntElementMethod(); return setElement(setIntElementMethod, index, value); } private VisageSequenceReference setFloatValue(int index, FloatValue value) { Method setFloatElementMethod = virtualMachine().visageSequencesType().setFloatElementMethod(); return setElement(setFloatElementMethod, index, value); } private VisageSequenceReference setObjectValue(int index, ObjectReference value) { Method setObjectElementMethod = virtualMachine().visageSequencesType().setObjectElementMethod(); return setElement(setObjectElementMethod, index, value); } private VisageSequenceReference setDoubleValue(int index, DoubleValue value) { Method setDoubleElementMethod = virtualMachine().visageSequencesType().setDoubleElementMethod(); return setElement(setDoubleElementMethod, index, value); } private VisageSequenceReference setBooleanValue(int index, BooleanValue value) { Method setBooleanElementMethod = virtualMachine().visageSequencesType().setBooleanElementMethod(); return setElement(setBooleanElementMethod, index, value); } private VisageSequenceReference setLongValue(int index, LongValue value) { Method setLongElementMethod = virtualMachine().visageSequencesType().setLongElementMethod(); return setElement(setLongElementMethod, index, value); } private VisageSequenceReference setShortValue(int index, ShortValue value) { Method setShortElementMethod = virtualMachine().visageSequencesType().setShortElementMethod(); return setElement(setShortElementMethod, index, value); } private VisageSequenceReference setByteValue(int index, ByteValue value) { Method setByteElementMethod = virtualMachine().visageSequencesType().setByteElementMethod(); return setElement(setByteElementMethod, index, value); } private VisageSequenceReference setCharValue(int index, CharValue value) { Method setCharElementMethod = virtualMachine().visageSequencesType().setCharElementMethod(); return setElement(setCharElementMethod, index, value); } // Internals only below this point private Value getElement(Method method, int index) { List<Value> args = new ArrayList<Value>(1); args.add(virtualMachine().mirrorOf(index)); Exception theExc; try { return invokeMethod(virtualMachine().uiThread(), method, args, ClassType.INVOKE_SINGLE_THREADED); } catch(InvalidTypeException ee) { theExc = ee; } catch(ClassNotLoadedException ee) { theExc = ee; } catch(IncompatibleThreadStateException ee) { theExc = ee; } catch(InvocationException ee) { theExc = ee; } virtualMachine().setLastFieldAccessException(theExc); return defaultValue(getElementType()); } private VisageSequenceReference setElement(Method method, int index, Value value) { List<Value> args = new ArrayList<Value>(3); args.add(this); args.add(value); args.add(virtualMachine().mirrorOf(index)); Exception theExc; try { return (VisageSequenceReference) virtualMachine().visageSequencesType(). invokeMethod(virtualMachine().uiThread(), method, args, ClassType.INVOKE_SINGLE_THREADED); } catch(InvalidTypeException ee) { theExc = ee; } catch(ClassNotLoadedException ee) { theExc = ee; } catch(IncompatibleThreadStateException ee) { theExc = ee; } catch(InvocationException ee) { theExc = ee; } virtualMachine().setLastFieldAccessException(theExc); return this; } private Types typesFromTypeInfo(ObjectReference typeInfo) { Field typeField = typeInfo.referenceType().fieldByName("type"); ObjectReference typeValue = (ObjectReference) typeInfo.getValue(typeField); Field nameField = typeValue.referenceType().fieldByName("name"); String typeName = ((StringReference)typeValue.getValue(nameField)).value(); return Types.valueOf(typeName); } private Value defaultValue(Types type) { VisageVirtualMachine visagevm = virtualMachine(); switch (type) { case BOOLEAN: return visagevm.booleanDefaultValue(); case BYTE: return visagevm.byteDefaultValue(); case CHAR: return visagevm.charDefaultValue(); case DOUBLE: return visagevm.doubleDefaultValue(); case FLOAT: return visagevm.floatDefaultValue(); case INT: return visagevm.integerDefaultValue(); case LONG: return visagevm.longDefaultValue(); case SHORT: return visagevm.shortDefaultValue(); default: return null; } } }