| /* |
| * Copyright (c) 1998, 2004, Oracle and/or its affiliates. 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. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.tools.jdi; |
| |
| import com.sun.jdi.*; |
| |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| |
| public class ArrayReferenceImpl extends ObjectReferenceImpl |
| implements ArrayReference |
| { |
| int length = -1; |
| |
| ArrayReferenceImpl(VirtualMachine aVm,long aRef) { |
| super(aVm,aRef); |
| } |
| |
| protected ClassTypeImpl invokableReferenceType(Method method) { |
| // The method has to be a method on Object since |
| // arrays don't have methods nor any other 'superclasses' |
| // So, use the ClassTypeImpl for Object instead of |
| // the ArrayTypeImpl for the array itself. |
| return (ClassTypeImpl)method.declaringType(); |
| } |
| |
| ArrayTypeImpl arrayType() { |
| return (ArrayTypeImpl)type(); |
| } |
| |
| /** |
| * Return array length. |
| * Need not be synchronized since it cannot be provably stale. |
| */ |
| public int length() { |
| if(length == -1) { |
| try { |
| length = JDWP.ArrayReference.Length. |
| process(vm, this).arrayLength; |
| } catch (JDWPException exc) { |
| throw exc.toJDIException(); |
| } |
| } |
| return length; |
| } |
| |
| public Value getValue(int index) { |
| List list = getValues(index, 1); |
| return (Value)list.get(0); |
| } |
| |
| public List<Value> getValues() { |
| return getValues(0, -1); |
| } |
| |
| /** |
| * Validate that the range to set/get is valid. |
| * length of -1 (meaning rest of array) has been converted |
| * before entry. |
| */ |
| private void validateArrayAccess(int index, int length) { |
| // because length can be computed from index, |
| // index must be tested first for correct error message |
| if ((index < 0) || (index > length())) { |
| throw new IndexOutOfBoundsException( |
| "Invalid array index: " + index); |
| } |
| if (length < 0) { |
| throw new IndexOutOfBoundsException( |
| "Invalid array range length: " + length); |
| } |
| if (index + length > length()) { |
| throw new IndexOutOfBoundsException( |
| "Invalid array range: " + |
| index + " to " + (index + length - 1)); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static <T> T cast(Object x) { |
| return (T)x; |
| } |
| |
| public List<Value> getValues(int index, int length) { |
| if (length == -1) { // -1 means the rest of the array |
| length = length() - index; |
| } |
| validateArrayAccess(index, length); |
| if (length == 0) { |
| return new ArrayList<Value>(); |
| } |
| |
| List<Value> vals; |
| try { |
| vals = cast(JDWP.ArrayReference.GetValues.process(vm, this, index, length).values); |
| } catch (JDWPException exc) { |
| throw exc.toJDIException(); |
| } |
| |
| return vals; |
| } |
| |
| public void setValue(int index, Value value) |
| throws InvalidTypeException, |
| ClassNotLoadedException { |
| List<Value> list = new ArrayList<Value>(1); |
| list.add(value); |
| setValues(index, list, 0, 1); |
| } |
| |
| public void setValues(List<? extends Value> values) |
| throws InvalidTypeException, |
| ClassNotLoadedException { |
| setValues(0, values, 0, -1); |
| } |
| |
| public void setValues(int index, List<? extends Value> values, |
| int srcIndex, int length) |
| throws InvalidTypeException, |
| ClassNotLoadedException { |
| |
| if (length == -1) { // -1 means the rest of the array |
| // shorter of, the rest of the array and rest of |
| // the source values |
| length = Math.min(length() - index, |
| values.size() - srcIndex); |
| } |
| validateMirrorsOrNulls(values); |
| validateArrayAccess(index, length); |
| |
| if ((srcIndex < 0) || (srcIndex > values.size())) { |
| throw new IndexOutOfBoundsException( |
| "Invalid source index: " + srcIndex); |
| } |
| if (srcIndex + length > values.size()) { |
| throw new IndexOutOfBoundsException( |
| "Invalid source range: " + |
| srcIndex + " to " + |
| (srcIndex + length - 1)); |
| } |
| |
| boolean somethingToSet = false;; |
| ValueImpl[] setValues = new ValueImpl[length]; |
| |
| for (int i = 0; i < length; i++) { |
| ValueImpl value = (ValueImpl)values.get(srcIndex + i); |
| |
| try { |
| // Validate and convert if necessary |
| setValues[i] = |
| ValueImpl.prepareForAssignment(value, |
| new Component()); |
| somethingToSet = true; |
| } catch (ClassNotLoadedException e) { |
| /* |
| * Since we got this exception, |
| * the component must be a reference type. |
| * This means the class has not yet been loaded |
| * through the defining class's class loader. |
| * If the value we're trying to set is null, |
| * then setting to null is essentially a |
| * no-op, and we should allow it without an |
| * exception. |
| */ |
| if (value != null) { |
| throw e; |
| } |
| } |
| } |
| if (somethingToSet) { |
| try { |
| JDWP.ArrayReference.SetValues. |
| process(vm, this, index, setValues); |
| } catch (JDWPException exc) { |
| throw exc.toJDIException(); |
| } |
| } |
| } |
| |
| public String toString() { |
| return "instance of " + arrayType().componentTypeName() + |
| "[" + length() + "] (id=" + uniqueID() + ")"; |
| } |
| |
| byte typeValueKey() { |
| return JDWP.Tag.ARRAY; |
| } |
| |
| void validateAssignment(ValueContainer destination) |
| throws InvalidTypeException, ClassNotLoadedException { |
| try { |
| super.validateAssignment(destination); |
| } catch (ClassNotLoadedException e) { |
| /* |
| * An array can be used extensively without the |
| * enclosing loader being recorded by the VM as an |
| * initiating loader of the array type. In addition, the |
| * load of an array class is fairly harmless as long as |
| * the component class is already loaded. So we relax the |
| * rules a bit and allow the assignment as long as the |
| * ultimate component types are assignable. |
| */ |
| boolean valid = false; |
| JNITypeParser destParser = new JNITypeParser( |
| destination.signature()); |
| JNITypeParser srcParser = new JNITypeParser( |
| arrayType().signature()); |
| int destDims = destParser.dimensionCount(); |
| if (destDims <= srcParser.dimensionCount()) { |
| /* |
| * Remove all dimensions from the destination. Remove |
| * the same number of dimensions from the source. |
| * Get types for both and check to see if they are |
| * compatible. |
| */ |
| String destComponentSignature = |
| destParser.componentSignature(destDims); |
| Type destComponentType = |
| destination.findType(destComponentSignature); |
| String srcComponentSignature = |
| srcParser.componentSignature(destDims); |
| Type srcComponentType = |
| arrayType().findComponentType(srcComponentSignature); |
| valid = ArrayTypeImpl.isComponentAssignable(destComponentType, |
| srcComponentType); |
| } |
| |
| if (!valid) { |
| throw new InvalidTypeException("Cannot assign " + |
| arrayType().name() + |
| " to " + |
| destination.typeName()); |
| } |
| } |
| } |
| |
| /* |
| * Represents an array component to other internal parts of this |
| * implementation. This is not exposed at the JDI level. Currently, |
| * this class is needed only for type checking so it does not even |
| * reference a particular component - just a generic component |
| * of this array. In the future we may need to expand its use. |
| */ |
| class Component implements ValueContainer { |
| public Type type() throws ClassNotLoadedException { |
| return arrayType().componentType(); |
| } |
| public String typeName() { |
| return arrayType().componentTypeName(); |
| } |
| public String signature() { |
| return arrayType().componentSignature(); |
| } |
| public Type findType(String signature) throws ClassNotLoadedException { |
| return arrayType().findComponentType(signature); |
| } |
| } |
| } |