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