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