1 /*
2  * Copyright (c) 2005, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug     4655503
27  * @summary Test for array cloning and slicing methods.
28  * @author  John Rose
29  * @key randomness
30  */
31 
32 import java.util.*;
33 import java.lang.reflect.*;
34 
35 public class CopyMethods {
36     static int muzzle;  // if !=0, suppresses ("muzzles") messages
37 
38     static int maxLen = 40;  // maximum length of test arrays
39     static int shortStepsNear = 4;  // interesting span near critical values
40     static int downShift = 3;
41 
42     static int testCasesRun = 0;
43     static long consing = 0;
44 
45     // very simple tests, mainly to test the framework itself
simpleTests()46     static void simpleTests() {
47         int[] a = (int[]) makeArray(3, int.class);
48         if (muzzle == 0)
49             System.out.println("int[] a = "+Arrays.toString(a));
50         check(a.length == 3);
51         check(a[0] == testValues[0]);
52         check(a[1] == testValues[1]);
53         check(a[2] == testValues[2]);
54         checkArray(a, int.class, 3, 0, 3);
55         // negative test of testing framework:
56         for (int bad = -2; bad < a.length; bad++) {
57             try {
58                 int[] aa = a.clone();
59                 if (bad < 0)  aa = new int[4];
60                 else          aa[bad] = 0;
61                 ++muzzle;
62                 // the following check should fail!
63                 if (bad == -2)
64                     checkArray(new String[3], int.class, 0, 0, a.length);
65                 else
66                     checkArray(aa, int.class, 0, 0, a.length);
67                 throw new Error("Should Not Reach Here");
68             } catch (RuntimeException ee) {
69                 --muzzle;
70                 if (muzzle == 0)
71                     System.out.println("Expected: "+ee);
72             }
73         }
74         checkArray(Arrays.copyOf(a, 0), int.class, 0, 0, 3);
75         checkArray(Arrays.copyOf(a, 1), int.class, 1, 0, 3);
76         checkArray(Arrays.copyOf(a, 2), int.class, 2, 0, 3);
77         checkArray(Arrays.copyOf(a, 3), int.class, 3, 0, 3);
78         checkArray(Arrays.copyOf(a, 4), int.class, 4, 0, 3);
79 
80         // quick test of copyOfRange
81         int[] ar = Arrays.copyOfRange(a, 1, 3);
82         check(ar.length == 2);
83         check(ar[0] == a[1]);
84         check(ar[1] == a[2]);
85         checkArray(ar, int.class, 2, 1, 2);
86         ar = Arrays.copyOfRange(a, 2, 4);
87         check(ar.length == 2);
88         check(ar[0] == a[2]);
89         check(ar[1] == 0);
90         checkArray(ar, int.class, 2, 2, 1);
91         ar = Arrays.copyOfRange(a, 3, 5);
92         check(ar.length == 2);
93         check(ar[0] == 0);
94         check(ar[1] == 0);
95         checkArray(ar, int.class, 2, 3, 0);
96         byte[] ba = (byte[]) makeArray(3, byte.class);
97         if (muzzle == 0)
98             System.out.println("byte[] ba = "+Arrays.toString(ba));
99         for (int j = 0; j <= ba.length+2; j++) {
100             byte[] bb = Arrays.copyOf(ba, j);
101             if (muzzle == 0)
102                 System.out.println("copyOf(ba,"+j+") = "+
103                                    Arrays.toString(bb));
104             checkArray(bb, byte.class, j, 0, ba.length);
105             byte[] bbr = Arrays.copyOfRange(ba, 0, j);
106             check(Arrays.equals(bb, bbr));
107         }
108         for (int i = 0; i <= a.length; i++) {
109             for (int j = i; j <= a.length+2; j++) {
110                 byte[] br = Arrays.copyOfRange(ba, i, j);
111                 if (muzzle == 0)
112                     System.out.println("copyOfRange(ba,"+i+","+j+") = "+
113                                        Arrays.toString(br));
114                 checkArray(br, byte.class, j-i, i, ba.length-i);
115             }
116         }
117         String[] sa = (String[]) makeArray(3, String.class);
118         if (muzzle == 0)
119             System.out.println("String[] sa = "+Arrays.toString(sa));
120         check(sa[0].equals(Integer.toHexString(testValues[0])));
121         check(sa[1].equals(Integer.toHexString(testValues[1])));
122         check(sa[2].equals(Integer.toHexString(testValues[2])));
123         checkArray(sa, String.class, sa.length, 0, sa.length);
124         String[] sa4 = Arrays.copyOf(sa, sa.length+1);
125         check(sa4[0] == sa[0]);
126         check(sa4[1] == sa[1]);
127         check(sa4[2] == sa[2]);
128         check(sa4[sa.length] == null);
129         checkArray(sa4, String.class, sa4.length, 0, sa.length);
130         String[] sr4 = Arrays.copyOfRange(sa, 1, 5);
131         check(sr4[0] == sa[1]);
132         check(sr4[1] == sa[2]);
133         check(sr4[2] == null);
134         check(sr4[3] == null);
135         checkArray(sr4, String.class, 4, 1, sa.length-1);
136         if (muzzle == 0)
137             System.out.println("simpleTests done");
138     }
139 
140     // the framework:  a fixed series of test values
141     static final int[] testValues;
142     static {
143         testValues = new int[1000];
144         Random r = new Random();
145         for (int i = 0; i < testValues.length; i++) {
146             testValues[i] = r.nextInt();
147         }
148     }
149     /** Return a canonical test value of a desired index and type.
150      *  The original test values are random ints.  Derive other test
151      *  values as follows:
152      *  <pre>
153      *  int tv = testValues[i]
154      *  (C)tv                    C is byte, short, char, long, float, double
155      *  (tv&1)!=0                C is boolean
156      *  (Integer)tv              C is Object and tv%16 != 0
157      *  null                     C is Object and tv%16 == 0
158      *  Integer.toHexString(tv)  C is String and tv != 0
159      *  null                     C is String and tv == 0
160      *  </pre>
161      *  are derived by ordinary Java coercions, except that boolean
162      *  samples the LSB of the int value, and String is the hex numeral.
163      *
164      *  (Also, the 0th String is null, and the 0th Object mod 16 is null,
165      *  regardless of the original int test value.)
166      */
testValue(int i, Class<?> c)167     static Object testValue(int i, Class<?> c) {
168         int tv = testValues[i % testValues.length];
169         if (i >= testValues.length)  tv ^= i;
170         // Turn the canonical int to a float, boolean, String, whatever:
171         return invoke(coercers.get(c), tv);
172     }
173     /** Build a test array of the given length,
174      *  packed with a subsequence of the test values.
175      *  The first element of the array is always testValue(0).
176      */
makeArray(int len, Class<?> c)177     static Object makeArray(int len, Class<?> c) {
178         Object a = Array.newInstance(c, len);
179         for (int i = 0; i < len; i++) {
180             Array.set(a, i, testValue(i, c));
181         }
182         return a;
183     }
184     /** Check that the given array has the required length.
185      *  Check also that it is packed, up to firstNull, with
186      *  a particular subsequence of the canonical test values.
187      *  The subsequence must begin with a[0] == testValue(offset).
188      *  At a[firstNull] and beyond, the array must contain null values.
189      */
checkArray(Object a, Class<?> c, int requiredLen, int offset, int firstNull)190     static void checkArray(Object a, Class<?> c, int requiredLen, int offset, int firstNull) {
191         check(c == a.getClass().getComponentType());
192         Object nullValue = nullValues.get(c);
193         // Note:  asserts in here are not part of the test program.
194         // They verify the integrity of the test method itself.
195         assert(nullValues.containsKey(c));
196 
197         int misses = 0;
198         int firstMiss = -1;
199         // Check required length first.
200         int length = Array.getLength(a);
201         if (length != requiredLen && requiredLen != -1) {
202             if (muzzle == 0)
203                 System.out.println("*** a.length = "+length+" != "+requiredLen);
204             ++misses;
205         }
206 
207         for (int i = 0; i < length; i++) {
208             Object tv = (i >= firstNull) ? nullValue : testValue(i+offset, c);
209             Object ai = Array.get(a, i);
210             if (!eq(ai, tv)) {
211                 if (muzzle == 0)
212                     System.out.println("*** a["+i+"] = "+ai+" != "+tv);
213                 if (misses == 0)  firstMiss = i;
214                 if (++misses > 10)  break;
215             }
216         }
217         if (misses != 0) {
218             Method toString = toStrings.get(c);
219             if (toString == null)  toString = toStrings.get(Object.class);
220             throw new RuntimeException("checkArray failed at "+firstMiss
221                                        +" "+c+"[]"
222                                        +" : "+invoke(toString, a));
223         }
224     }
225     // Typical comparison helper.  Why isn't this a method somewhere.
eq(Object x, Object y)226     static boolean eq(Object x, Object y) {
227         return x == null? y == null: x.equals(y);
228     }
229     // Exception-ignoring invoke function.
invoke(Method m, Object... args)230     static Object invoke(Method m, Object... args) {
231         Exception ex;
232         try {
233             return m.invoke(null, args);
234         } catch (InvocationTargetException ee) {
235             ex = ee;
236         } catch (IllegalAccessException ee) {
237             ex = ee;
238         } catch (IllegalArgumentException ee) {
239             ex = ee;
240         }
241         ArrayList<Object> call = new ArrayList<Object>();
242         call.add(m); Collections.addAll(call, args);
243         throw new RuntimeException(call+" : "+ex);
244     }
245     // version of assert() that runs unconditionally
check(boolean z)246     static void check(boolean z) {
247         if (!z)  throw new RuntimeException("check failed");
248     }
249 
250 
251     /** Run about 10**5 distinct parameter combinations
252      *  on copyOf and copyOfRange.  Use all primitive types,
253      *  and String and Object.
254      *  Try to all critical values, looking for fencepost errors.
255      */
fullTests(int maxLen, Class<?> c)256     static void fullTests(int maxLen, Class<?> c) {
257         Method cloner      = cloners.get(c);
258         assert(cloner != null) : c;
259         Method cloneRanger = cloneRangers.get(c);
260         // Note:  asserts in here are not part of the test program.
261         // They verify the integrity of the test method itself.
262         assert(cloneRanger != null) : c;
263         for (int src = 0; src <= maxLen; src = inc(src, 0, maxLen)) {
264             Object a = makeArray(src, c);
265             for (int x : new ArrayList<Integer>()) {}
266             for (int j = 0; j <= maxLen; j = inc(j, src, maxLen)) {
267                 // b = Arrays.copyOf(a, j);
268                 Object b = invoke(cloner, a, j);
269                 checkArray(b, c, j, 0, src);
270                 testCasesRun++;
271                 consing += j;
272 
273                 int maxI = Math.min(src, j);
274                 for (int i = 0; i <= maxI; i = inc(i, src, maxI)) {
275                     // r = Arrays.copyOfRange(a, i, j);
276                     Object r = invoke(cloneRanger, a, i, j);
277                     checkArray(r, c, j-i, i, src-i);
278                     //System.out.println("case c="+c+" src="+src+" i="+i+" j="+j);
279                     testCasesRun++;
280                     consing += j-i;
281                 }
282             }
283         }
284     }
285     // Increment x by at least one.  Increment by a little more unless
286     // it is near a critical value, either zero, crit1, or crit2.
inc(int x, int crit1, int crit2)287     static int inc(int x, int crit1, int crit2) {
288         int D = shortStepsNear;
289         if (crit1 > crit2) { int t = crit1; crit1 = crit2; crit2 = t; }
290         assert(crit1 <= crit2);
291         assert(x <= crit2);  // next1 or next2 must be the limit value
292         x += 1;
293         if (x > D) {
294             if (x < crit1-D) {
295                 x += (x << 1) >> downShift;  // giant step toward crit1-D
296                 if (x > crit1-D)  x = crit1-D;
297             } else if (x >= crit1+D && x < crit2-D) {
298                 x += (x << 1) >> downShift;  // giant step toward crit2-D
299                 if (x > crit2-D)  x = crit2-D;
300             }
301         }
302         return x;
303     }
304 
main(String[] av)305     public static void main(String[] av) {
306         boolean verbose = (av.length != 0);
307         muzzle = (verbose? 0: 1);
308         if (muzzle == 0)
309             System.out.println("test values: "+Arrays.toString(Arrays.copyOf(testValues, 5))+"...");
310 
311         simpleTests();
312 
313         muzzle = 0;  // turn on print statements (affects failures only)
314 
315         fullTests();
316         if (verbose)
317             System.out.println("ran "+testCasesRun+" tests, avg len="
318                                +(float)consing/testCasesRun);
319 
320         // test much larger arrays, more sparsely
321         maxLen = 500;
322         shortStepsNear = 2;
323         downShift = 0;
324         testCasesRun = 0;
325         consing = 0;
326         fullTests();
327         if (verbose)
328             System.out.println("ran "+testCasesRun+" tests, avg len="
329                                +(float)consing/testCasesRun);
330     }
331 
fullTests()332     static void fullTests() {
333         for (Class<?> c : allTypes) {
334             fullTests(maxLen, c);
335         }
336     }
337 
338     // We must run all the our tests on each of 8 distinct primitive types,
339     // and two reference types (Object, String) for good measure.
340     // This would be a pain to write out by hand, statically typed.
341     // So, use reflection.  Following are the tables of methods we use.
342     // (The initial simple tests exercise enough of the static typing
343     // features of the API to ensure that they compile as advertised.)
344 
coerceToObject(int x)345     static Object  coerceToObject(int x) { return (x & 0xF) == 0? null: new Integer(x); }
coerceToString(int x)346     static String  coerceToString(int x) { return (x == 0)? null: Integer.toHexString(x); }
coerceToInteger(int x)347     static Integer coerceToInteger(int x) { return (x == 0)? null: x; }
coerceToByte(int x)348     static byte    coerceToByte(int x) { return (byte)x; }
coerceToShort(int x)349     static short   coerceToShort(int x) { return (short)x; }
coerceToInt(int x)350     static int     coerceToInt(int x) { return x; }
coerceToLong(int x)351     static long    coerceToLong(int x) { return x; }
coerceToChar(int x)352     static char    coerceToChar(int x) { return (char)x; }
coerceToFloat(int x)353     static float   coerceToFloat(int x) { return x; }
coerceToDouble(int x)354     static double  coerceToDouble(int x) { return x; }
coerceToBoolean(int x)355     static boolean coerceToBoolean(int x) { return (x&1) != 0; }
356 
copyOfIntegerArray(Object[] a, int len)357     static Integer[] copyOfIntegerArray(Object[] a, int len) {
358         // This guy exercises the API based on a type-token.
359         // Note the static typing.
360         return Arrays.copyOf(a, len, Integer[].class);
361     }
copyOfIntegerArrayRange(Object[] a, int m, int n)362     static Integer[] copyOfIntegerArrayRange(Object[] a, int m, int n) {
363         // This guy exercises the API based on a type-token.
364         // Note the static typing.
365         return Arrays.copyOfRange(a, m, n, Integer[].class);
366     }
367 
368     static final List<Class<?>> allTypes
369         = Arrays.asList(new Class<?>[]
370                         {   Object.class, String.class, Integer.class,
371                             byte.class, short.class, int.class, long.class,
372                             char.class, float.class, double.class,
373                             boolean.class
374                         });
375     static final HashMap<Class<?>,Method> coercers;
376     static final HashMap<Class<?>,Method> cloners;
377     static final HashMap<Class<?>,Method> cloneRangers;
378     static final HashMap<Class<?>,Method> toStrings;
379     static final HashMap<Class<?>,Object> nullValues;
380     static {
381         coercers = new HashMap<Class<?>,Method>();
382         Method[] testMethods = CopyMethods.class.getDeclaredMethods();
383         Method cia = null, ciar = null;
384         for (int i = 0; i < testMethods.length; i++) {
385             Method m = testMethods[i];
386             if (!Modifier.isStatic(m.getModifiers()))  continue;
387             Class<?> rt = m.getReturnType();
388             if (m.getName().startsWith("coerceTo") && allTypes.contains(rt))
m.getReturnType()389                 coercers.put(m.getReturnType(), m);
390             if (m.getName().equals("copyOfIntegerArray"))
391                 cia = m;
392             if (m.getName().equals("copyOfIntegerArrayRange"))
393                 ciar = m;
394         }
395         Method[] arrayMethods = Arrays.class.getDeclaredMethods();
396         cloners      = new HashMap<Class<?>,Method>();
397         cloneRangers = new HashMap<Class<?>,Method>();
398         toStrings    = new HashMap<Class<?>,Method>();
399         for (int i = 0; i < arrayMethods.length; i++) {
400             Method m = arrayMethods[i];
401             if (!Modifier.isStatic(m.getModifiers()))  continue;
402             Class<?> rt = m.getReturnType();
403             if (m.getName().equals("copyOf")
404                 && m.getParameterTypes().length == 2)
rt.getComponentType()405                 cloners.put(rt.getComponentType(), m);
406             if (m.getName().equals("copyOfRange")
407                 && m.getParameterTypes().length == 3)
rt.getComponentType()408                 cloneRangers.put(rt.getComponentType(), m);
409             if (m.getName().equals("toString")) {
410                 Class<?> pt = m.getParameterTypes()[0];
pt.getComponentType()411                 toStrings.put(pt.getComponentType(), m);
412             }
413         }
cloners.put(String.class, cloners.get(Object.class))414         cloners.put(String.class, cloners.get(Object.class));
cloneRangers.put(String.class, cloneRangers.get(Object.class))415         cloneRangers.put(String.class, cloneRangers.get(Object.class));
416         assert(cia != null);
cloners.put(Integer.class, cia)417         cloners.put(Integer.class, cia);
418         assert(ciar != null);
cloneRangers.put(Integer.class, ciar)419         cloneRangers.put(Integer.class, ciar);
420         nullValues = new HashMap<Class<?>,Object>();
421         for (Class<?> c : allTypes) {
nullValues.put(c, invoke(coercers.get(c), 0))422             nullValues.put(c, invoke(coercers.get(c), 0));
423         }
424     }
425 }
426