1 /* 2 * Copyright (c) 2011, 2017, 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 /* @test 25 * @summary unit tests for method handles which permute their arguments 26 * @library /lib/testlibrary /java/lang/invoke/common 27 * @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -ea -esa -DPermuteArgsTest.MAX_ARITY=8 test.java.lang.invoke.PermuteArgsTest 28 */ 29 30 /* Examples of manual runs: 31 * java -DPermuteArgsTest.{DRY_RUN=true,MAX_ARITY=253} test.java.lang.invoke.PermuteArgsTest 32 * java -DPermuteArgsTest.{VERBOSE=true,MAX_ARITY=5} test.java.lang.invoke.PermuteArgsTest 33 * java test.java.lang.invoke.PermuteArgsTest list3I[2,0,1] listJLJ[2,0,1] 34 */ 35 36 package test.java.lang.invoke; 37 38 import org.testng.annotations.Test; 39 import test.java.lang.invoke.lib.CodeCacheOverflowProcessor; 40 41 import java.lang.invoke.MethodHandle; 42 import java.lang.invoke.MethodType; 43 import java.lang.invoke.WrongMethodTypeException; 44 import java.lang.reflect.Method; 45 import java.lang.reflect.Modifier; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.HashSet; 49 import java.util.Set; 50 51 import static java.lang.invoke.MethodHandles.Lookup; 52 import static java.lang.invoke.MethodHandles.lookup; 53 import static java.lang.invoke.MethodHandles.permuteArguments; 54 import static java.lang.invoke.MethodType.methodType; 55 56 public class PermuteArgsTest { 57 private static final Class<?> CLASS = PermuteArgsTest.class; 58 private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 8); 59 private static final boolean DRY_RUN = Boolean.getBoolean(CLASS.getSimpleName()+".DRY_RUN"); 60 private static final boolean VERBOSE = Boolean.getBoolean(CLASS.getSimpleName()+".VERBOSE") || DRY_RUN; 61 list2I(int x, int y)62 static Object list2I(int x, int y) { 63 return Arrays.asList(x, y); 64 } list3I(int x, int y, int z)65 static Object list3I(int x, int y, int z) { 66 return Arrays.asList(x, y, z); 67 } list4I(int w, int x, int y, int z)68 static Object list4I(int w, int x, int y, int z) { 69 return Arrays.asList(w, x, y, z); 70 } list2J(long x, long y)71 static Object list2J(long x, long y) { 72 return Arrays.asList(x, y); 73 } list3J(long x, long y, long z)74 static Object list3J(long x, long y, long z) { 75 return Arrays.asList(x, y, z); 76 } list4J(long w, long x, long y, long z)77 static Object list4J(long w, long x, long y, long z) { 78 return Arrays.asList(w, x, y, z); 79 } list2I2J(int w, int x, long y, long z)80 static Object list2I2J(int w, int x, long y, long z) { 81 return Arrays.asList(w, x, y, z); 82 } list2J2I(long w, long x, int y, int z)83 static Object list2J2I(long w, long x, int y, int z) { 84 return Arrays.asList(w, x, y, z); 85 } listLJJ(Object x, long y, long z)86 static Object listLJJ(Object x, long y, long z) { 87 return Arrays.asList(x, y, z); 88 } listJLJ(long x, Object y, long z)89 static Object listJLJ(long x, Object y, long z) { 90 return Arrays.asList(x, y, z); 91 } listJJL(long x, long y, Object z)92 static Object listJJL(long x, long y, Object z) { 93 return Arrays.asList(x, y, z); 94 } listJLL(long x, Object y, Object z)95 static Object listJLL(long x, Object y, Object z) { 96 return Arrays.asList(x, y, z); 97 } listLJL(Object x, long y, Object z)98 static Object listLJL(Object x, long y, Object z) { 99 return Arrays.asList(x, y, z); 100 } listLLJ(Object x, Object y, long z)101 static Object listLLJ(Object x, Object y, long z) { 102 return Arrays.asList(x, y, z); 103 } listJLLJ(long w, Object x, Object y, long z)104 static Object listJLLJ(long w, Object x, Object y, long z) { 105 return Arrays.asList(w, x, y, z); 106 } listLJJL(Object w, long x, long y, Object z)107 static Object listLJJL(Object w, long x, long y, Object z) { 108 return Arrays.asList(w, x, y, z); 109 } listI_etc(int... va)110 static Object listI_etc(int... va) { 111 ArrayList<Object> res = new ArrayList<>(); 112 for (int x : va) res.add(x); 113 return res; 114 } listIJL_etc(int x, long y, Object z, Object... va)115 static Object listIJL_etc(int x, long y, Object z, Object... va) { 116 ArrayList<Object> res = new ArrayList<>(); 117 res.addAll(Arrays.asList(x, y, z)); 118 res.addAll(Arrays.asList(va)); 119 return res; 120 } 121 main(String argv[])122 public static void main(String argv[]) throws Throwable { 123 if (argv.length > 0) { 124 for (String arg : argv) { 125 // arg ::= name[n,...] 126 int k = arg.indexOf('['); 127 String mhName = arg.substring(0, k).trim(); 128 String permString = arg.substring(k); 129 testOnePermutation(mhName, permString); 130 } 131 return; 132 } 133 new PermuteArgsTest().test(); 134 } 135 136 static int testCases; 137 138 @Test test()139 public void test() throws Throwable { 140 CodeCacheOverflowProcessor.runMHTest(this::test0); 141 } 142 test0()143 public void test0() throws Throwable { 144 testCases = 0; 145 Lookup lookup = lookup(); 146 for (Method m : lookup.lookupClass().getDeclaredMethods()) { 147 if (m.getName().startsWith("list") && 148 Modifier.isStatic(m.getModifiers())) { 149 test(m.getName(), lookup.unreflect(m)); 150 } 151 } 152 System.out.println("ran a total of "+testCases+" test cases"); 153 } 154 jump(int i, int min, int max)155 static int jump(int i, int min, int max) { 156 if (i >= min && i <= max-1) { 157 // jump faster 158 int len = max-min; 159 if (i < min + len/2) 160 i = min + len/2; 161 else 162 i = max-1; 163 } 164 return i; 165 } 166 test(String name, MethodHandle mh)167 static void test(String name, MethodHandle mh) throws Throwable { 168 if (VERBOSE) 169 System.out.println("mh = "+name+" : "+mh+" { " 170 +Arrays.toString(junkArgs(mh.type().parameterArray()))); 171 int testCases0 = testCases; 172 if (!mh.isVarargsCollector()) { 173 // normal case 174 testPermutations(mh); 175 } else { 176 // varargs case; add params up to MAX_ARITY 177 MethodType mt = mh.type(); 178 int posArgs = mt.parameterCount() - 1; 179 int arity0 = Math.max(3, posArgs); 180 for (int arity = arity0; arity <= MAX_ARITY; arity++) { 181 MethodHandle mh1; 182 try { 183 mh1 = adjustArity(mh, arity); 184 } catch (IllegalArgumentException ex) { 185 System.out.println("*** mh = "+name+" : "+mh+"; arity = "+arity+" => "+ex); 186 ex.printStackTrace(System.out); 187 break; // cannot get this arity for this type 188 } 189 test("("+arity+")"+name, mh1); 190 arity = jump(arity, arity0*2, MAX_ARITY); 191 } 192 } 193 if (VERBOSE) 194 System.out.println("ran "+(testCases - testCases0)+" test cases for "+name+" }"); 195 } 196 adjustArity(MethodHandle mh, int arity)197 static MethodHandle adjustArity(MethodHandle mh, int arity) { 198 MethodType mt = mh.type(); 199 int posArgs = mt.parameterCount() - 1; 200 Class<?> reptype = mt.parameterType(posArgs).getComponentType(); 201 MethodType mt1 = mt.dropParameterTypes(posArgs, posArgs+1); 202 while (mt1.parameterCount() < arity) { 203 Class<?> pt = reptype; 204 if (pt == Object.class && posArgs > 0) 205 // repeat types cyclically if possible: 206 pt = mt1.parameterType(mt1.parameterCount() - posArgs); 207 mt1 = mt1.appendParameterTypes(pt); 208 } 209 try { 210 return mh.asType(mt1); 211 } catch (WrongMethodTypeException | IllegalArgumentException ex) { 212 throw new IllegalArgumentException("cannot convert to type "+mt1+" from "+mh, ex); 213 } 214 } findTestMH(String name, int[] perm)215 static MethodHandle findTestMH(String name, int[] perm) 216 throws ReflectiveOperationException { 217 int arity = perm.length; 218 Lookup lookup = lookup(); 219 for (Method m : lookup.lookupClass().getDeclaredMethods()) { 220 if (m.getName().equals(name) && 221 Modifier.isStatic(m.getModifiers())) { 222 MethodHandle mh = lookup.unreflect(m); 223 int mhArity = mh.type().parameterCount(); 224 if (mh.isVarargsCollector()) { 225 if (mhArity-1 <= arity) 226 return adjustArity(mh, arity); 227 } else if (mhArity == arity) { 228 return mh; 229 } 230 } 231 } 232 throw new RuntimeException("no such method for arity "+arity+": "+name); 233 } 234 testPermutations(MethodHandle mh)235 static void testPermutations(MethodHandle mh) throws Throwable { 236 HashSet<String> done = new HashSet<>(); 237 MethodType mt = mh.type(); 238 int[] perm = nullPerm(mt.parameterCount()); 239 final int MARGIN = (perm.length <= 10 ? 2 : 0); 240 int testCases0 = testCases; 241 for (int j = 0; j <= 1; j++) { 242 int maxStart = perm.length-1; 243 if (j != 0) maxStart /= 2; 244 for (int start = 0; start <= maxStart; start++) { 245 int maxOmit = (maxStart - start) / 2; 246 if (start != 0) maxOmit = 2; 247 if (j != 0) maxOmit = 1; 248 for (int omit = 0; omit <= maxOmit; omit++) { 249 int end = perm.length - omit; 250 if (end - start >= 2) { 251 //System.out.println("testPermutations"+Arrays.asList(start, end)+(j == 0 ? "" : " (reverse)")); 252 testPermutations(mh, perm, start, end, done); 253 } 254 omit = jump(omit, (start == 0 && j == 0 ? MARGIN : 0), maxOmit); 255 } 256 start = jump(start, (j == 0 ? MARGIN : 0), maxStart); 257 } 258 // do everything in reverse: 259 reverse(perm, 0, perm.length); 260 } 261 switch (perm.length) { 262 case 2: assert(testCases - testCases0 == 2); break; 263 case 3: assert(testCases - testCases0 == 6); break; 264 case 4: assert(testCases - testCases0 == 24); break; 265 case 5: assert(testCases - testCases0 == 120); break; 266 case 6: assert(testCases - testCases0 > 720/3); break; 267 } 268 } 269 testPermutations(MethodHandle mh, int[] perm, int start, int end, Set<String> done)270 static void testPermutations(MethodHandle mh, int[] perm, int start, int end, 271 Set<String> done) throws Throwable { 272 if (end - start <= 1) return; 273 for (int j = 0; j <= 1; j++) { 274 testRotations(mh, perm, start, end, done); 275 if (end - start <= 2) return; 276 reverse(perm, start, end); 277 } 278 if (end - start <= 3) return; 279 int excess4 = (end - start) - 4; 280 // composed rotations: 281 int start2 = start + 1 + excess4/3; 282 int end2 = end - excess4/3; 283 end2 = start2 + Math.min(start == 0 ? 4 : 3, end2 - start2); 284 int skips = (perm.length+3)/5; 285 for (int i = start; i < end; i++) { 286 rotate(perm, start, end); 287 if (skips > 1 && ((i-start) + (i-start)/7) % skips != 0) continue; 288 for (int j = 0; j <= 1; j++) { 289 testPermutations(mh, perm, start2, end2, done); 290 reverse(perm, start, end); 291 } 292 } 293 } 294 testRotations(MethodHandle mh, int[] perm, int start, int end, Set<String> done)295 static void testRotations(MethodHandle mh, int[] perm, int start, int end, 296 Set<String> done) throws Throwable { 297 Object[] args = junkArgs(mh.type().parameterArray()); 298 for (int i = start; i < end; i++) { 299 if (done.add(Arrays.toString(perm))) 300 testOnePermutation(mh, perm, args); 301 rotate(perm, start, end); 302 } 303 } 304 testOnePermutation(MethodHandle mh, int[] perm, Object[] args)305 static void testOnePermutation(MethodHandle mh, int[] perm, Object[] args) 306 throws Throwable { 307 MethodType mt = mh.type(); 308 MethodType pmt = methodType(mt.returnType(), 309 unpermuteArgs(perm, mt.parameterArray(), Class[].class)); 310 if (VERBOSE) 311 System.out.println(Arrays.toString(perm)); 312 testCases += 1; 313 if (DRY_RUN) 314 return; 315 Object res = permuteArguments(mh, pmt, perm).invokeWithArguments(unpermuteArgs(perm, args)); 316 String str = String.valueOf(res); 317 if (!Arrays.toString(args).equals(str)) { 318 System.out.println(Arrays.toString(perm)+" "+str+" *** WRONG ***"); 319 } 320 } 321 322 // For reproducing failures: testOnePermutation(String mhName, String permString)323 static void testOnePermutation(String mhName, String permString) throws Throwable { 324 String s = permString; 325 s = s.replace('[', ' ').replace(']', ' ').replace(',', ' '); // easier to trim spaces 326 s = s.trim(); 327 int[] perm = new int[s.length()]; 328 int arity = 0; 329 while (!s.isEmpty()) { 330 int k = s.indexOf(' '); 331 if (k < 0) k = s.length(); 332 perm[arity++] = Integer.parseInt(s.substring(0, k)); 333 s = s.substring(k).trim(); 334 } 335 perm = Arrays.copyOf(perm, arity); 336 testOnePermutation(mhName, perm); 337 } testOnePermutation(String mhName, int[] perm)338 static void testOnePermutation(String mhName, int[] perm) throws Throwable { 339 MethodHandle mh = findTestMH(mhName, perm); 340 System.out.println("mh = "+mhName+" : "+mh+" { " 341 +Arrays.toString(junkArgs(mh.type().parameterArray()))); 342 Object[] args = junkArgs(mh.type().parameterArray()); 343 testOnePermutation(mh, perm, args); 344 System.out.println("}"); 345 } 346 junkArgs(Class<?>[] ptypes)347 static Object[] junkArgs(Class<?>[] ptypes) { 348 Object[] args = new Object[ptypes.length]; 349 for (int i = 0; i < ptypes.length; i++) { 350 Class<?> pt = ptypes[i]; 351 Object arg; 352 if (pt == Void.class) arg = null; 353 else if (pt == int.class) arg = i + 101; 354 else if (pt == long.class) arg = i + 10_000_000_001L; 355 else arg = "#" + (i + 1); 356 args[i] = arg; 357 } 358 return args; 359 } 360 nullPerm(int len)361 static int[] nullPerm(int len) { 362 int[] perm = new int[len]; 363 for (int i = 0; i < len; i++) 364 perm[i] = i; 365 return perm; 366 } rotate(int[] perm)367 static void rotate(int[] perm) { 368 rotate(perm, 0, perm.length); 369 } rotate(int[] perm, int start, int end)370 static void rotate(int[] perm, int start, int end) { 371 int x = perm[end-1]; 372 for (int j = start; j < end; j++) { 373 int y = perm[j]; perm[j] = x; x = y; 374 } 375 } reverse(int[] perm)376 static void reverse(int[] perm) { 377 reverse(perm, 0, perm.length); 378 } reverse(int[] perm, int start, int end)379 static void reverse(int[] perm, int start, int end) { 380 int mid = start + (end - start)/2; 381 for (int j = start; j < mid; j++) { 382 int k = (end-1) - j; 383 int x = perm[j]; perm[j] = perm[k]; perm[k] = x; 384 } 385 } 386 // Permute the args according to the inverse of perm. unpermuteArgs(int[] perm, Object[] args)387 static Object[] unpermuteArgs(int[] perm, Object[] args) { 388 return unpermuteArgs(perm, args, Object[].class); 389 } unpermuteArgs(int[] perm, T[] args, Class<T[]> Tclass)390 static <T> T[] unpermuteArgs(int[] perm, T[] args, Class<T[]> Tclass) { 391 T[] res = Arrays.copyOf(new Object[0], perm.length, Tclass); 392 for (int i = 0; i < perm.length; i++) 393 res[perm[i]] = args[i]; 394 return res; 395 } 396 } 397