1 /*
  2  * Copyright (c) 2019, 2024, 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 package runtime.valhalla.inlinetypes;
 26 
 27 import java.lang.reflect.*;
 28 import static jdk.test.lib.Asserts.*;
 29 
 30 import jdk.internal.vm.annotation.LooselyConsistentValue;
 31 
 32 /*
 33  * @test VarArgsArray
 34  * @summary Test if JVM API using varargs work with inline type arrays
 35  * @modules java.base/jdk.internal.value
 36  * @library /test/lib
 37  * @modules java.base/jdk.internal.vm.annotation
 38  * @enablePreview
 39  * @compile VarArgsArray.java
 40  * @run main/othervm runtime.valhalla.inlinetypes.VarArgsArray
 41  */
 42 
 43 @LooselyConsistentValue
 44 value class IntValue {
 45     int val;
 46     public IntValue()       { this(0); }
 47     public IntValue(int v)  { val = v; }
 48     public int getInt()     { return val; }
 49 }
 50 
 51 
 52 class NewInstanceFromConstructor {
 53 
 54     int value;
 55     static int consCalls = 0;
 56 
 57     public NewInstanceFromConstructor() {
 58         this(0);
 59     }
 60 
 61     public NewInstanceFromConstructor(int v) {
 62         value = v;
 63         consCalls++;
 64     }
 65 
 66     public NewInstanceFromConstructor(IntValue v) {
 67         this(v.getInt());
 68     }
 69 
 70     public NewInstanceFromConstructor(IntValue v1,
 71                                       IntValue v2) {
 72         this(v1.getInt() + v2.getInt());
 73     }
 74 
 75     public NewInstanceFromConstructor(IntValue v1,
 76                                       String s) {
 77         this(v1);
 78         throw new RuntimeException(s);
 79     }
 80 
 81     public int getValue() { return value; }
 82 
 83     public static int getConsCalls() { return consCalls; }
 84 }
 85 
 86 public class VarArgsArray {
 87 
 88     static final int TOKEN_VALUE = 4711;
 89 
 90     int methodACnt = 0;
 91     int methodBCnt = 0;
 92     int methodCCnt = 0;
 93 
 94     public VarArgsArray() {
 95     }
 96 
 97     public void test() throws Throwable {
 98         // test publicly accessable API in the VM...given an inline type array
 99         testJvmInvokeMethod();
100         testJvmNewInstanceFromConstructor();
101     }
102 
103     public void testJvmInvokeMethod() throws Throwable {
104         MyInt[] array0 = new MyInt[0];
105         MyInt[] array1 = new MyInt[] { new MyInt(TOKEN_VALUE) };
106         MyInt[] array2 = new MyInt[] { new MyInt(TOKEN_VALUE), new MyInt(TOKEN_VALUE) };
107 
108         Method methodARef = getClass().getDeclaredMethod("methodA", MyInt.class);
109         Method methodBRef = getClass().getDeclaredMethod("methodB", MyInt.class, MyInt.class);
110         Method methodCRef = getClass().getDeclaredMethod("methodC", MyInt.class, String.class);
111 
112         // Positive tests...
113         methodARef.invoke(this, (Object[])array1);
114         assertWithMsg(methodACnt == 1, "methodA did not invoke");
115 
116         methodARef.invoke(this, array1[0]);
117         assertWithMsg(methodACnt == 2, "methodA did not invoke");
118 
119         methodBRef.invoke(this, (Object[]) array2);
120         assertWithMsg(methodBCnt == 1, "methodB did not invoke");
121 
122         methodBRef.invoke(this, array2[0], array2[1]);
123         assertWithMsg(methodBCnt == 2, "methodB did not invoke");
124 
125         // Negative tests...
126         int argExCnt = 0;
127         try {
128             methodARef.invoke(this, (Object[]) array0);
129             throw new RuntimeException("Expected fail");
130         } catch (IllegalArgumentException argEx) { argExCnt++; }
131         try {
132             methodARef.invoke(this, (Object[]) array2);
133             throw new RuntimeException("Expected fail");
134         } catch (IllegalArgumentException argEx) { argExCnt++; }
135         try {
136             methodCRef.invoke(this, (Object[]) array2);
137             throw new RuntimeException("Expected fail");
138         } catch (IllegalArgumentException argEx) { argExCnt++; }
139         assertWithMsg(argExCnt == 3, "Did not see the correct number of exceptions");
140         assertWithMsg(methodACnt == 2, "methodA bad invoke count");
141         assertWithMsg(methodBCnt == 2, "methodB bad invoke count");
142         assertWithMsg(methodCCnt == 0, "methodC bad invoke count");
143     }
144 
145     public void testJvmNewInstanceFromConstructor() throws Throwable {
146         // Inner classes use outer in param list, so these won't exercise inline type array
147         Class tc = NewInstanceFromConstructor.class;
148         Class pt = IntValue.class;
149         Constructor consARef = tc.getConstructor(pt);
150         Constructor consBRef = tc.getConstructor(pt, pt);
151         Constructor consCRef = tc.getConstructor(pt, String.class);
152         IntValue[] array0 = new IntValue[0];
153         IntValue[] array1 = new IntValue[] { new IntValue(TOKEN_VALUE) };
154         IntValue[] array2 = new IntValue[] { new IntValue(TOKEN_VALUE),
155                                              new IntValue(TOKEN_VALUE) };
156 
157         // Positive tests...
158         consARef.newInstance((Object[])array1);
159         consARef.newInstance(array1[0]);
160         NewInstanceFromConstructor test = (NewInstanceFromConstructor)
161             consBRef.newInstance((Object[])array2);
162         assertWithMsg(test.getValue() == (2 * TOKEN_VALUE), "Param corrrupt");
163         consBRef.newInstance(array2[0], array2[1]);
164         assertWithMsg(NewInstanceFromConstructor.getConsCalls() == 4, "Constructor did not invoke");
165 
166         // Negative tests...
167         int argExCnt = 0;
168         try {
169             consARef.newInstance((Object[])array0);
170             throw new RuntimeException("Expected fail");
171         } catch (IllegalArgumentException argEx) { argExCnt++; }
172         try {
173             consARef.newInstance((Object[])array2);
174             throw new RuntimeException("Expected fail");
175         } catch (IllegalArgumentException argEx) { argExCnt++; }
176         try {
177             consCRef.newInstance((Object[])array2);
178             throw new RuntimeException("Expected fail");
179         } catch (IllegalArgumentException argEx) { argExCnt++; }
180         assertWithMsg(argExCnt == 3, "Did not see the correct number of exceptions");
181         assertWithMsg(NewInstanceFromConstructor.getConsCalls() == 4, "Constructor should have been invoked");
182     }
183 
184     public void methodA(MyInt a) {
185         assertWithMsg(a.value == TOKEN_VALUE, "Bad arg");
186         methodACnt++;
187     }
188 
189     public void methodB(MyInt a, MyInt b) {
190         assertWithMsg(a.value == TOKEN_VALUE, "Bad arg");
191         assertWithMsg(b.value == TOKEN_VALUE, "Bad arg");
192         methodBCnt++;
193     }
194 
195     public void methodC(MyInt a, String b) {
196         assertWithMsg(a.value == TOKEN_VALUE, "Bad arg");
197         methodCCnt++;
198     }
199 
200     static void assertWithMsg(boolean expr, String msg) throws RuntimeException {
201         assertTrue(expr, msg);
202     }
203 
204     public static void main(String[] args) throws Throwable {
205         new VarArgsArray().test();
206     }
207 
208     @LooselyConsistentValue
209     value class MyInt {
210         int value;
211         public MyInt() { this(0); }
212         public MyInt(int v) { this.value = v; }
213     }
214 
215 
216 }