/*
 * Copyright (c) 1998, 2011, 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.
/** * 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<Value> list = getValues(index, 1); return 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.
/** * 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); } } }