1 /*
2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.tools.jdi;
27 
28 import com.sun.jdi.*;
29 
30 import java.util.List;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Iterator;
34 
35 public class ArrayReferenceImpl extends ObjectReferenceImpl
36     implements ArrayReference
37 {
38     int length = -1;
39 
ArrayReferenceImpl(VirtualMachine aVm,long aRef)40     ArrayReferenceImpl(VirtualMachine aVm,long aRef) {
41         super(aVm,aRef);
42     }
43 
invokableReferenceType(Method method)44     protected ClassTypeImpl invokableReferenceType(Method method) {
45         // The method has to be a method on Object since
46         // arrays don't have methods nor any other 'superclasses'
47         // So, use the ClassTypeImpl for Object instead of
48         // the ArrayTypeImpl for the array itself.
49         return (ClassTypeImpl)method.declaringType();
50     }
51 
arrayType()52     ArrayTypeImpl arrayType() {
53         return (ArrayTypeImpl)type();
54     }
55 
56     /**
57      * Return array length.
58      * Need not be synchronized since it cannot be provably stale.
59      */
length()60     public int length() {
61         if(length == -1) {
62             try {
63                 length = JDWP.ArrayReference.Length.
64                     process(vm, this).arrayLength;
65             } catch (JDWPException exc) {
66                 throw exc.toJDIException();
67             }
68         }
69         return length;
70     }
71 
getValue(int index)72     public Value getValue(int index) {
73         List<Value> list = getValues(index, 1);
74         return list.get(0);
75     }
76 
getValues()77     public List<Value> getValues() {
78         return getValues(0, -1);
79     }
80 
81     /**
82      * Validate that the range to set/get is valid.
83      * length of -1 (meaning rest of array) has been converted
84      * before entry.
85      */
validateArrayAccess(int index, int length)86     private void validateArrayAccess(int index, int length) {
87         // because length can be computed from index,
88         // index must be tested first for correct error message
89         if ((index < 0) || (index > length())) {
90             throw new IndexOutOfBoundsException(
91                         "Invalid array index: " + index);
92         }
93         if (length < 0) {
94             throw new IndexOutOfBoundsException(
95                         "Invalid array range length: " + length);
96         }
97         if (index + length > length()) {
98             throw new IndexOutOfBoundsException(
99                         "Invalid array range: " +
100                         index + " to " + (index + length - 1));
101         }
102     }
103 
104     @SuppressWarnings("unchecked")
cast(Object x)105     private static <T> T cast(Object x) {
106         return (T)x;
107     }
108 
getValues(int index, int length)109     public List<Value> getValues(int index, int length) {
110         if (length == -1) { // -1 means the rest of the array
111            length = length() - index;
112         }
113         validateArrayAccess(index, length);
114         if (length == 0) {
115             return new ArrayList<Value>();
116         }
117 
118         List<Value> vals;
119         try {
120             vals = cast(JDWP.ArrayReference.GetValues.process(vm, this, index, length).values);
121         } catch (JDWPException exc) {
122             throw exc.toJDIException();
123         }
124 
125         return vals;
126     }
127 
setValue(int index, Value value)128     public void setValue(int index, Value value)
129             throws InvalidTypeException,
130                    ClassNotLoadedException {
131         List<Value> list = new ArrayList<Value>(1);
132         list.add(value);
133         setValues(index, list, 0, 1);
134     }
135 
setValues(List<? extends Value> values)136     public void setValues(List<? extends Value> values)
137             throws InvalidTypeException,
138                    ClassNotLoadedException {
139         setValues(0, values, 0, -1);
140     }
141 
setValues(int index, List<? extends Value> values, int srcIndex, int length)142     public void setValues(int index, List<? extends Value> values,
143                           int srcIndex, int length)
144             throws InvalidTypeException,
145                    ClassNotLoadedException {
146 
147         if (length == -1) { // -1 means the rest of the array
148             // shorter of, the rest of the array and rest of
149             // the source values
150             length = Math.min(length() - index,
151                               values.size() - srcIndex);
152         }
153         validateMirrorsOrNulls(values);
154         validateArrayAccess(index, length);
155 
156         if ((srcIndex < 0) || (srcIndex > values.size())) {
157             throw new IndexOutOfBoundsException(
158                         "Invalid source index: " + srcIndex);
159         }
160         if (srcIndex + length > values.size()) {
161             throw new IndexOutOfBoundsException(
162                         "Invalid source range: " +
163                         srcIndex + " to " +
164                         (srcIndex + length - 1));
165         }
166 
167         boolean somethingToSet = false;;
168         ValueImpl[] setValues = new ValueImpl[length];
169 
170         for (int i = 0; i < length; i++) {
171             ValueImpl value = (ValueImpl)values.get(srcIndex + i);
172 
173             try {
174                 // Validate and convert if necessary
175                 setValues[i] =
176                   ValueImpl.prepareForAssignment(value,
177                                                  new Component());
178                 somethingToSet = true;
179             } catch (ClassNotLoadedException e) {
180                 /*
181                  * Since we got this exception,
182                  * the component must be a reference type.
183                  * This means the class has not yet been loaded
184                  * through the defining class's class loader.
185                  * If the value we're trying to set is null,
186                  * then setting to null is essentially a
187                  * no-op, and we should allow it without an
188                  * exception.
189                  */
190                 if (value != null) {
191                     throw e;
192                 }
193             }
194         }
195         if (somethingToSet) {
196             try {
197                 JDWP.ArrayReference.SetValues.
198                     process(vm, this, index, setValues);
199             } catch (JDWPException exc) {
200                 throw exc.toJDIException();
201             }
202         }
203     }
204 
toString()205     public String toString() {
206         return "instance of " + arrayType().componentTypeName() +
207                "[" + length() + "] (id=" + uniqueID() + ")";
208     }
209 
typeValueKey()210     byte typeValueKey() {
211         return JDWP.Tag.ARRAY;
212     }
213 
validateAssignment(ValueContainer destination)214     void validateAssignment(ValueContainer destination)
215                             throws InvalidTypeException, ClassNotLoadedException {
216         try {
217             super.validateAssignment(destination);
218         } catch (ClassNotLoadedException e) {
219             /*
220              * An array can be used extensively without the
221              * enclosing loader being recorded by the VM as an
222              * initiating loader of the array type. In addition, the
223              * load of an array class is fairly harmless as long as
224              * the component class is already loaded. So we relax the
225              * rules a bit and allow the assignment as long as the
226              * ultimate component types are assignable.
227              */
228             boolean valid = false;
229             JNITypeParser destParser = new JNITypeParser(
230                                        destination.signature());
231             JNITypeParser srcParser = new JNITypeParser(
232                                        arrayType().signature());
233             int destDims = destParser.dimensionCount();
234             if (destDims <= srcParser.dimensionCount()) {
235                 /*
236                  * Remove all dimensions from the destination. Remove
237                  * the same number of dimensions from the source.
238                  * Get types for both and check to see if they are
239                  * compatible.
240                  */
241                 String destComponentSignature =
242                     destParser.componentSignature(destDims);
243                 Type destComponentType =
244                     destination.findType(destComponentSignature);
245                 String srcComponentSignature =
246                     srcParser.componentSignature(destDims);
247                 Type srcComponentType =
248                     arrayType().findComponentType(srcComponentSignature);
249                 valid = ArrayTypeImpl.isComponentAssignable(destComponentType,
250                                                           srcComponentType);
251             }
252 
253             if (!valid) {
254                 throw new InvalidTypeException("Cannot assign " +
255                                                arrayType().name() +
256                                                " to " +
257                                                destination.typeName());
258             }
259         }
260     }
261 
262     /*
263      * Represents an array component to other internal parts of this
264      * implementation. This is not exposed at the JDI level. Currently,
265      * this class is needed only for type checking so it does not even
266      * reference a particular component - just a generic component
267      * of this array. In the future we may need to expand its use.
268      */
269     class Component implements ValueContainer {
type()270         public Type type() throws ClassNotLoadedException {
271             return arrayType().componentType();
272         }
typeName()273         public String typeName() {
274             return arrayType().componentTypeName();
275         }
signature()276         public String signature() {
277             return arrayType().componentSignature();
278         }
findType(String signature)279         public Type findType(String signature) throws ClassNotLoadedException {
280             return arrayType().findComponentType(signature);
281         }
282     }
283 }
284