1 /*
   2  * Copyright (c) 2018, 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 import java.lang.invoke.MethodType;
  25 import java.lang.constant.ClassDesc;
  26 import java.lang.constant.MethodTypeDesc;
  27 import java.util.Arrays;
  28 import java.util.List;
  29 import java.util.stream.IntStream;
  30 import java.util.stream.Stream;
  31 
  32 import org.testng.annotations.Test;
  33 
  34 import static java.lang.constant.ConstantDescs.CD_int;
  35 import static java.lang.constant.ConstantDescs.CD_void;
  36 import static java.util.stream.Collectors.joining;
  37 import static java.util.stream.Collectors.toList;
  38 import static org.testng.Assert.assertEquals;
  39 import static org.testng.Assert.fail;
  40 
  41 /**
  42  * @test
  43  * @compile MethodTypeDescTest.java
  44  * @run testng MethodTypeDescTest
  45  * @summary unit tests for java.lang.constant.MethodTypeDesc
  46  */
  47 @Test
  48 public class MethodTypeDescTest extends SymbolicDescTest {
  49 
  50     private void testMethodTypeDesc(MethodTypeDesc r) throws ReflectiveOperationException {
  51         testSymbolicDesc(r);
  52 
  53         // Tests accessors (rType, pType, pCount, pList, pArray, descriptorString),
  54         // factories (ofDescriptor, of), equals
  55         assertEquals(r, MethodTypeDesc.ofDescriptor(r.descriptorString()));
  56         assertEquals(r, MethodTypeDesc.of(r.returnType(), r.parameterArray()));
  57         assertEquals(r, MethodTypeDesc.of(r.returnType(), r.parameterList().toArray(new ClassDesc[0])));
  58         assertEquals(r, MethodTypeDesc.of(r.returnType(), r.parameterList().stream().toArray(ClassDesc[]::new)));
  59         assertEquals(r, MethodTypeDesc.of(r.returnType(), IntStream.range(0, r.parameterCount())
  60                                                                    .mapToObj(r::parameterType)
  61                                                                    .toArray(ClassDesc[]::new)));
  62     }
  63 
  64     private void testMethodTypeDesc(MethodTypeDesc r, MethodType mt) throws ReflectiveOperationException {
  65         testMethodTypeDesc(r);
  66 
  67         assertEquals(r.resolveConstantDesc(LOOKUP), mt);
  68         assertEquals(mt.describeConstable().get(), r);
  69 
  70         assertEquals(r.descriptorString(), mt.toMethodDescriptorString());
  71         assertEquals(r.parameterCount(), mt.parameterCount());
  72         assertEquals(r.parameterList(), mt.parameterList().stream().map(SymbolicDescTest::classToDesc).collect(toList()));
  73         assertEquals(r.parameterArray(), Stream.of(mt.parameterArray()).map(SymbolicDescTest::classToDesc).toArray(ClassDesc[]::new));
  74         for (int i=0; i<r.parameterCount(); i++)
  75             assertEquals(r.parameterType(i), classToDesc(mt.parameterType(i)));
  76         assertEquals(r.returnType(), classToDesc(mt.returnType()));
  77     }
  78 
  79     private void assertMethodType(ClassDesc returnType,
  80                                   ClassDesc... paramTypes) throws ReflectiveOperationException {
  81         String descriptor = Stream.of(paramTypes).map(ClassDesc::descriptorString).collect(joining("", "(", ")"))
  82                             + returnType.descriptorString();
  83         MethodTypeDesc mtDesc = MethodTypeDesc.of(returnType, paramTypes);
  84 
  85         // MTDesc accessors
  86         assertEquals(descriptor, mtDesc.descriptorString());
  87         assertEquals(returnType, mtDesc.returnType());
  88         assertEquals(paramTypes, mtDesc.parameterArray());
  89         assertEquals(Arrays.asList(paramTypes), mtDesc.parameterList());
  90         assertEquals(paramTypes.length, mtDesc.parameterCount());
  91         for (int i=0; i<paramTypes.length; i++)
  92             assertEquals(paramTypes[i], mtDesc.parameterType(i));
  93 
  94         // Consistency between MT and MTDesc
  95         MethodType mt = MethodType.fromMethodDescriptorString(descriptor, null);
  96         testMethodTypeDesc(mtDesc, mt);
  97 
  98         // changeReturnType
  99         for (String r : returnDescs) {
 100             ClassDesc rc = ClassDesc.ofDescriptor(r);
 101             MethodTypeDesc newDesc = mtDesc.changeReturnType(rc);
 102             assertEquals(newDesc, MethodTypeDesc.of(rc, paramTypes));
 103             testMethodTypeDesc(newDesc, mt.changeReturnType((Class<?>)rc.resolveConstantDesc(LOOKUP)));
 104         }
 105 
 106         // changeParamType
 107         for (int i=0; i<paramTypes.length; i++) {
 108             for (String p : paramDescs) {
 109                 ClassDesc pc = ClassDesc.ofDescriptor(p);
 110                 ClassDesc[] ps = paramTypes.clone();
 111                 ps[i] = pc;
 112                 MethodTypeDesc newDesc = mtDesc.changeParameterType(i, pc);
 113                 assertEquals(newDesc, MethodTypeDesc.of(returnType, ps));
 114                 testMethodTypeDesc(newDesc, mt.changeParameterType(i, (Class<?>)pc.resolveConstantDesc(LOOKUP)));
 115             }
 116         }
 117 
 118         // dropParamType
 119         for (int i=0; i<paramTypes.length; i++) {
 120             int k = i;
 121             ClassDesc[] ps = IntStream.range(0, paramTypes.length)
 122                                       .filter(j -> j != k)
 123                                       .mapToObj(j -> paramTypes[j])
 124                                       .toArray(ClassDesc[]::new);
 125             MethodTypeDesc newDesc = mtDesc.dropParameterTypes(i, i + 1);
 126             assertEquals(newDesc, MethodTypeDesc.of(returnType, ps));
 127             testMethodTypeDesc(newDesc, mt.dropParameterTypes(i, i+1));
 128         }
 129 
 130         badDropParametersTypes(CD_void, paramDescs);
 131 
 132         // addParam
 133         for (int i=0; i <= paramTypes.length; i++) {
 134             for (ClassDesc p : paramTypes) {
 135                 int k = i;
 136                 ClassDesc[] ps = IntStream.range(0, paramTypes.length + 1)
 137                                           .mapToObj(j -> (j < k) ? paramTypes[j] : (j == k) ? p : paramTypes[j-1])
 138                                           .toArray(ClassDesc[]::new);
 139                 MethodTypeDesc newDesc = mtDesc.insertParameterTypes(i, p);
 140                 assertEquals(newDesc, MethodTypeDesc.of(returnType, ps));
 141                 testMethodTypeDesc(newDesc, mt.insertParameterTypes(i, (Class<?>)p.resolveConstantDesc(LOOKUP)));
 142             }
 143         }
 144 
 145         badInsertParametersTypes(CD_void, paramDescs);
 146     }
 147 
 148     private void badInsertParametersTypes(ClassDesc returnType, String... paramDescTypes) {
 149         ClassDesc[] paramTypes =
 150                 IntStream.rangeClosed(0, paramDescTypes.length - 1)
 151                         .mapToObj(i -> ClassDesc.ofDescriptor(paramDescTypes[i])).toArray(ClassDesc[]::new);
 152         MethodTypeDesc mtDesc = MethodTypeDesc.of(returnType, paramTypes);
 153         try {
 154             MethodTypeDesc newDesc = mtDesc.insertParameterTypes(-1, paramTypes);
 155             fail("pos < 0 should have failed");
 156         } catch (IndexOutOfBoundsException ex) {
 157             // good
 158         }
 159 
 160         try {
 161             MethodTypeDesc newDesc = mtDesc.insertParameterTypes(paramTypes.length + 1, paramTypes);
 162             fail("pos > current arguments length should have failed");
 163         } catch (IndexOutOfBoundsException ex) {
 164             // good
 165         }
 166     }
 167 
 168     private void badDropParametersTypes(ClassDesc returnType, String... paramDescTypes) {
 169         ClassDesc[] paramTypes =
 170                 IntStream.rangeClosed(0, paramDescTypes.length - 1)
 171                         .mapToObj(i -> ClassDesc.ofDescriptor(paramDescTypes[i])).toArray(ClassDesc[]::new);
 172         MethodTypeDesc mtDesc = MethodTypeDesc.of(returnType, paramTypes);
 173         try {
 174             MethodTypeDesc newDesc = mtDesc.dropParameterTypes(-1, 0);
 175             fail("start index < 0 should have failed");
 176         } catch (IndexOutOfBoundsException ex) {
 177             // good
 178         }
 179 
 180         try {
 181             MethodTypeDesc newDesc = mtDesc.dropParameterTypes(paramTypes.length, 0);
 182             fail("start index = arguments.length should have failed");
 183         } catch (IndexOutOfBoundsException ex) {
 184             // good
 185         }
 186 
 187         try {
 188             MethodTypeDesc newDesc = mtDesc.dropParameterTypes(paramTypes.length + 1, 0);
 189             fail("start index > arguments.length should have failed");
 190         } catch (IndexOutOfBoundsException ex) {
 191             // good
 192         }
 193 
 194         try {
 195             MethodTypeDesc newDesc = mtDesc.dropParameterTypes(0, paramTypes.length + 1);
 196             fail("end index > arguments.length should have failed");
 197         } catch (IndexOutOfBoundsException ex) {
 198             // good
 199         }
 200 
 201         try {
 202             MethodTypeDesc newDesc = mtDesc.dropParameterTypes(1, 0);
 203             fail("start index > end index should have failed");
 204         } catch (IllegalArgumentException ex) {
 205             // good
 206         }
 207     }
 208 
 209     public void testMethodTypeDesc() throws ReflectiveOperationException {
 210         for (String r : returnDescs) {
 211             assertMethodType(ClassDesc.ofDescriptor(r));
 212             for (String p1 : paramDescs) {
 213                 assertMethodType(ClassDesc.ofDescriptor(r), ClassDesc.ofDescriptor(p1));
 214                 for (String p2 : paramDescs) {
 215                     assertMethodType(ClassDesc.ofDescriptor(r), ClassDesc.ofDescriptor(p1), ClassDesc.ofDescriptor(p2));
 216                 }
 217             }
 218         }
 219     }
 220 
 221     public void testBadMethodTypeRefs() {
 222         List<String> badDescriptors = List.of("()II", "()I;", "(I;)", "(I)", "()L", "(V)V",
 223                                               "(java.lang.String)V", "()[]", "(Ljava/lang/String)V",
 224                                               "(Ljava.lang.String;)V", "(java/lang/String)V");
 225 
 226         for (String d : badDescriptors) {
 227             try {
 228                 MethodTypeDesc r = MethodTypeDesc.ofDescriptor(d);
 229                 fail(d);
 230             }
 231             catch (IllegalArgumentException e) {
 232                 // good
 233             }
 234         }
 235 
 236         // try with void arguments, this will stress another code path in particular
 237         // ConstantMethodTypeDesc::init
 238         try {
 239             MethodTypeDesc r = MethodTypeDesc.of(CD_int, CD_void);
 240             fail("can't reach here");
 241         }
 242         catch (IllegalArgumentException e) {
 243             // good
 244         }
 245     }
 246 }