1 /*
  2  * Copyright (c) 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  * @test
 26  * @bug 8324873
 27  * @summary Test valid placements of super()/this() in constructors
 28  * @enablePreview
 29  */
 30 
 31 import java.util.ArrayList;
 32 import java.util.List;
 33 
 34 public value class ValueClassSuperInitGood {
 35 
 36     ValueClassSuperInitGood(Object obj) {
 37     }
 38 
 39     ValueClassSuperInitGood(int x) {
 40     }
 41 
 42     // Default constructor provided by compiler
 43     static value class Test0 {
 44     }
 45 
 46     // No explicit calls to this()/super()
 47     static abstract value class Test1 {
 48         Test1() {
 49         }
 50         Test1(int a) {
 51             super();
 52             this.hashCode();
 53         }
 54     }
 55 
 56     // Explicit calls to this()/super()
 57     static abstract value class Test2<T> {
 58         static int i;
 59         Test2() {
 60             this(0);
 61         }
 62         Test2(int i) {
 63             Test2.i = i;
 64             super();
 65         }
 66         Test2(T obj) {
 67             this(java.util.Objects.hashCode(obj));
 68         }
 69         public T get() {
 70             return null;
 71         }
 72     }
 73 
 74     // Explicit this()/super() with stuff in front
 75     static value class Test3 {
 76         int x;
 77         final int y;
 78         final int z;
 79 
 80         Test3() {
 81             new Object().hashCode();
 82             new Object().hashCode();
 83             this.x = new Object().hashCode();
 84             this.y = new Object().hashCode() % 17;
 85             this.z = this.x + this.y;
 86             super();
 87         }
 88     }
 89 
 90     static abstract value class Test5Abstract {
 91         Test5Abstract(Object obj) {}
 92     }
 93 
 94     // Reference within constructor to outer class that's also my superclass
 95     abstract value class Test5 extends Test5Abstract {
 96         Test5(Object obj) {
 97             if (obj == null)
 98                 throw new IllegalArgumentException();
 99             super(ValueClassSuperInitGood.this);      // NOT a 'this' reference
100         }
101     }
102 
103     // Initialization blocks
104     value class Test6 {
105         final long startTime;
106         List<String> l = new ArrayList<>();
107         {
108             l.add("");
109         }
110         Test6() {
111             long now = System.nanoTime();
112             long then = now + 1000000L;
113             while (System.nanoTime() < then) {
114                 try {
115                     Thread.sleep(1);
116                 } catch (InterruptedException e) {
117                     Thread.currentThread().interrupt();
118                     break;
119                 }
120             }
121             this.startTime = now;
122             super();
123         }
124     }
125 
126     // Mix up inner classes, proxies, and super() calls
127     // Copied mostly from UnverifiableInitForNestedLocalClassTest.java
128     public static void test7(final String arg) {
129         final String inlined = " inlined ";
130         abstract value class LocalClass {
131             String m() {
132                 return "LocalClass " + arg + inlined;
133             }
134 
135             abstract value class SubClass extends LocalClass {
136                 @Override
137                 String m() {
138                     return "SubClass " + arg + inlined;
139                 }
140             }
141 
142             value class SubSubClass extends SubClass {
143                 @Override
144                 String m() {
145                     return "SubSubClass " + arg + inlined;
146                 }
147             }
148 
149             value class AnotherLocal {
150                 value class AnotherSub extends LocalClass {
151                     AnotherSub() {
152                     }
153                     AnotherSub(int x) {
154                         this((char)x);
155                     }
156                     AnotherSub(char y) {
157                         super();
158                     }
159                     @Override
160                     String m() {
161                         return "AnotherSub " + arg + inlined;
162                     }
163                 }
164             }
165         }
166     }
167 
168     // Anonymous inner class
169     public static void test8() {
170         new Test2<Byte>(null) {
171             @Override
172             public Byte get() {
173                 return (byte)-1;
174             }
175         };
176     }
177 
178     // Qualified super() invocation
179     public static value class Test9 extends Test5 {
180 
181         public Test9(ValueClassSuperInitGood implicit, Object obj) {
182             obj.hashCode();
183             implicit.super(obj);
184         }
185     }
186 
187     // Copied from WhichImplicitThis6
188     public static abstract value class Test10 {
189         private int i;
190         public Test10(int i) { this.i = i; }
191         public value class Sub extends Test10 {
192             public Sub() {
193                 super(i); // i is not inherited, so it is the enclosing i
194             }
195         }
196     }
197 
198     // Two constructors where only one invokes super()
199     public static value class Test11 {
200         public Test11() {
201         }
202         public Test11(int x) {
203             super();
204         }
205     }
206 
207     // Nested version of the previous test
208     public static value class Test12 {
209         Test12() {
210             class Sub {
211                 public Sub() {
212                 }
213                 public Sub(int j) {
214                     super();
215                 }
216             }
217         }
218     }
219 
220     // Nested super()'s requiring initialization code appended
221     public static value class Test13 extends Test5Abstract {
222         final int x = new Object().hashCode();
223         Test13() {
224             super(new Object() {
225                 public void foo() {
226                     class Bar {
227                         final int y = new Object().hashCode();
228                         Bar() {
229                             super();
230                         }
231                         Bar(int ignored) {
232                         }
233                     }
234                 }
235             });
236         }
237     }
238 
239     // Qualified super() invocation with superclass instance
240     public static abstract value class Test15 {
241 
242         final String name;
243 
244         public Test15(String name) {
245             this.name = name;
246         }
247 
248         public abstract value class Test15b extends Test15 {
249 
250             public Test15b(String name) {
251                 super(name);
252             }
253 
254             public String getName() {
255                 return Test15.this.name;
256             }
257         }
258     }
259 
260     public static value class Test15c extends Test15.Test15b {
261         public Test15c(Test15 a, String name) {
262             a.super(name);
263         }
264     }
265 
266     // Mixing up outer instances, proxies, and initializers
267     public static value class Test16 {
268 
269         final String x = String.valueOf(new Object().hashCode());
270 
271         public void run() {
272 
273             final String y = String.valueOf(new Object().hashCode());
274 
275             class Sub {
276 
277                 final String z;
278 
279                 Sub(String z, int ignored) {
280                     this(z, (float)ignored);
281                 }
282 
283                 Sub(String z, float ignored) {
284                     this.z = z;
285                 }
286 
287                 Sub(String z, byte ignored) {
288                     super();
289                     this.z = z;
290                 }
291 
292                 Sub(String z, char ignored) {
293                     this(z, (int)ignored);
294                 }
295 
296                 String x() {
297                     return x;
298                 }
299 
300                 String y() {
301                     return y;
302                 }
303 
304                 String z() {
305                     return z;
306                 }
307             }
308 
309             final String z = String.valueOf(new Object().hashCode());
310 
311             final Sub[] subs = new Sub[] {
312                 new Sub(z, 1),
313                 new Sub(z, -1),
314                 new Sub(z, (float)0),
315                 new Sub(z, (byte)0),
316                 new Sub(z, (char)0)
317             };
318 
319             for (int i = 0; i < subs.length; i++) {
320                 //System.err.println("i = " + i);
321                 final Sub sub = subs[i];
322                 final String subx = sub.x();
323                 final String suby = sub.y();
324                 final String subz = sub.z();
325                 if (!x.equals(subx))
326                     throw new RuntimeException("x=" + x + " but sub[" + i + "].x()=" + subx);
327                 if (!y.equals(suby))
328                     throw new RuntimeException("y=" + y + " but sub[" + i + "].y()=" + suby);
329                 if (!z.equals(subz))
330                     throw new RuntimeException("z=" + z + " but sub[" + i + "].z()=" + subz);
331             }
332         }
333     }
334 
335     // Records
336     public value class Test17 {
337 
338         record Rectangle(float length, float width) { }
339 
340         record StringHolder(String string) {
341             StringHolder {
342                 java.util.Objects.requireNonNull(string);
343             }
344         }
345 
346         record ValueHolder(int value) {
347             ValueHolder(float x) {
348                 if (Float.isNaN(x))
349                     throw new IllegalArgumentException();
350                 this((int)x);
351             }
352         }
353     }
354 
355     static abstract value class AR<V> implements java.io.Serializable {
356         public AR(V initialValue) {
357         }
358 
359         public AR() {
360         }
361 
362         public final V get() {
363             return null;
364         }
365     }
366 
367     // super()/this() within outer try block but inside inner class
368     public static value class Test19 {
369         public Test19(int x) {
370             try {
371                 new Test1(x) {
372                     @Override
373                     public int hashCode() {
374                         return x ^ super.hashCode();
375                     }
376                 };
377             } catch (StackOverflowError e) {
378                 // ignore
379             }
380         }
381     }
382 
383     public static value class Test20 {
384         private final int[] data1 = new int[10];
385         private final int[] data2 = new int[10];
386         private final int[] data3 = new int[10];
387         Test20() {
388             for (int i = 0; i < data1.length; i++) {
389                 data1[i] = i; // OK we are assigning to an array component
390                 this.data2[i] = i; // OK we are assigning to an array component
391                 Test20.this.data3[i] = i; // OK we are assigning to an array component
392             }
393         }
394     }
395 
396     public static void main(String[] args) {
397         new Test0();
398         new Test1() {};
399         new Test1(7) {};
400         new Test2<Byte>() {};
401         new Test2<>(args) {};
402         new Test3();
403         new ValueClassSuperInitGood(3).new Test5(3) {};
404         new ValueClassSuperInitGood(3).new Test6();
405         ValueClassSuperInitGood.test7("foo");
406         ValueClassSuperInitGood.test8();
407         new Test9(new ValueClassSuperInitGood(5), "abc");
408         new Test10(7) {};
409         new Test11(9);
410         new Test12();
411         new Test13();
412         new Test15c(new Test15("foo"){}, "bar");
413         new Test16().run();
414         new Test17.StringHolder("foo");
415         try {
416             new Test17.StringHolder(null);
417             throw new Error();
418         } catch (NullPointerException e) {
419             // expected
420         }
421         new Test19(123);
422         new Test20();
423     }
424 }